Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Full implementation of webkitNotifications API for Firefox 22+.
// ==UserScript==
// @name webkitNotifications shim for Firefox 22+
// @namespace https://github.com/Rob--w/
// @author Rob Wu <gwnRob@gmail.com>
// @description Enable desktop notifications on Firefox 22+ for websites which use the webkitNotification API.
// @include *
// @version 1.0.2
// @grant none
// ==/UserScript==
/**
* (c) 2013 Rob Wu <gwnRob@gmail.com>
* License: WTFPL
*
* This shim fully implements the legacy webkitNotifications API
* Usable as a library or user script.
*
* References:
* - http://www.chromium.org/developers/design-documents/desktop-notifications/api-specification
* - http://notifications.spec.whatwg.org/
* - chromium/src/third_party/WebKit/Source/modules/notifications/Notification.h
* - chromium/src/third_party/WebKit/Source/modules/notifications/Notification.idl
*/
(function(global) {
'use strict';
if (global.webkitNotifications || typeof Notification == 'undefined') return;
global.webkitNotifications = {
createNotification: createNotification,
PERMISSION_ALLOWED: 0,
PERMISSION_NOT_ALLOWED: 1,
PERMISSION_DENIED: 2,
checkPermission: function() { return permission; },
requestPermission: requestPermission
};
var permission = 1; // Default permission = "Not allowed"
function createNotification(iconURL, title, text) {
var notificationOptions = {};
if (text) notificationOptions.body = text;
if (iconURL) notificationOptions.icon = iconURL;
notificationOptions.title = title || '';
var notification;
// List of events which we want to expose
var eventNames = [
'onshow',
'onerror',
'onclose',
'onclick'
];
// Object which holds the DOM0 event listener until the notification is shown.
var eventTarget = {};
// An implementation of the webkitNotification interface
var webkitNotification = {
show: function show() {
if (notification) return;
notification = new Notification(title, notificationOptions);
// Call delayed addEventListener, removeEventListener, ...
var o;
while( (o = replayQueue.shift()) ) {
notification[o.methodName].apply(notification, o.arguments);
}
// Synchronize DOM0 events
eventNames.forEach(function(eventName) {
notification[eventName] = eventTarget[eventName];
});
eventTarget = notification;
},
cancel: function cancel() {
if (notification) notification.close();
},
// Not in spec, but present in Chrome. Included for consistency.
close: function close() {
if (notification) notification.close();
}
};
// Implement EventTarget on webkitNotification
var replayQueue = [];
[
'addEventListener',
'removeEventListener',
'dispatchEvent'
].forEach(function(methodName) {
webkitNotification[methodName] = function() {
// Call method directly on the notification object, if available.
if (notification) return notification[methodName].apply(notification, arguments);
// Notification not yet visible, queue the call for later use.
replayQueue.push({methodName: methodName, arguments: arguments});
};
});
// Implement DOM0 event binding.
eventNames.forEach(function(eventName) {
defProp(eventName, {
get: function() { return eventTarget[eventName] || null; },
set: function(f) { eventTarget[eventName] = typeof f == 'function' ? f : null; }
});
});
// Alias ondisplay <> onshow
defProp('ondisplay', Object.getOwnPropertyDescriptor(webkitNotification, 'onshow'));
// dir and tag setters. Only relevant before the show() method has been called.
['dir', 'tag'].forEach(function(propertyName) {
defProp(propertyName, {
get: function() { return notificationOptions[propertyName] || ''; },
set: function(value) { notificationOptions[propertyName] = value; }
});
});
// Alias replaceId <> tag
defProp('replaceid', Object.getOwnPropertyDescriptor(webkitNotification, 'tag'));
// Shorthand for defining properties on webkitNotification
function defProp(propertyName, descriptor) {
descriptor.enumerable = true;
Object.defineProperty(webkitNotification, propertyName, descriptor);
}
return webkitNotification;
}
function requestPermission(callback) {
Notification.requestPermission(function(p) {
permission = p == 'granted' ? 0 : p == 'denied' ? 2 : /*'default*/ 1;
if (callback) {
callback();
}
});
}
})(typeof unsafeWindow == 'undefined' ? this : unsafeWindow);
@gigaherz

This comment has been minimized.

Copy link

commented Jul 13, 2013

This seems to have an issue: requestPermission can take a callback function for when the user closes the request dialog. I fixed it as:

function requestPermission(cb) {
    Notification.requestPermission(function(p) {
        permission = p == 'granted' ? 0 : p == 'denied' ? 2 : /*'default*/ 1;
        if(cb)
            cb();
    });
}
@fastcat

This comment has been minimized.

Copy link

commented Sep 11, 2013

This is working for me for google calendar, but not gmail. Every time I load gmail, it offers to enable notifications, and I click enable. The first time I got a popup to confirm the permissions, but no more. gmail never shows me any notifications, and I don't get any errors in the error console.

@Eralph

This comment has been minimized.

Copy link

commented Sep 27, 2013

I got this working in my end. The only problem is that when you refresh the browser, it seems to discard the permission that you have chosen. Any thoughts on this one?

BTW, awesome job in this one!

@adambom

This comment has been minimized.

Copy link

commented Dec 2, 2013

@Eraiph fixed in my fork https://gist.github.com/adambom/7755821

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.