gecko-dev/services/common/observers.js
Kris Maglione e930b89c34 Bug 1514594: Part 3 - Change ChromeUtils.import API.
***
Bug 1514594: Part 3a - Change ChromeUtils.import to return an exports object; not pollute global. r=mccr8

This changes the behavior of ChromeUtils.import() to return an exports object,
rather than a module global, in all cases except when `null` is passed as a
second argument, and changes the default behavior not to pollute the global
scope with the module's exports. Thus, the following code written for the old
model:

  ChromeUtils.import("resource://gre/modules/Services.jsm");

is approximately the same as the following, in the new model:

  var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");

Since the two behaviors are mutually incompatible, this patch will land with a
scripted rewrite to update all existing callers to use the new model rather
than the old.
***
Bug 1514594: Part 3b - Mass rewrite all JS code to use the new ChromeUtils.import API. rs=Gijs

This was done using the followng script:

https://bitbucket.org/kmaglione/m-c-rewrites/src/tip/processors/cu-import-exports.jsm
***
Bug 1514594: Part 3c - Update ESLint plugin for ChromeUtils.import API changes. r=Standard8

Differential Revision: https://phabricator.services.mozilla.com/D16747
***
Bug 1514594: Part 3d - Remove/fix hundreds of duplicate imports from sync tests. r=Gijs

Differential Revision: https://phabricator.services.mozilla.com/D16748
***
Bug 1514594: Part 3e - Remove no-op ChromeUtils.import() calls. r=Gijs

Differential Revision: https://phabricator.services.mozilla.com/D16749
***
Bug 1514594: Part 3f.1 - Cleanup various test corner cases after mass rewrite. r=Gijs
***
Bug 1514594: Part 3f.2 - Cleanup various non-test corner cases after mass rewrite. r=Gijs

Differential Revision: https://phabricator.services.mozilla.com/D16750

--HG--
extra : rebase_source : 359574ee3064c90f33bf36c2ebe3159a24cc8895
extra : histedit_source : b93c8f42808b1599f9122d7842d2c0b3e656a594%2C64a3a4e3359dc889e2ab2b49461bab9e27fc10a7
2019-01-17 10:18:31 -08:00

144 lines
5.1 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/. */
var EXPORTED_SYMBOLS = ["Observers"];
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
/**
* A service for adding, removing and notifying observers of notifications.
* Wraps the nsIObserverService interface.
*
* @version 0.2
*/
var Observers = {
/**
* 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 thisObject {Object} [optional]
* the object to use as |this| when calling a Function callback
*
* @returns the observer
*/
add(topic, callback, thisObject) {
let observer = new Observer(topic, callback, thisObject);
this._cache.push(observer);
Services.obs.addObserver(observer, topic, true);
return observer;
},
/**
* 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 thisObject {Object} [optional]
* the object being used as |this| when calling a Function callback
*/
remove(topic, callback, thisObject) {
// This seems fairly inefficient, but I'm not sure how much better
// we can make it. We could index by topic, but we can't index by callback
// or thisObject, as far as I know, since the keys to JavaScript hashes
// (a.k.a. objects) can apparently only be primitive values.
let [observer] = this._cache.filter(v => v.topic == topic &&
v.callback == callback &&
v.thisObject == thisObject);
if (observer) {
Services.obs.removeObserver(observer, topic);
this._cache.splice(this._cache.indexOf(observer), 1);
} else {
throw new Error("Attempt to remove non-existing observer");
}
},
/**
* 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 })
*/
notify(topic, subject, data) {
subject = (typeof subject == "undefined") ? null : new Subject(subject);
data = (typeof data == "undefined") ? null : data;
Services.obs.notifyObservers(subject, topic, data);
},
/**
* A cache of observers that have been added.
*
* We use this to remove observers when a caller calls |remove|.
*
* XXX This might result in reference cycles, causing memory leaks,
* if we hold a reference to an observer that holds a reference to us.
* Could we fix that by making this an independent top-level object
* rather than a property of this object?
*/
_cache: [],
};
function Observer(topic, callback, thisObject) {
this.topic = topic;
this.callback = callback;
this.thisObject = thisObject;
}
Observer.prototype = {
QueryInterface: ChromeUtils.generateQI([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
observe(subject, topic, data) {
// Extract the wrapped object for subjects that are one of our wrappers
// around a JS object. This way we support both wrapped subjects created
// using this module and those that are real XPCOM components.
if (subject && typeof subject == "object" &&
("wrappedJSObject" in subject) &&
("observersModuleSubjectWrapper" in subject.wrappedJSObject))
subject = subject.wrappedJSObject.object;
if (typeof this.callback == "function") {
if (this.thisObject)
this.callback.call(this.thisObject, subject, data);
else
this.callback(subject, data);
} else // typeof this.callback == "object" (nsIObserver)
this.callback.observe(subject, topic, data);
},
};
function Subject(object) {
// Double-wrap the object and set a property identifying the wrappedJSObject
// as one of our wrappers to distinguish between subjects that are one of our
// wrappers (which we should unwrap when notifying our observers) and those
// that are real JS XPCOM components (which we should pass through unaltered).
this.wrappedJSObject = { observersModuleSubjectWrapper: true, object };
}
Subject.prototype = {
QueryInterface: ChromeUtils.generateQI([]),
getScriptableHelper() {},
getInterfaces() {},
};