mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-14 13:55:43 +00:00
Bug 1471102: Move more code out of ExtensionUtils.jsm. r=aswan
MozReview-Commit-ID: Fqlv5BRuuW8 --HG-- extra : rebase_source : 348f037abd9cecfa080183bc365e5f005eac1bd6 extra : amend_source : 05dbfd12f553fc3f2a93374402e34d271e26d767
This commit is contained in:
parent
766cc497e9
commit
fcedebb912
@ -21,6 +21,7 @@ var EXPORTED_SYMBOLS = ["ExtensionControlledPopup"];
|
||||
|
||||
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/ExtensionUtils.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/ExtensionCommon.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
ChromeUtils.defineModuleGetter(this, "AddonManager",
|
||||
@ -32,7 +33,9 @@ ChromeUtils.defineModuleGetter(this, "CustomizableUI",
|
||||
ChromeUtils.defineModuleGetter(this, "ExtensionSettingsStore",
|
||||
"resource://gre/modules/ExtensionSettingsStore.jsm");
|
||||
|
||||
let {makeWidgetId} = ExtensionUtils;
|
||||
let {
|
||||
makeWidgetId,
|
||||
} = ExtensionCommon;
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "strBundle", function() {
|
||||
return Services.strings.createBundle("chrome://global/locale/extensions.properties");
|
||||
|
@ -18,14 +18,18 @@ ChromeUtils.defineModuleGetter(this, "setTimeout",
|
||||
"resource://gre/modules/Timer.jsm");
|
||||
|
||||
ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/ExtensionCommon.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/ExtensionUtils.jsm");
|
||||
|
||||
var {
|
||||
DefaultWeakMap,
|
||||
makeWidgetId,
|
||||
promiseEvent,
|
||||
} = ExtensionUtils;
|
||||
|
||||
const {
|
||||
makeWidgetId,
|
||||
} = ExtensionCommon;
|
||||
|
||||
|
||||
const POPUP_LOAD_TIMEOUT_MS = 200;
|
||||
|
||||
|
@ -18,7 +18,7 @@ var {
|
||||
* @param {string} panelOptions.id
|
||||
* The id of the addon devtools panel registered in the main process.
|
||||
*/
|
||||
class ChildDevToolsPanel extends ExtensionUtils.EventEmitter {
|
||||
class ChildDevToolsPanel extends ExtensionCommon.EventEmitter {
|
||||
constructor(context, {id}) {
|
||||
super();
|
||||
|
||||
@ -144,7 +144,7 @@ class ChildDevToolsPanel extends ExtensionUtils.EventEmitter {
|
||||
* @param {string} sidebarOptions.id
|
||||
* The id of the addon devtools sidebar registered in the main process.
|
||||
*/
|
||||
class ChildDevToolsInspectorSidebar extends ExtensionUtils.EventEmitter {
|
||||
class ChildDevToolsInspectorSidebar extends ExtensionCommon.EventEmitter {
|
||||
constructor(context, {id}) {
|
||||
super();
|
||||
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
var {
|
||||
withHandlingUserInput,
|
||||
} = ExtensionUtils;
|
||||
} = ExtensionCommon;
|
||||
|
||||
// If id is not specified for an item we use an integer.
|
||||
// This ID need only be unique within a single addon. Since all addon code that
|
||||
|
@ -13,9 +13,12 @@ ChromeUtils.defineModuleGetter(this, "BrowserWindowTracker",
|
||||
|
||||
var {
|
||||
ExtensionError,
|
||||
defineLazyGetter,
|
||||
} = ExtensionUtils;
|
||||
|
||||
var {
|
||||
defineLazyGetter,
|
||||
} = ExtensionCommon;
|
||||
|
||||
const READER_MODE_PREFIX = "about:reader";
|
||||
|
||||
let tabTracker;
|
||||
@ -31,8 +34,8 @@ const getSender = (extension, target, sender) => {
|
||||
// page-open listener below).
|
||||
tabId = sender.tabId;
|
||||
delete sender.tabId;
|
||||
} else if (ExtensionUtils.instanceOf(target, "XULElement") ||
|
||||
ExtensionUtils.instanceOf(target, "HTMLIFrameElement")) {
|
||||
} else if (ExtensionCommon.instanceOf(target, "XULElement") ||
|
||||
ExtensionCommon.instanceOf(target, "HTMLIFrameElement")) {
|
||||
tabId = tabTracker.getBrowserData(target).tabId;
|
||||
}
|
||||
|
||||
|
@ -9,7 +9,7 @@ ChromeUtils.defineModuleGetter(this, "Services",
|
||||
|
||||
var {
|
||||
normalizeTime,
|
||||
} = ExtensionUtils;
|
||||
} = ExtensionCommon;
|
||||
|
||||
let nsINavHistoryService = Ci.nsINavHistoryService;
|
||||
const TRANSITION_TO_TRANSITION_TYPES_MAP = new Map([
|
||||
|
@ -6,8 +6,8 @@ ChromeUtils.defineModuleGetter(this, "PlacesTestUtils",
|
||||
"resource://testing-common/PlacesTestUtils.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "PlacesUtils",
|
||||
"resource://gre/modules/PlacesUtils.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "ExtensionUtils",
|
||||
"resource://gre/modules/ExtensionUtils.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "ExtensionCommon",
|
||||
"resource://gre/modules/ExtensionCommon.jsm");
|
||||
|
||||
ChromeUtils.import("resource://testing-common/PromiseTestUtils.jsm");
|
||||
|
||||
@ -308,7 +308,7 @@ add_task(async function test_add_url() {
|
||||
equal(results.result.title, results.details.title, "URL was added with the correct title");
|
||||
if (results.details.visitTime) {
|
||||
equal(results.result.lastVisitTime,
|
||||
Number(ExtensionUtils.normalizeTime(results.details.visitTime)),
|
||||
Number(ExtensionCommon.normalizeTime(results.details.visitTime)),
|
||||
"URL was added with the correct date");
|
||||
}
|
||||
}
|
||||
|
@ -9,14 +9,18 @@ ChromeUtils.defineModuleGetter(this, "PrivateBrowsingUtils",
|
||||
/* globals EventDispatcher */
|
||||
ChromeUtils.import("resource://gre/modules/Messaging.jsm");
|
||||
|
||||
ChromeUtils.import("resource://gre/modules/ExtensionCommon.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/ExtensionUtils.jsm");
|
||||
|
||||
var {
|
||||
DefaultWeakMap,
|
||||
ExtensionError,
|
||||
defineLazyGetter,
|
||||
} = ExtensionUtils;
|
||||
|
||||
var {
|
||||
defineLazyGetter,
|
||||
} = ExtensionCommon;
|
||||
|
||||
global.GlobalEventDispatcher = EventDispatcher.instance;
|
||||
|
||||
const BrowserStatusFilter = Components.Constructor(
|
||||
|
@ -42,7 +42,6 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
AppConstants: "resource://gre/modules/AppConstants.jsm",
|
||||
AsyncShutdown: "resource://gre/modules/AsyncShutdown.jsm",
|
||||
ContextualIdentityService: "resource://gre/modules/ContextualIdentityService.jsm",
|
||||
ExtensionCommon: "resource://gre/modules/ExtensionCommon.jsm",
|
||||
ExtensionPermissions: "resource://gre/modules/ExtensionPermissions.jsm",
|
||||
ExtensionStorage: "resource://gre/modules/ExtensionStorage.jsm",
|
||||
ExtensionTestCommon: "resource://testing-common/ExtensionTestCommon.jsm",
|
||||
@ -77,6 +76,7 @@ XPCOMUtils.defineLazyGetter(
|
||||
() => Services.io.getProtocolHandler("resource")
|
||||
.QueryInterface(Ci.nsIResProtocolHandler));
|
||||
|
||||
ChromeUtils.import("resource://gre/modules/ExtensionCommon.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/ExtensionParent.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/ExtensionUtils.jsm");
|
||||
|
||||
@ -96,12 +96,15 @@ var {
|
||||
} = ExtensionParent;
|
||||
|
||||
const {
|
||||
EventEmitter,
|
||||
getUniqueId,
|
||||
promiseTimeout,
|
||||
} = ExtensionUtils;
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "console", ExtensionUtils.getConsole);
|
||||
const {
|
||||
EventEmitter,
|
||||
} = ExtensionCommon;
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "console", ExtensionCommon.getConsole);
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "LocaleData", () => ExtensionCommon.LocaleData);
|
||||
|
||||
@ -1430,7 +1433,7 @@ class Extension extends ExtensionData {
|
||||
return true;
|
||||
}
|
||||
|
||||
return ExtensionUtils.checkLoadURL(url, this.principal, options);
|
||||
return ExtensionCommon.checkLoadURL(url, this.principal, options);
|
||||
}
|
||||
|
||||
async promiseLocales(locale) {
|
||||
|
@ -42,21 +42,21 @@ ChromeUtils.import("resource://gre/modules/ExtensionUtils.jsm");
|
||||
|
||||
const {
|
||||
DefaultMap,
|
||||
EventEmitter,
|
||||
LimitedSet,
|
||||
defineLazyGetter,
|
||||
getMessageManager,
|
||||
getUniqueId,
|
||||
getWinUtils,
|
||||
withHandlingUserInput,
|
||||
} = ExtensionUtils;
|
||||
|
||||
const {
|
||||
EventEmitter,
|
||||
EventManager,
|
||||
LocalAPIImplementation,
|
||||
LocaleData,
|
||||
NoCloneSpreadArgs,
|
||||
SchemaAPIInterface,
|
||||
defineLazyGetter,
|
||||
withHandlingUserInput,
|
||||
} = ExtensionCommon;
|
||||
|
||||
const isContentProcess = Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT;
|
||||
|
@ -22,6 +22,7 @@ XPCOMUtils.defineLazyGlobalGetters(this, ["fetch"]);
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
AppConstants: "resource://gre/modules/AppConstants.jsm",
|
||||
ConsoleAPI: "resource://gre/modules/Console.jsm",
|
||||
MessageChannel: "resource://gre/modules/MessageChannel.jsm",
|
||||
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
|
||||
Schemas: "resource://gre/modules/Schemas.jsm",
|
||||
@ -37,16 +38,20 @@ ChromeUtils.import("resource://gre/modules/ExtensionUtils.jsm");
|
||||
var {
|
||||
DefaultMap,
|
||||
DefaultWeakMap,
|
||||
EventEmitter,
|
||||
ExtensionError,
|
||||
defineLazyGetter,
|
||||
filterStack,
|
||||
getConsole,
|
||||
getInnerWindowID,
|
||||
getUniqueId,
|
||||
getWinUtils,
|
||||
} = ExtensionUtils;
|
||||
|
||||
function getConsole() {
|
||||
return new ConsoleAPI({
|
||||
maxLogLevelPref: "extensions.webextensions.log.level",
|
||||
prefix: "WebExtensions",
|
||||
});
|
||||
}
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "console", getConsole);
|
||||
|
||||
XPCOMUtils.defineLazyPreferenceGetter(this, "DELAYED_BG_STARTUP",
|
||||
@ -54,6 +59,121 @@ XPCOMUtils.defineLazyPreferenceGetter(this, "DELAYED_BG_STARTUP",
|
||||
|
||||
var ExtensionCommon;
|
||||
|
||||
// Run a function and report exceptions.
|
||||
function runSafeSyncWithoutClone(f, ...args) {
|
||||
try {
|
||||
return f(...args);
|
||||
} catch (e) {
|
||||
dump(`Extension error: ${e} ${e.fileName} ${e.lineNumber}\n[[Exception stack\n${filterStack(e)}Current stack\n${filterStack(Error())}]]\n`);
|
||||
Cu.reportError(e);
|
||||
}
|
||||
}
|
||||
|
||||
// Return true if the given value is an instance of the given
|
||||
// native type.
|
||||
function instanceOf(value, type) {
|
||||
return (value && typeof value === "object" &&
|
||||
ChromeUtils.getClassName(value) === type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert any of several different representations of a date/time to a Date object.
|
||||
* Accepts several formats:
|
||||
* a Date object, an ISO8601 string, or a number of milliseconds since the epoch as
|
||||
* either a number or a string.
|
||||
*
|
||||
* @param {Date|string|number} date
|
||||
* The date to convert.
|
||||
* @returns {Date}
|
||||
* A Date object
|
||||
*/
|
||||
function normalizeTime(date) {
|
||||
// Of all the formats we accept the "number of milliseconds since the epoch as a string"
|
||||
// is an outlier, everything else can just be passed directly to the Date constructor.
|
||||
return new Date((typeof date == "string" && /^\d+$/.test(date))
|
||||
? parseInt(date, 10) : date);
|
||||
}
|
||||
|
||||
function withHandlingUserInput(window, callable) {
|
||||
let handle = getWinUtils(window).setHandlingUserInput(true);
|
||||
try {
|
||||
return callable();
|
||||
} finally {
|
||||
handle.destruct();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a lazy getter for the given property on the given object. The
|
||||
* first time the property is accessed, the return value of the getter
|
||||
* is defined on the current `this` object with the given property name.
|
||||
* Importantly, this means that a lazy getter defined on an object
|
||||
* prototype will be invoked separately for each object instance that
|
||||
* it's accessed on.
|
||||
*
|
||||
* @param {object} object
|
||||
* The prototype object on which to define the getter.
|
||||
* @param {string|Symbol} prop
|
||||
* The property name for which to define the getter.
|
||||
* @param {function} getter
|
||||
* The function to call in order to generate the final property
|
||||
* value.
|
||||
*/
|
||||
function defineLazyGetter(object, prop, getter) {
|
||||
let redefine = (obj, value) => {
|
||||
Object.defineProperty(obj, prop, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value,
|
||||
});
|
||||
return value;
|
||||
};
|
||||
|
||||
Object.defineProperty(object, prop, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
|
||||
get() {
|
||||
return redefine(this, getter.call(this));
|
||||
},
|
||||
|
||||
set(value) {
|
||||
redefine(this, value);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function checkLoadURL(url, principal, options) {
|
||||
let ssm = Services.scriptSecurityManager;
|
||||
|
||||
let flags = ssm.STANDARD;
|
||||
if (!options.allowScript) {
|
||||
flags |= ssm.DISALLOW_SCRIPT;
|
||||
}
|
||||
if (!options.allowInheritsPrincipal) {
|
||||
flags |= ssm.DISALLOW_INHERIT_PRINCIPAL;
|
||||
}
|
||||
if (options.dontReportErrors) {
|
||||
flags |= ssm.DONT_REPORT_ERRORS;
|
||||
}
|
||||
|
||||
try {
|
||||
ssm.checkLoadURIWithPrincipal(principal,
|
||||
Services.io.newURI(url),
|
||||
flags);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function makeWidgetId(id) {
|
||||
id = id.toLowerCase();
|
||||
// FIXME: This allows for collisions.
|
||||
return id.replace(/[^a-z0-9_-]/g, "_");
|
||||
}
|
||||
|
||||
/**
|
||||
* A sentinel class to indicate that an array of values should be
|
||||
* treated as an array when used as a promise resolution value, but as a
|
||||
@ -85,12 +205,119 @@ class NoCloneSpreadArgs {
|
||||
}
|
||||
}
|
||||
|
||||
const LISTENERS = Symbol("listeners");
|
||||
const ONCE_MAP = Symbol("onceMap");
|
||||
|
||||
class EventEmitter {
|
||||
constructor() {
|
||||
this[LISTENERS] = new Map();
|
||||
this[ONCE_MAP] = new WeakMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given function as a listener for the given event.
|
||||
*
|
||||
* The listener function may optionally return a Promise which
|
||||
* resolves when it has completed all operations which event
|
||||
* dispatchers may need to block on.
|
||||
*
|
||||
* @param {string} event
|
||||
* The name of the event to listen for.
|
||||
* @param {function(string, ...any)} listener
|
||||
* The listener to call when events are emitted.
|
||||
*/
|
||||
on(event, listener) {
|
||||
let listeners = this[LISTENERS].get(event);
|
||||
if (!listeners) {
|
||||
listeners = new Set();
|
||||
this[LISTENERS].set(event, listeners);
|
||||
}
|
||||
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given function as a listener for the given event.
|
||||
*
|
||||
* @param {string} event
|
||||
* The name of the event to stop listening for.
|
||||
* @param {function(string, ...any)} listener
|
||||
* The listener function to remove.
|
||||
*/
|
||||
off(event, listener) {
|
||||
let set = this[LISTENERS].get(event);
|
||||
if (set) {
|
||||
set.delete(listener);
|
||||
set.delete(this[ONCE_MAP].get(listener));
|
||||
if (!set.size) {
|
||||
this[LISTENERS].delete(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given function as a listener for the given event once.
|
||||
*
|
||||
* @param {string} event
|
||||
* The name of the event to listen for.
|
||||
* @param {function(string, ...any)} listener
|
||||
* The listener to call when events are emitted.
|
||||
*/
|
||||
once(event, listener) {
|
||||
let wrapper = (...args) => {
|
||||
this.off(event, wrapper);
|
||||
this[ONCE_MAP].delete(listener);
|
||||
|
||||
return listener(...args);
|
||||
};
|
||||
this[ONCE_MAP].set(listener, wrapper);
|
||||
|
||||
this.on(event, wrapper);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Triggers all listeners for the given event. If any listeners return
|
||||
* a value, returns a promise which resolves when all returned
|
||||
* promises have resolved. Otherwise, returns undefined.
|
||||
*
|
||||
* @param {string} event
|
||||
* The name of the event to emit.
|
||||
* @param {any} args
|
||||
* Arbitrary arguments to pass to the listener functions, after
|
||||
* the event name.
|
||||
* @returns {Promise?}
|
||||
*/
|
||||
emit(event, ...args) {
|
||||
let listeners = this[LISTENERS].get(event);
|
||||
|
||||
if (listeners) {
|
||||
let promises = [];
|
||||
|
||||
for (let listener of listeners) {
|
||||
try {
|
||||
let result = listener(event, ...args);
|
||||
if (result !== undefined) {
|
||||
promises.push(result);
|
||||
}
|
||||
} catch (e) {
|
||||
Cu.reportError(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (promises.length) {
|
||||
return Promise.all(promises);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Base class for WebExtension APIs. Each API creates a new class
|
||||
* that inherits from this class, the derived class is instantiated
|
||||
* once for each extension that uses the API.
|
||||
*/
|
||||
class ExtensionAPI extends ExtensionUtils.EventEmitter {
|
||||
class ExtensionAPI extends EventEmitter {
|
||||
constructor(extension) {
|
||||
super();
|
||||
|
||||
@ -246,7 +473,7 @@ class BaseContext {
|
||||
return true;
|
||||
}
|
||||
|
||||
return ExtensionUtils.checkLoadURL(url, this.principal, options);
|
||||
return checkLoadURL(url, this.principal, options);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2074,14 +2301,23 @@ ExtensionCommon = {
|
||||
CanOfAPIs,
|
||||
EventManager,
|
||||
ExtensionAPI,
|
||||
EventEmitter,
|
||||
LocalAPIImplementation,
|
||||
LocaleData,
|
||||
NoCloneSpreadArgs,
|
||||
SchemaAPIInterface,
|
||||
SchemaAPIManager,
|
||||
SpreadArgs,
|
||||
checkLoadURL,
|
||||
defineLazyGetter,
|
||||
getConsole,
|
||||
ignoreEvent,
|
||||
instanceOf,
|
||||
makeWidgetId,
|
||||
normalizeTime,
|
||||
runSafeSyncWithoutClone,
|
||||
stylesheetMap,
|
||||
withHandlingUserInput,
|
||||
|
||||
MultiAPIManager,
|
||||
LazyAPIManager,
|
||||
|
@ -39,19 +39,19 @@ XPCOMUtils.defineLazyGlobalGetters(this, ["crypto", "TextEncoder"]);
|
||||
const {
|
||||
DefaultMap,
|
||||
DefaultWeakMap,
|
||||
defineLazyGetter,
|
||||
getInnerWindowID,
|
||||
getWinUtils,
|
||||
promiseDocumentIdle,
|
||||
promiseDocumentLoaded,
|
||||
promiseDocumentReady,
|
||||
runSafeSyncWithoutClone,
|
||||
} = ExtensionUtils;
|
||||
|
||||
const {
|
||||
BaseContext,
|
||||
CanOfAPIs,
|
||||
SchemaAPIManager,
|
||||
defineLazyGetter,
|
||||
runSafeSyncWithoutClone,
|
||||
} = ExtensionCommon;
|
||||
|
||||
const {
|
||||
@ -60,7 +60,7 @@ const {
|
||||
Messenger,
|
||||
} = ExtensionChild;
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "console", ExtensionUtils.getConsole);
|
||||
XPCOMUtils.defineLazyGetter(this, "console", ExtensionCommon.getConsole);
|
||||
|
||||
|
||||
var DocumentManager;
|
||||
|
@ -37,7 +37,6 @@ ChromeUtils.import("resource://gre/modules/ExtensionChild.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/ExtensionUtils.jsm");
|
||||
|
||||
const {
|
||||
defineLazyGetter,
|
||||
getInnerWindowID,
|
||||
promiseEvent,
|
||||
} = ExtensionUtils;
|
||||
@ -46,6 +45,7 @@ const {
|
||||
BaseContext,
|
||||
CanOfAPIs,
|
||||
SchemaAPIManager,
|
||||
defineLazyGetter,
|
||||
} = ExtensionCommon;
|
||||
|
||||
const {
|
||||
|
@ -25,8 +25,9 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
E10SUtils: "resource://gre/modules/E10SUtils.jsm",
|
||||
ExtensionData: "resource://gre/modules/Extension.jsm",
|
||||
MessageChannel: "resource://gre/modules/MessageChannel.jsm",
|
||||
OS: "resource://gre/modules/osfile.jsm",
|
||||
MessageManagerProxy: "resource://gre/modules/MessageManagerProxy.jsm",
|
||||
NativeApp: "resource://gre/modules/NativeMessaging.jsm",
|
||||
OS: "resource://gre/modules/osfile.jsm",
|
||||
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
|
||||
Schemas: "resource://gre/modules/Schemas.jsm",
|
||||
});
|
||||
@ -43,14 +44,13 @@ var {
|
||||
CanOfAPIs,
|
||||
SchemaAPIManager,
|
||||
SpreadArgs,
|
||||
defineLazyGetter,
|
||||
} = ExtensionCommon;
|
||||
|
||||
var {
|
||||
DefaultMap,
|
||||
DefaultWeakMap,
|
||||
ExtensionError,
|
||||
MessageManagerProxy,
|
||||
defineLazyGetter,
|
||||
promiseDocumentLoaded,
|
||||
promiseEvent,
|
||||
promiseObserved,
|
||||
|
@ -48,6 +48,7 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
CollectionKeyManager: "resource://services-sync/record.js",
|
||||
CommonUtils: "resource://services-common/utils.js",
|
||||
CryptoUtils: "resource://services-crypto/utils.js",
|
||||
ExtensionCommon: "resource://gre/modules/ExtensionCommon.jsm",
|
||||
fxAccounts: "resource://gre/modules/FxAccounts.jsm",
|
||||
KintoHttpClient: "resource://services-common/kinto-http-client.js",
|
||||
Kinto: "resource://services-common/kinto-offline-client.js",
|
||||
@ -68,7 +69,6 @@ XPCOMUtils.defineLazyGetter(this, "WeaveCrypto", function() {
|
||||
|
||||
const {
|
||||
DefaultMap,
|
||||
runSafeSyncWithoutClone,
|
||||
} = ExtensionUtils;
|
||||
|
||||
// Map of Extensions to Set<Contexts> to track contexts that are still
|
||||
@ -1249,7 +1249,7 @@ class ExtensionStorageSync {
|
||||
let listeners = this.listeners.get(extension) || new Set();
|
||||
if (listeners) {
|
||||
for (let listener of listeners) {
|
||||
runSafeSyncWithoutClone(listener, changes);
|
||||
ExtensionCommon.runSafeSyncWithoutClone(listener, changes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ ChromeUtils.defineModuleGetter(this, "OS",
|
||||
XPCOMUtils.defineLazyGetter(this, "apiManager",
|
||||
() => ExtensionParent.apiManager);
|
||||
|
||||
ChromeUtils.import("resource://gre/modules/ExtensionCommon.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/ExtensionUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyServiceGetter(this, "uuidGen",
|
||||
@ -41,10 +42,13 @@ XPCOMUtils.defineLazyServiceGetter(this, "uuidGen",
|
||||
|
||||
const {
|
||||
flushJarCache,
|
||||
instanceOf,
|
||||
} = ExtensionUtils;
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "console", ExtensionUtils.getConsole);
|
||||
const {
|
||||
instanceOf,
|
||||
} = ExtensionCommon;
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "console", () => ExtensionCommon.getConsole());
|
||||
|
||||
|
||||
/**
|
||||
|
@ -10,20 +10,9 @@ var EXPORTED_SYMBOLS = ["ExtensionUtils"];
|
||||
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
ChromeUtils.defineModuleGetter(this, "ConsoleAPI",
|
||||
"resource://gre/modules/Console.jsm");
|
||||
ChromeUtils.defineModuleGetter(this, "setTimeout",
|
||||
"resource://gre/modules/Timer.jsm");
|
||||
|
||||
function getConsole() {
|
||||
return new ConsoleAPI({
|
||||
maxLogLevelPref: "extensions.webextensions.log.level",
|
||||
prefix: "WebExtensions",
|
||||
});
|
||||
}
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "console", getConsole);
|
||||
|
||||
// xpcshell doesn't handle idle callbacks well.
|
||||
XPCOMUtils.defineLazyGetter(this, "idleTimeout",
|
||||
() => Services.appinfo.name === "XPCShell" ? 500 : undefined);
|
||||
@ -62,23 +51,6 @@ function filterStack(error) {
|
||||
return String(error.stack).replace(/(^.*(Task\.jsm|Promise-backend\.js).*\n)+/gm, "<Promise Chain>\n");
|
||||
}
|
||||
|
||||
// Run a function and report exceptions.
|
||||
function runSafeSyncWithoutClone(f, ...args) {
|
||||
try {
|
||||
return f(...args);
|
||||
} catch (e) {
|
||||
dump(`Extension error: ${e} ${e.fileName} ${e.lineNumber}\n[[Exception stack\n${filterStack(e)}Current stack\n${filterStack(Error())}]]\n`);
|
||||
Cu.reportError(e);
|
||||
}
|
||||
}
|
||||
|
||||
// Return true if the given value is an instance of the given
|
||||
// native type.
|
||||
function instanceOf(value, type) {
|
||||
return (value && typeof value === "object" &&
|
||||
ChromeUtils.getClassName(value) === type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Similar to a WeakMap, but creates a new key with the given
|
||||
* constructor if one is not present.
|
||||
@ -129,122 +101,6 @@ function getInnerWindowID(window) {
|
||||
return getWinUtils(window).currentInnerWindowID;
|
||||
}
|
||||
|
||||
function withHandlingUserInput(window, callable) {
|
||||
let handle = getWinUtils(window).setHandlingUserInput(true);
|
||||
try {
|
||||
return callable();
|
||||
} finally {
|
||||
handle.destruct();
|
||||
}
|
||||
}
|
||||
|
||||
const LISTENERS = Symbol("listeners");
|
||||
const ONCE_MAP = Symbol("onceMap");
|
||||
|
||||
class EventEmitter {
|
||||
constructor() {
|
||||
this[LISTENERS] = new Map();
|
||||
this[ONCE_MAP] = new WeakMap();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given function as a listener for the given event.
|
||||
*
|
||||
* The listener function may optionally return a Promise which
|
||||
* resolves when it has completed all operations which event
|
||||
* dispatchers may need to block on.
|
||||
*
|
||||
* @param {string} event
|
||||
* The name of the event to listen for.
|
||||
* @param {function(string, ...any)} listener
|
||||
* The listener to call when events are emitted.
|
||||
*/
|
||||
on(event, listener) {
|
||||
let listeners = this[LISTENERS].get(event);
|
||||
if (!listeners) {
|
||||
listeners = new Set();
|
||||
this[LISTENERS].set(event, listeners);
|
||||
}
|
||||
|
||||
listeners.add(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the given function as a listener for the given event.
|
||||
*
|
||||
* @param {string} event
|
||||
* The name of the event to stop listening for.
|
||||
* @param {function(string, ...any)} listener
|
||||
* The listener function to remove.
|
||||
*/
|
||||
off(event, listener) {
|
||||
let set = this[LISTENERS].get(event);
|
||||
if (set) {
|
||||
set.delete(listener);
|
||||
set.delete(this[ONCE_MAP].get(listener));
|
||||
if (!set.size) {
|
||||
this[LISTENERS].delete(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the given function as a listener for the given event once.
|
||||
*
|
||||
* @param {string} event
|
||||
* The name of the event to listen for.
|
||||
* @param {function(string, ...any)} listener
|
||||
* The listener to call when events are emitted.
|
||||
*/
|
||||
once(event, listener) {
|
||||
let wrapper = (...args) => {
|
||||
this.off(event, wrapper);
|
||||
this[ONCE_MAP].delete(listener);
|
||||
|
||||
return listener(...args);
|
||||
};
|
||||
this[ONCE_MAP].set(listener, wrapper);
|
||||
|
||||
this.on(event, wrapper);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Triggers all listeners for the given event. If any listeners return
|
||||
* a value, returns a promise which resolves when all returned
|
||||
* promises have resolved. Otherwise, returns undefined.
|
||||
*
|
||||
* @param {string} event
|
||||
* The name of the event to emit.
|
||||
* @param {any} args
|
||||
* Arbitrary arguments to pass to the listener functions, after
|
||||
* the event name.
|
||||
* @returns {Promise?}
|
||||
*/
|
||||
emit(event, ...args) {
|
||||
let listeners = this[LISTENERS].get(event);
|
||||
|
||||
if (listeners) {
|
||||
let promises = [];
|
||||
|
||||
for (let listener of listeners) {
|
||||
try {
|
||||
let result = listener(event, ...args);
|
||||
if (result !== undefined) {
|
||||
promises.push(result);
|
||||
}
|
||||
} catch (e) {
|
||||
Cu.reportError(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (promises.length) {
|
||||
return Promise.all(promises);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A set with a limited number of slots, which flushes older entries as
|
||||
* newer ones are added.
|
||||
@ -404,303 +260,21 @@ function flushJarCache(jarPath) {
|
||||
Services.obs.notifyObservers(null, "flush-cache-entry", jarPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert any of several different representations of a date/time to a Date object.
|
||||
* Accepts several formats:
|
||||
* a Date object, an ISO8601 string, or a number of milliseconds since the epoch as
|
||||
* either a number or a string.
|
||||
*
|
||||
* @param {Date|string|number} date
|
||||
* The date to convert.
|
||||
* @returns {Date}
|
||||
* A Date object
|
||||
*/
|
||||
function normalizeTime(date) {
|
||||
// Of all the formats we accept the "number of milliseconds since the epoch as a string"
|
||||
// is an outlier, everything else can just be passed directly to the Date constructor.
|
||||
return new Date((typeof date == "string" && /^\d+$/.test(date))
|
||||
? parseInt(date, 10) : date);
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a lazy getter for the given property on the given object. The
|
||||
* first time the property is accessed, the return value of the getter
|
||||
* is defined on the current `this` object with the given property name.
|
||||
* Importantly, this means that a lazy getter defined on an object
|
||||
* prototype will be invoked separately for each object instance that
|
||||
* it's accessed on.
|
||||
*
|
||||
* @param {object} object
|
||||
* The prototype object on which to define the getter.
|
||||
* @param {string|Symbol} prop
|
||||
* The property name for which to define the getter.
|
||||
* @param {function} getter
|
||||
* The function to call in order to generate the final property
|
||||
* value.
|
||||
*/
|
||||
function defineLazyGetter(object, prop, getter) {
|
||||
let redefine = (obj, value) => {
|
||||
Object.defineProperty(obj, prop, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value,
|
||||
});
|
||||
return value;
|
||||
};
|
||||
|
||||
Object.defineProperty(object, prop, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
|
||||
get() {
|
||||
return redefine(this, getter.call(this));
|
||||
},
|
||||
|
||||
set(value) {
|
||||
redefine(this, value);
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Acts as a proxy for a message manager or message manager owner, and
|
||||
* tracks docShell swaps so that messages are always sent to the same
|
||||
* receiver, even if it is moved to a different <browser>.
|
||||
*
|
||||
* @param {nsIMessageSender|Element} target
|
||||
* The target message manager on which to send messages, or the
|
||||
* <browser> element which owns it.
|
||||
*/
|
||||
class MessageManagerProxy {
|
||||
constructor(target) {
|
||||
this.listeners = new DefaultMap(() => new Map());
|
||||
this.closed = false;
|
||||
|
||||
if (target instanceof Ci.nsIMessageSender) {
|
||||
this.messageManager = target;
|
||||
} else {
|
||||
this.addListeners(target);
|
||||
}
|
||||
|
||||
Services.obs.addObserver(this, "message-manager-close");
|
||||
}
|
||||
|
||||
/**
|
||||
* Disposes of the proxy object, removes event listeners, and drops
|
||||
* all references to the underlying message manager.
|
||||
*
|
||||
* Must be called before the last reference to the proxy is dropped,
|
||||
* unless the underlying message manager or <browser> is also being
|
||||
* destroyed.
|
||||
*/
|
||||
dispose() {
|
||||
if (this.eventTarget) {
|
||||
this.removeListeners(this.eventTarget);
|
||||
this.eventTarget = null;
|
||||
}
|
||||
this.messageManager = null;
|
||||
|
||||
Services.obs.removeObserver(this, "message-manager-close");
|
||||
}
|
||||
|
||||
observe(subject, topic, data) {
|
||||
if (topic === "message-manager-close") {
|
||||
if (subject === this.messageManager) {
|
||||
this.closed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given target is the same as, or owns, the given
|
||||
* message manager.
|
||||
*
|
||||
* @param {nsIMessageSender|MessageManagerProxy|Element} target
|
||||
* The message manager, MessageManagerProxy, or <browser>
|
||||
* element against which to match.
|
||||
* @param {nsIMessageSender} messageManager
|
||||
* The message manager against which to match `target`.
|
||||
*
|
||||
* @returns {boolean}
|
||||
* True if `messageManager` is the same object as `target`, or
|
||||
* `target` is a MessageManagerProxy or <browser> element that
|
||||
* is tied to it.
|
||||
*/
|
||||
static matches(target, messageManager) {
|
||||
return target === messageManager || target.messageManager === messageManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* @property {nsIMessageSender|null} messageManager
|
||||
* The message manager that is currently being proxied. This
|
||||
* may change during the life of the proxy object, so should
|
||||
* not be stored elsewhere.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Sends a message on the proxied message manager.
|
||||
*
|
||||
* @param {array} args
|
||||
* Arguments to be passed verbatim to the underlying
|
||||
* sendAsyncMessage method.
|
||||
* @returns {undefined}
|
||||
*/
|
||||
sendAsyncMessage(...args) {
|
||||
if (this.messageManager) {
|
||||
return this.messageManager.sendAsyncMessage(...args);
|
||||
}
|
||||
|
||||
Cu.reportError(`Cannot send message: Other side disconnected: ${uneval(args)}`);
|
||||
}
|
||||
|
||||
get isDisconnected() {
|
||||
return this.closed || !this.messageManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a message listener to the current message manager, and
|
||||
* transfers it to the new message manager after a docShell swap.
|
||||
*
|
||||
* @param {string} message
|
||||
* The name of the message to listen for.
|
||||
* @param {nsIMessageListener} listener
|
||||
* The listener to add.
|
||||
* @param {boolean} [listenWhenClosed = false]
|
||||
* If true, the listener will receive messages which were sent
|
||||
* after the remote side of the listener began closing.
|
||||
*/
|
||||
addMessageListener(message, listener, listenWhenClosed = false) {
|
||||
this.messageManager.addMessageListener(message, listener, listenWhenClosed);
|
||||
this.listeners.get(message).set(listener, listenWhenClosed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a message listener from the current message manager.
|
||||
*
|
||||
* @param {string} message
|
||||
* The name of the message to stop listening for.
|
||||
* @param {nsIMessageListener} listener
|
||||
* The listener to remove.
|
||||
*/
|
||||
removeMessageListener(message, listener) {
|
||||
this.messageManager.removeMessageListener(message, listener);
|
||||
|
||||
let listeners = this.listeners.get(message);
|
||||
listeners.delete(listener);
|
||||
if (!listeners.size) {
|
||||
this.listeners.delete(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Iterates over all of the currently registered message listeners.
|
||||
*/
|
||||
* iterListeners() {
|
||||
for (let [message, listeners] of this.listeners) {
|
||||
for (let [listener, listenWhenClosed] of listeners) {
|
||||
yield {message, listener, listenWhenClosed};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Adds docShell swap listeners to the message manager owner.
|
||||
*
|
||||
* @param {Element} target
|
||||
* The target element.
|
||||
*/
|
||||
addListeners(target) {
|
||||
target.addEventListener("SwapDocShells", this);
|
||||
|
||||
this.eventTarget = target;
|
||||
this.messageManager = target.messageManager;
|
||||
|
||||
for (let {message, listener, listenWhenClosed} of this.iterListeners()) {
|
||||
this.messageManager.addMessageListener(message, listener, listenWhenClosed);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Removes docShell swap listeners to the message manager owner.
|
||||
*
|
||||
* @param {Element} target
|
||||
* The target element.
|
||||
*/
|
||||
removeListeners(target) {
|
||||
target.removeEventListener("SwapDocShells", this);
|
||||
|
||||
for (let {message, listener} of this.iterListeners()) {
|
||||
this.messageManager.removeMessageListener(message, listener);
|
||||
}
|
||||
}
|
||||
|
||||
handleEvent(event) {
|
||||
if (event.type == "SwapDocShells") {
|
||||
this.removeListeners(this.eventTarget);
|
||||
this.addListeners(event.detail);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function checkLoadURL(url, principal, options) {
|
||||
let ssm = Services.scriptSecurityManager;
|
||||
|
||||
let flags = ssm.STANDARD;
|
||||
if (!options.allowScript) {
|
||||
flags |= ssm.DISALLOW_SCRIPT;
|
||||
}
|
||||
if (!options.allowInheritsPrincipal) {
|
||||
flags |= ssm.DISALLOW_INHERIT_PRINCIPAL;
|
||||
}
|
||||
if (options.dontReportErrors) {
|
||||
flags |= ssm.DONT_REPORT_ERRORS;
|
||||
}
|
||||
|
||||
try {
|
||||
ssm.checkLoadURIWithPrincipal(principal,
|
||||
Services.io.newURI(url),
|
||||
flags);
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function makeWidgetId(id) {
|
||||
id = id.toLowerCase();
|
||||
// FIXME: This allows for collisions.
|
||||
return id.replace(/[^a-z0-9_-]/g, "_");
|
||||
}
|
||||
|
||||
var ExtensionUtils = {
|
||||
checkLoadURL,
|
||||
defineLazyGetter,
|
||||
flushJarCache,
|
||||
getConsole,
|
||||
getInnerWindowID,
|
||||
getMessageManager,
|
||||
getUniqueId,
|
||||
filterStack,
|
||||
getWinUtils,
|
||||
instanceOf,
|
||||
makeWidgetId,
|
||||
normalizeTime,
|
||||
promiseDocumentIdle,
|
||||
promiseDocumentLoaded,
|
||||
promiseDocumentReady,
|
||||
promiseEvent,
|
||||
promiseObserved,
|
||||
promiseTimeout,
|
||||
runSafeSyncWithoutClone,
|
||||
withHandlingUserInput,
|
||||
DefaultMap,
|
||||
DefaultWeakMap,
|
||||
EventEmitter,
|
||||
ExtensionError,
|
||||
LimitedSet,
|
||||
MessageManagerProxy,
|
||||
};
|
||||
|
@ -105,9 +105,19 @@ ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/ExtensionUtils.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const {
|
||||
MessageManagerProxy,
|
||||
} = ExtensionUtils;
|
||||
ChromeUtils.defineModuleGetter(this, "MessageManagerProxy",
|
||||
"resource://gre/modules/MessageManagerProxy.jsm");
|
||||
|
||||
function getMessageManager(target) {
|
||||
if (typeof target.sendAsyncMessage === "function") {
|
||||
return target;
|
||||
}
|
||||
return new MessageManagerProxy(target);
|
||||
}
|
||||
|
||||
function matches(target, messageManager) {
|
||||
return target === messageManager || target.messageManager === messageManager;
|
||||
}
|
||||
|
||||
const {DEBUG} = AppConstants;
|
||||
|
||||
@ -919,7 +929,7 @@ this.MessageChannel = {
|
||||
return;
|
||||
}
|
||||
|
||||
let target = new MessageManagerProxy(data.target);
|
||||
let target = getMessageManager(data.target);
|
||||
|
||||
let deferred = {
|
||||
sender: data.sender,
|
||||
@ -930,7 +940,9 @@ this.MessageChannel = {
|
||||
|
||||
let cleanup = () => {
|
||||
this.pendingResponses.delete(deferred);
|
||||
target.dispose();
|
||||
if (target.dispose) {
|
||||
target.dispose();
|
||||
}
|
||||
};
|
||||
this.pendingResponses.add(deferred);
|
||||
|
||||
@ -1078,7 +1090,7 @@ this.MessageChannel = {
|
||||
*/
|
||||
abortMessageManager(target, reason) {
|
||||
for (let response of this.pendingResponses) {
|
||||
if (MessageManagerProxy.matches(response.messageManager, target)) {
|
||||
if (matches(response.messageManager, target)) {
|
||||
this.abortedResponses.add(response.channelId);
|
||||
response.reject(reason);
|
||||
}
|
||||
|
198
toolkit/components/extensions/MessageManagerProxy.jsm
Normal file
198
toolkit/components/extensions/MessageManagerProxy.jsm
Normal file
@ -0,0 +1,198 @@
|
||||
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set sts=2 sw=2 et tw=80: */
|
||||
/* 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";
|
||||
|
||||
var EXPORTED_SYMBOLS = ["MessageManagerProxy"];
|
||||
|
||||
ChromeUtils.import("resource://gre/modules/ExtensionUtils.jsm");
|
||||
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
const {
|
||||
DefaultMap,
|
||||
} = ExtensionUtils;
|
||||
|
||||
/**
|
||||
* Acts as a proxy for a message manager or message manager owner, and
|
||||
* tracks docShell swaps so that messages are always sent to the same
|
||||
* receiver, even if it is moved to a different <browser>.
|
||||
*
|
||||
* @param {nsIMessageSender|Element} target
|
||||
* The target message manager on which to send messages, or the
|
||||
* <browser> element which owns it.
|
||||
*/
|
||||
class MessageManagerProxy {
|
||||
constructor(target) {
|
||||
this.listeners = new DefaultMap(() => new Map());
|
||||
this.closed = false;
|
||||
|
||||
if (target instanceof Ci.nsIMessageSender) {
|
||||
this.messageManager = target;
|
||||
} else {
|
||||
this.addListeners(target);
|
||||
}
|
||||
|
||||
Services.obs.addObserver(this, "message-manager-close");
|
||||
}
|
||||
|
||||
/**
|
||||
* Disposes of the proxy object, removes event listeners, and drops
|
||||
* all references to the underlying message manager.
|
||||
*
|
||||
* Must be called before the last reference to the proxy is dropped,
|
||||
* unless the underlying message manager or <browser> is also being
|
||||
* destroyed.
|
||||
*/
|
||||
dispose() {
|
||||
if (this.eventTarget) {
|
||||
this.removeListeners(this.eventTarget);
|
||||
this.eventTarget = null;
|
||||
}
|
||||
this.messageManager = null;
|
||||
|
||||
Services.obs.removeObserver(this, "message-manager-close");
|
||||
}
|
||||
|
||||
observe(subject, topic, data) {
|
||||
if (topic === "message-manager-close") {
|
||||
if (subject === this.messageManager) {
|
||||
this.closed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the given target is the same as, or owns, the given
|
||||
* message manager.
|
||||
*
|
||||
* @param {nsIMessageSender|MessageManagerProxy|Element} target
|
||||
* The message manager, MessageManagerProxy, or <browser>
|
||||
* element against which to match.
|
||||
* @param {nsIMessageSender} messageManager
|
||||
* The message manager against which to match `target`.
|
||||
*
|
||||
* @returns {boolean}
|
||||
* True if `messageManager` is the same object as `target`, or
|
||||
* `target` is a MessageManagerProxy or <browser> element that
|
||||
* is tied to it.
|
||||
*/
|
||||
static matches(target, messageManager) {
|
||||
return target === messageManager || target.messageManager === messageManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* @property {nsIMessageSender|null} messageManager
|
||||
* The message manager that is currently being proxied. This
|
||||
* may change during the life of the proxy object, so should
|
||||
* not be stored elsewhere.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Sends a message on the proxied message manager.
|
||||
*
|
||||
* @param {array} args
|
||||
* Arguments to be passed verbatim to the underlying
|
||||
* sendAsyncMessage method.
|
||||
* @returns {undefined}
|
||||
*/
|
||||
sendAsyncMessage(...args) {
|
||||
if (this.messageManager) {
|
||||
return this.messageManager.sendAsyncMessage(...args);
|
||||
}
|
||||
|
||||
Cu.reportError(`Cannot send message: Other side disconnected: ${uneval(args)}`);
|
||||
}
|
||||
|
||||
get isDisconnected() {
|
||||
return this.closed || !this.messageManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a message listener to the current message manager, and
|
||||
* transfers it to the new message manager after a docShell swap.
|
||||
*
|
||||
* @param {string} message
|
||||
* The name of the message to listen for.
|
||||
* @param {nsIMessageListener} listener
|
||||
* The listener to add.
|
||||
* @param {boolean} [listenWhenClosed = false]
|
||||
* If true, the listener will receive messages which were sent
|
||||
* after the remote side of the listener began closing.
|
||||
*/
|
||||
addMessageListener(message, listener, listenWhenClosed = false) {
|
||||
this.messageManager.addMessageListener(message, listener, listenWhenClosed);
|
||||
this.listeners.get(message).set(listener, listenWhenClosed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a message listener from the current message manager.
|
||||
*
|
||||
* @param {string} message
|
||||
* The name of the message to stop listening for.
|
||||
* @param {nsIMessageListener} listener
|
||||
* The listener to remove.
|
||||
*/
|
||||
removeMessageListener(message, listener) {
|
||||
this.messageManager.removeMessageListener(message, listener);
|
||||
|
||||
let listeners = this.listeners.get(message);
|
||||
listeners.delete(listener);
|
||||
if (!listeners.size) {
|
||||
this.listeners.delete(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Iterates over all of the currently registered message listeners.
|
||||
*/
|
||||
* iterListeners() {
|
||||
for (let [message, listeners] of this.listeners) {
|
||||
for (let [listener, listenWhenClosed] of listeners) {
|
||||
yield {message, listener, listenWhenClosed};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Adds docShell swap listeners to the message manager owner.
|
||||
*
|
||||
* @param {Element} target
|
||||
* The target element.
|
||||
*/
|
||||
addListeners(target) {
|
||||
target.addEventListener("SwapDocShells", this);
|
||||
|
||||
this.eventTarget = target;
|
||||
this.messageManager = target.messageManager;
|
||||
|
||||
for (let {message, listener, listenWhenClosed} of this.iterListeners()) {
|
||||
this.messageManager.addMessageListener(message, listener, listenWhenClosed);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @private
|
||||
* Removes docShell swap listeners to the message manager owner.
|
||||
*
|
||||
* @param {Element} target
|
||||
* The target element.
|
||||
*/
|
||||
removeListeners(target) {
|
||||
target.removeEventListener("SwapDocShells", this);
|
||||
|
||||
for (let {message, listener} of this.iterListeners()) {
|
||||
this.messageManager.removeMessageListener(message, listener);
|
||||
}
|
||||
}
|
||||
|
||||
handleEvent(event) {
|
||||
if (event.type == "SwapDocShells") {
|
||||
this.removeListeners(this.eventTarget);
|
||||
this.addListeners(event.detail);
|
||||
}
|
||||
}
|
||||
}
|
@ -38,7 +38,6 @@ const PROXY_TIMEOUT_SEC = 10;
|
||||
|
||||
const {
|
||||
ExtensionError,
|
||||
defineLazyGetter,
|
||||
} = ExtensionUtils;
|
||||
|
||||
const {
|
||||
@ -46,6 +45,7 @@ const {
|
||||
CanOfAPIs,
|
||||
LocalAPIImplementation,
|
||||
SchemaAPIManager,
|
||||
defineLazyGetter,
|
||||
} = ExtensionCommon;
|
||||
|
||||
const PROXY_TYPES = Object.freeze({
|
||||
|
@ -16,13 +16,14 @@ ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetters(this, {
|
||||
ExtensionChild: "resource://gre/modules/ExtensionChild.jsm",
|
||||
ExtensionCommon: "resource://gre/modules/ExtensionCommon.jsm",
|
||||
ExtensionContent: "resource://gre/modules/ExtensionContent.jsm",
|
||||
ExtensionPageChild: "resource://gre/modules/ExtensionPageChild.jsm",
|
||||
});
|
||||
|
||||
ChromeUtils.import("resource://gre/modules/ExtensionUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "console", () => ExtensionUtils.getConsole());
|
||||
XPCOMUtils.defineLazyGetter(this, "console", () => ExtensionCommon.getConsole());
|
||||
|
||||
const {
|
||||
DefaultWeakMap,
|
||||
|
@ -26,6 +26,7 @@ EXTRA_JS_MODULES += [
|
||||
'FindContent.jsm',
|
||||
'LegacyExtensionsUtils.jsm',
|
||||
'MessageChannel.jsm',
|
||||
'MessageManagerProxy.jsm',
|
||||
'NativeManifests.jsm',
|
||||
'NativeMessaging.jsm',
|
||||
'ProxyScriptContext.jsm',
|
||||
|
@ -13,10 +13,6 @@ ChromeUtils.defineModuleGetter(this, "FileUtils",
|
||||
|
||||
var {
|
||||
EventEmitter,
|
||||
normalizeTime,
|
||||
} = ExtensionUtils;
|
||||
|
||||
var {
|
||||
ignoreEvent,
|
||||
} = ExtensionCommon;
|
||||
|
||||
@ -262,7 +258,7 @@ const downloadQuery = query => {
|
||||
if (arg == null) {
|
||||
return before ? Number.MAX_VALUE : 0;
|
||||
}
|
||||
return normalizeTime(arg).getTime();
|
||||
return ExtensionCommon.normalizeTime(arg).getTime();
|
||||
}
|
||||
|
||||
const startedBefore = normalizeDownloadTime(query.startedBefore, true);
|
||||
|
@ -32,7 +32,7 @@ const getIdleObserver = (extension, context) => {
|
||||
let observerInfo = getIdleObserverInfo(extension, context);
|
||||
let {observer, detectionInterval} = observerInfo;
|
||||
if (!observer) {
|
||||
observer = new class extends ExtensionUtils.EventEmitter {
|
||||
observer = new class extends ExtensionCommon.EventEmitter {
|
||||
observe(subject, topic, data) {
|
||||
if (topic == "idle" || topic == "active") {
|
||||
this.emit("stateChanged", topic);
|
||||
|
@ -95,7 +95,7 @@ function checkAllowedAddon(addon) {
|
||||
return allowedTypes.includes(addon.type);
|
||||
}
|
||||
|
||||
class AddonListener extends ExtensionUtils.EventEmitter {
|
||||
class AddonListener extends ExtensionCommon.EventEmitter {
|
||||
constructor() {
|
||||
super();
|
||||
AddonManager.addAddonListener(this);
|
||||
|
@ -18,10 +18,13 @@ var {
|
||||
DefaultMap,
|
||||
DefaultWeakMap,
|
||||
ExtensionError,
|
||||
defineLazyGetter,
|
||||
getWinUtils,
|
||||
} = ExtensionUtils;
|
||||
|
||||
var {
|
||||
defineLazyGetter,
|
||||
} = ExtensionCommon;
|
||||
|
||||
/**
|
||||
* The platform-specific type of native tab objects, which are wrapped by
|
||||
* TabBase instances.
|
||||
|
@ -19,7 +19,7 @@ XPCOMUtils.defineLazyGlobalGetters(this, ["URL"]);
|
||||
|
||||
ChromeUtils.import("resource://gre/modules/ExtensionCommon.jsm");
|
||||
|
||||
global.EventEmitter = ExtensionUtils.EventEmitter;
|
||||
global.EventEmitter = ExtensionCommon.EventEmitter;
|
||||
global.EventManager = ExtensionCommon.EventManager;
|
||||
|
||||
/* globals DEFAULT_STORE, PRIVATE_STORE, CONTAINER_STORE */
|
||||
|
Loading…
Reference in New Issue
Block a user