Skip to content

Instantly share code, notes, and snippets.

@adam-p
Last active December 22, 2015 09:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save adam-p/6450414 to your computer and use it in GitHub Desktop.
Save adam-p/6450414 to your computer and use it in GitHub Desktop.
Code to show the native Firefox per-tab notification on all tabs, and have the notification be cleared from all tabs when the user takes action on one of them. (If you have the Firefox "Browser Console" enabled, you can paste this code directly into it. (Tested in Aurora 25.))
// This function is from https://github.com/adam-p/markdown-here/blob/3fca89d704d431dc55e78c68b1afae762794c7e7/src/firefox/chrome/content/ff-overlay.js#L382
/*
* doFunction will be passed a [browser](https://developer.mozilla.org/en-US/docs/XUL/browser)
* (which is approximately analogous to a tab)
* and a [tabbrowser](https://developer.mozilla.org/en-US/docs/XUL/tabbrowser)
* (which is approximately analogous to the window containing the tab)
* for each open tab. browser.contentDocument can be used to access the page's
* document object.
*/
function forAllTabsDo(doFunction) {
// Tab enumerating code from: https://developer.mozilla.org/en-US/docs/Code_snippets/Tabbed_browser#Reusing_tabs
var windowMediator = Components.classes['@mozilla.org/appshell/window-mediator;1']
.getService(Components.interfaces.nsIWindowMediator);
var isNormalTab = function(browser) {
// Someday we might want to make this smarter or optional (maybe the caller
// wants to enumerate `about:` and `resource:` tabs?), but for now we'll
// restrict it to normal web page tabs by looking for http:// and https://
if (!browser.currentURI.spec.match(/^https?:\/\//i)) {
return false;
}
// Tabs that haven't loaded properly seem to have a null body.
if (!browser.contentDocument || !browser.contentDocument.body) {
return false;
}
return true;
};
// Iterate through all browser windows...
var browserEnumerator = windowMediator.getEnumerator("navigator:browser");
while (browserEnumerator.hasMoreElements()) {
var browserWin = browserEnumerator.getNext();
var tabbrowser = browserWin.gBrowser;
// ...and through all tabs in the windows
var numTabs = tabbrowser.browsers.length;
for (var index = 0; index < numTabs; index++) {
var browser = tabbrowser.getBrowserAtIndex(index);
if (isNormalTab(browser)) {
// Do the per-tab work
doFunction(browser, tabbrowser);
}
}
}
}
// Documentation:
// appendNotification: https://developer.mozilla.org/en-US/docs/XUL/Method/appendNotification
// removeNotification: https://developer.mozilla.org/en-US/docs/XUL/Method/removeNotification
// getNotificiationBox: http://mdn.beonex.com/en/XUL/Method/getNotificationBox.html
// getNotificationWithValue: https://developer.mozilla.org/en-US/docs/XUL/Method/getNotificationWithValue
var NOTIFICATION_VALUE = 'markdown-here-updated-notification';
var globalNotificationClearNeeded = false;
function showNotificationOnTab(browser, tabbrowser) {
var nb = tabbrowser.getNotificationBox(browser);
var notification = nb.getNotificationWithValue(NOTIFICATION_VALUE);
if (notification) {
// This tab already has our notification.
return;
}
var buttons = [
{
accessKey: 'v',
label: 'View changes',
popup: null, // "the id of a popup for the button" (presumably defined in you XUL); null means no popup
callback: function(notification, button) {
// Fires when the button is clicked.
console.log(notification); console.log(button);
if (globalNotificationClearNeeded) {
globalNotificationClearNeeded = false;
forAllTabsDo(clearNotificationFromTab);
}
// Do something useful, like show the options+changelist page.
}
}];
notification = nb.appendNotification(
'Markdown Here has been updated',
NOTIFICATION_VALUE,
'resource://markdown_here_common/images/icon16.png',
nb.PRIORITY_INFO_LOW,
buttons,
function(eventName) {
// Per the documentation, the only possible value for `eventName` is "removed".
// The callback fires when the user dismisses the notification.
if (globalNotificationClearNeeded) {
globalNotificationClearNeeded = false;
forAllTabsDo(clearNotificationFromTab);
}
});
}
function clearNotificationFromTab(browser, tabbrowser) {
var nb = tabbrowser.getNotificationBox(browser);
var notification = nb.getNotificationWithValue(NOTIFICATION_VALUE);
if (notification) {
nb.removeNotification(notification);
}
}
forAllTabsDo(showNotificationOnTab);
globalNotificationClearNeeded = true;

This was one of the ways I considered showing the "Markdown Here has been updated" notification to users (to fix this issue). I ended up going with an in-page notification. Both are pretty hack-ish for what I wanted to do, since an extension updating is a browser-level event, so showing per-page notifications is... dirty.

Note that one shortcoming in this code is new tabs won't show the notification -- you'll either have to capture new tab events, or use setInterval to keep showing the tab on all tabs until the user clears it. This is particularly relevant to the browser-extension-updated notifications, since they will appear when the browser first opens, and the user might not have restored any tabs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment