135 lines
3.9 KiB
JavaScript
135 lines
3.9 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
"use strict";
|
|
|
|
module.metadata = {
|
|
"stability": "deprecated"
|
|
};
|
|
|
|
const { Cc, Ci } = require("chrome");
|
|
const { when: unload } = require("../system/unload");
|
|
const { ns } = require("../core/namespace");
|
|
const { on, off, emit, once } = require("../system/events");
|
|
const { id } = require("../self");
|
|
|
|
const subscribers = ns();
|
|
const cache = [];
|
|
|
|
/**
|
|
* Topics specifically available to Jetpack-generated extensions.
|
|
*
|
|
* Using these predefined consts instead of the platform strings is good:
|
|
* - allows us to scope topics specifically for Jetpacks
|
|
* - addons aren't dependent on strings nor behavior of core platform topics
|
|
* - the core platform topics are not clearly named
|
|
*
|
|
*/
|
|
exports.topics = {
|
|
/**
|
|
* A topic indicating that the application is in a state usable
|
|
* by add-ons.
|
|
*/
|
|
APPLICATION_READY: id + "_APPLICATION_READY"
|
|
};
|
|
|
|
function Listener(callback, target) {
|
|
return function listener({ subject, data }) {
|
|
callback.call(target || callback, subject, data);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Register the given callback as an observer of the given topic.
|
|
*
|
|
* @param topic {String}
|
|
* the topic to observe
|
|
*
|
|
* @param callback {Object}
|
|
* the callback; an Object that implements nsIObserver or a Function
|
|
* that gets called when the notification occurs
|
|
*
|
|
* @param target {Object} [optional]
|
|
* the object to use as |this| when calling a Function callback
|
|
*
|
|
* @returns the observer
|
|
*/
|
|
function add(topic, callback, target) {
|
|
let listeners = subscribers(callback);
|
|
if (!(topic in listeners)) {
|
|
let listener = Listener(callback, target);
|
|
listeners[topic] = listener;
|
|
|
|
// Cache callback unless it's already cached.
|
|
if (!~cache.indexOf(callback))
|
|
cache.push(callback);
|
|
|
|
on(topic, listener);
|
|
}
|
|
};
|
|
exports.add = add;
|
|
|
|
/**
|
|
* Unregister the given callback as an observer of the given topic.
|
|
*
|
|
* @param topic {String}
|
|
* the topic being observed
|
|
*
|
|
* @param callback {Object}
|
|
* the callback doing the observing
|
|
*
|
|
* @param target {Object} [optional]
|
|
* the object being used as |this| when calling a Function callback
|
|
*/
|
|
function remove(topic, callback, target) {
|
|
let listeners = subscribers(callback);
|
|
if (topic in listeners) {
|
|
let listener = listeners[topic];
|
|
delete listeners[topic];
|
|
|
|
// If no more observers are registered and callback is still in cache
|
|
// then remove it.
|
|
let index = cache.indexOf(callback);
|
|
if (~index && !Object.keys(listeners).length)
|
|
cache.splice(index, 1)
|
|
|
|
off(topic, listener);
|
|
}
|
|
};
|
|
exports.remove = remove;
|
|
|
|
/**
|
|
* Notify observers about something.
|
|
*
|
|
* @param topic {String}
|
|
* the topic to notify observers about
|
|
*
|
|
* @param subject {Object} [optional]
|
|
* some information about the topic; can be any JS object or primitive
|
|
*
|
|
* @param data {String} [optional] [deprecated]
|
|
* some more information about the topic; deprecated as the subject
|
|
* is sufficient to pass all needed information to the JS observers
|
|
* that this module targets; if you have multiple values to pass to
|
|
* the observer, wrap them in an object and pass them via the subject
|
|
* parameter (i.e.: { foo: 1, bar: "some string", baz: myObject })
|
|
*/
|
|
function notify(topic, subject, data) {
|
|
emit(topic, {
|
|
subject: subject === undefined ? null : subject,
|
|
data: data === undefined ? null : data
|
|
});
|
|
}
|
|
exports.notify = notify;
|
|
|
|
unload(function() {
|
|
// Make a copy of cache first, since cache will be changing as we
|
|
// iterate through it.
|
|
cache.slice().forEach(function(callback) {
|
|
Object.keys(subscribers(callback)).forEach(function(topic) {
|
|
remove(topic, callback);
|
|
});
|
|
});
|
|
})
|