--- a/browser/components/extensions/ext-bookmarks.js
+++ b/browser/components/extensions/ext-bookmarks.js
@@ -1,16 +1,12 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
-var {
- SingletonEventManager,
-} = ExtensionUtils;
-
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
"resource://gre/modules/PlacesUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Task",
"resource://gre/modules/Task.jsm");
let listenerCount = 0;
function getTree(rootGuid, onlyChildren) {
--- a/browser/components/extensions/ext-browserAction.js
+++ b/browser/components/extensions/ext-browserAction.js
@@ -15,17 +15,16 @@ XPCOMUtils.defineLazyServiceGetter(this,
"@mozilla.org/inspector/dom-utils;1",
"inIDOMUtils");
Cu.import("resource://devtools/shared/event-emitter.js");
Cu.import("resource://gre/modules/Task.jsm");
var {
IconDetails,
- SingletonEventManager,
} = ExtensionUtils;
const POPUP_PRELOAD_TIMEOUT_MS = 200;
var XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
function isAncestorOrSelf(target, node) {
for (; node; node = node.parentNode) {
--- a/browser/components/extensions/ext-c-devtools-panels.js
+++ b/browser/components/extensions/ext-c-devtools-panels.js
@@ -2,17 +2,16 @@
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
XPCOMUtils.defineLazyModuleGetter(this, "EventEmitter",
"resource://devtools/shared/event-emitter.js");
var {
promiseDocumentLoaded,
- SingletonEventManager,
} = ExtensionUtils;
/**
* Represents an addon devtools panel in the child process.
*
* @param {DevtoolsExtensionContext}
* A devtools extension context running in a child process.
* @param {object} panelOptions
--- a/browser/components/extensions/ext-c-omnibox.js
+++ b/browser/components/extensions/ext-c-omnibox.js
@@ -1,16 +1,12 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
-var {
- SingletonEventManager,
-} = ExtensionUtils;
-
this.omnibox = class extends ExtensionAPI {
getAPI(context) {
return {
omnibox: {
onInputChanged: new SingletonEventManager(context, "omnibox.onInputChanged", fire => {
let listener = (text, id) => {
fire.asyncWithoutClone(text, suggestions => {
context.childManager.callParentFunctionNoReturn("omnibox_internal.addSuggestions", [
--- a/browser/components/extensions/ext-commands.js
+++ b/browser/components/extensions/ext-commands.js
@@ -1,14 +1,13 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
var {
- SingletonEventManager,
PlatformInfo,
} = ExtensionUtils;
var XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
this.commands = class extends ExtensionAPI {
onManifestEntry(entryName) {
let {extension} = this;
--- a/browser/components/extensions/ext-contextMenus.js
+++ b/browser/components/extensions/ext-contextMenus.js
@@ -6,17 +6,16 @@ Cu.import("resource://gre/modules/MatchP
Cu.import("resource://gre/modules/Services.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
"resource://gre/modules/NetUtil.jsm");
var {
ExtensionError,
IconDetails,
- SingletonEventManager,
} = ExtensionUtils;
const ACTION_MENU_TOP_LEVEL_LIMIT = 6;
// Map[Extension -> Map[ID -> MenuItem]]
// Note: we want to enumerate all the menu items so
// this cannot be a weak map.
var gContextMenuMap = new Map();
--- a/browser/components/extensions/ext-devtools-network.js
+++ b/browser/components/extensions/ext-devtools-network.js
@@ -1,16 +1,12 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
-var {
- SingletonEventManager,
-} = ExtensionUtils;
-
this.devtools_network = class extends ExtensionAPI {
getAPI(context) {
return {
devtools: {
network: {
onNavigated: new SingletonEventManager(context, "devtools.onNavigated", fire => {
let listener = (event, data) => {
fire.async(data.url);
--- a/browser/components/extensions/ext-history.js
+++ b/browser/components/extensions/ext-history.js
@@ -4,17 +4,16 @@
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
"resource://gre/modules/NetUtil.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
"resource://gre/modules/PlacesUtils.jsm");
var {
normalizeTime,
- SingletonEventManager,
} = ExtensionUtils;
let nsINavHistoryService = Ci.nsINavHistoryService;
const TRANSITION_TO_TRANSITION_TYPES_MAP = new Map([
["link", nsINavHistoryService.TRANSITION_LINK],
["typed", nsINavHistoryService.TRANSITION_TYPED],
["auto_bookmark", nsINavHistoryService.TRANSITION_BOOKMARK],
["auto_subframe", nsINavHistoryService.TRANSITION_EMBED],
--- a/browser/components/extensions/ext-omnibox.js
+++ b/browser/components/extensions/ext-omnibox.js
@@ -1,17 +1,14 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionSearchHandler",
"resource://gre/modules/ExtensionSearchHandler.jsm");
-var {
- SingletonEventManager,
-} = ExtensionUtils;
this.omnibox = class extends ExtensionAPI {
onManifestEntry(entryName) {
let {extension} = this;
let {manifest} = extension;
let keyword = manifest.omnibox.keyword;
try {
--- a/browser/components/extensions/ext-pageAction.js
+++ b/browser/components/extensions/ext-pageAction.js
@@ -3,17 +3,16 @@
"use strict";
XPCOMUtils.defineLazyModuleGetter(this, "PanelPopup",
"resource:///modules/ExtensionPopups.jsm");
Cu.import("resource://gre/modules/Task.jsm");
var {
- SingletonEventManager,
IconDetails,
} = ExtensionUtils;
// WeakMap[Extension -> PageAction]
let pageActionMap = new WeakMap();
this.pageAction = class extends ExtensionAPI {
static for(extension) {
--- a/browser/components/extensions/ext-sessions.js
+++ b/browser/components/extensions/ext-sessions.js
@@ -1,15 +1,14 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
var {
promiseObserved,
- SingletonEventManager,
} = ExtensionUtils;
XPCOMUtils.defineLazyModuleGetter(this, "SessionStore",
"resource:///modules/sessionstore/SessionStore.jsm");
const SS_ON_CLOSED_OBJECTS_CHANGED = "sessionstore-closed-objects-changed";
function getRecentlyClosed(maxResults, extension) {
--- a/browser/components/extensions/ext-tabs.js
+++ b/browser/components/extensions/ext-tabs.js
@@ -10,20 +10,16 @@ XPCOMUtils.defineLazyModuleGetter(this,
"resource://gre/modules/MatchPattern.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PromiseUtils",
"resource://gre/modules/PromiseUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Services",
"resource://gre/modules/Services.jsm");
-var {
- SingletonEventManager,
-} = ExtensionUtils;
-
let tabListener = {
tabReadyInitialized: false,
tabReadyPromises: new WeakMap(),
initializingTabs: new WeakSet(),
initTabReady() {
if (!this.tabReadyInitialized) {
windowTracker.addListener("progress", this);
--- a/browser/components/extensions/ext-utils.js
+++ b/browser/components/extensions/ext-utils.js
@@ -9,17 +9,16 @@ XPCOMUtils.defineLazyModuleGetter(this,
Cu.import("resource://gre/modules/ExtensionTabs.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "styleSheetService",
"@mozilla.org/content/style-sheet-service;1",
"nsIStyleSheetService");
var {
ExtensionError,
- SingletonEventManager,
defineLazyGetter,
} = ExtensionUtils;
let tabTracker;
let windowTracker;
// This file provides some useful code for the |tabs| and |windows|
// modules. All of the code is installed on |global|, which is a scope
--- a/browser/components/extensions/ext-windows.js
+++ b/browser/components/extensions/ext-windows.js
@@ -6,17 +6,16 @@ XPCOMUtils.defineLazyServiceGetter(this,
"@mozilla.org/browser/aboutnewtab-service;1",
"nsIAboutNewTabService");
XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
"resource://gre/modules/AppConstants.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
var {
- SingletonEventManager,
promiseObserved,
} = ExtensionUtils;
function onXULFrameLoaderCreated({target}) {
target.messageManager.sendAsyncMessage("AllowScriptsToClose", {});
}
this.windows = class extends ExtensionAPI {
--- a/mobile/android/components/extensions/ext-pageAction.js
+++ b/mobile/android/components/extensions/ext-pageAction.js
@@ -11,17 +11,16 @@ XPCOMUtils.defineLazyModuleGetter(this,
// Import the android PageActions module.
XPCOMUtils.defineLazyModuleGetter(this, "PageActions",
"resource://gre/modules/PageActions.jsm");
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
var {
IconDetails,
- SingletonEventManager,
} = ExtensionUtils;
// WeakMap[Extension -> PageAction]
var pageActionMap = new WeakMap();
function PageAction(options, extension) {
this.id = null;
--- a/mobile/android/components/extensions/ext-tabs.js
+++ b/mobile/android/components/extensions/ext-tabs.js
@@ -10,20 +10,16 @@ XPCOMUtils.defineLazyModuleGetter(this,
"resource://gre/modules/MatchPattern.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PromiseUtils",
"resource://gre/modules/PromiseUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Services",
"resource://gre/modules/Services.jsm");
-var {
- SingletonEventManager,
-} = ExtensionUtils;
-
// This function is pretty tightly tied to Extension.jsm.
// Its job is to fill in the |tab| property of the sender.
function getSender(extension, target, sender) {
let tabId;
if ("tabId" in sender) {
// The message came from a privileged extension page running in a tab. In
// that case, it should include a tabId property (which is filled in by the
// page-open listener below).
--- a/mobile/android/components/extensions/ext-utils.js
+++ b/mobile/android/components/extensions/ext-utils.js
@@ -10,17 +10,16 @@ Cu.import("resource://gre/modules/Extens
/* globals EventDispatcher */
Cu.import("resource://gre/modules/Messaging.jsm");
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
var {
DefaultWeakMap,
ExtensionError,
- SingletonEventManager,
defineLazyGetter,
} = ExtensionUtils;
global.GlobalEventDispatcher = EventDispatcher.instance;
const BrowserStatusFilter = Components.Constructor(
"@mozilla.org/appshell/component/browser-status-filter;1", "nsIWebProgress",
"addProgressListener");
--- a/toolkit/components/extensions/.eslintrc.js
+++ b/toolkit/components/extensions/.eslintrc.js
@@ -32,16 +32,17 @@ module.exports = {
"isValidCookieStoreId": true,
"NetUtil": true,
"openOptionsPage": true,
"require": false,
"runSafe": true,
"runSafeSync": true,
"runSafeSyncWithoutClone": true,
"Services": true,
+ "SingletonEventManager": true,
"tabTracker": false,
"XPCOMUtils": true,
},
"rules": {
// Rules from the mozilla plugin
"mozilla/balanced-listeners": "error",
"mozilla/no-aArgs": "error",
--- a/toolkit/components/extensions/Extension.jsm
+++ b/toolkit/components/extensions/Extension.jsm
@@ -31,16 +31,18 @@ Cu.import("resource://gre/modules/Servic
XPCOMUtils.defineLazyPreferenceGetter(this, "processCount", "dom.ipc.processCount.extension");
XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
"resource://gre/modules/AddonManager.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
"resource://gre/modules/AppConstants.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionAPIs",
"resource://gre/modules/ExtensionAPI.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "ExtensionCommon",
+ "resource://gre/modules/ExtensionCommon.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionPermissions",
"resource://gre/modules/ExtensionPermissions.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionStorage",
"resource://gre/modules/ExtensionStorage.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionTestCommon",
"resource://testing-common/ExtensionTestCommon.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Locale",
"resource://gre/modules/Locale.jsm");
@@ -79,24 +81,58 @@ var {
GlobalManager,
ParentAPIManager,
apiManager: Management,
} = ExtensionParent;
const {
classifyPermission,
EventEmitter,
- LocaleData,
StartupCache,
getUniqueId,
- validateThemeManifest,
} = ExtensionUtils;
XPCOMUtils.defineLazyGetter(this, "console", ExtensionUtils.getConsole);
+XPCOMUtils.defineLazyGetter(this, "LocaleData", () => ExtensionCommon.LocaleData);
+
+
+// The list of properties that themes are allowed to contain.
+XPCOMUtils.defineLazyGetter(this, "allowedThemeProperties", () => {
+ Cu.import("resource://gre/modules/ExtensionParent.jsm");
+ let propertiesInBaseManifest = ExtensionParent.baseManifestProperties;
+
+ // The properties found in the base manifest contain all of the properties that
+ // themes are allowed to have. However, the list also contains several properties
+ // that aren't allowed, so we need to filter them out first before the list can
+ // be used to validate themes.
+ return propertiesInBaseManifest.filter(prop => {
+ const propertiesToRemove = ["background", "content_scripts", "permissions"];
+ return !propertiesToRemove.includes(prop);
+ });
+});
+
+/**
+ * Validates a theme to ensure it only contains static resources.
+ *
+ * @param {Array<string>} manifestProperties The list of top-level keys found in the
+ * the extension's manifest.
+ * @returns {Array<string>} A list of invalid properties or an empty list
+ * if none are found.
+ */
+function validateThemeManifest(manifestProperties) {
+ let invalidProps = [];
+ for (let propName of manifestProperties) {
+ if (propName != "theme" && !allowedThemeProperties.includes(propName)) {
+ invalidProps.push(propName);
+ }
+ }
+ return invalidProps;
+}
+
const LOGGER_ID_BASE = "addons.webextension.";
const UUID_MAP_PREF = "extensions.webextensions.uuids";
const LEAVE_STORAGE_PREF = "extensions.webextensions.keepStorageOnUninstall";
const LEAVE_UUID_PREF = "extensions.webextensions.keepUuidOnUninstall";
const COMMENT_REGEXP = new RegExp(String.raw`
^
(
--- a/toolkit/components/extensions/ExtensionChild.jsm
+++ b/toolkit/components/extensions/ExtensionChild.jsm
@@ -39,28 +39,28 @@ XPCOMUtils.defineLazyModuleGetter(this,
Cu.import("resource://gre/modules/ExtensionCommon.jsm");
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
const {
DefaultMap,
EventEmitter,
LimitedSet,
- LocaleData,
- SingletonEventManager,
SpreadArgs,
defineLazyGetter,
getMessageManager,
getUniqueId,
injectAPI,
} = ExtensionUtils;
const {
LocalAPIImplementation,
+ LocaleData,
SchemaAPIInterface,
+ SingletonEventManager,
} = ExtensionCommon;
/**
* Abstraction for a Port object in the extension API.
*
* @param {BaseContext} context The context that owns this port.
* @param {nsIMessageSender} senderMM The message manager to send messages to.
* @param {Array<nsIMessageListenerManager>} receiverMMs Message managers to
--- a/toolkit/components/extensions/ExtensionCommon.jsm
+++ b/toolkit/components/extensions/ExtensionCommon.jsm
@@ -13,18 +13,22 @@ const {classes: Cc, interfaces: Ci, util
/* exported ExtensionCommon */
this.EXPORTED_SYMBOLS = ["ExtensionCommon"];
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Locale",
+ "resource://gre/modules/Locale.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "MessageChannel",
"resource://gre/modules/MessageChannel.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Preferences",
+ "resource://gre/modules/Preferences.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
"resource://gre/modules/PrivateBrowsingUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Schemas",
"resource://gre/modules/Schemas.jsm");
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
var {
@@ -1113,15 +1117,327 @@ class SchemaAPIManager extends EventEmit
if (Schemas.checkPermissions(api.namespace, {hasPermission})) {
api = api.getAPI(context);
deepCopy(obj, api);
}
}
}
}
+function LocaleData(data) {
+ this.defaultLocale = data.defaultLocale;
+ this.selectedLocale = data.selectedLocale;
+ this.locales = data.locales || new Map();
+ this.warnedMissingKeys = new Set();
+
+ // Map(locale-name -> Map(message-key -> localized-string))
+ //
+ // Contains a key for each loaded locale, each of which is a
+ // Map of message keys to their localized strings.
+ this.messages = data.messages || new Map();
+
+ if (data.builtinMessages) {
+ this.messages.set(this.BUILTIN, data.builtinMessages);
+ }
+}
+
+LocaleData.prototype = {
+ // Representation of the object to send to content processes. This
+ // should include anything the content process might need.
+ serialize() {
+ return {
+ defaultLocale: this.defaultLocale,
+ selectedLocale: this.selectedLocale,
+ messages: this.messages,
+ locales: this.locales,
+ };
+ },
+
+ BUILTIN: "@@BUILTIN_MESSAGES",
+
+ has(locale) {
+ return this.messages.has(locale);
+ },
+
+ // https://developer.chrome.com/extensions/i18n
+ localizeMessage(message, substitutions = [], options = {}) {
+ let defaultOptions = {
+ locale: this.selectedLocale,
+ defaultValue: "",
+ cloneScope: null,
+ };
+
+ options = Object.assign(defaultOptions, options);
+
+ let locales = new Set([this.BUILTIN, options.locale, this.defaultLocale]
+ .filter(locale => this.messages.has(locale)));
+
+ // Message names are case-insensitive, so normalize them to lower-case.
+ message = message.toLowerCase();
+ for (let locale of locales) {
+ let messages = this.messages.get(locale);
+ if (messages.has(message)) {
+ let str = messages.get(message);
+
+ if (!Array.isArray(substitutions)) {
+ substitutions = [substitutions];
+ }
+
+ let replacer = (matched, index, dollarSigns) => {
+ if (index) {
+ // This is not quite Chrome-compatible. Chrome consumes any number
+ // of digits following the $, but only accepts 9 substitutions. We
+ // accept any number of substitutions.
+ index = parseInt(index, 10) - 1;
+ return index in substitutions ? substitutions[index] : "";
+ }
+ // For any series of contiguous `$`s, the first is dropped, and
+ // the rest remain in the output string.
+ return dollarSigns;
+ };
+ return str.replace(/\$(?:([1-9]\d*)|(\$+))/g, replacer);
+ }
+ }
+
+ // Check for certain pre-defined messages.
+ if (message == "@@ui_locale") {
+ return this.uiLocale;
+ } else if (message.startsWith("@@bidi_")) {
+ let registry = Cc["@mozilla.org/chrome/chrome-registry;1"].getService(Ci.nsIXULChromeRegistry);
+ let rtl = registry.isLocaleRTL("global");
+
+ if (message == "@@bidi_dir") {
+ return rtl ? "rtl" : "ltr";
+ } else if (message == "@@bidi_reversed_dir") {
+ return rtl ? "ltr" : "rtl";
+ } else if (message == "@@bidi_start_edge") {
+ return rtl ? "right" : "left";
+ } else if (message == "@@bidi_end_edge") {
+ return rtl ? "left" : "right";
+ }
+ }
+
+ if (!this.warnedMissingKeys.has(message)) {
+ let error = `Unknown localization message ${message}`;
+ if (options.cloneScope) {
+ error = new options.cloneScope.Error(error);
+ }
+ Cu.reportError(error);
+ this.warnedMissingKeys.add(message);
+ }
+ return options.defaultValue;
+ },
+
+ // Localize a string, replacing all |__MSG_(.*)__| tokens with the
+ // matching string from the current locale, as determined by
+ // |this.selectedLocale|.
+ //
+ // This may not be called before calling either |initLocale| or
+ // |initAllLocales|.
+ localize(str, locale = this.selectedLocale) {
+ if (!str) {
+ return str;
+ }
+
+ return str.replace(/__MSG_([A-Za-z0-9@_]+?)__/g, (matched, message) => {
+ return this.localizeMessage(message, [], {locale, defaultValue: matched});
+ });
+ },
+
+ // Validates the contents of a locale JSON file, normalizes the
+ // messages into a Map of message key -> localized string pairs.
+ addLocale(locale, messages, extension) {
+ let result = new Map();
+
+ // Chrome does not document the semantics of its localization
+ // system very well. It handles replacements by pre-processing
+ // messages, replacing |$[a-zA-Z0-9@_]+$| tokens with the value of their
+ // replacements. Later, it processes the resulting string for
+ // |$[0-9]| replacements.
+ //
+ // Again, it does not document this, but it accepts any number
+ // of sequential |$|s, and replaces them with that number minus
+ // 1. It also accepts |$| followed by any number of sequential
+ // digits, but refuses to process a localized string which
+ // provides more than 9 substitutions.
+ if (!instanceOf(messages, "Object")) {
+ extension.packagingError(`Invalid locale data for ${locale}`);
+ return result;
+ }
+
+ for (let key of Object.keys(messages)) {
+ let msg = messages[key];
+
+ if (!instanceOf(msg, "Object") || typeof(msg.message) != "string") {
+ extension.packagingError(`Invalid locale message data for ${locale}, message ${JSON.stringify(key)}`);
+ continue;
+ }
+
+ // Substitutions are case-insensitive, so normalize all of their names
+ // to lower-case.
+ let placeholders = new Map();
+ if (instanceOf(msg.placeholders, "Object")) {
+ for (let key of Object.keys(msg.placeholders)) {
+ placeholders.set(key.toLowerCase(), msg.placeholders[key]);
+ }
+ }
+
+ let replacer = (match, name) => {
+ let replacement = placeholders.get(name.toLowerCase());
+ if (instanceOf(replacement, "Object") && "content" in replacement) {
+ return replacement.content;
+ }
+ return "";
+ };
+
+ let value = msg.message.replace(/\$([A-Za-z0-9@_]+)\$/g, replacer);
+
+ // Message names are also case-insensitive, so normalize them to lower-case.
+ result.set(key.toLowerCase(), value);
+ }
+
+ this.messages.set(locale, result);
+ return result;
+ },
+
+ get acceptLanguages() {
+ let result = Preferences.get("intl.accept_languages", "", Ci.nsIPrefLocalizedString);
+ return result.split(/\s*,\s*/g);
+ },
+
+
+ get uiLocale() {
+ // Return the browser locale, but convert it to a Chrome-style
+ // locale code.
+ return Locale.getLocale().replace(/-/g, "_");
+ },
+};
+
+// This is a generic class for managing event listeners. Example usage:
+//
+// new SingletonEventManager(context, "api.subAPI", fire => {
+// let listener = (...) => {
+// // Fire any listeners registered with addListener.
+// fire.async(arg1, arg2);
+// };
+// // Register the listener.
+// SomehowRegisterListener(listener);
+// return () => {
+// // Return a way to unregister the listener.
+// SomehowUnregisterListener(listener);
+// };
+// }).api()
+//
+// The result is an object with addListener, removeListener, and
+// hasListener methods. |context| is an add-on scope (either an
+// ExtensionContext in the chrome process or ExtensionContext in a
+// content process). |name| is for debugging. |register| is a function
+// to register the listener. |register| should return an
+// unregister function that will unregister the listener.
+function SingletonEventManager(context, name, register) {
+ this.context = context;
+ this.name = name;
+ this.register = register;
+ this.unregister = new Map();
+}
+
+SingletonEventManager.prototype = {
+ addListener(callback, ...args) {
+ if (this.unregister.has(callback)) {
+ return;
+ }
+
+ let shouldFire = () => {
+ if (this.context.unloaded) {
+ dump(`${this.name} event fired after context unloaded.\n`);
+ } else if (!this.context.active) {
+ dump(`${this.name} event fired while context is inactive.\n`);
+ } else if (this.unregister.has(callback)) {
+ return true;
+ }
+ return false;
+ };
+
+ let fire = {
+ sync: (...args) => {
+ if (shouldFire()) {
+ return this.context.runSafe(callback, ...args);
+ }
+ },
+ async: (...args) => {
+ return Promise.resolve().then(() => {
+ if (shouldFire()) {
+ return this.context.runSafe(callback, ...args);
+ }
+ });
+ },
+ raw: (...args) => {
+ if (!shouldFire()) {
+ throw new Error("Called raw() on unloaded/inactive context");
+ }
+ return callback(...args);
+ },
+ asyncWithoutClone: (...args) => {
+ return Promise.resolve().then(() => {
+ if (shouldFire()) {
+ return this.context.runSafeWithoutClone(callback, ...args);
+ }
+ });
+ },
+ };
+
+
+ let unregister = this.register(fire, ...args);
+ this.unregister.set(callback, unregister);
+ this.context.callOnClose(this);
+ },
+
+ removeListener(callback) {
+ if (!this.unregister.has(callback)) {
+ return;
+ }
+
+ let unregister = this.unregister.get(callback);
+ this.unregister.delete(callback);
+ try {
+ unregister();
+ } catch (e) {
+ Cu.reportError(e);
+ }
+ if (this.unregister.size == 0) {
+ this.context.forgetOnClose(this);
+ }
+ },
+
+ hasListener(callback) {
+ return this.unregister.has(callback);
+ },
+
+ revoke() {
+ for (let callback of this.unregister.keys()) {
+ this.removeListener(callback);
+ }
+ },
+
+ close() {
+ this.revoke();
+ },
+
+ api() {
+ return {
+ addListener: (...args) => this.addListener(...args),
+ removeListener: (...args) => this.removeListener(...args),
+ hasListener: (...args) => this.hasListener(...args),
+ [Schemas.REVOKE]: () => this.revoke(),
+ };
+ },
+};
+
+
const ExtensionCommon = {
BaseContext,
CanOfAPIs,
LocalAPIImplementation,
+ LocaleData,
SchemaAPIInterface,
SchemaAPIManager,
+ SingletonEventManager,
};
--- a/toolkit/components/extensions/ExtensionUtils.jsm
+++ b/toolkit/components/extensions/ExtensionUtils.jsm
@@ -21,24 +21,18 @@ XPCOMUtils.defineLazyModuleGetter(this,
XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
"resource://gre/modules/AppConstants.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ConsoleAPI",
"resource://gre/modules/Console.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionManagement",
"resource://gre/modules/ExtensionManagement.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "IndexedDB",
"resource://gre/modules/IndexedDB.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "LanguageDetector",
- "resource:///modules/translation/LanguageDetector.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "Locale",
- "resource://gre/modules/Locale.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "MessageChannel",
"resource://gre/modules/MessageChannel.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
- "resource://gre/modules/NetUtil.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Preferences",
"resource://gre/modules/Preferences.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Schemas",
"resource://gre/modules/Schemas.jsm");
XPCOMUtils.defineLazyServiceGetter(this, "styleSheetService",
"@mozilla.org/content/style-sheet-service;1",
"nsIStyleSheetService");
@@ -56,49 +50,16 @@ XPCOMUtils.defineLazyGetter(this, "conso
let nextId = 0;
XPCOMUtils.defineLazyGetter(this, "uniqueProcessID", () => Services.appinfo.uniqueProcessID);
function getUniqueId() {
return `${nextId++}-${uniqueProcessID}`;
}
-// The list of properties that themes are allowed to contain.
-XPCOMUtils.defineLazyGetter(this, "gAllowedThemeProperties", () => {
- Cu.import("resource://gre/modules/ExtensionParent.jsm");
- let propertiesInBaseManifest = ExtensionParent.baseManifestProperties;
-
- // The properties found in the base manifest contain all of the properties that
- // themes are allowed to have. However, the list also contains several properties
- // that aren't allowed, so we need to filter them out first before the list can
- // be used to validate themes.
- return propertiesInBaseManifest.filter(prop => {
- const propertiesToRemove = ["background", "content_scripts", "permissions"];
- return !propertiesToRemove.includes(prop);
- });
-});
-
-/**
- * Validates a theme to ensure it only contains static resources.
- *
- * @param {Array<string>} manifestProperties The list of top-level keys found in the
- * the extension's manifest.
- * @returns {Array<string>} A list of invalid properties or an empty list
- * if none are found.
- */
-function validateThemeManifest(manifestProperties) {
- let invalidProps = [];
- for (let propName of manifestProperties) {
- if (propName != "theme" && !gAllowedThemeProperties.includes(propName)) {
- invalidProps.push(propName);
- }
- }
- return invalidProps;
-}
-
let StartupCache = {
DB_NAME: "ExtensionStartupCache",
SCHEMA_VERSION: 1,
STORE_NAMES: Object.freeze(["locales", "manifests", "schemas"]),
dbPromise: null,
@@ -571,325 +532,16 @@ class EventEmitter {
let promises = Array.from(listeners, listener => {
return runSafeSyncWithoutClone(listener, event, ...args);
});
return Promise.all(promises);
}
}
-function LocaleData(data) {
- this.defaultLocale = data.defaultLocale;
- this.selectedLocale = data.selectedLocale;
- this.locales = data.locales || new Map();
- this.warnedMissingKeys = new Set();
-
- // Map(locale-name -> Map(message-key -> localized-string))
- //
- // Contains a key for each loaded locale, each of which is a
- // Map of message keys to their localized strings.
- this.messages = data.messages || new Map();
-
- if (data.builtinMessages) {
- this.messages.set(this.BUILTIN, data.builtinMessages);
- }
-}
-
-
-LocaleData.prototype = {
- // Representation of the object to send to content processes. This
- // should include anything the content process might need.
- serialize() {
- return {
- defaultLocale: this.defaultLocale,
- selectedLocale: this.selectedLocale,
- messages: this.messages,
- locales: this.locales,
- };
- },
-
- BUILTIN: "@@BUILTIN_MESSAGES",
-
- has(locale) {
- return this.messages.has(locale);
- },
-
- // https://developer.chrome.com/extensions/i18n
- localizeMessage(message, substitutions = [], options = {}) {
- let defaultOptions = {
- locale: this.selectedLocale,
- defaultValue: "",
- cloneScope: null,
- };
-
- options = Object.assign(defaultOptions, options);
-
- let locales = new Set([this.BUILTIN, options.locale, this.defaultLocale]
- .filter(locale => this.messages.has(locale)));
-
- // Message names are case-insensitive, so normalize them to lower-case.
- message = message.toLowerCase();
- for (let locale of locales) {
- let messages = this.messages.get(locale);
- if (messages.has(message)) {
- let str = messages.get(message);
-
- if (!Array.isArray(substitutions)) {
- substitutions = [substitutions];
- }
-
- let replacer = (matched, index, dollarSigns) => {
- if (index) {
- // This is not quite Chrome-compatible. Chrome consumes any number
- // of digits following the $, but only accepts 9 substitutions. We
- // accept any number of substitutions.
- index = parseInt(index, 10) - 1;
- return index in substitutions ? substitutions[index] : "";
- }
- // For any series of contiguous `$`s, the first is dropped, and
- // the rest remain in the output string.
- return dollarSigns;
- };
- return str.replace(/\$(?:([1-9]\d*)|(\$+))/g, replacer);
- }
- }
-
- // Check for certain pre-defined messages.
- if (message == "@@ui_locale") {
- return this.uiLocale;
- } else if (message.startsWith("@@bidi_")) {
- let rtl = Services.locale.isAppLocaleRTL;
-
- if (message == "@@bidi_dir") {
- return rtl ? "rtl" : "ltr";
- } else if (message == "@@bidi_reversed_dir") {
- return rtl ? "ltr" : "rtl";
- } else if (message == "@@bidi_start_edge") {
- return rtl ? "right" : "left";
- } else if (message == "@@bidi_end_edge") {
- return rtl ? "left" : "right";
- }
- }
-
- if (!this.warnedMissingKeys.has(message)) {
- let error = `Unknown localization message ${message}`;
- if (options.cloneScope) {
- error = new options.cloneScope.Error(error);
- }
- Cu.reportError(error);
- this.warnedMissingKeys.add(message);
- }
- return options.defaultValue;
- },
-
- // Localize a string, replacing all |__MSG_(.*)__| tokens with the
- // matching string from the current locale, as determined by
- // |this.selectedLocale|.
- //
- // This may not be called before calling either |initLocale| or
- // |initAllLocales|.
- localize(str, locale = this.selectedLocale) {
- if (!str) {
- return str;
- }
-
- return str.replace(/__MSG_([A-Za-z0-9@_]+?)__/g, (matched, message) => {
- return this.localizeMessage(message, [], {locale, defaultValue: matched});
- });
- },
-
- // Validates the contents of a locale JSON file, normalizes the
- // messages into a Map of message key -> localized string pairs.
- addLocale(locale, messages, extension) {
- let result = new Map();
-
- // Chrome does not document the semantics of its localization
- // system very well. It handles replacements by pre-processing
- // messages, replacing |$[a-zA-Z0-9@_]+$| tokens with the value of their
- // replacements. Later, it processes the resulting string for
- // |$[0-9]| replacements.
- //
- // Again, it does not document this, but it accepts any number
- // of sequential |$|s, and replaces them with that number minus
- // 1. It also accepts |$| followed by any number of sequential
- // digits, but refuses to process a localized string which
- // provides more than 9 substitutions.
- if (!instanceOf(messages, "Object")) {
- extension.packagingError(`Invalid locale data for ${locale}`);
- return result;
- }
-
- for (let key of Object.keys(messages)) {
- let msg = messages[key];
-
- if (!instanceOf(msg, "Object") || typeof(msg.message) != "string") {
- extension.packagingError(`Invalid locale message data for ${locale}, message ${JSON.stringify(key)}`);
- continue;
- }
-
- // Substitutions are case-insensitive, so normalize all of their names
- // to lower-case.
- let placeholders = new Map();
- if (instanceOf(msg.placeholders, "Object")) {
- for (let key of Object.keys(msg.placeholders)) {
- placeholders.set(key.toLowerCase(), msg.placeholders[key]);
- }
- }
-
- let replacer = (match, name) => {
- let replacement = placeholders.get(name.toLowerCase());
- if (instanceOf(replacement, "Object") && "content" in replacement) {
- return replacement.content;
- }
- return "";
- };
-
- let value = msg.message.replace(/\$([A-Za-z0-9@_]+)\$/g, replacer);
-
- // Message names are also case-insensitive, so normalize them to lower-case.
- result.set(key.toLowerCase(), value);
- }
-
- this.messages.set(locale, result);
- return result;
- },
-
- get acceptLanguages() {
- let result = Preferences.get("intl.accept_languages", "", Ci.nsIPrefLocalizedString);
- return result.split(/\s*,\s*/g);
- },
-
-
- get uiLocale() {
- // Return the browser locale, but convert it to a Chrome-style
- // locale code.
- return Locale.getLocale().replace(/-/g, "_");
- },
-};
-
-// This is a generic class for managing event listeners. Example usage:
-//
-// new SingletonEventManager(context, "api.subAPI", fire => {
-// let listener = (...) => {
-// // Fire any listeners registered with addListener.
-// fire.async(arg1, arg2);
-// };
-// // Register the listener.
-// SomehowRegisterListener(listener);
-// return () => {
-// // Return a way to unregister the listener.
-// SomehowUnregisterListener(listener);
-// };
-// }).api()
-//
-// The result is an object with addListener, removeListener, and
-// hasListener methods. |context| is an add-on scope (either an
-// ExtensionContext in the chrome process or ExtensionContext in a
-// content process). |name| is for debugging. |register| is a function
-// to register the listener. |register| should return an
-// unregister function that will unregister the listener.
-function SingletonEventManager(context, name, register) {
- this.context = context;
- this.name = name;
- this.register = register;
- this.unregister = new Map();
-}
-
-SingletonEventManager.prototype = {
- addListener(callback, ...args) {
- if (this.unregister.has(callback)) {
- return;
- }
-
- let shouldFire = () => {
- if (this.context.unloaded) {
- dump(`${this.name} event fired after context unloaded.\n`);
- } else if (!this.context.active) {
- dump(`${this.name} event fired while context is inactive.\n`);
- } else if (this.unregister.has(callback)) {
- return true;
- }
- return false;
- };
-
- let fire = {
- sync: (...args) => {
- if (shouldFire()) {
- return this.context.runSafe(callback, ...args);
- }
- },
- async: (...args) => {
- return Promise.resolve().then(() => {
- if (shouldFire()) {
- return this.context.runSafe(callback, ...args);
- }
- });
- },
- raw: (...args) => {
- if (!shouldFire()) {
- throw new Error("Called raw() on unloaded/inactive context");
- }
- return callback(...args);
- },
- asyncWithoutClone: (...args) => {
- return Promise.resolve().then(() => {
- if (shouldFire()) {
- return this.context.runSafeWithoutClone(callback, ...args);
- }
- });
- },
- };
-
-
- let unregister = this.register(fire, ...args);
- this.unregister.set(callback, unregister);
- this.context.callOnClose(this);
- },
-
- removeListener(callback) {
- if (!this.unregister.has(callback)) {
- return;
- }
-
- let unregister = this.unregister.get(callback);
- this.unregister.delete(callback);
- try {
- unregister();
- } catch (e) {
- Cu.reportError(e);
- }
- if (this.unregister.size == 0) {
- this.context.forgetOnClose(this);
- }
- },
-
- hasListener(callback) {
- return this.unregister.has(callback);
- },
-
- revoke() {
- for (let callback of this.unregister.keys()) {
- this.removeListener(callback);
- }
- },
-
- close() {
- this.revoke();
- },
-
- api() {
- return {
- addListener: (...args) => this.addListener(...args),
- removeListener: (...args) => this.removeListener(...args),
- hasListener: (...args) => this.hasListener(...args),
- [Schemas.REVOKE]: () => this.revoke(),
- };
- },
-};
-
// Simple API for event listeners where events never fire.
function ignoreEvent(context, name) {
return {
addListener: function(callback) {
let id = context.extension.id;
let frame = Components.stack.caller;
let msg = `In add-on ${id}, attempting to use listener "${name}", which is unimplemented.`;
let scriptError = Cc["@mozilla.org/scripterror;1"]
@@ -1075,28 +727,16 @@ function PlatformInfo() {
} else if (arch == "x86_64") {
arch = "x86-64";
}
return arch;
})(),
});
}
-function detectLanguage(text) {
- return LanguageDetector.detectLanguage(text).then(result => ({
- isReliable: result.confident,
- languages: result.languages.map(lang => {
- return {
- language: lang.languageCode,
- percentage: lang.percent,
- };
- }),
- }));
-}
-
/**
* 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.
@@ -1106,17 +746,17 @@ function detectLanguage(text) {
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);
}
const stylesheetMap = new DefaultMap(url => {
- let uri = NetUtil.newURI(url);
+ let uri = Services.io.newURI(url);
return styleSheetService.preloadSheet(uri, styleSheetService.AGENT_SHEET);
});
/**
* 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
@@ -1374,17 +1014,16 @@ function classifyPermission(perm) {
return {api: match[2]};
}
return {permission: perm};
}
this.ExtensionUtils = {
classifyPermission,
defineLazyGetter,
- detectLanguage,
extend,
findPathInObject,
flushJarCache,
getConsole,
getInnerWindowID,
getMessageManager,
getUniqueId,
filterStack,
@@ -1397,23 +1036,20 @@ this.ExtensionUtils = {
promiseDocumentReady,
promiseEvent,
promiseObserved,
runSafe,
runSafeSync,
runSafeSyncWithoutClone,
runSafeWithoutClone,
stylesheetMap,
- validateThemeManifest,
DefaultMap,
DefaultWeakMap,
EventEmitter,
ExtensionError,
IconDetails,
LimitedSet,
- LocaleData,
MessageManagerProxy,
- SingletonEventManager,
SpreadArgs,
StartupCache,
};
XPCOMUtils.defineLazyGetter(this.ExtensionUtils, "PlatformInfo", PlatformInfo);
--- a/toolkit/components/extensions/ext-alarms.js
+++ b/toolkit/components/extensions/ext-alarms.js
@@ -1,14 +1,10 @@
"use strict";
-var {
- SingletonEventManager,
-} = ExtensionUtils;
-
// WeakMap[Extension -> Map[name -> Alarm]]
let alarmsMap = new WeakMap();
// WeakMap[Extension -> Set[callback]]
let alarmCallbacksMap = new WeakMap();
// Manages an alarm created by the extension (alarms API).
function Alarm(extension, name, alarmInfo) {
--- a/toolkit/components/extensions/ext-c-test.js
+++ b/toolkit/components/extensions/ext-c-test.js
@@ -1,14 +1,10 @@
"use strict";
-var {
- SingletonEventManager,
-} = ExtensionUtils;
-
/**
* Checks whether the given error matches the given expectations.
*
* @param {*} error
* The error to check.
* @param {string|RegExp|function|null} expectedError
* The expectation to check against. If this parameter is:
*
--- a/toolkit/components/extensions/ext-c-toolkit.js
+++ b/toolkit/components/extensions/ext-c-toolkit.js
@@ -1,10 +1,14 @@
"use strict";
+Cu.import("resource://gre/modules/ExtensionCommon.jsm");
+
+global.SingletonEventManager = ExtensionCommon.SingletonEventManager;
+
global.initializeBackgroundPage = (contentWindow) => {
// Override the `alert()` method inside background windows;
// we alias it to console.log().
// See: https://bugzilla.mozilla.org/show_bug.cgi?id=1203394
let alertDisplayedWarning = false;
let alertOverwrite = text => {
if (!alertDisplayedWarning) {
require("devtools/client/framework/devtools-browser");
--- a/toolkit/components/extensions/ext-cookies.js
+++ b/toolkit/components/extensions/ext-cookies.js
@@ -2,20 +2,16 @@
XPCOMUtils.defineLazyModuleGetter(this, "ContextualIdentityService",
"resource://gre/modules/ContextualIdentityService.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
"resource://gre/modules/NetUtil.jsm");
/* globals DEFAULT_STORE, PRIVATE_STORE */
-var {
- SingletonEventManager,
-} = ExtensionUtils;
-
function convert({cookie, isPrivate}) {
let result = {
name: cookie.name,
value: cookie.value,
domain: cookie.host,
hostOnly: !cookie.isDomain,
path: cookie.path,
secure: cookie.isSecure,
--- a/toolkit/components/extensions/ext-downloads.js
+++ b/toolkit/components/extensions/ext-downloads.js
@@ -11,17 +11,16 @@ XPCOMUtils.defineLazyModuleGetter(this,
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
"resource://gre/modules/NetUtil.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "EventEmitter",
"resource://devtools/shared/event-emitter.js");
var {
ignoreEvent,
normalizeTime,
- SingletonEventManager,
PlatformInfo,
} = ExtensionUtils;
const DOWNLOAD_ITEM_FIELDS = ["id", "url", "referrer", "filename", "incognito",
"danger", "mime", "startTime", "endTime",
"estimatedEndTime", "state",
"paused", "canResume", "error",
"bytesReceived", "totalBytes",
--- a/toolkit/components/extensions/ext-i18n.js
+++ b/toolkit/components/extensions/ext-i18n.js
@@ -1,13 +1,13 @@
"use strict";
-var {
- detectLanguage,
-} = ExtensionUtils;
+XPCOMUtils.defineLazyModuleGetter(this, "LanguageDetector",
+ "resource:///modules/translation/LanguageDetector.jsm");
+
this.i18n = class extends ExtensionAPI {
getAPI(context) {
let {extension} = context;
return {
i18n: {
getMessage: function(messageName, substitutions) {
return extension.localizeMessage(messageName, substitutions, {cloneScope: context.cloneScope});
@@ -18,14 +18,22 @@ this.i18n = class extends ExtensionAPI {
return Promise.resolve(result);
},
getUILanguage: function() {
return extension.localeData.uiLocale;
},
detectLanguage: function(text) {
- return detectLanguage(text);
+ return LanguageDetector.detectLanguage(text).then(result => ({
+ isReliable: result.confident,
+ languages: result.languages.map(lang => {
+ return {
+ language: lang.languageCode,
+ percentage: lang.percent,
+ };
+ }),
+ }));
},
},
};
}
};
--- a/toolkit/components/extensions/ext-idle.js
+++ b/toolkit/components/extensions/ext-idle.js
@@ -1,18 +1,15 @@
"use strict";
XPCOMUtils.defineLazyModuleGetter(this, "EventEmitter",
"resource://devtools/shared/event-emitter.js");
XPCOMUtils.defineLazyServiceGetter(this, "idleService",
"@mozilla.org/widget/idleservice;1",
"nsIIdleService");
-var {
- SingletonEventManager,
-} = ExtensionUtils;
// WeakMap[Extension -> Object]
let observersMap = new WeakMap();
function getObserverInfo(extension, context) {
let observerInfo = observersMap.get(extension);
if (!observerInfo) {
observerInfo = {
--- a/toolkit/components/extensions/ext-notifications.js
+++ b/toolkit/components/extensions/ext-notifications.js
@@ -1,15 +1,14 @@
"use strict";
XPCOMUtils.defineLazyModuleGetter(this, "EventEmitter",
"resource://devtools/shared/event-emitter.js");
var {
- SingletonEventManager,
ignoreEvent,
} = ExtensionUtils;
// WeakMap[Extension -> Map[id -> Notification]]
let notificationsMap = new WeakMap();
// Manages a notification popup (notifications API) created by the extension.
function Notification(extension, id, options) {
--- a/toolkit/components/extensions/ext-proxy.js
+++ b/toolkit/components/extensions/ext-proxy.js
@@ -5,20 +5,16 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
XPCOMUtils.defineLazyModuleGetter(this, "ProxyScriptContext",
"resource://gre/modules/ProxyScriptContext.jsm");
-var {
- SingletonEventManager,
-} = ExtensionUtils;
-
// WeakMap[Extension -> ProxyScriptContext]
let proxyScriptContextMap = new WeakMap();
this.proxy = class extends ExtensionAPI {
onShutdown() {
let {extension} = this;
let proxyScriptContext = proxyScriptContextMap.get(extension);
--- a/toolkit/components/extensions/ext-runtime.js
+++ b/toolkit/components/extensions/ext-runtime.js
@@ -4,20 +4,16 @@ XPCOMUtils.defineLazyModuleGetter(this,
"resource://gre/modules/AddonManager.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Extension",
"resource://gre/modules/Extension.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionManagement",
"resource://gre/modules/ExtensionManagement.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
"resource://gre/modules/NetUtil.jsm");
-var {
- SingletonEventManager,
-} = ExtensionUtils;
-
this.runtime = class extends ExtensionAPI {
getAPI(context) {
let {extension} = context;
return {
runtime: {
onStartup: new SingletonEventManager(context, "runtime.onStartup", fire => {
if (context.incognito) {
// This event should not fire if we are operating in a private profile.
--- a/toolkit/components/extensions/ext-storage.js
+++ b/toolkit/components/extensions/ext-storage.js
@@ -4,17 +4,16 @@ XPCOMUtils.defineLazyModuleGetter(this,
"resource://gre/modules/ExtensionStorage.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "extensionStorageSync",
"resource://gre/modules/ExtensionStorageSync.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "AddonManagerPrivate",
"resource://gre/modules/AddonManager.jsm");
var {
ExtensionError,
- SingletonEventManager,
} = ExtensionUtils;
function enforceNoTemporaryAddon(extensionId) {
const EXCEPTION_MESSAGE =
"The storage API will not work with a temporary addon ID. " +
"Please add an explicit addon ID to your manifest. " +
"For more information see https://bugzil.la/1323228.";
if (AddonManagerPrivate.isTemporaryInstallID(extensionId)) {
--- a/toolkit/components/extensions/ext-toolkit.js
+++ b/toolkit/components/extensions/ext-toolkit.js
@@ -1,13 +1,17 @@
"use strict";
XPCOMUtils.defineLazyModuleGetter(this, "ContextualIdentityService",
"resource://gre/modules/ContextualIdentityService.jsm");
+Cu.import("resource://gre/modules/ExtensionCommon.jsm");
+
+global.SingletonEventManager = ExtensionCommon.SingletonEventManager;
+
/* globals DEFAULT_STORE, PRIVATE_STORE, CONTAINER_STORE */
global.DEFAULT_STORE = "firefox-default";
global.PRIVATE_STORE = "firefox-private";
global.CONTAINER_STORE = "firefox-container-";
global.getCookieStoreIdForTab = function(data, tab) {
if (data.incognito) {
--- a/toolkit/components/extensions/ext-webNavigation.js
+++ b/toolkit/components/extensions/ext-webNavigation.js
@@ -2,20 +2,16 @@
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionManagement",
"resource://gre/modules/ExtensionManagement.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "MatchURLFilters",
"resource://gre/modules/MatchPattern.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "WebNavigation",
"resource://gre/modules/WebNavigation.jsm");
-var {
- SingletonEventManager,
-} = ExtensionUtils;
-
const defaultTransitionTypes = {
topFrame: "link",
subFrame: "auto_subframe",
};
const frameTransitions = {
anyFrame: {
qualifiers: ["server_redirect", "client_redirect", "forward_back"],
--- a/toolkit/components/extensions/ext-webRequest.js
+++ b/toolkit/components/extensions/ext-webRequest.js
@@ -1,44 +1,39 @@
"use strict";
+XPCOMUtils.defineLazyModuleGetter(this, "ExtensionManagement",
+ "resource://gre/modules/ExtensionManagement.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "MatchPattern",
"resource://gre/modules/MatchPattern.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
- "resource://gre/modules/NetUtil.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "WebRequest",
"resource://gre/modules/WebRequest.jsm");
-Cu.import("resource://gre/modules/ExtensionManagement.jsm");
-var {
- SingletonEventManager,
-} = ExtensionUtils;
-
// EventManager-like class specifically for WebRequest. Inherits from
// SingletonEventManager. Takes care of converting |details| parameter
// when invoking listeners.
function WebRequestEventManager(context, eventName) {
let name = `webRequest.${eventName}`;
let register = (fire, filter, info) => {
let listener = data => {
// Prevent listening in on requests originating from system principal to
// prevent tinkering with OCSP, app and addon updates, etc.
if (data.isSystemPrincipal) {
return;
}
// Check hosts permissions for both the resource being requested,
const hosts = context.extension.whiteListedHosts;
- if (!hosts.matchesIgnoringPath(NetUtil.newURI(data.url))) {
+ if (!hosts.matchesIgnoringPath(Services.io.newURI(data.url))) {
return;
}
// and the origin that is loading the resource.
const origin = data.documentUrl;
const own = origin && origin.startsWith(context.extension.getURL());
- if (origin && !own && !hosts.matchesIgnoringPath(NetUtil.newURI(origin))) {
+ if (origin && !own && !hosts.matchesIgnoringPath(Services.io.newURI(origin))) {
return;
}
let browserData = {tabId: -1, windowId: -1};
if (data.browser) {
browserData = tabTracker.getBrowserData(data.browser);
}
if (filter.tabId != null && browserData.tabId != filter.tabId) {
--- a/toolkit/components/extensions/test/xpcshell/test_ext_contexts.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_contexts.js
@@ -1,25 +1,21 @@
"use strict";
const global = this;
Cu.import("resource://gre/modules/Timer.jsm");
Cu.import("resource://gre/modules/ExtensionCommon.jsm");
-Cu.import("resource://gre/modules/ExtensionUtils.jsm");
var {
BaseContext,
+ SingletonEventManager,
} = ExtensionCommon;
-var {
- SingletonEventManager,
-} = ExtensionUtils;
-
class StubContext extends BaseContext {
constructor() {
let fakeExtension = {id: "test@web.extension"};
super("testEnv", fakeExtension);
this.sandbox = Cu.Sandbox(global);
}
get cloneScope() {
--- a/toolkit/components/extensions/test/xpcshell/test_ext_themes_supported_properties.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_themes_supported_properties.js
@@ -1,18 +1,17 @@
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set sts=2 sw=2 et tw=80: */
"use strict";
-Cu.import("resource://gre/modules/ExtensionUtils.jsm");
Cu.import("resource://gre/modules/Schemas.jsm");
const {
validateThemeManifest,
-} = ExtensionUtils;
+} = Cu.import("resource://gre/modules/Extension.jsm", {});
const BASE_SCHEMA_URL = "chrome://extensions/content/schemas/manifest.json";
const CATEGORY_EXTENSION_SCHEMAS = "webextension-schemas";
const baseManifestProperties = [
"manifest_version",
"minimum_chrome_version",
"applications",