--- a/common/moz.build
+++ b/common/moz.build
@@ -3,12 +3,8 @@
# 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/.
DIRS += [
'public',
'src',
'saxparser'
]
-
-XPCSHELL_TESTS_MANIFESTS += [
- 'test/xpcshell/xpcshell.ini',
-]
--- a/common/src/ExtensionSupport.jsm
+++ b/common/src/ExtensionSupport.jsm
@@ -4,205 +4,23 @@
/**
* Helper functions for use by extensions that should ease them plug
* into the application.
*/
this.EXPORTED_SYMBOLS = ["ExtensionSupport"];
-const { AddonManager } = ChromeUtils.import(
- "resource://gre/modules/AddonManager.jsm"
-);
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
-// ChromeUtils.import("resource://gre/modules/Deprecated.jsm") - needed for warning.
-const { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
-
-var { fixIterator } = ChromeUtils.import(
- "resource:///modules/iteratorUtils.jsm"
-);
-const { IOUtils } = ChromeUtils.import("resource:///modules/IOUtils.jsm");
var extensionHooks = new Map();
-var legacyExtensions = new Map();
-var bootstrapExtensions = new Set();
var openWindowList;
var ExtensionSupport = {
/**
- * A Map-like object which tracks legacy extension status. The "has" method
- * returns only active extensions for compatibility with existing code.
- */
- loadedLegacyExtensions: {
- set(id, state) {
- legacyExtensions.set(id, state);
- },
- get(id) {
- return legacyExtensions.get(id);
- },
- has(id) {
- if (!legacyExtensions.has(id)) {
- return false;
- }
-
- let state = legacyExtensions.get(id);
- return !["install", "enable"].includes(state.pendingOperation);
- },
- hasAnyState(id) {
- return legacyExtensions.has(id);
- },
- _maybeDelete(id, newPendingOperation) {
- if (!legacyExtensions.has(id)) {
- return;
- }
-
- let state = legacyExtensions.get(id);
- if (
- state.pendingOperation == "enable" &&
- newPendingOperation == "disable"
- ) {
- legacyExtensions.delete(id);
- this.notifyObservers(state);
- } else if (
- state.pendingOperation == "install" &&
- newPendingOperation == "uninstall"
- ) {
- legacyExtensions.delete(id);
- this.notifyObservers(state);
- }
- },
- notifyObservers(state) {
- let wrappedState = { wrappedJSObject: state };
- Services.obs.notifyObservers(wrappedState, "legacy-addon-status-changed");
- },
- // AddonListener
- onDisabled(ev) {
- this._maybeDelete(ev.id, "disable");
- },
- onUninstalled(ev) {
- this._maybeDelete(ev.id, "uninstall");
- },
- async listRemoved() {
- let running = [...legacyExtensions.keys()];
- let installed = await AddonManager.getAddonsByIDs(running);
- let runningButNotInstalled = [];
- for (let i = 0; i < running.length; i++) {
- if (installed[i] === null) {
- runningButNotInstalled.push(legacyExtensions.get(running[i]));
- }
- }
- return runningButNotInstalled;
- },
- },
-
- loadedBootstrapExtensions: bootstrapExtensions,
-
- loadAddonPrefs(addonFile) {
- function setPref(preferDefault, name, value) {
- let branch = preferDefault
- ? Services.prefs.getDefaultBranch("")
- : Services.prefs.getBranch("");
-
- if (typeof value == "boolean") {
- branch.setBoolPref(name, value);
- } else if (typeof value == "string") {
- if (value.startsWith("chrome://") && value.endsWith(".properties")) {
- let valueLocal = Cc[
- "@mozilla.org/pref-localizedstring;1"
- ].createInstance(Ci.nsIPrefLocalizedString);
- valueLocal.data = value;
- branch.setComplexValue(name, Ci.nsIPrefLocalizedString, valueLocal);
- } else {
- branch.setStringPref(name, value);
- }
- } else if (typeof value == "number" && Number.isInteger(value)) {
- branch.setIntPref(name, value);
- } else if (typeof value == "number" && Number.isFloat(value)) {
- // Floats are set as char prefs, then retrieved using getFloatPref
- branch.setCharPref(name, value);
- }
- }
-
- function walkExtensionPrefs(extensionRoot) {
- let prefFile = extensionRoot.clone();
- let foundPrefStrings = [];
- if (!prefFile.exists()) {
- return [];
- }
-
- if (prefFile.isDirectory()) {
- prefFile.append("defaults");
- prefFile.append("preferences");
- if (!prefFile.exists() || !prefFile.isDirectory()) {
- return [];
- }
-
- let unsortedFiles = [];
- for (let file of fixIterator(prefFile.directoryEntries, Ci.nsIFile)) {
- if (file.isFile() && file.leafName.toLowerCase().endsWith(".js")) {
- unsortedFiles.push(file);
- }
- }
-
- for (let file of unsortedFiles.sort((a, b) =>
- a.path < b.path ? 1 : -1
- )) {
- foundPrefStrings.push(IOUtils.loadFileToString(file));
- }
- } else if (prefFile.isFile() && prefFile.leafName.endsWith("xpi")) {
- let zipReader = Cc["@mozilla.org/libjar/zip-reader;1"].createInstance(
- Ci.nsIZipReader
- );
- zipReader.open(prefFile);
- let entries = zipReader.findEntries("defaults/preferences/*.js");
-
- for (let entryName of [...entries].sort().reverse()) {
- let stream = zipReader.getInputStream(entryName);
- let entrySize = zipReader.getEntry(entryName).realSize;
- if (entrySize > 0) {
- let content = NetUtil.readInputStreamToString(stream, entrySize, {
- charset: "utf-8",
- replacement: "?",
- });
- foundPrefStrings.push(content);
- }
- }
- }
-
- return foundPrefStrings;
- }
-
- let sandbox = new Cu.Sandbox(null);
- sandbox.pref = setPref.bind(undefined, true);
- sandbox.user_pref = setPref.bind(undefined, false);
-
- let prefDataStrings = walkExtensionPrefs(addonFile);
- for (let prefDataString of prefDataStrings) {
- try {
- Cu.evalInSandbox(prefDataString, sandbox);
- } catch (e) {
- Cu.reportError(
- "Error reading default prefs of addon " +
- addonFile.leafName +
- ": " +
- e
- );
- }
- }
-
- /*
- TODO: decide whether we need to warn the user/make addon authors to migrate away from these pref files.
- if (prefDataStrings.length > 0) {
- Deprecated.warning(addon.defaultLocale.name + " uses defaults/preferences/*.js files to load prefs",
- "https://bugzilla.mozilla.org/show_bug.cgi?id=1414398");
- }
- */
- },
-
- /**
* Register listening for windows getting opened that will run the specified callback function
* when a matching window is loaded.
*
* @param aID {String} Some identification of the caller, usually the extension ID.
* @param aExtensionHook {Object} The object describing the hook the caller wants to register.
* Members of the object can be (all optional, but one callback must be supplied):
* chromeURLs {Array} An array of strings of document URLs on which
* the given callback should run. If not specified,
@@ -410,10 +228,8 @@ var ExtensionSupport = {
}
}
},
get registeredWindowListenerCount() {
return extensionHooks.size;
},
};
-
-AddonManager.addAddonListener(ExtensionSupport.loadedLegacyExtensions);
deleted file mode 100644
--- a/common/src/Overlays.jsm
+++ /dev/null
@@ -1,603 +0,0 @@
-/* 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/. */
-
-/**
- * Load overlays in a similar way as XUL did for legacy XUL add-ons.
- */
-
-"use strict";
-
-this.EXPORTED_SYMBOLS = ["Overlays"];
-
-const { ConsoleAPI } = ChromeUtils.import("resource://gre/modules/Console.jsm");
-ChromeUtils.defineModuleGetter(
- this,
- "Services",
- "resource://gre/modules/Services.jsm"
-);
-ChromeUtils.defineModuleGetter(
- this,
- "setTimeout",
- "resource://gre/modules/Timer.jsm"
-);
-
-let oconsole = new ConsoleAPI({
- prefix: "Overlays.jsm",
- consoleID: "overlays-jsm",
- maxLogLevelPref: "extensions.overlayloader.loglevel",
-});
-
-/**
- * The overlays class, providing support for loading overlays like they used to work. This class
- * should likely be called through its static method Overlays.load()
- */
-class Overlays {
- /**
- * Load overlays for the given window using the overlay provider, which can for example be a
- * ChromeManifest object.
- *
- * @param {ChromeManifest} overlayProvider The overlay provider that contains information
- * about styles and overlays.
- * @param {DOMWindow} window The window to load into
- */
- static load(overlayProvider, window) {
- let instance = new Overlays(overlayProvider, window);
-
- let urls = overlayProvider.overlay.get(instance.location, false);
- instance.load(urls);
- }
-
- /**
- * Constructs the overlays instance. This class should be called via Overlays.load() instead.
- *
- * @param {ChromeManifest} overlayProvider The overlay provider that contains information
- * about styles and overlays.
- * @param {DOMWindow} window The window to load into
- */
- constructor(overlayProvider, window) {
- this.overlayProvider = overlayProvider;
- this.window = window;
- if (window.location.protocol == "about:") {
- this.location = window.location.protocol + window.location.pathname;
- } else {
- this.location = window.location.origin + window.location.pathname;
- }
- }
-
- /**
- * A shorthand to this.window.document
- */
- get document() {
- return this.window.document;
- }
-
- /**
- * Loads the given urls into the window, recursively loading further overlays as provided by the
- * overlayProvider.
- *
- * @param {String[]} urls The urls to load
- */
- load(urls) {
- let unloadedOverlays = this._collectOverlays(this.document).concat(urls);
- let forwardReferences = [];
- let unloadedScripts = [];
- let unloadedSheets = [];
- this._toolbarsToResolve = [];
- let xulStore = Services.xulStore;
- this.persistedIDs = new Set();
-
- // Load css styles from the registry
- for (let sheet of this.overlayProvider.style.get(this.location, false)) {
- unloadedSheets.push(sheet);
- }
-
- if (!unloadedOverlays.length && !unloadedSheets.length) {
- return;
- }
-
- while (unloadedOverlays.length) {
- let url = unloadedOverlays.shift();
- let xhr = this.fetchOverlay(url);
- let doc = xhr.responseXML;
-
- oconsole.debug(`Applying ${url} to ${this.location}`);
-
- // clean the document a bit
- let emptyNodes = doc.evaluate(
- "//text()[normalize-space(.) = '']",
- doc,
- null,
- 7,
- null
- );
- for (let i = 0, len = emptyNodes.snapshotLength; i < len; ++i) {
- let node = emptyNodes.snapshotItem(i);
- node.remove();
- }
-
- let commentNodes = doc.evaluate("//comment()", doc, null, 7, null);
- for (let i = 0, len = commentNodes.snapshotLength; i < len; ++i) {
- let node = commentNodes.snapshotItem(i);
- node.remove();
- }
-
- // Force a re-evaluation of inline styles to work around an issue
- // causing inline styles to be initially ignored.
- let styledNodes = doc.evaluate("//*[@style]", doc, null, 7, null);
- for (let i = 0, len = styledNodes.snapshotLength; i < len; ++i) {
- let node = styledNodes.snapshotItem(i);
- node.style.display = node.style.display; // eslint-disable-line no-self-assign
- }
-
- // Load css styles from the registry
- for (let sheet of this.overlayProvider.style.get(url, false)) {
- unloadedSheets.push(sheet);
- }
-
- // Load css processing instructions from the overlay
- let stylesheets = doc.evaluate(
- "/processing-instruction('xml-stylesheet')",
- doc,
- null,
- 7,
- null
- );
- for (let i = 0, len = stylesheets.snapshotLength; i < len; ++i) {
- let node = stylesheets.snapshotItem(i);
- let match = node.nodeValue.match(/href=["']([^"']*)["']/);
- if (match) {
- unloadedSheets.push(new URL(match[1], node.baseURI).href);
- }
- }
-
- // Prepare loading further nested xul overlays from the overlay
- unloadedOverlays.push(...this._collectOverlays(doc));
-
- // Prepare loading further nested xul overlays from the registry
- for (let overlayUrl of this.overlayProvider.overlay.get(url, false)) {
- unloadedOverlays.push(overlayUrl);
- }
-
- // Run through all overlay nodes on the first level (hookup nodes). Scripts will be deferred
- // until later for simplicity (c++ code seems to process them earlier?).
- for (let node of doc.documentElement.children) {
- if (node.localName == "script") {
- unloadedScripts.push(node);
- } else {
- forwardReferences.push(node);
- }
- }
- }
-
- let ids = xulStore.getIDsEnumerator(this.location);
- while (ids.hasMore()) {
- this.persistedIDs.add(ids.getNext());
- }
-
- // At this point, all (recursive) overlays are loaded. Unloaded scripts and sheets are ready and
- // in order, and forward references are good to process.
- let previous = 0;
- while (forwardReferences.length && forwardReferences.length != previous) {
- previous = forwardReferences.length;
- let unresolved = [];
-
- for (let ref of forwardReferences) {
- if (!this._resolveForwardReference(ref)) {
- unresolved.push(ref);
- }
- }
-
- forwardReferences = unresolved;
- }
-
- if (forwardReferences.length) {
- oconsole.warn(
- `Could not resolve ${forwardReferences.length} references`,
- forwardReferences
- );
- }
-
- // Loading the sheets now to avoid race conditions with xbl bindings
- for (let sheet of unloadedSheets) {
- this.loadCSS(sheet);
- }
-
- this._decksToResolve = new Map();
- for (let id of this.persistedIDs.values()) {
- let element = this.document.getElementById(id);
- if (element) {
- let attrNames = xulStore.getAttributeEnumerator(this.location, id);
- while (attrNames.hasMore()) {
- let attrName = attrNames.getNext();
- let attrValue = xulStore.getValue(this.location, id, attrName);
- if (attrName == "selectedIndex" && element.localName == "deck") {
- this._decksToResolve.set(element, attrValue);
- } else if (
- element != this.document.documentElement ||
- !["height", "screenX", "screenY", "sizemode", "width"].includes(
- attrName
- )
- ) {
- element.setAttribute(attrName, attrValue);
- }
- }
- }
- }
-
- // We've resolved all the forward references we can, we can now go ahead and load the scripts
- let deferredLoad = [];
- for (let script of unloadedScripts) {
- deferredLoad.push(...this.loadScript(script));
- }
-
- if (this.document.readyState == "complete") {
- setTimeout(() => {
- this._finish();
-
- // Now execute load handlers since we are done loading scripts
- let bubbles = [];
- for (let { listener, useCapture } of deferredLoad) {
- if (useCapture) {
- this._fireEventListener(listener);
- } else {
- bubbles.push(listener);
- }
- }
-
- for (let listener of bubbles) {
- this._fireEventListener(listener);
- }
- });
- } else {
- this.document.defaultView.addEventListener(
- "load",
- this._finish.bind(this),
- { once: true }
- );
- }
- }
-
- _finish() {
- for (let [deck, selectedIndex] of this._decksToResolve.entries()) {
- deck.setAttribute("selectedIndex", selectedIndex);
- }
-
- for (let bar of this._toolbarsToResolve) {
- let currentset = Services.xulStore.getValue(
- this.location,
- bar.id,
- "currentset"
- );
- if (currentset) {
- bar.currentSet = currentset;
- } else if (bar.getAttribute("defaultset")) {
- bar.currentSet = bar.getAttribute("defaultset");
- }
- }
- }
-
- /**
- * Gets the overlays referenced by processing instruction on a document.
- *
- * @param {DOMDocument} document The document to read instuctions from
- * @return {String[]} URLs of the overlays from the document
- */
- _collectOverlays(doc) {
- let urls = [];
- let instructions = doc.evaluate(
- "/processing-instruction('xul-overlay')",
- doc,
- null,
- 7,
- null
- );
- for (let i = 0, len = instructions.snapshotLength; i < len; ++i) {
- let node = instructions.snapshotItem(i);
- let match = node.nodeValue.match(/href=["']([^"']*)["']/);
- if (match) {
- urls.push(match[1]);
- }
- }
- return urls;
- }
-
- /**
- * Fires a "load" event for the given listener, using the current window
- *
- * @param {EventListener|Function} listener The event listener to call
- */
- _fireEventListener(listener) {
- let fakeEvent = new this.window.UIEvent("load", { view: this.window });
- if (typeof listener == "function") {
- listener(fakeEvent);
- } else if (listener && typeof listener == "object") {
- listener.handleEvent(fakeEvent);
- } else {
- oconsole.error("Unknown listener type", listener);
- }
- }
-
- /**
- * Resolves forward references for the given node. If the node exists in the target document, it
- * is merged in with the target node. If the node has no id it is inserted at documentElement
- * level.
- *
- * @param {Element} node The DOM Element to resolve in the target document.
- * @return {Boolean} True, if the node was merged/inserted, false otherwise
- */
- _resolveForwardReference(node) {
- if (node.id) {
- let target = this.document.getElementById(node.id);
- if (node.localName == "toolbarpalette") {
- let box;
- if (target) {
- box = target.closest("toolbox");
- } else {
- // These vanish from the document but still exist via the palette property
- let boxes = [...this.document.getElementsByTagName("toolbox")];
- box = boxes.find(box => box.palette && box.palette.id == node.id);
- let palette = box ? box.palette : null;
-
- if (!palette) {
- oconsole.debug(
- `The palette for ${
- node.id
- } could not be found, deferring to later`
- );
- return false;
- }
-
- target = palette;
- }
-
- this._toolbarsToResolve.push(...box.querySelectorAll("toolbar"));
- this._toolbarsToResolve.push(
- ...this.document.querySelectorAll(`toolbar[toolboxid="${box.id}"]`)
- );
- } else if (!target) {
- oconsole.debug(
- `The node ${node.id} could not be found, deferring to later`
- );
- return false;
- }
-
- this._mergeElement(target, node);
- } else {
- this._insertElement(this.document.documentElement, node);
- }
- return true;
- }
-
- /**
- * Insert the node in the given parent, observing the insertbefore/insertafter/position attributes
- *
- * @param {Element} parent The parent element to insert the node into.
- * @param {Element} node The node to insert.
- */
- _insertElement(parent, node) {
- // These elements need their values set before they are added to
- // the document, or bad things happen.
- for (let element of node.querySelectorAll("menulist")) {
- if (element.id && this.persistedIDs.has(element.id)) {
- element.setAttribute(
- "value",
- Services.xulStore.getValue(this.location, element.id, "value")
- );
- }
- }
-
- if (node.localName == "toolbar") {
- this._toolbarsToResolve.push(node);
- } else {
- this._toolbarsToResolve.push(...node.querySelectorAll("toolbar"));
- }
-
- let wasInserted = false;
- let pos = node.getAttribute("insertafter");
- let after = true;
-
- if (!pos) {
- pos = node.getAttribute("insertbefore");
- after = false;
- }
-
- if (pos) {
- for (let id of pos.split(",")) {
- let targetchild = this.document.getElementById(id);
- if (targetchild && targetchild.parentNode == parent) {
- parent.insertBefore(
- node,
- after ? targetchild.nextElementSibling : targetchild
- );
- wasInserted = true;
- break;
- }
- }
- }
-
- if (!wasInserted) {
- // position is 1-based
- let position = parseInt(node.getAttribute("position"), 10);
- if (position > 0 && position - 1 <= parent.children.length) {
- parent.insertBefore(node, parent.children[position - 1]);
- wasInserted = true;
- }
- }
-
- if (!wasInserted) {
- parent.appendChild(node);
- }
- }
-
- /**
- * Merge the node into the target, adhering to the removeelement attribute, merging further
- * attributes into the target node, and merging children as appropriate for xul nodes. If a child
- * has an id, it will be searched in the target document and recursively merged.
- *
- * @param {Element} target The node to merge into
- * @param {Element} node The node that is being merged
- */
- _mergeElement(target, node) {
- for (let attribute of node.attributes) {
- if (attribute.name == "id") {
- continue;
- }
-
- if (attribute.name == "removeelement" && attribute.value == "true") {
- target.remove();
- return;
- }
-
- target.setAttributeNS(
- attribute.namespaceURI,
- attribute.name,
- attribute.value
- );
- }
-
- for (let i = 0, len = node.childElementCount; i < len; i++) {
- let child = node.firstElementChild;
- child.remove();
-
- let elementInDocument = child.id
- ? this.document.getElementById(child.id)
- : null;
- let parentId = elementInDocument ? elementInDocument.parentNode.id : null;
-
- if (parentId && parentId == target.id) {
- this._mergeElement(elementInDocument, child);
- } else {
- this._insertElement(target, child);
- }
- }
- }
-
- /**
- * Fetches the overlay from the given chrome:// or resource:// URL. This happen synchronously so
- * we have a chance to complete before the load event.
- *
- * @param {String} srcUrl The URL to load
- * @return {XMLHttpRequest} The completed XHR.
- */
- fetchOverlay(srcUrl) {
- if (!srcUrl.startsWith("chrome://") && !srcUrl.startsWith("resource://")) {
- throw new Error(
- "May only load overlays from chrome:// or resource:// uris"
- );
- }
-
- let xhr = new this.window.XMLHttpRequest();
- xhr.overrideMimeType("application/xml");
- xhr.open("GET", srcUrl, false);
-
- // Elevate the request, so DTDs will work. Should not be a security issue since we
- // only load chrome, resource and file URLs, and that is our privileged chrome package.
- try {
- xhr.channel.owner = Services.scriptSecurityManager.getSystemPrincipal();
- } catch (ex) {
- oconsole.error(
- "Failed to set system principal while fetching overlay " + srcUrl
- );
- xhr.close();
- throw new Error("Failed to set system principal");
- }
-
- xhr.send(null);
- return xhr;
- }
-
- /**
- * Loads scripts described by the given script node. The node can either have a src attribute, or
- * be an inline script with textContent.
- *
- * @param {Element} node The <script> element to load the script from
- * @return {Object[]} An object with listener and useCapture,
- * describing load handlers the script creates
- * when first run.
- */
- loadScript(node) {
- let deferredLoad = [];
-
- let oldAddEventListener = this.window.addEventListener;
- if (this.document.readyState == "complete") {
- this.window.addEventListener = function(
- type,
- listener,
- useCapture,
- ...args
- ) {
- if (type == "load") {
- if (typeof useCapture == "object") {
- useCapture = useCapture.capture;
- }
-
- if (typeof useCapture == "undefined") {
- useCapture = true;
- }
- deferredLoad.push({ listener, useCapture });
- return null;
- }
- return oldAddEventListener.call(
- this,
- type,
- listener,
- useCapture,
- ...args
- );
- };
- }
-
- if (node.hasAttribute("src")) {
- let url = new URL(node.getAttribute("src"), node.baseURI).href;
- oconsole.debug(`Loading script ${url} into ${this.window.location}`);
- try {
- Services.scriptloader.loadSubScript(url, this.window);
- } catch (ex) {
- Cu.reportError(ex);
- }
- } else if (node.textContent) {
- oconsole.debug(`Loading eval'd script into ${this.window.location}`);
- try {
- let dataURL =
- "data:application/javascript," + encodeURIComponent(node.textContent);
- // It would be great if we could have script errors show the right url, but for now
- // loadSubScript will have to do.
- Services.scriptloader.loadSubScript(dataURL, this.window);
- } catch (ex) {
- Cu.reportError(ex);
- }
- }
-
- if (this.document.readyState == "complete") {
- this.window.addEventListener = oldAddEventListener;
- }
-
- // This works because we only care about immediately executed addEventListener calls and
- // loadSubScript is synchronous. Everyone else should be checking readyState anyway.
- return deferredLoad;
- }
-
- /**
- * Load the CSS stylesheet from the given url
- *
- * @param {String} url The url to load from
- * @return {Element} An HTML link element for this stylesheet
- */
- loadCSS(url) {
- oconsole.debug(`Loading ${url} into ${this.window.location}`);
-
- // domWindowUtils.loadSheetUsingURIString doesn't record the sheet in document.styleSheets,
- // adding a html link element seems to do so.
- let link = this.document.createElementNS(
- "http://www.w3.org/1999/xhtml",
- "link"
- );
- link.setAttribute("rel", "stylesheet");
- link.setAttribute("type", "text/css");
- link.setAttribute("href", url);
-
- this.document.documentElement.appendChild(link);
- return link;
- }
-}
--- a/common/src/moz.build
+++ b/common/src/moz.build
@@ -1,17 +1,16 @@
# vim: set filetype=python:
# 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/.
EXTRA_JS_MODULES += [
'ChromeManifest.jsm',
'ExtensionSupport.jsm',
- 'Overlays.jsm',
]
SOURCES += [
'nsCommonModule.cpp',
'nsComponentManagerExtra.cpp',
]
if CONFIG['OS_ARCH'] == 'WINNT':
deleted file mode 100644
--- a/common/test/xpcshell/data/BootstrapMonitor.jsm
+++ /dev/null
@@ -1,41 +0,0 @@
-/* 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/. */
-
-const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
-
-var EXPORTED_SYMBOLS = ["monitor"];
-
-function notify(event, originalMethod, data, reason) {
- let info = {
- event,
- data: Object.assign({}, data, {
- resourceURI: data.resourceURI.spec,
- }),
- reason,
- };
-
- let subject = { wrappedJSObject: { data } };
-
- Services.obs.notifyObservers(
- subject,
- "bootstrapmonitor-event",
- JSON.stringify(info)
- );
-
- // If the bootstrap scope already declares a method call it
- if (originalMethod) {
- originalMethod(data, reason);
- }
-}
-
-// Allows a simple one-line bootstrap script:
-// Components.utils.import("resource://xpcshelldata/bootstrapmonitor.jsm").monitor(this);
-var monitor = function(
- scope,
- methods = ["install", "startup", "shutdown", "uninstall"]
-) {
- for (let event of methods) {
- scope[event] = notify.bind(null, event, scope[event]);
- }
-};
deleted file mode 100644
--- a/common/test/xpcshell/head_addons.js
+++ /dev/null
@@ -1,1534 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
-
-const PREF_EM_CHECK_UPDATE_SECURITY = "extensions.checkUpdateSecurity";
-const PREF_EM_STRICT_COMPATIBILITY = "extensions.strictCompatibility";
-const PREF_GETADDONS_BYIDS = "extensions.getAddons.get.url";
-const PREF_COMPAT_OVERRIDES = "extensions.getAddons.compatOverides.url";
-const PREF_XPI_SIGNATURES_REQUIRED = "xpinstall.signatures.required";
-
-const PREF_DISABLE_SECURITY =
- "security.turn_off_all_security_so_that_" +
- "viruses_can_take_over_this_computer";
-
-// Maximum error in file modification times. Some file systems don't store
-// modification times exactly. As long as we are closer than this then it
-// still passes.
-const MAX_TIME_DIFFERENCE = 3000;
-
-// Time to reset file modified time relative to Date.now() so we can test that
-// times are modified (10 hours old).
-const MAKE_FILE_OLD_DIFFERENCE = 10 * 3600 * 1000;
-
-var { AppConstants } = ChromeUtils.import(
- "resource://gre/modules/AppConstants.jsm"
-);
-var { FileUtils } = ChromeUtils.import("resource://gre/modules/FileUtils.jsm");
-var { NetUtil } = ChromeUtils.import("resource://gre/modules/NetUtil.jsm");
-var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
-var { XPCOMUtils } = ChromeUtils.import(
- "resource://gre/modules/XPCOMUtils.jsm"
-);
-var { AddonRepository } = ChromeUtils.import(
- "resource://gre/modules/addons/AddonRepository.jsm"
-);
-var { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm");
-
-var { AddonTestUtils, MockAsyncShutdown } = ChromeUtils.import(
- "resource://testing-common/AddonTestUtils.jsm"
-);
-
-ChromeUtils.defineModuleGetter(
- this,
- "Blocklist",
- "resource://gre/modules/Blocklist.jsm"
-);
-ChromeUtils.defineModuleGetter(
- this,
- "Extension",
- "resource://gre/modules/Extension.jsm"
-);
-ChromeUtils.defineModuleGetter(
- this,
- "ExtensionTestUtils",
- "resource://testing-common/ExtensionXPCShellUtils.jsm"
-);
-ChromeUtils.defineModuleGetter(
- this,
- "ExtensionTestCommon",
- "resource://testing-common/ExtensionTestCommon.jsm"
-);
-ChromeUtils.defineModuleGetter(
- this,
- "HttpServer",
- "resource://testing-common/httpd.js"
-);
-ChromeUtils.defineModuleGetter(
- this,
- "MockRegistrar",
- "resource://testing-common/MockRegistrar.jsm"
-);
-ChromeUtils.defineModuleGetter(
- this,
- "MockRegistry",
- "resource://testing-common/MockRegistry.jsm"
-);
-ChromeUtils.defineModuleGetter(
- this,
- "PromiseTestUtils",
- "resource://testing-common/PromiseTestUtils.jsm"
-);
-ChromeUtils.defineModuleGetter(
- this,
- "TestUtils",
- "resource://testing-common/TestUtils.jsm"
-);
-
-XPCOMUtils.defineLazyServiceGetter(
- this,
- "aomStartup",
- "@mozilla.org/addons/addon-manager-startup;1",
- "amIAddonManagerStartup"
-);
-
-const {
- createAppInfo,
- createHttpServer,
- createTempWebExtensionFile,
- getFileForAddon,
- manuallyInstall,
- manuallyUninstall,
- overrideBuiltIns,
- promiseAddonEvent,
- promiseCompleteAllInstalls,
- promiseCompleteInstall,
- promiseConsoleOutput,
- promiseFindAddonUpdates,
- promiseInstallAllFiles,
- promiseInstallFile,
- promiseRestartManager,
- promiseSetExtensionModifiedTime,
- promiseShutdownManager,
- promiseStartupManager,
- promiseWebExtensionStartup,
- promiseWriteProxyFileToDir,
- registerDirectory,
- setExtensionModifiedTime,
- writeFilesToZip,
-} = AddonTestUtils;
-
-// WebExtension wrapper for ease of testing
-ExtensionTestUtils.init(this);
-
-AddonTestUtils.init(this, false);
-AddonTestUtils.overrideCertDB();
-
-XPCOMUtils.defineLazyGetter(
- this,
- "BOOTSTRAP_REASONS",
- () => AddonManagerPrivate.BOOTSTRAP_REASONS
-);
-
-function getReasonName(reason) {
- for (let key of Object.keys(BOOTSTRAP_REASONS)) {
- if (BOOTSTRAP_REASONS[key] == reason) {
- return key;
- }
- }
- throw new Error("This shouldn't happen.");
-}
-
-Object.defineProperty(this, "gAppInfo", {
- get() {
- return AddonTestUtils.appInfo;
- },
-});
-
-Object.defineProperty(this, "gAddonStartup", {
- get() {
- return AddonTestUtils.addonStartup.clone();
- },
-});
-
-Object.defineProperty(this, "gInternalManager", {
- get() {
- return AddonTestUtils.addonIntegrationService.QueryInterface(
- Ci.nsITimerCallback
- );
- },
-});
-
-Object.defineProperty(this, "gProfD", {
- get() {
- return AddonTestUtils.profileDir.clone();
- },
-});
-
-Object.defineProperty(this, "gTmpD", {
- get() {
- return AddonTestUtils.tempDir.clone();
- },
-});
-
-Object.defineProperty(this, "gUseRealCertChecks", {
- get() {
- return AddonTestUtils.useRealCertChecks;
- },
- set(val) {
- return (AddonTestUtils.useRealCertChecks = val);
- },
-});
-
-Object.defineProperty(this, "TEST_UNPACKED", {
- get() {
- return AddonTestUtils.testUnpacked;
- },
- set(val) {
- return (AddonTestUtils.testUnpacked = val);
- },
-});
-
-// We need some internal bits of AddonManager
-var {
- AddonManager,
- AddonManagerInternal,
- AddonManagerPrivate,
-} = ChromeUtils.import("resource://gre/modules/AddonManager.jsm");
-
-const promiseAddonByID = AddonManager.getAddonByID;
-const promiseAddonsByIDs = AddonManager.getAddonsByIDs;
-
-var gPort = null;
-var gUrlToFileMap = {};
-
-// Map resource://xpcshell-data/ to the data directory
-var resHandler = Services.io
- .getProtocolHandler("resource")
- .QueryInterface(Ci.nsISubstitutingProtocolHandler);
-// Allow non-existent files because of bug 1207735
-var dataURI = NetUtil.newURI(do_get_file("data", true));
-resHandler.setSubstitution("xpcshell-data", dataURI);
-
-function isManifestRegistered(file) {
- let manifests = Components.manager.getManifestLocations();
- for (let i = 0; i < manifests.length; i++) {
- let manifest = manifests.queryElementAt(i, Ci.nsIURI);
-
- // manifest is the url to the manifest file either in an XPI or a directory.
- // We want the location of the XPI or directory itself.
- if (manifest instanceof Ci.nsIJARURI) {
- manifest = manifest.JARFile.QueryInterface(Ci.nsIFileURL).file;
- } else if (manifest instanceof Ci.nsIFileURL) {
- manifest = manifest.file.parent;
- } else {
- continue;
- }
-
- if (manifest.equals(file)) {
- return true;
- }
- }
- return false;
-}
-
-const BOOTSTRAP_MONITOR_BOOTSTRAP_JS = `
- ChromeUtils.import("resource://xpcshell-data/BootstrapMonitor.jsm").monitor(this);
-`;
-
-// Listens to messages from bootstrap.js telling us what add-ons were started
-// and stopped etc. and performs some sanity checks that only installed add-ons
-// are started etc.
-this.BootstrapMonitor = {
- inited: false,
-
- // Contain the current state of add-ons in the system
- installed: new Map(),
- started: new Map(),
-
- // Contain the last state of shutdown and uninstall calls for an add-on
- stopped: new Map(),
- uninstalled: new Map(),
-
- startupPromises: [],
- installPromises: [],
-
- restartfulIds: new Set(),
-
- init() {
- this.inited = true;
- Services.obs.addObserver(this, "bootstrapmonitor-event");
- },
-
- shutdownCheck() {
- if (!this.inited) {
- return;
- }
-
- Assert.equal(this.started.size, 0);
- },
-
- clear(id) {
- this.installed.delete(id);
- this.started.delete(id);
- this.stopped.delete(id);
- this.uninstalled.delete(id);
- },
-
- promiseAddonStartup(id) {
- return new Promise(resolve => {
- this.startupPromises.push(resolve);
- });
- },
-
- promiseAddonInstall(id) {
- return new Promise(resolve => {
- this.installPromises.push(resolve);
- });
- },
-
- checkMatches(cached, current) {
- Assert.notEqual(cached, undefined);
- Assert.equal(current.data.version, cached.data.version);
- Assert.equal(current.data.installPath, cached.data.installPath);
- Assert.ok(
- Services.io
- .newURI(current.data.resourceURI)
- .equals(Services.io.newURI(cached.data.resourceURI)),
- `Resource URIs match: "${current.data.resourceURI}" == "${
- cached.data.resourceURI
- }"`
- );
- },
-
- checkAddonStarted(id, version = undefined) {
- let started = this.started.get(id);
- Assert.notEqual(started, undefined);
- if (version != undefined) {
- Assert.equal(started.data.version, version);
- }
-
- // Chrome should be registered by now
- // Get the install path from the resource URI, since it is not passed any longer.
- let uri = Services.io.newURI(started.data.resourceURI);
- let jarFile = uri.QueryInterface(Ci.nsIJARURI).JARFile;
- let installPath = jarFile.QueryInterface(Ci.nsIFileURL).file;
-
- let isRegistered = isManifestRegistered(installPath);
- Assert.ok(isRegistered);
- },
-
- checkAddonNotStarted(id) {
- Assert.ok(!this.started.has(id));
- },
-
- checkAddonInstalled(id, version = undefined) {
- const installed = this.installed.get(id);
- notEqual(installed, undefined);
- if (version !== undefined) {
- equal(installed.data.version, version);
- }
- return installed;
- },
-
- checkAddonNotInstalled(id) {
- Assert.ok(!this.installed.has(id));
- },
-
- observe(subject, topic, data) {
- let info = JSON.parse(data);
- let id = info.data.id;
-
- // Get the install path from the resource URI, since it is not passed any longer.
- let uri = Services.io.newURI(info.data.resourceURI);
- let jarFile = uri.QueryInterface(Ci.nsIJARURI).JARFile;
- let installPath = jarFile.QueryInterface(Ci.nsIFileURL).file;
-
- if (subject && subject.wrappedJSObject) {
- // NOTE: in some of the new tests, we need to received the real objects instead of
- // their JSON representations, but most of the current tests expect intallPath
- // and resourceURI to have been converted to strings.
- info.data = Object.assign({}, subject.wrappedJSObject.data, {
- installPath: info.data.installPath,
- resourceURI: info.data.resourceURI,
- });
- }
-
- // If this is the install event the add-ons shouldn't already be installed
- if (info.event == "install") {
- this.checkAddonNotInstalled(id);
-
- this.installed.set(id, info);
-
- for (let resolve of this.installPromises) {
- resolve();
- }
- this.installPromises = [];
- } else {
- this.checkMatches(this.installed.get(id), info);
- }
-
- // If this is the shutdown event than the add-on should already be started
- if (info.event == "shutdown") {
- this.checkMatches(this.started.get(id), info);
-
- this.started.delete(id);
- this.stopped.set(id, info);
-
- // Chrome should still be registered at this point
- let isRegistered = isManifestRegistered(installPath);
- Assert.ok(isRegistered);
-
- // XPIProvider doesn't bother unregistering chrome on app shutdown but
- // since we simulate restarts we must do so manually to keep the registry
- // consistent.
- if (info.reason == 2 /* APP_SHUTDOWN */) {
- Components.manager.removeBootstrappedManifestLocation(installPath);
- }
- } else {
- this.checkAddonNotStarted(id);
- }
-
- if (info.event == "uninstall") {
- // We currently support registering, but not unregistering,
- // restartful add-on manifests during xpcshell AOM "restarts".
- if (!this.restartfulIds.has(id)) {
- // Chrome should be unregistered at this point
- let isRegistered = isManifestRegistered(installPath);
- Assert.ok(!isRegistered);
- }
-
- this.installed.delete(id);
- this.uninstalled.set(id, info);
- } else if (info.event == "startup") {
- this.started.set(id, info);
-
- // Chrome should be registered at this point
- let isRegistered = isManifestRegistered(installPath);
- Assert.ok(isRegistered);
-
- for (let resolve of this.startupPromises) {
- resolve();
- }
- this.startupPromises = [];
- }
- },
-};
-
-AddonTestUtils.on("addon-manager-shutdown", () => {
- BootstrapMonitor.shutdownCheck();
- Cu.unload("resource:///modules/BootstrapLoader.jsm");
-});
-
-var SlightlyLessDodgyBootstrapMonitor = {
- started: new Map(),
- stopped: new Map(),
- installed: new Map(),
- uninstalled: new Map(),
-
- init() {
- this.onEvent = this.onEvent.bind(this);
-
- AddonTestUtils.on("addon-manager-shutdown", this.onEvent);
- AddonTestUtils.on("bootstrap-method", this.onEvent);
- },
-
- shutdownCheck() {
- equal(
- this.started.size,
- 0,
- "Should have no add-ons that were started but not shutdown"
- );
- },
-
- onEvent(msg, data) {
- switch (msg) {
- case "addon-manager-shutdown":
- this.shutdownCheck();
- break;
- case "bootstrap-method":
- this.onBootstrapMethod(data.method, data.params, data.reason);
- break;
- }
- },
-
- onBootstrapMethod(method, params, reason) {
- let { id } = params;
-
- info(
- `Bootstrap method ${method} for ${params.id} version ${params.version}`
- );
-
- if (method !== "install") {
- this.checkInstalled(id);
- }
-
- switch (method) {
- case "install":
- this.checkNotInstalled(id);
- this.installed.set(id, { reason, params });
- this.uninstalled.delete(id);
- break;
- case "startup":
- this.checkNotStarted(id);
- this.started.set(id, { reason, params });
- this.stopped.delete(id);
- break;
- case "shutdown":
- this.checkMatches("shutdown", "startup", params, this.started.get(id));
- this.checkStarted(id);
- this.stopped.set(id, { reason, params });
- this.started.delete(id);
- break;
- case "uninstall":
- this.checkMatches(
- "uninstall",
- "install",
- params,
- this.installed.get(id)
- );
- this.uninstalled.set(id, { reason, params });
- this.installed.delete(id);
- break;
- case "update":
- this.checkMatches("update", "install", params, this.installed.get(id));
- this.installed.set(id, { reason, params });
- break;
- }
- },
-
- clear(id) {
- this.installed.delete(id);
- this.started.delete(id);
- this.stopped.delete(id);
- this.uninstalled.delete(id);
- },
-
- checkMatches(method, lastMethod, params, { params: lastParams } = {}) {
- ok(
- lastParams,
- `Expecting matching ${lastMethod} call for add-on ${
- params.id
- } ${method} call`
- );
-
- if (method == "update") {
- equal(
- params.oldVersion,
- lastParams.version,
- "params.version should match last call"
- );
- } else {
- equal(
- params.version,
- lastParams.version,
- "params.version should match last call"
- );
- }
-
- if (method !== "update" && method !== "uninstall") {
- equal(
- params.installPath.path,
- lastParams.installPath.path,
- `params.installPath should match last call`
- );
-
- ok(
- params.resourceURI.equals(lastParams.resourceURI),
- `params.resourceURI should match: "${params.resourceURI.spec}" == "${
- lastParams.resourceURI.spec
- }"`
- );
- }
- },
-
- checkStarted(id, version = undefined) {
- let started = this.started.get(id);
- ok(started, `Should have seen startup method call for ${id}`);
-
- if (version !== undefined) {
- equal(started.params.version, version, "Expected version number");
- }
- },
-
- checkNotStarted(id) {
- ok(
- !this.started.has(id),
- `Should not have seen startup method call for ${id}`
- );
- },
-
- checkInstalled(id, version = undefined) {
- const installed = this.installed.get(id);
- ok(installed, `Should have seen install call for ${id}`);
-
- if (version !== undefined) {
- equal(installed.params.version, version, "Expected version number");
- }
-
- return installed;
- },
-
- checkNotInstalled(id) {
- ok(
- !this.installed.has(id),
- `Should not have seen install method call for ${id}`
- );
- },
-};
-
-function isNightlyChannel() {
- var channel = Services.prefs.getCharPref("app.update.channel", "default");
-
- return (
- channel != "aurora" &&
- channel != "beta" &&
- channel != "release" &&
- channel != "esr"
- );
-}
-
-async function restartWithLocales(locales) {
- Services.locale.requestedLocales = locales;
- await promiseRestartManager();
-}
-
-/**
- * Returns a map of Addon objects for installed add-ons with the given
- * IDs. The returned map contains a key for the ID of each add-on that
- * is found. IDs for add-ons which do not exist are not present in the
- * map.
- *
- * @param {sequence<string>} ids
- * The list of add-on IDs to get.
- * @returns {Promise<string, Addon>}
- * Map of add-ons that were found.
- */
-async function getAddons(ids) {
- let addons = new Map();
- for (let addon of await AddonManager.getAddonsByIDs(ids)) {
- if (addon) {
- addons.set(addon.id, addon);
- }
- }
- return addons;
-}
-
-/**
- * Checks that the given add-on has the given expected properties.
- *
- * @param {string} id
- * The id of the add-on.
- * @param {Addon?} addon
- * The add-on object, or null if the add-on does not exist.
- * @param {object?} expected
- * An object containing the expected values for properties of the
- * add-on, or null if the add-on is expected not to exist.
- */
-function checkAddon(id, addon, expected) {
- info(`Checking state of addon ${id}`);
-
- if (expected === null) {
- ok(!addon, `Addon ${id} should not exist`);
- } else {
- ok(addon, `Addon ${id} should exist`);
- for (let [key, value] of Object.entries(expected)) {
- if (value instanceof Ci.nsIURI) {
- equal(
- addon[key] && addon[key].spec,
- value.spec,
- `Expected value of addon.${key}`
- );
- } else {
- deepEqual(addon[key], value, `Expected value of addon.${key}`);
- }
- }
- }
-}
-
-/**
- * Tests that an add-on does appear in the crash report annotations, if
- * crash reporting is enabled. The test will fail if the add-on is not in the
- * annotation.
- * @param aId
- * The ID of the add-on
- * @param aVersion
- * The version of the add-on
- */
-function do_check_in_crash_annotation(aId, aVersion) {
- if (!AppConstants.MOZ_CRASHREPORTER) {
- return;
- }
-
- if (!("Add-ons" in gAppInfo.annotations)) {
- Assert.ok(false, "Cannot find Add-ons entry in crash annotations");
- return;
- }
-
- let addons = gAppInfo.annotations["Add-ons"].split(",");
- Assert.ok(
- addons.includes(
- `${encodeURIComponent(aId)}:${encodeURIComponent(aVersion)}`
- )
- );
-}
-
-/**
- * Tests that an add-on does not appear in the crash report annotations, if
- * crash reporting is enabled. The test will fail if the add-on is in the
- * annotation.
- * @param aId
- * The ID of the add-on
- * @param aVersion
- * The version of the add-on
- */
-function do_check_not_in_crash_annotation(aId, aVersion) {
- if (!AppConstants.MOZ_CRASHREPORTER) {
- return;
- }
-
- if (!("Add-ons" in gAppInfo.annotations)) {
- Assert.ok(true);
- return;
- }
-
- let addons = gAppInfo.annotations["Add-ons"].split(",");
- Assert.ok(
- !addons.includes(
- `${encodeURIComponent(aId)}:${encodeURIComponent(aVersion)}`
- )
- );
-}
-
-function do_get_file_hash(aFile, aAlgorithm) {
- if (!aAlgorithm) {
- aAlgorithm = "sha1";
- }
-
- let crypto = Cc["@mozilla.org/security/hash;1"].createInstance(
- Ci.nsICryptoHash
- );
- crypto.initWithString(aAlgorithm);
- let fis = Cc["@mozilla.org/network/file-input-stream;1"].createInstance(
- Ci.nsIFileInputStream
- );
- fis.init(aFile, -1, -1, false);
- crypto.updateFromStream(fis, aFile.fileSize);
-
- // return the two-digit hexadecimal code for a byte
- let toHexString = charCode => ("0" + charCode.toString(16)).slice(-2);
-
- let binary = crypto.finish(false);
- let hash = Array.from(binary, c => toHexString(c.charCodeAt(0)));
- return aAlgorithm + ":" + hash.join("");
-}
-
-/**
- * Returns an extension uri spec
- *
- * @param aProfileDir
- * The extension install directory
- * @return a uri spec pointing to the root of the extension
- */
-function do_get_addon_root_uri(aProfileDir, aId) {
- let path = aProfileDir.clone();
- path.append(aId);
- if (!path.exists()) {
- path.leafName += ".xpi";
- return "jar:" + Services.io.newFileURI(path).spec + "!/";
- }
- return Services.io.newFileURI(path).spec;
-}
-
-function do_get_expected_addon_name(aId) {
- if (TEST_UNPACKED) {
- return aId;
- }
- return aId + ".xpi";
-}
-
-/**
- * Check that an array of actual add-ons is the same as an array of
- * expected add-ons.
- *
- * @param aActualAddons
- * The array of actual add-ons to check.
- * @param aExpectedAddons
- * The array of expected add-ons to check against.
- * @param aProperties
- * An array of properties to check.
- */
-function do_check_addons(aActualAddons, aExpectedAddons, aProperties) {
- Assert.notEqual(aActualAddons, null);
- Assert.equal(aActualAddons.length, aExpectedAddons.length);
- for (let i = 0; i < aActualAddons.length; i++) {
- do_check_addon(aActualAddons[i], aExpectedAddons[i], aProperties);
- }
-}
-
-/**
- * Check that the actual add-on is the same as the expected add-on.
- *
- * @param aActualAddon
- * The actual add-on to check.
- * @param aExpectedAddon
- * The expected add-on to check against.
- * @param aProperties
- * An array of properties to check.
- */
-function do_check_addon(aActualAddon, aExpectedAddon, aProperties) {
- Assert.notEqual(aActualAddon, null);
-
- aProperties.forEach(function(aProperty) {
- let actualValue = aActualAddon[aProperty];
- let expectedValue = aExpectedAddon[aProperty];
-
- // Check that all undefined expected properties are null on actual add-on
- if (!(aProperty in aExpectedAddon)) {
- if (actualValue !== undefined && actualValue !== null) {
- do_throw(
- "Unexpected defined/non-null property for add-on " +
- aExpectedAddon.id +
- " (addon[" +
- aProperty +
- "] = " +
- actualValue.toSource() +
- ")"
- );
- }
-
- return;
- }
- if (expectedValue && !actualValue) {
- do_throw(
- "Missing property for add-on " +
- aExpectedAddon.id +
- ": expected addon[" +
- aProperty +
- "] = " +
- expectedValue
- );
- return;
- }
-
- switch (aProperty) {
- case "creator":
- do_check_author(actualValue, expectedValue);
- break;
-
- case "developers":
- Assert.equal(actualValue.length, expectedValue.length);
- for (let i = 0; i < actualValue.length; i++) {
- do_check_author(actualValue[i], expectedValue[i]);
- }
- break;
-
- case "screenshots":
- Assert.equal(actualValue.length, expectedValue.length);
- for (let i = 0; i < actualValue.length; i++) {
- do_check_screenshot(actualValue[i], expectedValue[i]);
- }
- break;
-
- case "sourceURI":
- Assert.equal(actualValue.spec, expectedValue);
- break;
-
- case "updateDate":
- Assert.equal(actualValue.getTime(), expectedValue.getTime());
- break;
-
- case "compatibilityOverrides":
- Assert.equal(actualValue.length, expectedValue.length);
- for (let i = 0; i < actualValue.length; i++) {
- do_check_compatibilityoverride(actualValue[i], expectedValue[i]);
- }
- break;
-
- case "icons":
- do_check_icons(actualValue, expectedValue);
- break;
-
- default:
- if (actualValue !== expectedValue) {
- do_throw(
- "Failed for " +
- aProperty +
- " for add-on " +
- aExpectedAddon.id +
- " (" +
- actualValue +
- " === " +
- expectedValue +
- ")"
- );
- }
- }
- });
-}
-
-/**
- * Check that the actual author is the same as the expected author.
- *
- * @param aActual
- * The actual author to check.
- * @param aExpected
- * The expected author to check against.
- */
-function do_check_author(aActual, aExpected) {
- Assert.equal(aActual.toString(), aExpected.name);
- Assert.equal(aActual.name, aExpected.name);
- Assert.equal(aActual.url, aExpected.url);
-}
-
-/**
- * Check that the actual screenshot is the same as the expected screenshot.
- *
- * @param aActual
- * The actual screenshot to check.
- * @param aExpected
- * The expected screenshot to check against.
- */
-function do_check_screenshot(aActual, aExpected) {
- Assert.equal(aActual.toString(), aExpected.url);
- Assert.equal(aActual.url, aExpected.url);
- Assert.equal(aActual.width, aExpected.width);
- Assert.equal(aActual.height, aExpected.height);
- Assert.equal(aActual.thumbnailURL, aExpected.thumbnailURL);
- Assert.equal(aActual.thumbnailWidth, aExpected.thumbnailWidth);
- Assert.equal(aActual.thumbnailHeight, aExpected.thumbnailHeight);
- Assert.equal(aActual.caption, aExpected.caption);
-}
-
-/**
- * Check that the actual compatibility override is the same as the expected
- * compatibility override.
- *
- * @param aAction
- * The actual compatibility override to check.
- * @param aExpected
- * The expected compatibility override to check against.
- */
-function do_check_compatibilityoverride(aActual, aExpected) {
- Assert.equal(aActual.type, aExpected.type);
- Assert.equal(aActual.minVersion, aExpected.minVersion);
- Assert.equal(aActual.maxVersion, aExpected.maxVersion);
- Assert.equal(aActual.appID, aExpected.appID);
- Assert.equal(aActual.appMinVersion, aExpected.appMinVersion);
- Assert.equal(aActual.appMaxVersion, aExpected.appMaxVersion);
-}
-
-function do_check_icons(aActual, aExpected) {
- for (var size in aExpected) {
- Assert.equal(aActual[size], aExpected[size]);
- }
-}
-
-function isThemeInAddonsList(aDir, aId) {
- return AddonTestUtils.addonsList.hasTheme(aDir, aId);
-}
-
-function isExtensionInBootstrappedList(aDir, aId) {
- return AddonTestUtils.addonsList.hasExtension(aDir, aId);
-}
-
-/**
- * Writes a manifest.json manifest into an extension using the properties passed
- * in a JS object.
- *
- * @param aManifest
- * The data to write
- * @param aDir
- * The install directory to add the extension to
- * @param aId
- * An optional string to override the default installation aId
- * @return A file pointing to where the extension was installed
- */
-function promiseWriteWebManifestForExtension(
- aData,
- aDir,
- aId = aData.applications.gecko.id
-) {
- let files = {
- "manifest.json": JSON.stringify(aData),
- };
- return AddonTestUtils.promiseWriteFilesToExtension(aDir.path, aId, files);
-}
-
-/**
- * Creates an XPI file for some manifest data in the temporary directory and
- * returns the nsIFile for it. The file will be deleted when the test completes.
- *
- * @param aData
- * The object holding data about the add-on
- * @return A file pointing to the created XPI file
- */
-function createTempXPIFile(aData, aExtraFile) {
- let files = {
- "install.rdf": aData,
- };
- if (typeof aExtraFile == "object") {
- Object.assign(files, aExtraFile);
- } else if (aExtraFile) {
- files[aExtraFile] = "";
- }
-
- return AddonTestUtils.createTempXPIFile(files);
-}
-
-var gExpectedEvents = {};
-var gExpectedInstalls = [];
-var gNext = null;
-
-function getExpectedEvent(aId) {
- if (!(aId in gExpectedEvents)) {
- do_throw("Wasn't expecting events for " + aId);
- }
- if (gExpectedEvents[aId].length == 0) {
- do_throw("Too many events for " + aId);
- }
- let event = gExpectedEvents[aId].shift();
- if (event instanceof Array) {
- return event;
- }
- return [event, true];
-}
-
-function getExpectedInstall(aAddon) {
- if (gExpectedInstalls instanceof Array) {
- return gExpectedInstalls.shift();
- }
- if (!aAddon || !aAddon.id) {
- return gExpectedInstalls.NO_ID.shift();
- }
- let id = aAddon.id;
- if (!(id in gExpectedInstalls) || !(gExpectedInstalls[id] instanceof Array)) {
- do_throw("Wasn't expecting events for " + id);
- }
- if (gExpectedInstalls[id].length == 0) {
- do_throw("Too many events for " + id);
- }
- return gExpectedInstalls[id].shift();
-}
-
-const AddonListener = {
- onPropertyChanged(aAddon, aProperties) {
- info(`Got onPropertyChanged event for ${aAddon.id}`);
- let [event, properties] = getExpectedEvent(aAddon.id);
- Assert.equal("onPropertyChanged", event);
- Assert.equal(aProperties.length, properties.length);
- properties.forEach(function(aProperty) {
- // Only test that the expected properties are listed, having additional
- // properties listed is not necessary a problem
- if (!aProperties.includes(aProperty)) {
- do_throw("Did not see property change for " + aProperty);
- }
- });
- return check_test_completed(arguments);
- },
-
- onEnabling(aAddon, aRequiresRestart) {
- info(`Got onEnabling event for ${aAddon.id}`);
- let [event, expectedRestart] = getExpectedEvent(aAddon.id);
- Assert.equal("onEnabling", event);
- Assert.equal(aRequiresRestart, expectedRestart);
- if (expectedRestart) {
- Assert.ok(hasFlag(aAddon.pendingOperations, AddonManager.PENDING_ENABLE));
- }
- Assert.ok(!hasFlag(aAddon.permissions, AddonManager.PERM_CAN_ENABLE));
- return check_test_completed(arguments);
- },
-
- onEnabled(aAddon) {
- info(`Got onEnabled event for ${aAddon.id}`);
- let [event] = getExpectedEvent(aAddon.id);
- Assert.equal("onEnabled", event);
- Assert.ok(!hasFlag(aAddon.permissions, AddonManager.PERM_CAN_ENABLE));
- return check_test_completed(arguments);
- },
-
- onDisabling(aAddon, aRequiresRestart) {
- info(`Got onDisabling event for ${aAddon.id}`);
- let [event, expectedRestart] = getExpectedEvent(aAddon.id);
- Assert.equal("onDisabling", event);
- Assert.equal(aRequiresRestart, expectedRestart);
- if (expectedRestart) {
- Assert.ok(
- hasFlag(aAddon.pendingOperations, AddonManager.PENDING_DISABLE)
- );
- }
- Assert.ok(!hasFlag(aAddon.permissions, AddonManager.PERM_CAN_DISABLE));
- return check_test_completed(arguments);
- },
-
- onDisabled(aAddon) {
- info(`Got onDisabled event for ${aAddon.id}`);
- let [event] = getExpectedEvent(aAddon.id);
- Assert.equal("onDisabled", event);
- Assert.ok(!hasFlag(aAddon.permissions, AddonManager.PERM_CAN_DISABLE));
- return check_test_completed(arguments);
- },
-
- onInstalling(aAddon, aRequiresRestart) {
- info(`Got onInstalling event for ${aAddon.id}`);
- let [event, expectedRestart] = getExpectedEvent(aAddon.id);
- Assert.equal("onInstalling", event);
- Assert.equal(aRequiresRestart, expectedRestart);
- if (expectedRestart) {
- Assert.ok(
- hasFlag(aAddon.pendingOperations, AddonManager.PENDING_INSTALL)
- );
- }
- return check_test_completed(arguments);
- },
-
- onInstalled(aAddon) {
- info(`Got onInstalled event for ${aAddon.id}`);
- let [event] = getExpectedEvent(aAddon.id);
- Assert.equal("onInstalled", event);
- return check_test_completed(arguments);
- },
-
- onUninstalling(aAddon, aRequiresRestart) {
- info(`Got onUninstalling event for ${aAddon.id}`);
- let [event, expectedRestart] = getExpectedEvent(aAddon.id);
- Assert.equal("onUninstalling", event);
- Assert.equal(aRequiresRestart, expectedRestart);
- if (expectedRestart) {
- Assert.ok(
- hasFlag(aAddon.pendingOperations, AddonManager.PENDING_UNINSTALL)
- );
- }
- return check_test_completed(arguments);
- },
-
- onUninstalled(aAddon) {
- info(`Got onUninstalled event for ${aAddon.id}`);
- let [event] = getExpectedEvent(aAddon.id);
- Assert.equal("onUninstalled", event);
- return check_test_completed(arguments);
- },
-
- onOperationCancelled(aAddon) {
- info(`Got onOperationCancelled event for ${aAddon.id}`);
- let [event] = getExpectedEvent(aAddon.id);
- Assert.equal("onOperationCancelled", event);
- return check_test_completed(arguments);
- },
-};
-
-const InstallListener = {
- onNewInstall(install) {
- if (
- install.state != AddonManager.STATE_DOWNLOADED &&
- install.state != AddonManager.STATE_DOWNLOAD_FAILED &&
- install.state != AddonManager.STATE_AVAILABLE
- ) {
- do_throw("Bad install state " + install.state);
- }
- if (install.state != AddonManager.STATE_DOWNLOAD_FAILED) {
- Assert.equal(install.error, 0);
- } else {
- Assert.notEqual(install.error, 0);
- }
- Assert.equal("onNewInstall", getExpectedInstall());
- return check_test_completed(arguments);
- },
-
- onDownloadStarted(install) {
- Assert.equal(install.state, AddonManager.STATE_DOWNLOADING);
- Assert.equal(install.error, 0);
- Assert.equal("onDownloadStarted", getExpectedInstall());
- return check_test_completed(arguments);
- },
-
- onDownloadEnded(install) {
- Assert.equal(install.state, AddonManager.STATE_DOWNLOADED);
- Assert.equal(install.error, 0);
- Assert.equal("onDownloadEnded", getExpectedInstall());
- return check_test_completed(arguments);
- },
-
- onDownloadFailed(install) {
- Assert.equal(install.state, AddonManager.STATE_DOWNLOAD_FAILED);
- Assert.equal("onDownloadFailed", getExpectedInstall());
- return check_test_completed(arguments);
- },
-
- onDownloadCancelled(install) {
- Assert.equal(install.state, AddonManager.STATE_CANCELLED);
- Assert.equal(install.error, 0);
- Assert.equal("onDownloadCancelled", getExpectedInstall());
- return check_test_completed(arguments);
- },
-
- onInstallStarted(install) {
- Assert.equal(install.state, AddonManager.STATE_INSTALLING);
- Assert.equal(install.error, 0);
- Assert.equal("onInstallStarted", getExpectedInstall(install.addon));
- return check_test_completed(arguments);
- },
-
- onInstallEnded(install, newAddon) {
- Assert.equal(install.state, AddonManager.STATE_INSTALLED);
- Assert.equal(install.error, 0);
- Assert.equal("onInstallEnded", getExpectedInstall(install.addon));
- return check_test_completed(arguments);
- },
-
- onInstallFailed(install) {
- Assert.equal(install.state, AddonManager.STATE_INSTALL_FAILED);
- Assert.equal("onInstallFailed", getExpectedInstall(install.addon));
- return check_test_completed(arguments);
- },
-
- onInstallCancelled(install) {
- // If the install was cancelled by a listener returning false from
- // onInstallStarted, then the state will revert to STATE_DOWNLOADED.
- let possibleStates = [
- AddonManager.STATE_CANCELLED,
- AddonManager.STATE_DOWNLOADED,
- ];
- Assert.ok(possibleStates.includes(install.state));
- Assert.equal(install.error, 0);
- Assert.equal("onInstallCancelled", getExpectedInstall(install.addon));
- return check_test_completed(arguments);
- },
-
- onExternalInstall(aAddon, existingAddon, aRequiresRestart) {
- Assert.equal("onExternalInstall", getExpectedInstall(aAddon));
- Assert.ok(!aRequiresRestart);
- return check_test_completed(arguments);
- },
-};
-
-function hasFlag(aBits, aFlag) {
- return (aBits & aFlag) != 0;
-}
-
-// Just a wrapper around setting the expected events.
-function prepare_test(aExpectedEvents, aExpectedInstalls, aNext) {
- AddonManager.addAddonListener(AddonListener);
- AddonManager.addInstallListener(InstallListener);
-
- gExpectedInstalls = aExpectedInstalls;
- gExpectedEvents = aExpectedEvents;
- gNext = aNext;
-}
-
-function clearListeners() {
- AddonManager.removeAddonListener(AddonListener);
- AddonManager.removeInstallListener(InstallListener);
-}
-
-function end_test() {
- clearListeners();
-}
-
-// Checks if all expected events have been seen and if so calls the callback.
-function check_test_completed(aArgs) {
- if (!gNext) {
- return undefined;
- }
-
- if (gExpectedInstalls instanceof Array && gExpectedInstalls.length > 0) {
- return undefined;
- }
-
- for (let id in gExpectedInstalls) {
- let installList = gExpectedInstalls[id];
- if (installList.length > 0) {
- return undefined;
- }
- }
-
- for (let id in gExpectedEvents) {
- if (gExpectedEvents[id].length > 0) {
- return undefined;
- }
- }
-
- return gNext.apply(null, aArgs);
-}
-
-// Verifies that all the expected events for all add-ons were seen.
-function ensure_test_completed() {
- for (let i in gExpectedEvents) {
- if (gExpectedEvents[i].length > 0) {
- do_throw(
- `Didn't see all the expected events for ${i}: Still expecting ${
- gExpectedEvents[i]
- }`
- );
- }
- }
- gExpectedEvents = {};
- if (gExpectedInstalls) {
- Assert.equal(gExpectedInstalls.length, 0);
- }
-}
-
-/**
- * A helper method to install an array of AddonInstall to completion and then
- * call a provided callback.
- *
- * @param aInstalls
- * The array of AddonInstalls to install
- * @param aCallback
- * The callback to call when all installs have finished
- */
-function completeAllInstalls(aInstalls, aCallback) {
- promiseCompleteAllInstalls(aInstalls).then(aCallback);
-}
-
-/**
- * A helper method to install an array of files and call a callback after the
- * installs are completed.
- *
- * @param aFiles
- * The array of files to install
- * @param aCallback
- * The callback to call when all installs have finished
- * @param aIgnoreIncompatible
- * Optional parameter to ignore add-ons that are incompatible in
- * aome way with the application
- */
-function installAllFiles(aFiles, aCallback, aIgnoreIncompatible) {
- promiseInstallAllFiles(aFiles, aIgnoreIncompatible).then(aCallback);
-}
-
-const EXTENSIONS_DB = "extensions.json";
-var gExtensionsJSON = gProfD.clone();
-gExtensionsJSON.append(EXTENSIONS_DB);
-
-async function promiseInstallWebExtension(aData) {
- let addonFile = createTempWebExtensionFile(aData);
-
- let { addon } = await promiseInstallFile(addonFile);
- return addon;
-}
-
-// By default use strict compatibility.
-Services.prefs.setBoolPref("extensions.strictCompatibility", true);
-
-// Ensure signature checks are enabled by default.
-Services.prefs.setBoolPref(PREF_XPI_SIGNATURES_REQUIRED, false);
-
-Services.prefs.setBoolPref("extensions.legacy.enabled", true);
-
-// Copies blocklistFile (an nsIFile) to gProfD/blocklist.xml.
-function copyBlocklistToProfile(blocklistFile) {
- var dest = gProfD.clone();
- dest.append("blocklist.xml");
- if (dest.exists()) {
- dest.remove(false);
- }
- blocklistFile.copyTo(gProfD, "blocklist.xml");
- dest.lastModifiedTime = Date.now();
-}
-
-// Make sure that a given path does not exist.
-function pathShouldntExist(file) {
- if (file.exists()) {
- do_throw(`Test cleanup: path ${file.path} exists when it should not`);
- }
-}
-
-// Wrap a function (typically a callback) to catch and report exceptions.
-function do_exception_wrap(func) {
- return function() {
- try {
- func.apply(null, arguments);
- } catch (e) {
- do_report_unexpected_exception(e);
- }
- };
-}
-
-/**
- * Change the schema version of the JSON extensions database.
- */
-async function changeXPIDBVersion(aNewVersion) {
- let json = await loadJSON(gExtensionsJSON.path);
- json.schemaVersion = aNewVersion;
- await saveJSON(json, gExtensionsJSON.path);
-}
-
-/**
- * Load a file into a string.
- */
-async function loadFile(aFile) {
- let buffer = await OS.File.read(aFile);
- return new TextDecoder().decode(buffer);
-}
-
-/**
- * Raw load of a JSON file.
- */
-async function loadJSON(aFile) {
- let data = await loadFile(aFile);
- info("Loaded JSON file " + aFile);
- return JSON.parse(data);
-}
-
-/**
- * Raw save of a JSON blob to file.
- */
-async function saveJSON(aData, aFile) {
- info("Starting to save JSON file " + aFile);
- await OS.File.writeAtomic(
- aFile,
- new TextEncoder().encode(JSON.stringify(aData, null, 2))
- );
- info("Done saving JSON file " + aFile.path);
-}
-
-/**
- * Create a callback function that calls do_execute_soon on an actual callback and arguments.
- */
-function callback_soon(aFunction) {
- return function(...args) {
- executeSoon(
- function() {
- aFunction.apply(null, args);
- },
- aFunction.name ? "delayed callback " + aFunction.name : "delayed callback"
- );
- };
-}
-
-XPCOMUtils.defineLazyServiceGetter(
- this,
- "pluginHost",
- "@mozilla.org/plugin/host;1",
- "nsIPluginHost"
-);
-
-class MockPluginTag {
- constructor(opts, enabledState = Ci.nsIPluginTag.STATE_ENABLED) {
- this.pluginTag = pluginHost.createFakePlugin({
- handlerURI: "resource://fake-plugin/${Math.random()}.xhtml",
- mimeEntries: [{ type: "application/x-fake-plugin" }],
- fileName: `${opts.name}.so`,
- ...opts,
- });
- this.pluginTag.enabledState = enabledState;
-
- this.name = opts.name;
- this.version = opts.version;
- }
- async isBlocklisted() {
- let state = await Blocklist.getPluginBlocklistState(this.pluginTag);
- return state == Services.blocklist.STATE_BLOCKED;
- }
- get disabled() {
- return this.pluginTag.enabledState == Ci.nsIPluginTag.STATE_DISABLED;
- }
- set disabled(val) {
- this.enabledState =
- Ci.nsIPluginTag[val ? "STATE_DISABLED" : "STATE_ENABLED"];
- }
- get enabledState() {
- return this.pluginTag.enabledState;
- }
- set enabledState(val) {
- this.pluginTag.enabledState = val;
- }
-}
-
-function mockPluginHost(plugins) {
- let PluginHost = {
- getPluginTags(count) {
- count.value = plugins.length;
- return plugins.map(p => p.pluginTag);
- },
-
- QueryInterface: ChromeUtils.generateQI(["nsIPluginHost"]),
- };
-
- MockRegistrar.register("@mozilla.org/plugin/host;1", PluginHost);
-}
-
-async function setInitialState(addon, initialState) {
- if (initialState.userDisabled) {
- await addon.disable();
- } else if (initialState.userDisabled === false) {
- await addon.enable();
- }
-}
-
-/**
- * Escapes any occurrences of &, ", < or > with XML entities.
- *
- * @param {string} str
- * The string to escape.
- * @returns {string} The escaped string.
- */
-function escapeXML(str) {
- let replacements = {
- "&": "&",
- '"': """,
- "'": "'",
- "<": "<",
- ">": ">",
- };
- return String(str).replace(/[&"''<>]/g, m => replacements[m]);
-}
-
-/**
- * A tagged template function which escapes any XML metacharacters in
- * interpolated values.
- *
- * @param {Array<string>} strings
- * An array of literal strings extracted from the templates.
- * @param {Array} values
- * An array of interpolated values extracted from the template.
- * @returns {string}
- * The result of the escaped values interpolated with the literal
- * strings.
- */
-function escaped(strings, ...values) {
- let result = [];
-
- for (let [i, string] of strings.entries()) {
- result.push(string);
- if (i < values.length) {
- result.push(escapeXML(values[i]));
- }
- }
-
- return result.join("");
-}
-
-function _writeProps(obj, props, indent = " ") {
- let items = [];
- for (let prop of props) {
- if (obj[prop] !== undefined) {
- items.push(escaped`${indent}<em:${prop}>${obj[prop]}</em:${prop}>\n`);
- }
- }
- return items.join("");
-}
-
-function _writeArrayProps(obj, props, indent = " ") {
- let items = [];
- for (let prop of props) {
- for (let val of obj[prop] || []) {
- items.push(escaped`${indent}<em:${prop}>${val}</em:${prop}>\n`);
- }
- }
- return items.join("");
-}
-
-function _writeLocaleStrings(data) {
- let items = [];
-
- items.push(
- this._writeProps(data, ["name", "description", "creator", "homepageURL"])
- );
- items.push(
- this._writeArrayProps(data, ["developer", "translator", "contributor"])
- );
-
- return items.join("");
-}
deleted file mode 100644
--- a/common/test/xpcshell/test_bootstrap.js
+++ /dev/null
@@ -1,819 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
-
-const APP_STARTUP = 1;
-const APP_SHUTDOWN = 2;
-const ADDON_ENABLE = 3;
-const ADDON_DISABLE = 4;
-const ADDON_INSTALL = 5;
-const ADDON_UNINSTALL = 6;
-const ADDON_UPGRADE = 7;
-const ADDON_DOWNGRADE = 8;
-
-const ID1 = "bootstrap1@tests.mozilla.org";
-const ID2 = "bootstrap2@tests.mozilla.org";
-
-// This verifies that bootstrappable add-ons can be used without restarts.
-var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
-
-// Enable loading extensions from the user scopes
-Services.prefs.setIntPref(
- "extensions.enabledScopes",
- AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_USER
-);
-
-// Make Cu.isInAutomation true.
-Services.prefs.setBoolPref(
- "security.turn_off_all_security_so_that_viruses_can_take_over_this_computer",
- true
-);
-Services.prefs.setIntPref("extensions.sideloadScopes", AddonManager.SCOPE_ALL);
-
-BootstrapMonitor.init();
-
-createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
-
-const profileDir = gProfD.clone();
-profileDir.append("extensions");
-const userExtDir = gProfD.clone();
-userExtDir.append("extensions2");
-userExtDir.append(gAppInfo.ID);
-registerDirectory("XREUSysExt", userExtDir.parent);
-
-const ADDONS = {
- test_bootstrap1_1: {
- "manifest.json": JSON.stringify({
- applications: {
- gecko: {
- id: "bootstrap1@tests.mozilla.org",
- },
- },
- legacy: {
- type: "bootstrap",
- },
- manifest_version: 2,
-
- name: "Test Bootstrap 1",
- version: "1.0",
- }),
- "bootstrap.js": BOOTSTRAP_MONITOR_BOOTSTRAP_JS,
- },
- test_bootstrap1_2: {
- "manifest.json": JSON.stringify({
- applications: {
- gecko: {
- id: "bootstrap1@tests.mozilla.org",
- },
- },
- legacy: {
- type: "bootstrap",
- },
- manifest_version: 2,
- version: "2.0",
-
- name: "Test Bootstrap 1",
- }),
- "bootstrap.js": BOOTSTRAP_MONITOR_BOOTSTRAP_JS,
- },
- test_bootstrap1_3: {
- "manifest.json": JSON.stringify({
- applications: {
- gecko: {
- id: "bootstrap1@tests.mozilla.org",
- },
- },
- legacy: {
- type: "bootstrap",
- },
- manifest_version: 2,
- version: "3.0",
-
- name: "Test Bootstrap 1",
-
- targetApplications: [
- {
- applications: {
- gecko: {
- id: "undefined",
- },
- },
- legacy: {
- type: "bootstrap",
- },
- manifest_version: 2,
- minVersion: "1",
- maxVersion: "1",
- },
- ],
- }),
- "bootstrap.js": BOOTSTRAP_MONITOR_BOOTSTRAP_JS,
- },
- test_bootstrap2_1: {
- "manifest.json": JSON.stringify({
- applications: {
- gecko: {
- id: "bootstrap2@tests.mozilla.org",
- },
- },
- legacy: {
- type: "bootstrap",
- },
- manifest_version: 2,
- }),
- "bootstrap.js": BOOTSTRAP_MONITOR_BOOTSTRAP_JS,
- },
-};
-
-var startupCacheMonitor = {
- notificationFired: false,
- observe() {
- this.notificationFired = true;
- },
- // Checks that the notification has been fired since the last time this was called.
- check(expected) {
- equal(this.notificationFired, expected);
- this.notificationFired = false;
- },
-};
-Services.obs.addObserver(startupCacheMonitor, "startupcache-invalidate");
-
-var testserver = AddonTestUtils.createHttpServer({ hosts: ["example.com"] });
-
-const XPIS = {};
-for (let [name, addon] of Object.entries(ADDONS)) {
- XPIS[name] = AddonTestUtils.createTempXPIFile(addon);
- testserver.registerFile(`/addons/${name}.xpi`, XPIS[name]);
-}
-
-function getStartupReason() {
- let info = BootstrapMonitor.started.get(ID1);
- return info ? info.reason : undefined;
-}
-
-function getShutdownReason() {
- let info = BootstrapMonitor.stopped.get(ID1);
- return info ? info.reason : undefined;
-}
-
-function getInstallReason() {
- let info = BootstrapMonitor.installed.get(ID1);
- return info ? info.reason : undefined;
-}
-
-function getUninstallReason() {
- let info = BootstrapMonitor.uninstalled.get(ID1);
- return info ? info.reason : undefined;
-}
-
-function getStartupOldVersion() {
- let info = BootstrapMonitor.started.get(ID1);
- return info ? info.data.oldVersion : undefined;
-}
-
-function getShutdownNewVersion() {
- let info = BootstrapMonitor.stopped.get(ID1);
- return info ? info.data.newVersion : undefined;
-}
-
-function getInstallOldVersion() {
- let info = BootstrapMonitor.installed.get(ID1);
- return info ? info.data.oldVersion : undefined;
-}
-
-function getUninstallNewVersion() {
- let info = BootstrapMonitor.uninstalled.get(ID1);
- return info ? info.data.newVersion : undefined;
-}
-
-async function checkBootstrappedPref() {
- let { XPIInternal } = ChromeUtils.import(
- "resource://gre/modules/addons/XPIProvider.jsm"
- );
-
- let data = new Map();
- for (let entry of XPIInternal.XPIStates.enabledAddons()) {
- data.set(entry.id, entry);
- }
-
- let addons = await AddonManager.getAddonsByTypes(["extension"]);
- for (let addon of addons) {
- if (!addon.id.endsWith("@tests.mozilla.org")) {
- continue;
- }
- if (!addon.isActive) {
- continue;
- }
- if (
- addon.operationsRequiringRestart != AddonManager.OP_NEEDS_RESTART_NONE
- ) {
- continue;
- }
-
- ok(data.has(addon.id));
- let addonData = data.get(addon.id);
- data.delete(addon.id);
-
- equal(addonData.version, addon.version);
- equal(addonData.type, addon.type);
-
- let resourceURI = addon.getResourceURI();
- if (resourceURI instanceof Ci.nsIJARURI) {
- resourceURI = resourceURI.JARFile;
- }
- let file = resourceURI.QueryInterface(Ci.nsIFileURL).file;
- equal(addonData.path, file.path);
- }
- equal(data.size, 0);
-}
-
-add_task(async function run_test() {
- promiseStartupManager();
-
- ok(!gExtensionsJSON.exists());
- ok(!gAddonStartup.exists());
-});
-
-// Tests that installing doesn't require a restart
-add_task(async function test_1() {
- prepare_test({}, ["onNewInstall"]);
-
- let install = await AddonManager.getInstallForFile(XPIS.test_bootstrap1_1);
- ensure_test_completed();
-
- notEqual(install, null);
- equal(install.type, "extension");
- equal(install.version, "1.0");
- equal(install.name, "Test Bootstrap 1");
- equal(install.state, AddonManager.STATE_DOWNLOADED);
- notEqual(install.addon.syncGUID, null);
- equal(
- install.addon.operationsRequiringRestart &
- AddonManager.OP_NEEDS_RESTART_INSTALL,
- 0
- );
- do_check_not_in_crash_annotation(ID1, "1.0");
-
- let addon = install.addon;
-
- await Promise.all([
- BootstrapMonitor.promiseAddonStartup(ID1),
- new Promise(resolve => {
- prepare_test(
- {
- [ID1]: [["onInstalling", false], "onInstalled"],
- },
- ["onInstallStarted", "onInstallEnded"],
- function() {
- // startup should not have been called yet.
- BootstrapMonitor.checkAddonNotStarted(ID1);
- resolve();
- }
- );
- install.install();
- }),
- ]);
-
- await checkBootstrappedPref();
- let installSyncGUID = addon.syncGUID;
-
- let installs = await AddonManager.getAllInstalls();
- // There should be no active installs now since the install completed and
- // doesn't require a restart.
- equal(installs.length, 0);
-
- let b1 = await AddonManager.getAddonByID(ID1);
- notEqual(b1, null);
- equal(b1.version, "1.0");
- notEqual(b1.syncGUID, null);
- equal(b1.syncGUID, installSyncGUID);
- ok(!b1.appDisabled);
- ok(!b1.userDisabled);
- ok(b1.isActive);
- ok(!b1.isSystem);
- BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
- BootstrapMonitor.checkAddonStarted(ID1, "1.0");
- equal(getStartupReason(), ADDON_INSTALL);
- equal(getStartupOldVersion(), undefined);
- do_check_in_crash_annotation(ID1, "1.0");
-
- let dir = do_get_addon_root_uri(profileDir, ID1);
- equal(b1.getResourceURI("bootstrap.js").spec, dir + "bootstrap.js");
-
- startupCacheMonitor.check(false);
-});
-
-// Tests that disabling doesn't require a restart
-add_task(async function test_2() {
- let b1 = await AddonManager.getAddonByID(ID1);
- prepare_test({
- [ID1]: [["onDisabling", false], "onDisabled"],
- });
-
- equal(
- b1.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_DISABLE,
- 0
- );
- await b1.disable();
- ensure_test_completed();
-
- await new Promise(executeSoon);
-
- notEqual(b1, null);
- equal(b1.version, "1.0");
- ok(!b1.appDisabled);
- ok(b1.userDisabled);
- ok(!b1.isActive);
- BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
- BootstrapMonitor.checkAddonNotStarted(ID1);
- equal(getShutdownReason(), ADDON_DISABLE);
- equal(getShutdownNewVersion(), undefined);
- do_check_not_in_crash_annotation(ID1, "1.0");
-
- let newb1 = await AddonManager.getAddonByID(ID1);
- notEqual(newb1, null);
- equal(newb1.version, "1.0");
- ok(!newb1.appDisabled);
- ok(newb1.userDisabled);
- ok(!newb1.isActive);
-
- await checkBootstrappedPref();
- startupCacheMonitor.check(false);
-});
-
-// Test that restarting doesn't accidentally re-enable
-add_task(async function test_3() {
- await promiseShutdownManager();
-
- BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
- BootstrapMonitor.checkAddonNotStarted(ID1);
- equal(getShutdownReason(), ADDON_DISABLE);
- equal(getShutdownNewVersion(), undefined);
-
- await promiseStartupManager();
-
- BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
- BootstrapMonitor.checkAddonNotStarted(ID1);
- equal(getShutdownReason(), ADDON_DISABLE);
- equal(getShutdownNewVersion(), undefined);
- do_check_not_in_crash_annotation(ID1, "1.0");
-
- ok(gAddonStartup.exists());
-
- let b1 = await AddonManager.getAddonByID(ID1);
- notEqual(b1, null);
- equal(b1.version, "1.0");
- ok(!b1.appDisabled);
- ok(b1.userDisabled);
- ok(!b1.isActive);
-
- await checkBootstrappedPref();
- startupCacheMonitor.check(false);
-});
-
-// Tests that enabling doesn't require a restart
-add_task(async function test_4() {
- let b1 = await AddonManager.getAddonByID(ID1);
- prepare_test({
- [ID1]: [["onEnabling", false], "onEnabled"],
- });
-
- equal(
- b1.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_ENABLE,
- 0
- );
- await b1.enable();
- ensure_test_completed();
-
- notEqual(b1, null);
- equal(b1.version, "1.0");
- ok(!b1.appDisabled);
- ok(!b1.userDisabled);
- ok(b1.isActive);
- ok(!b1.isSystem);
- BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
- BootstrapMonitor.checkAddonStarted(ID1, "1.0");
- equal(getStartupReason(), ADDON_ENABLE);
- equal(getStartupOldVersion(), undefined);
- do_check_in_crash_annotation(ID1, "1.0");
-
- let newb1 = await AddonManager.getAddonByID(ID1);
- notEqual(newb1, null);
- equal(newb1.version, "1.0");
- ok(!newb1.appDisabled);
- ok(!newb1.userDisabled);
- ok(newb1.isActive);
-
- await checkBootstrappedPref();
- startupCacheMonitor.check(false);
-});
-
-// Tests that a restart shuts down and restarts the add-on
-add_task(async function test_5() {
- await promiseShutdownManager();
- // By the time we've shut down, the database must have been written
- ok(gExtensionsJSON.exists());
-
- BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
- BootstrapMonitor.checkAddonNotStarted(ID1);
- equal(getShutdownReason(), APP_SHUTDOWN);
- equal(getShutdownNewVersion(), undefined);
- do_check_not_in_crash_annotation(ID1, "1.0");
- await promiseStartupManager();
- BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
- BootstrapMonitor.checkAddonStarted(ID1, "1.0");
- equal(getStartupReason(), APP_STARTUP);
- equal(getStartupOldVersion(), undefined);
- do_check_in_crash_annotation(ID1, "1.0");
-
- let b1 = await AddonManager.getAddonByID(ID1);
- notEqual(b1, null);
- equal(b1.version, "1.0");
- ok(!b1.appDisabled);
- ok(!b1.userDisabled);
- ok(b1.isActive);
- ok(!b1.isSystem);
-
- await checkBootstrappedPref();
- startupCacheMonitor.check(false);
-});
-
-// Tests that installing an upgrade doesn't require a restart
-add_task(async function test_6() {
- prepare_test({}, ["onNewInstall"]);
-
- let install = await AddonManager.getInstallForFile(XPIS.test_bootstrap1_2);
- ensure_test_completed();
-
- notEqual(install, null);
- equal(install.type, "extension");
- equal(install.version, "2.0");
- equal(install.name, "Test Bootstrap 1");
- equal(install.state, AddonManager.STATE_DOWNLOADED);
-
- await Promise.all([
- BootstrapMonitor.promiseAddonStartup(ID1),
- new Promise(resolve => {
- prepare_test(
- {
- [ID1]: [["onInstalling", false], "onInstalled"],
- },
- ["onInstallStarted", "onInstallEnded"],
- resolve
- );
- install.install();
- }),
- ]);
-
- startupCacheMonitor.check(true);
-
- let b1 = await AddonManager.getAddonByID(ID1);
- notEqual(b1, null);
- equal(b1.version, "2.0");
- ok(!b1.appDisabled);
- ok(!b1.userDisabled);
- ok(b1.isActive);
- ok(!b1.isSystem);
- BootstrapMonitor.checkAddonInstalled(ID1, "2.0");
- BootstrapMonitor.checkAddonStarted(ID1, "2.0");
- equal(getStartupReason(), ADDON_UPGRADE);
- equal(getInstallOldVersion(), 1);
- equal(getStartupOldVersion(), 1);
- equal(getShutdownReason(), ADDON_UPGRADE);
- equal(getShutdownNewVersion(), 2);
- equal(getUninstallNewVersion(), 2);
- do_check_not_in_crash_annotation(ID1, "1.0");
- do_check_in_crash_annotation(ID1, "2.0");
-
- await checkBootstrappedPref();
-});
-
-// Tests that uninstalling doesn't require a restart
-add_task(async function test_7() {
- let b1 = await AddonManager.getAddonByID(ID1);
- prepare_test({
- [ID1]: [["onUninstalling", false], "onUninstalled"],
- });
-
- equal(
- b1.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_UNINSTALL,
- 0
- );
- await b1.uninstall();
-
- await checkBootstrappedPref();
- startupCacheMonitor.check(true);
-
- ensure_test_completed();
- BootstrapMonitor.checkAddonNotInstalled(ID1);
- BootstrapMonitor.checkAddonNotStarted(ID1);
- equal(getShutdownReason(), ADDON_DISABLE);
- equal(getShutdownNewVersion(), undefined);
- do_check_not_in_crash_annotation(ID1, "2.0");
-
- b1 = await AddonManager.getAddonByID(ID1);
- equal(b1, null);
-
- await promiseRestartManager();
-
- let newb1 = await AddonManager.getAddonByID(ID1);
- equal(newb1, null);
-
- await checkBootstrappedPref();
- startupCacheMonitor.check(false);
-});
-
-// Test that a bootstrapped extension dropped into the profile loads properly
-// on startup and doesn't cause an EM restart
-add_task(async function test_8() {
- await promiseShutdownManager();
-
- await manuallyInstall(XPIS.test_bootstrap1_1, profileDir, ID1);
-
- startupCacheMonitor.check(false);
- await promiseStartupManager();
-
- let b1 = await AddonManager.getAddonByID(ID1);
- notEqual(b1, null);
- equal(b1.version, "1.0");
- ok(!b1.appDisabled);
- ok(!b1.userDisabled);
- ok(b1.isActive);
- ok(!b1.isSystem);
- BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
- BootstrapMonitor.checkAddonStarted(ID1, "1.0");
- equal(getStartupReason(), ADDON_INSTALL);
- equal(getStartupOldVersion(), undefined);
- do_check_in_crash_annotation(ID1, "1.0");
-
- await checkBootstrappedPref();
-});
-
-// Test that items detected as removed during startup get removed properly
-add_task(async function test_9() {
- await promiseShutdownManager();
-
- manuallyUninstall(profileDir, ID1);
-
- await promiseStartupManager();
- startupCacheMonitor.check(true);
- BootstrapMonitor.clear(ID1);
-
- let b1 = await AddonManager.getAddonByID(ID1);
- equal(b1, null);
- do_check_not_in_crash_annotation(ID1, "1.0");
-
- await checkBootstrappedPref();
- startupCacheMonitor.check(false);
-});
-
-// Tests that installing a downgrade sends the right reason
-add_task(async function test_10() {
- prepare_test({}, ["onNewInstall"]);
-
- let install = await AddonManager.getInstallForFile(XPIS.test_bootstrap1_2);
- ensure_test_completed();
-
- notEqual(install, null);
- equal(install.type, "extension");
- equal(install.version, "2.0");
- equal(install.name, "Test Bootstrap 1");
- equal(install.state, AddonManager.STATE_DOWNLOADED);
- do_check_not_in_crash_annotation(ID1, "2.0");
-
- await Promise.all([
- BootstrapMonitor.promiseAddonStartup(ID1),
- new Promise(resolve => {
- prepare_test(
- {
- [ID1]: [["onInstalling", false], "onInstalled"],
- },
- ["onInstallStarted", "onInstallEnded"],
- resolve
- );
- install.install();
- }),
- ]);
-
- startupCacheMonitor.check(false);
-
- let b1 = await AddonManager.getAddonByID(ID1);
- notEqual(b1, null);
- equal(b1.version, "2.0");
- ok(!b1.appDisabled);
- ok(!b1.userDisabled);
- ok(b1.isActive);
- ok(!b1.isSystem);
- BootstrapMonitor.checkAddonInstalled(ID1, "2.0");
- BootstrapMonitor.checkAddonStarted(ID1, "2.0");
- equal(getStartupReason(), ADDON_INSTALL);
- equal(getStartupOldVersion(), undefined);
- do_check_in_crash_annotation(ID1, "2.0");
-
- prepare_test({}, ["onNewInstall"]);
-
- install = await AddonManager.getInstallForFile(XPIS.test_bootstrap1_1);
- ensure_test_completed();
-
- notEqual(install, null);
- equal(install.type, "extension");
- equal(install.version, "1.0");
- equal(install.name, "Test Bootstrap 1");
- equal(install.state, AddonManager.STATE_DOWNLOADED);
-
- await Promise.all([
- BootstrapMonitor.promiseAddonStartup(ID1),
- new Promise(resolve => {
- prepare_test(
- {
- [ID1]: [["onInstalling", false], "onInstalled"],
- },
- ["onInstallStarted", "onInstallEnded"],
- resolve
- );
- install.install();
- }),
- ]);
-
- startupCacheMonitor.check(true);
-
- b1 = await AddonManager.getAddonByID(ID1);
- notEqual(b1, null);
- equal(b1.version, "1.0");
- ok(!b1.appDisabled);
- ok(!b1.userDisabled);
- ok(b1.isActive);
- ok(!b1.isSystem);
- BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
- BootstrapMonitor.checkAddonStarted(ID1, "1.0");
- equal(getStartupReason(), ADDON_DOWNGRADE);
- equal(getInstallOldVersion(), 2);
- equal(getStartupOldVersion(), 2);
- equal(getShutdownReason(), ADDON_DOWNGRADE);
- equal(getShutdownNewVersion(), 1);
- equal(getUninstallNewVersion(), 1);
- do_check_in_crash_annotation(ID1, "1.0");
- do_check_not_in_crash_annotation(ID1, "2.0");
-
- await checkBootstrappedPref();
- startupCacheMonitor.check(false);
-});
-
-// Tests that uninstalling a disabled add-on still calls the uninstall method
-add_task(async function test_11() {
- let b1 = await AddonManager.getAddonByID(ID1);
- prepare_test({
- [ID1]: [
- ["onDisabling", false],
- "onDisabled",
- ["onUninstalling", false],
- "onUninstalled",
- ],
- });
-
- await b1.disable();
-
- BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
- BootstrapMonitor.checkAddonNotStarted(ID1);
- equal(getShutdownReason(), ADDON_DISABLE);
- equal(getShutdownNewVersion(), undefined);
- do_check_not_in_crash_annotation(ID1, "1.0");
-
- await b1.uninstall();
- startupCacheMonitor.check(true);
-
- ensure_test_completed();
- BootstrapMonitor.checkAddonNotInstalled(ID1);
- BootstrapMonitor.checkAddonNotStarted(ID1);
- do_check_not_in_crash_annotation(ID1, "1.0");
-
- await checkBootstrappedPref();
-});
-
-// Tests that bootstrapped extensions are correctly loaded even if the app is
-// upgraded at the same time
-add_task(async function test_12() {
- await promiseShutdownManager();
-
- await manuallyInstall(XPIS.test_bootstrap1_1, profileDir, ID1);
-
- await promiseStartupManager();
-
- let b1 = await AddonManager.getAddonByID(ID1);
- notEqual(b1, null);
- equal(b1.version, "1.0");
- ok(!b1.appDisabled);
- ok(!b1.userDisabled);
- ok(b1.isActive);
- ok(!b1.isSystem);
- BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
- BootstrapMonitor.checkAddonStarted(ID1, "1.0");
- equal(getStartupReason(), ADDON_INSTALL);
- equal(getStartupOldVersion(), undefined);
- do_check_in_crash_annotation(ID1, "1.0");
-
- await b1.uninstall();
- startupCacheMonitor.check(true);
-
- await promiseRestartManager();
- await checkBootstrappedPref();
-});
-
-// Check that a bootstrapped extension in a non-profile location is loaded
-add_task(async function test_17() {
- await promiseShutdownManager();
-
- await manuallyInstall(XPIS.test_bootstrap1_1, userExtDir, ID1);
-
- await promiseStartupManager();
-
- let b1 = await AddonManager.getAddonByID(ID1);
- // Should have installed and started
- BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
- BootstrapMonitor.checkAddonStarted(ID1, "1.0");
- notEqual(b1, null);
- equal(b1.version, "1.0");
- ok(b1.isActive);
- ok(!b1.isSystem);
-
- await checkBootstrappedPref();
- startupCacheMonitor.check(false);
-});
-
-// Check that installing a new bootstrapped extension in the profile replaces
-// the existing one
-add_task(async function test_18() {
- await Promise.all([
- BootstrapMonitor.promiseAddonStartup(ID1),
- promiseInstallFile(XPIS.test_bootstrap1_2),
- ]);
- startupCacheMonitor.check(true);
-
- let b1 = await AddonManager.getAddonByID(ID1);
- // Should have installed and started
- BootstrapMonitor.checkAddonInstalled(ID1, "2.0");
- BootstrapMonitor.checkAddonStarted(ID1, "2.0");
- notEqual(b1, null);
- equal(b1.version, "2.0");
- ok(b1.isActive);
- ok(!b1.isSystem);
-
- equal(getShutdownReason(), ADDON_UPGRADE);
- equal(getUninstallReason(), ADDON_UPGRADE);
- equal(getInstallReason(), ADDON_UPGRADE);
- equal(getStartupReason(), ADDON_UPGRADE);
-
- equal(getShutdownNewVersion(), 2);
- equal(getUninstallNewVersion(), 2);
- equal(getInstallOldVersion(), 1);
- equal(getStartupOldVersion(), 1);
-
- await checkBootstrappedPref();
- startupCacheMonitor.check(false);
-});
-
-// Check that uninstalling the profile version reveals the non-profile one
-add_task(async function test_19() {
- let b1 = await AddonManager.getAddonByID(ID1);
- // The revealed add-on gets activated asynchronously
- await new Promise(resolve => {
- prepare_test(
- {
- [ID1]: [
- ["onUninstalling", false],
- "onUninstalled",
- ["onInstalling", false],
- "onInstalled",
- ],
- },
- [],
- resolve
- );
-
- b1.uninstall();
- });
-
- startupCacheMonitor.check(true);
-
- b1 = await AddonManager.getAddonByID(ID1);
- // Should have reverted to the older version
- BootstrapMonitor.checkAddonInstalled(ID1, "1.0");
- BootstrapMonitor.checkAddonStarted(ID1, "1.0");
- notEqual(b1, null);
- equal(b1.version, "1.0");
- ok(b1.isActive);
- ok(!b1.isSystem);
-
- equal(getShutdownReason(), ADDON_DOWNGRADE);
- equal(getUninstallReason(), ADDON_DOWNGRADE);
- equal(getInstallReason(), ADDON_DOWNGRADE);
- equal(getStartupReason(), ADDON_DOWNGRADE);
-
- equal(getShutdownNewVersion(), "1.0");
- equal(getUninstallNewVersion(), "1.0");
- equal(getInstallOldVersion(), "2.0");
- equal(getStartupOldVersion(), "2.0");
-
- await checkBootstrappedPref();
- startupCacheMonitor.check(false);
-});
deleted file mode 100644
--- a/common/test/xpcshell/test_bootstrap_const.js
+++ /dev/null
@@ -1,38 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
-
-createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
-
-const ADDONS = {
- test_bootstrap_const: {
- "manifest.json": JSON.stringify({
- applications: {
- gecko: {
- id: "bootstrap@tests.mozilla.org",
- },
- },
- legacy: {
- type: "bootstrap",
- },
- manifest_version: 2,
- name: "Test Bootstrap 1",
- version: "1.0",
- }),
- "bootstrap.js":
- 'var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");\n\nconst install = function() {\n Services.obs.notifyObservers(null, "addon-install");\n};\n',
- },
-};
-
-add_task(async function() {
- await promiseStartupManager();
-
- let sawInstall = false;
- Services.obs.addObserver(function() {
- sawInstall = true;
- }, "addon-install");
-
- await AddonTestUtils.promiseInstallXPI(ADDONS.test_bootstrap_const);
-
- ok(sawInstall);
-});
deleted file mode 100644
--- a/common/test/xpcshell/test_bootstrap_globals.js
+++ /dev/null
@@ -1,74 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
-
-// This verifies that bootstrap.js has the expected globals defined
-var { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
-
-createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1");
-
-const ADDONS = {
- bootstrap_globals: {
- "manifest.json": JSON.stringify({
- applications: {
- gecko: {
- id: "bootstrap_globals@tests.mozilla.org",
- },
- },
- legacy: {
- type: "bootstrap",
- },
- manifest_version: 2,
- name: "Test Bootstrap 1",
- version: "1.0",
- }),
- "bootstrap.js": String.raw`var {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
-
-var seenGlobals = new Set();
-var scope = this;
-function checkGlobal(name, type) {
- if (scope[name] && typeof(scope[name]) == type)
- seenGlobals.add(name);
-}
-
-var wrapped = {};
-Services.obs.notifyObservers({ wrappedJSObject: wrapped }, "bootstrap-request-globals");
-for (let [name, type] of wrapped.expectedGlobals) {
- checkGlobal(name, type);
-}
-
-function startup(data, reason) {
- Services.obs.notifyObservers({ wrappedJSObject: seenGlobals }, "bootstrap-seen-globals");
-}
-
-function install(data, reason) {}
-function shutdown(data, reason) {}
-function uninstall(data, reason) {}
-`,
- },
-};
-
-const EXPECTED_GLOBALS = [["console", "object"]];
-
-async function run_test() {
- do_test_pending();
- await promiseStartupManager();
- let sawGlobals = false;
-
- Services.obs.addObserver(function(subject) {
- subject.wrappedJSObject.expectedGlobals = EXPECTED_GLOBALS;
- }, "bootstrap-request-globals");
-
- Services.obs.addObserver(function({ wrappedJSObject: seenGlobals }) {
- for (let [name] of EXPECTED_GLOBALS) {
- Assert.ok(seenGlobals.has(name));
- }
-
- sawGlobals = true;
- }, "bootstrap-seen-globals");
-
- await AddonTestUtils.promiseInstallXPI(ADDONS.bootstrap_globals);
- Assert.ok(sawGlobals);
- await promiseShutdownManager();
- do_test_finished();
-}
deleted file mode 100644
--- a/common/test/xpcshell/test_bootstrapped_chrome_manifest.js
+++ /dev/null
@@ -1,63 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
-
-const ADDON = {
- "manifest.json": JSON.stringify({
- applications: {
- gecko: {
- id: "bug675371@tests.mozilla.org",
- },
- },
- legacy: {
- type: "bootstrap",
- },
- manifest_version: 2,
- name: "Test Bootstrap 1",
- version: "1.0",
- }),
- "chrome.manifest": `content bug675371 .`,
- "test.js": `var active = true;`,
-};
-
-add_task(async function run_test() {
- createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
- await promiseStartupManager();
-});
-
-function checkActive(expected) {
- let target = { active: false };
- let load = () => {
- Services.scriptloader.loadSubScript(
- "chrome://bug675371/content/test.js",
- target
- );
- };
-
- if (expected) {
- load();
- } else {
- Assert.throws(load, /Error opening input stream/);
- }
- equal(target.active, expected, "Manifest is active?");
-}
-
-add_task(async function test() {
- let { addon } = await AddonTestUtils.promiseInstallXPI(ADDON);
-
- Assert.ok(addon.isActive);
-
- // Tests that chrome.manifest is registered when the addon is installed.
- checkActive(true);
-
- await addon.disable();
- checkActive(false);
-
- await addon.enable();
- checkActive(true);
-
- await promiseShutdownManager();
-
- // Tests that chrome.manifest remains registered at app shutdown.
- checkActive(true);
-});
deleted file mode 100644
--- a/common/test/xpcshell/xpcshell.ini
+++ /dev/null
@@ -1,10 +0,0 @@
-[DEFAULT]
-tags = addons
-head = head_addons.js
-support-files =
- data/**
-
-[test_bootstrap.js]
-[test_bootstrap_const.js]
-[test_bootstrap_globals.js]
-[test_bootstrapped_chrome_manifest.js]
--- a/mail/base/content/aboutAddonsExtra.js
+++ b/mail/base/content/aboutAddonsExtra.js
@@ -1,26 +1,14 @@
/* 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/. */
/* import-globals-from ../../../../toolkit/mozapps/extensions/content/aboutaddons.js */
-var { ExtensionSupport } = ChromeUtils.import(
- "resource:///modules/ExtensionSupport.jsm"
-);
-var { BrowserUtils } = ChromeUtils.import(
- "resource://gre/modules/BrowserUtils.jsm"
-);
-
-var mailExtBundle = Services.strings.createBundle(
- "chrome://messenger/locale/extensionsOverlay.properties"
-);
-var extensionsNeedingRestart = new Set();
-
const THUNDERBIRD_THEME_PREVIEWS = new Map([
[
"thunderbird-compact-light@mozilla.org",
"chrome://mozapps/content/extensions/firefox-compact-light.svg",
],
[
"thunderbird-compact-dark@mozilla.org",
"chrome://mozapps/content/extensions/firefox-compact-dark.svg",
@@ -62,136 +50,9 @@ const THUNDERBIRD_THEME_PREVIEWS = new M
let _getScreenshotUrlForAddon = getScreenshotUrlForAddon;
getScreenshotUrlForAddon = function(addon) {
if (THUNDERBIRD_THEME_PREVIEWS.has(addon.id)) {
return THUNDERBIRD_THEME_PREVIEWS.get(addon.id);
}
return _getScreenshotUrlForAddon(addon);
};
-
- let _getAddonMessageInfo = getAddonMessageInfo;
- getAddonMessageInfo = async function(addon) {
- let result = await _getAddonMessageInfo(addon);
- if (!result.message) {
- let { stringName } = getTrueState(addon, "gDetailView._addon");
- if (stringName) {
- result.message = mailExtBundle.formatStringFromName(stringName, [
- addon.name,
- brandBundle.GetStringFromName("brandShortName"),
- ]);
- result.type = "success";
- extensionsNeedingRestart.add(addon.id);
- } else {
- extensionsNeedingRestart.delete(addon.id);
- }
- setRestartBar();
- }
- return result;
- };
-
- let listener = {
- onUninstalling(addon) {
- if (ExtensionSupport.loadedLegacyExtensions.hasAnyState(addon.id)) {
- extensionsNeedingRestart.add(addon.id);
- setRestartBar();
- }
- },
- onUninstalled(addon) {
- if (ExtensionSupport.loadedLegacyExtensions.hasAnyState(addon.id)) {
- extensionsNeedingRestart.add(addon.id);
- setRestartBar();
- }
- },
- };
- AddonManager.addAddonListener(listener);
- window.addEventListener("unload", () =>
- AddonManager.removeAddonListener(listener)
- );
-
- // If a legacy extension has been removed, it needs a restart but is not in the list
- // - show the restart bar anyway.
- let removed = await ExtensionSupport.loadedLegacyExtensions.listRemoved();
- for (let removedExtension of removed) {
- extensionsNeedingRestart.add(removedExtension.id);
- }
- setRestartBar();
})();
-
-function setRestartBar() {
- let list = document.querySelector("addon-list");
- if (!list || list.type != "extension") {
- return;
- }
-
- let restartBar = document.getElementById("restartBar");
- if (extensionsNeedingRestart.size == 0) {
- if (restartBar) {
- restartBar.remove();
- }
- return;
- }
- if (restartBar) {
- return;
- }
-
- restartBar = document.createElement("message-bar");
- restartBar.id = "restartBar";
- restartBar.setAttribute("type", "warning");
-
- const message = document.createElement("span");
- message.textContent = mailExtBundle.formatStringFromName(
- "globalRestartMessage",
- [brandBundle.GetStringFromName("brandShortName")]
- );
-
- const restart = document.createElement("button");
- restart.textContent = mailExtBundle.GetStringFromName("globalRestartButton");
- restart.addEventListener("click", () => {
- BrowserUtils.restartApplication();
- });
-
- restartBar.append(message, restart);
- list.pendingUninstallStack.append(restartBar);
-}
-
-/**
- * The true status of legacy extensions, which AddonManager doesn't know
- * about because it thinks all extensions are restartless.
- *
- * @return An object of three properties:
- * stringName: a string to display to the user, from extensionsOverlay.properties.
- * undoFunction: function to call, should the user want to return to the previous state.
- * version: the current version of the extension.
- */
-function getTrueState(addon) {
- let state = ExtensionSupport.loadedLegacyExtensions.get(addon.id);
- let returnObject = {};
-
- if (!state) {
- return returnObject;
- }
-
- if (
- addon.pendingOperations & AddonManager.PENDING_UNINSTALL &&
- ExtensionSupport.loadedLegacyExtensions.has(addon.id)
- ) {
- returnObject.stringName = "warnLegacyUninstall";
- returnObject.undoFunction = addon.cancelUninstall;
- } else if (state.pendingOperation == "install") {
- returnObject.stringName = "warnLegacyInstall";
- returnObject.undoFunction = addon.uninstall;
- } else if (addon.userDisabled) {
- returnObject.stringName = "warnLegacyDisable";
- returnObject.undoFunction = addon.enable;
- } else if (state.pendingOperation == "enable") {
- returnObject.stringName = "warnLegacyEnable";
- returnObject.undoFunction = addon.disable;
- } else if (state.pendingOperation == "upgrade") {
- returnObject.stringName = "warnLegacyUpgrade";
- returnObject.version = state.version;
- } else if (state.pendingOperation == "downgrade") {
- returnObject.stringName = "warnLegacyDowngrade";
- returnObject.version = state.version;
- }
-
- return returnObject;
-}
--- a/mail/base/content/mailWindowOverlay.js
+++ b/mail/base/content/mailWindowOverlay.js
@@ -3968,35 +3968,16 @@ async function initAddonPrefsMenu(
} else if (addon.optionsType === null || addon.optionsType == 3) {
addonsFound.push({
addon,
optionsURL: addon.optionsURL,
optionsOpenInTab: addon.optionsType == 3,
});
}
}
- if (
- ExtensionSupport.loadedLegacyExtensions.has(addon.id) ||
- ExtensionSupport.loadedBootstrapExtensions.has(addon.id)
- ) {
- let webextension = ExtensionParent.GlobalManager.getExtension(addon.id);
- let legacy = webextension.manifest.legacy;
- if (
- typeof legacy == "boolean" ||
- !legacy.options ||
- !legacy.options.page
- ) {
- continue;
- }
- addonsFound.push({
- addon,
- optionsURL: legacy.options.page,
- optionsOpenInTab: legacy.options.open_in_tab,
- });
- }
}
// Populate the menu with addon names and icons.
// Note: Having the following code in the getAddonsByTypes() async callback
// above works on Windows and Linux but doesn't work on Mac, see bug 1419145.
if (addonsFound.length > 0) {
addonsFound.sort((a, b) => a.addon.name.localeCompare(b.addon.name));
for (let {
--- a/mail/base/content/messenger.xhtml
+++ b/mail/base/content/messenger.xhtml
@@ -484,17 +484,16 @@
<label id="addon-webext-perm-intro" class="addon-webext-perm-text"/>
<html:ul id="addon-webext-perm-list" class="addon-webext-perm-list"/>
</popupnotificationcontent>
</popupnotification>
<popupnotification id="addon-installed-notification" hidden="true">
<popupnotificationcontent class="addon-installed-notification-content" orient="vertical">
<html:ul id="addon-installed-list" class="addon-installed-list"/>
- <description id="addon-installed-restart-text" class="addon-installed-restart-text"/>
</popupnotificationcontent>
</popupnotification>
#ifdef MOZ_UPDATER
<popupnotification id="app-update-notification" hidden="true">
<popupnotificationcontent class="app-update-notification-content" orient="vertical">
<description id="app-update-text" class="app-update-text"/>
</popupnotificationcontent>
--- a/mail/base/modules/ExtensionsUI.jsm
+++ b/mail/base/modules/ExtensionsUI.jsm
@@ -9,19 +9,16 @@ const ADDONS_PROPERTIES = "chrome://mess
const BRAND_PROPERTIES = "chrome://branding/locale/brand.properties";
const DEFAULT_EXTENSION_ICON =
"chrome://mozapps/skin/extensions/extensionGeneric.svg";
const HTML_NS = "http://www.w3.org/1999/xhtml";
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
-const { BrowserUtils } = ChromeUtils.import(
- "resource://gre/modules/BrowserUtils.jsm"
-);
XPCOMUtils.defineLazyModuleGetters(this, {
AddonManager: "resource://gre/modules/AddonManager.jsm",
AddonManagerPrivate: "resource://gre/modules/AddonManager.jsm",
ExtensionData: "resource://gre/modules/Extension.jsm",
PluralForm: "resource://gre/modules/PluralForm.jsm",
Services: "resource://gre/modules/Services.jsm",
setTimeout: "resource://gre/modules/Timer.jsm",
StringBundle: "resource:///modules/StringBundle.jsm",
@@ -323,82 +320,46 @@ var gXPInstallObserver = {
let brandBundle = document.getElementById("bundle_brand");
let appName = brandBundle.getString("brandShortName");
let message = addonsBundle.getFormattedString("addonPostInstall.message1", [
"<>",
appName,
]);
- let restartRequired = await this._installRequiresRestart(addon);
let icon = DEFAULT_EXTENSION_ICON;
if (addon.isWebExtension) {
icon = AddonManager.getPreferredIconURL(addon, 32, window) || icon;
}
let options = {
hideClose: true,
timeout: Date.now() + 30000,
popupIconURL: icon,
name: addon.name,
};
let list = document.getElementById("addon-installed-list");
list.hidden = true;
- this._showInstallNotification(browser, restartRequired, message, options);
+ this._showInstallNotification(browser, message, options);
},
- _showInstallNotification(browser, restartRequired, message, options) {
- let document = getTopWindow().document;
-
- let brandBundle = document.getElementById("bundle_brand");
- let appName = brandBundle.getString("brandShortName");
-
- let action;
- let secondaryActions = null;
- let textEl = document.getElementById("addon-installed-restart-text");
- if (restartRequired) {
- action = {
- label: addonsBundle.getString("addonPostInstall.restart.label"),
- accessKey: addonsBundle.getString("addonPostInstall.restart.accesskey"),
- callback: () => {
- BrowserUtils.restartApplication();
- },
- };
- secondaryActions = [
- {
- label: addonsBundle.getString("addonPostInstall.noRestart.label"),
- accessKey: addonsBundle.getString(
- "addonPostInstall.noRestart.accesskey"
- ),
- callback: () => {},
- },
- ];
- textEl.textContent = addonsBundle.getFormattedString(
- "addonPostInstall.restartRequired.message",
- [appName]
- );
- textEl.hidden = false;
- } else {
- action = {
- label: addonsBundle.getString("addonPostInstall.okay.label"),
- accessKey: addonsBundle.getString("addonPostInstall.okay.accesskey"),
- callback: () => {},
- };
- textEl.hidden = true;
- }
-
+ _showInstallNotification(browser, message, options) {
showNotification(
browser,
"addon-installed",
message,
"addons-notification-icon",
- action,
- secondaryActions,
+ {
+ label: addonsBundle.getString("addonPostInstall.okay.label"),
+ accessKey: addonsBundle.getString("addonPostInstall.okay.accesskey"),
+ callback: () => {},
+ },
+ null,
options
);
},
/* eslint-disable complexity */
observe(subject, topic, data) {
let installInfo = subject.wrappedJSObject;
let browser = installInfo.browser || installInfo.target;
@@ -783,40 +744,16 @@ var gXPInstallObserver = {
_removeProgressNotification(browser) {
let notification = getNotification("addon-progress", browser);
if (notification) {
notification.remove();
}
},
- async _installRequiresRestart(addon) {
- if (!addon.isWebExtension) {
- return false;
- }
-
- let data = new ExtensionData(addon.getResourceURI());
- await data.loadManifest();
-
- if (!data.manifest.legacy) {
- return false;
- }
-
- switch (typeof data.manifest.legacy) {
- case "boolean":
- return data.manifest.legacy;
- case "object":
- return !(
- data.manifest.legacy.type && data.manifest.legacy.type == "bootstrap"
- );
- default:
- return false;
- }
- },
-
async _checkForSideloaded(browser) {
let sideloaded = await AddonManagerPrivate.getNewSideloads();
if (sideloaded.length == 0) {
return;
}
// Check if the user wants any sideloaded add-ons installed.
@@ -859,39 +796,30 @@ var gXPInstallObserver = {
[appName]
);
let list = document.getElementById("addon-installed-list");
list.hidden = false;
while (list.lastChild) {
list.lastChild.remove();
}
- let textEl = document.getElementById("addon-installed-restart-text");
- textEl.textContent = addonsBundle.getFormattedString(
- "addonPostInstall.restartRequired.message",
- [appName]
- );
- textEl.hidden = false;
- let restartRequired = false;
for (let addon of enabled) {
let item = document.createElementNS(HTML_NS, "li");
item.textContent = addon.name;
list.appendChild(item);
- restartRequired =
- restartRequired || (await this._installRequiresRestart(addon));
}
let options = {
popupIconURL: DEFAULT_EXTENSION_ICON,
hideClose: true,
timeout: Date.now() + 30000,
};
- this._showInstallNotification(browser, restartRequired, message, options);
+ this._showInstallNotification(browser, message, options);
},
};
Services.obs.addObserver(gXPInstallObserver, "addon-install-disabled");
Services.obs.addObserver(gXPInstallObserver, "addon-install-origin-blocked");
Services.obs.addObserver(gXPInstallObserver, "addon-install-blocked");
Services.obs.addObserver(gXPInstallObserver, "addon-install-started");
Services.obs.addObserver(gXPInstallObserver, "addon-install-failed");
--- a/mail/components/extensions/ext-mail.json
+++ b/mail/components/extensions/ext-mail.json
@@ -80,23 +80,16 @@
"geckoProfiler": {
"url": "chrome://extensions/content/parent/ext-geckoProfiler.js",
"schema": "chrome://extensions/content/schemas/geckoProfiler.json",
"scopes": ["addon_parent"],
"paths": [
["geckoProfiler"]
]
},
- "legacy": {
- "url": "chrome://messenger/content/parent/ext-legacy.js",
- "schema": "chrome://messenger/content/schemas/legacy.json",
- "scopes": ["addon_parent"],
- "events": ["disable", "uninstall", "update"],
- "manifest": ["legacy"]
- },
"mailTabs": {
"url": "chrome://messenger/content/parent/ext-mailTabs.js",
"schema": "chrome://messenger/content/schemas/mailTabs.json",
"scopes": ["addon_parent"],
"manifest": ["mailTabs"],
"paths": [
["mailTabs"]
]
--- a/mail/components/extensions/jar.mn
+++ b/mail/components/extensions/jar.mn
@@ -15,17 +15,16 @@ messenger.jar:
content/messenger/parent/ext-addressBook.js (parent/ext-addressBook.js)
content/messenger/parent/ext-browserAction.js (parent/ext-browserAction.js)
content/messenger/parent/ext-chrome-settings-overrides.js (parent/ext-chrome-settings-overrides.js)
content/messenger/parent/ext-cloudFile.js (parent/ext-cloudFile.js)
content/messenger/parent/ext-commands.js (../../../../browser/components/extensions/parent/ext-commands.js)
content/messenger/parent/ext-compose.js (parent/ext-compose.js)
content/messenger/parent/ext-composeAction.js (parent/ext-composeAction.js)
content/messenger/parent/ext-folders.js (parent/ext-folders.js)
- content/messenger/parent/ext-legacy.js (parent/ext-legacy.js)
content/messenger/parent/ext-mail.js (parent/ext-mail.js)
content/messenger/parent/ext-mailTabs.js (parent/ext-mailTabs.js)
content/messenger/parent/ext-menus.js (parent/ext-menus.js)
content/messenger/parent/ext-messageDisplay.js (parent/ext-messageDisplay.js)
content/messenger/parent/ext-messageDisplayAction.js (parent/ext-messageDisplayAction.js)
content/messenger/parent/ext-messages.js (parent/ext-messages.js)
content/messenger/parent/ext-pkcs11.js (../../../../browser/components/extensions/parent/ext-pkcs11.js)
content/messenger/parent/ext-tabs.js (parent/ext-tabs.js)
@@ -35,17 +34,16 @@ messenger.jar:
content/messenger/schemas/addressBook.json (schemas/addressBook.json)
content/messenger/schemas/browserAction.json (schemas/browserAction.json)
content/messenger/schemas/chrome_settings_overrides.json (schemas/chrome_settings_overrides.json)
content/messenger/schemas/cloudFile.json (schemas/cloudFile.json)
content/messenger/schemas/commands.json (../../../../browser/components/extensions/schemas/commands.json)
content/messenger/schemas/compose.json (schemas/compose.json)
content/messenger/schemas/composeAction.json (schemas/composeAction.json)
content/messenger/schemas/folders.json (schemas/folders.json)
- content/messenger/schemas/legacy.json (schemas/legacy.json)
content/messenger/schemas/mailTabs.json (schemas/mailTabs.json)
content/messenger/schemas/menus.json (schemas/menus.json)
content/messenger/schemas/menus_child.json (schemas/menus_child.json)
content/messenger/schemas/messageDisplay.json (schemas/messageDisplay.json)
content/messenger/schemas/messageDisplayAction.json (schemas/messageDisplayAction.json)
content/messenger/schemas/messages.json (schemas/messages.json)
content/messenger/schemas/pkcs11.json (../../../../browser/components/extensions/schemas/pkcs11.json)
content/messenger/schemas/tabs.json (schemas/tabs.json)
deleted file mode 100644
--- a/mail/components/extensions/parent/ext-legacy.js
+++ /dev/null
@@ -1,469 +0,0 @@
-/* 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/. */
-
-ChromeUtils.defineModuleGetter(
- this,
- "ChromeManifest",
- "resource:///modules/ChromeManifest.jsm"
-);
-ChromeUtils.defineModuleGetter(
- this,
- "ExtensionSupport",
- "resource:///modules/ExtensionSupport.jsm"
-);
-ChromeUtils.defineModuleGetter(
- this,
- "Overlays",
- "resource:///modules/Overlays.jsm"
-);
-ChromeUtils.defineModuleGetter(
- this,
- "XPIInternal",
- "resource://gre/modules/addons/XPIProvider.jsm"
-);
-
-Cu.importGlobalProperties(["fetch"]);
-
-var { XPCOMUtils } = ChromeUtils.import(
- "resource://gre/modules/XPCOMUtils.jsm"
-);
-var { ConsoleAPI } = ChromeUtils.import("resource://gre/modules/Console.jsm");
-
-XPCOMUtils.defineLazyGetter(this, "BOOTSTRAP_REASONS", () => {
- const { XPIProvider } = ChromeUtils.import(
- "resource://gre/modules/addons/XPIProvider.jsm"
- );
- return XPIProvider.BOOTSTRAP_REASONS;
-});
-
-const { Log } = ChromeUtils.import("resource://gre/modules/Log.jsm");
-var logger = Log.repository.getLogger("addons.bootstrap");
-
-let bootstrapScopes = new Map();
-let cachedParams = new Map();
-
-Services.obs.addObserver(() => {
- for (let [id, scope] of bootstrapScopes.entries()) {
- if (ExtensionSupport.loadedBootstrapExtensions.has(id)) {
- scope.shutdown(
- { ...cachedParams.get(id) },
- BOOTSTRAP_REASONS.APP_SHUTDOWN
- );
- }
- }
-}, "quit-application-granted");
-
-var { ExtensionError } = ExtensionUtils;
-
-this.legacy = class extends ExtensionAPI {
- async onManifestEntry(entryName) {
- if (this.extension.manifest.legacy) {
- if (this.extension.manifest.legacy.type == "bootstrap") {
- await this.registerBootstrapped();
- } else {
- await this.registerNonBootstrapped();
- }
- }
- }
-
- // This function is for non-bootstrapped add-ons.
-
- async registerNonBootstrapped() {
- this.extension.legacyLoaded = true;
-
- let state = {
- id: this.extension.id,
- pendingOperation: null,
- version: this.extension.version,
- };
- if (ExtensionSupport.loadedLegacyExtensions.has(this.extension.id)) {
- state = ExtensionSupport.loadedLegacyExtensions.get(this.extension.id);
- let versionComparison = Services.vc.compare(
- this.extension.version,
- state.version
- );
- if (versionComparison != 0) {
- if (versionComparison > 0) {
- state.pendingOperation = "upgrade";
- ExtensionSupport.loadedLegacyExtensions.notifyObservers(state);
- } else if (versionComparison < 0) {
- state.pendingOperation = "downgrade";
- ExtensionSupport.loadedLegacyExtensions.notifyObservers(state);
- }
-
- // Forget any cached files we might've had from another version of this extension.
- Services.obs.notifyObservers(null, "startupcache-invalidate");
- }
- console.log(
- `Legacy WebExtension ${
- this.extension.id
- } has already been loaded in this run, refusing to do so again. Please restart.`
- );
- return;
- }
-
- ExtensionSupport.loadedLegacyExtensions.set(this.extension.id, state);
- if (this.extension.startupReason == "ADDON_INSTALL") {
- // Usually, sideloaded extensions are disabled when they first appear,
- // but to run calendar tests, we disable this.
- let scope = XPIInternal.XPIStates.findAddon(this.extension.id).location
- .scope;
- let autoDisableScopes = Services.prefs.getIntPref(
- "extensions.autoDisableScopes"
- );
-
- // If the extension was just installed from the distribution folder,
- // it's in the profile extensions folder. We don't want to disable it.
- let isDistroAddon = Services.prefs.getBoolPref(
- "extensions.installedDistroAddon." + this.extension.id,
- false
- );
-
- if (!isDistroAddon && scope & autoDisableScopes) {
- state.pendingOperation = "install";
- console.log(
- `Legacy WebExtension ${
- this.extension.id
- } loading for other reason than startup (${
- this.extension.startupReason
- }), refusing to load immediately.`
- );
- ExtensionSupport.loadedLegacyExtensions.notifyObservers(state);
-
- // Forget any cached files we might've had if this extension was previously installed.
- Services.obs.notifyObservers(null, "startupcache-invalidate");
- return;
- }
- }
- if (this.extension.startupReason == "ADDON_ENABLE") {
- state.pendingOperation = "enable";
- console.log(
- `Legacy WebExtension ${
- this.extension.id
- } loading for other reason than startup (${
- this.extension.startupReason
- }), refusing to load immediately.`
- );
- ExtensionSupport.loadedLegacyExtensions.notifyObservers(state);
- return;
- }
-
- let extensionRoot;
- if (this.extension.rootURI instanceof Ci.nsIJARURI) {
- extensionRoot = this.extension.rootURI.JARFile.QueryInterface(
- Ci.nsIFileURL
- ).file;
- console.log("Loading packed extension from", extensionRoot.path);
- } else {
- extensionRoot = this.extension.rootURI.QueryInterface(Ci.nsIFileURL).file;
- console.log("Loading unpacked extension from", extensionRoot.path);
- }
-
- // Have Gecko do as much loading as is still possible
- try {
- Cc["@mozilla.org/component-manager-extra;1"]
- .getService(Ci.nsIComponentManagerExtra)
- .addLegacyExtensionManifestLocation(extensionRoot);
- } catch (e) {
- throw new ExtensionError(e.message, e.fileName, e.lineNumber);
- }
-
- // Load chrome.manifest
- let appinfo = Services.appinfo;
- let options = {
- application: appinfo.ID,
- appversion: appinfo.version,
- platformversion: appinfo.platformVersion,
- os: appinfo.OS,
- osversion: Services.sysinfo.getProperty("version"),
- abi: appinfo.XPCOMABI,
- };
- let loader = async filename => {
- let url = this.extension.getURL(filename);
- return fetch(url).then(response => response.text());
- };
- let chromeManifest = new ChromeManifest(loader, options);
- await chromeManifest.parse("chrome.manifest");
-
- // Load preference files
- console.log("Loading add-on preferences from ", extensionRoot.path);
- ExtensionSupport.loadAddonPrefs(extensionRoot);
-
- // Fire profile-after-change notifications, because we are past that event by now
- console.log("Firing profile-after-change listeners for", this.extension.id);
- let profileAfterChange = chromeManifest.category.get(
- "profile-after-change"
- );
- for (let contractid of profileAfterChange.values()) {
- let service = contractid.startsWith("service,");
- let instance;
- try {
- if (service) {
- instance = Cc[contractid.substr(8)].getService(Ci.nsIObserver);
- } else {
- instance = Cc[contractid].createInstance(Ci.nsIObserver);
- }
-
- instance.observe(null, "profile-after-change", null);
- } catch (e) {
- console.error(
- "Error firing profile-after-change listener for",
- contractid
- );
- }
- }
-
- // Overlays.load must only be called once per window per extension.
- // We use this WeakSet to remember all windows we've already seen.
- let seenDocuments = new WeakSet();
-
- // Listen for new windows to overlay.
- let documentObserver = {
- observe(doc) {
- if (
- ExtensionCommon.instanceOf(doc, "HTMLDocument") &&
- !seenDocuments.has(doc)
- ) {
- seenDocuments.add(doc);
- Overlays.load(chromeManifest, doc.defaultView);
- }
- },
- };
- Services.obs.addObserver(documentObserver, "chrome-document-interactive");
-
- // Add overlays to all existing windows.
- getAllWindows().forEach(win => {
- if (
- ["interactive", "complete"].includes(win.document.readyState) &&
- !seenDocuments.has(win.document)
- ) {
- seenDocuments.add(win.document);
- Overlays.load(chromeManifest, win);
- }
- });
-
- this.extension.callOnClose({
- close: () => {
- Services.obs.removeObserver(
- documentObserver,
- "chrome-document-interactive"
- );
- },
- });
- }
-
- // The following functions are for bootstrapped add-ons.
-
- async registerBootstrapped() {
- let oldParams = cachedParams.get(this.extension.id);
- let params = {
- id: this.extension.id,
- version: this.extension.version,
- resourceURI: Services.io.newURI(this.extension.resourceURL),
- installPath: this.extensionFile.path,
- };
- cachedParams.set(this.extension.id, { ...params });
-
- if (
- oldParams &&
- ["ADDON_UPGRADE", "ADDON_DOWNGRADE"].includes(
- this.extension.startupReason
- )
- ) {
- params.oldVersion = oldParams.version;
- }
-
- let scope = await this.loadScope();
- bootstrapScopes.set(this.extension.id, scope);
-
- if (
- ["ADDON_INSTALL", "ADDON_UPGRADE", "ADDON_DOWNGRADE"].includes(
- this.extension.startupReason
- )
- ) {
- scope.install(params, BOOTSTRAP_REASONS[this.extension.startupReason]);
- }
- scope.startup(params, BOOTSTRAP_REASONS[this.extension.startupReason]);
- ExtensionSupport.loadedBootstrapExtensions.add(this.extension.id);
- }
-
- static onDisable(id) {
- if (bootstrapScopes.has(id)) {
- bootstrapScopes
- .get(id)
- .shutdown({ ...cachedParams.get(id) }, BOOTSTRAP_REASONS.ADDON_DISABLE);
- ExtensionSupport.loadedBootstrapExtensions.delete(id);
- }
- }
-
- static onUpdate(id, manifest) {
- if (bootstrapScopes.has(id)) {
- let params = {
- ...cachedParams.get(id),
- newVersion: manifest.version,
- };
- let reason = BOOTSTRAP_REASONS.ADDON_UPGRADE;
- if (Services.vc.compare(params.newVersion, params.version) < 0) {
- reason = BOOTSTRAP_REASONS.ADDON_DOWNGRADE;
- }
-
- let scope = bootstrapScopes.get(id);
- scope.shutdown(params, reason);
- scope.uninstall(params, reason);
- ExtensionSupport.loadedBootstrapExtensions.delete(id);
- bootstrapScopes.delete(id);
- }
- }
-
- static onUninstall(id) {
- if (bootstrapScopes.has(id)) {
- bootstrapScopes
- .get(id)
- .uninstall(
- { ...cachedParams.get(id) },
- BOOTSTRAP_REASONS.ADDON_UNINSTALL
- );
- bootstrapScopes.delete(id);
- }
- }
-
- get extensionFile() {
- let uri = Services.io.newURI(this.extension.resourceURL);
- if (uri instanceof Ci.nsIJARURI) {
- uri = uri.QueryInterface(Ci.nsIJARURI).JARFile;
- }
- return uri.QueryInterface(Ci.nsIFileURL).file;
- }
-
- loadScope() {
- let { extension } = this;
- let file = this.extensionFile;
- let uri = this.extension.getURL("bootstrap.js");
- let principal = Services.scriptSecurityManager.getSystemPrincipal();
-
- let sandbox = new Cu.Sandbox(principal, {
- sandboxName: uri,
- addonId: this.extension.id,
- wantGlobalProperties: ["ChromeUtils"],
- metadata: { addonID: this.extension.id, URI: uri },
- });
-
- try {
- Object.assign(sandbox, BOOTSTRAP_REASONS);
-
- XPCOMUtils.defineLazyGetter(
- sandbox,
- "console",
- () => new ConsoleAPI({ consoleID: `addon/${this.extension.id}` })
- );
-
- Services.scriptloader.loadSubScript(uri, sandbox);
- } catch (e) {
- logger.warn(`Error loading bootstrap.js for ${this.extension.id}`, e);
- }
-
- function findMethod(name) {
- if (sandbox.name) {
- return sandbox.name;
- }
-
- try {
- let method = Cu.evalInSandbox(name, sandbox);
- return method;
- } catch (err) {}
-
- return () => {
- logger.warn(
- `Add-on ${extension.id} is missing bootstrap method ${name}`
- );
- };
- }
-
- let install = findMethod("install");
- let uninstall = findMethod("uninstall");
- let startup = findMethod("startup");
- let shutdown = findMethod("shutdown");
-
- return {
- install(...args) {
- try {
- install(...args);
- } catch (ex) {
- logger.warn(
- `Exception running bootstrap method install on ${extension.id}`,
- ex
- );
- }
- },
-
- uninstall(...args) {
- try {
- uninstall(...args);
- } catch (ex) {
- logger.warn(
- `Exception running bootstrap method uninstall on ${extension.id}`,
- ex
- );
- } finally {
- // Forget any cached files we might've had from this extension.
- Services.obs.notifyObservers(null, "startupcache-invalidate");
- }
- },
-
- startup(...args) {
- logger.debug(`Registering manifest for ${file.path}\n`);
- Components.manager.addBootstrappedManifestLocation(file);
- try {
- startup(...args);
- } catch (ex) {
- logger.warn(
- `Exception running bootstrap method startup on ${extension.id}`,
- ex
- );
- }
- },
-
- shutdown(data, reason) {
- try {
- shutdown(data, reason);
- } catch (ex) {
- logger.warn(
- `Exception running bootstrap method shutdown on ${extension.id}`,
- ex
- );
- } finally {
- if (reason != BOOTSTRAP_REASONS.APP_SHUTDOWN) {
- logger.debug(`Removing manifest for ${file.path}\n`);
- Components.manager.removeBootstrappedManifestLocation(file);
- }
- }
- },
- };
- }
-};
-
-function getAllWindows() {
- function getChildDocShells(parentDocShell) {
- let docShells = parentDocShell.getAllDocShellsInSubtree(
- Ci.nsIDocShellTreeItem.typeAll,
- Ci.nsIDocShell.ENUMERATE_FORWARDS
- );
-
- for (let docShell of docShells) {
- docShell
- .QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIWebProgress);
- domWindows.push(docShell.domWindow);
- }
- }
-
- let domWindows = [];
- for (let win of Services.ww.getWindowEnumerator()) {
- let parentDocShell = win
- .getInterface(Ci.nsIWebNavigation)
- .QueryInterface(Ci.nsIDocShell);
- getChildDocShells(parentDocShell);
- }
- return domWindows;
-}
deleted file mode 100644
--- a/mail/components/extensions/schemas/legacy.json
+++ /dev/null
@@ -1,43 +0,0 @@
-[
- {
- "namespace": "manifest",
- "types": [
- {
- "$extend": "WebExtensionManifest",
- "properties": {
- "legacy": {
- "optional": true,
- "choices": [
- {
- "type": "boolean"
- },
- {
- "type": "object",
- "properties": {
- "type": {
- "type": "string",
- "enum": ["xul", "bootstrap"],
- "optional": true
- },
- "options": {
- "type": "object",
- "properties": {
- "page": {
- "type": "string"
- },
- "open_in_tab": {
- "type": "boolean",
- "optional": true
- }
- },
- "optional": true
- }
- }
- }
- ]
- }
- }
- }
- ]
- }
-]
--- a/mail/locales/en-US/chrome/messenger/addons.properties
+++ b/mail/locales/en-US/chrome/messenger/addons.properties
@@ -17,24 +17,16 @@ xpinstallDisabledButton.accesskey=n
# %2$S is replaced with the localized name of the application.
addonPostInstall.message1=%1$S has been added to %2$S.
# LOCALIZATION NOTE (addonPostInstall.multiple.message1)
# %1$S is replaced with the localized name of the application.
addonPostInstall.multiple.message=These add-ons have been added to %1$S:
addonPostInstall.okay.label=OK
addonPostInstall.okay.accesskey=O
-# LOCALIZATION NOTE (addonPostInstall.restartRequired.message)
-# %S is the application name
-addonPostInstall.restartRequired.message=%S must be restarted to complete the installation.
-addonPostInstall.restart.label=Restart now
-addonPostInstall.restart.accesskey=R
-addonPostInstall.noRestart.label=Not now
-addonPostInstall.noRestart.accesskey=N
-
# LOCALIZATION NOTE (addonDownloadingAndVerifying):
# Semicolon-separated list of plural forms. See:
# http://developer.mozilla.org/en/docs/Localization_and_Plurals
# Also see https://bugzilla.mozilla.org/show_bug.cgi?id=570012 for mockups
addonDownloadingAndVerifying=Downloading and verifying add-on…;Downloading and verifying #1 add-ons…
addonDownloadVerifying=Verifying
addonInstall.unsigned=(Unverified)
deleted file mode 100644
--- a/mail/locales/en-US/chrome/messenger/extensionsOverlay.properties
+++ /dev/null
@@ -1,17 +0,0 @@
-# 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/.
-
-# LOCALIZATION NOTE (warnLegacyUpgrade, warnLegacyDowngrade, warnLegacyEnable, warnLegacyDisable, warnLegacyInstall, warnLegacyUninstall)
-# %1$S is the add-on name, %2$S is brand name
-warnLegacyUpgrade=%1$S will be upgraded after you restart %2$S.
-warnLegacyDowngrade=%1$S will be downgraded after you restart %2$S.
-warnLegacyEnable=%1$S will be enabled after you restart %2$S.
-warnLegacyDisable=%1$S will be disabled after you restart %2$S.
-warnLegacyInstall=%1$S will be installed after you restart %2$S.
-warnLegacyUninstall=%1$S will be uninstalled after you restart %2$S.
-warnLegacyRestartButton=Restart
-warnLegacyUndoButton=Undo
-
-globalRestartMessage=Some operations will not be completed until you restart %S.
-globalRestartButton=Restart Now
--- a/mail/locales/jar.mn
+++ b/mail/locales/jar.mn
@@ -83,17 +83,16 @@
locale/@AB_CD@/messenger/folderpane.dtd (%chrome/messenger/folderpane.dtd)
locale/@AB_CD@/messenger/folderProps.dtd (%chrome/messenger/folderProps.dtd)
locale/@AB_CD@/messenger/folderWidgets.properties (%chrome/messenger/folderWidgets.properties)
locale/@AB_CD@/messenger/subscribe.dtd (%chrome/messenger/subscribe.dtd)
locale/@AB_CD@/messenger/subscribe.properties (%chrome/messenger/subscribe.properties)
locale/@AB_CD@/messenger/msgHdrViewOverlay.dtd (%chrome/messenger/msgHdrViewOverlay.dtd)
locale/@AB_CD@/messenger/editContactOverlay.dtd (%chrome/messenger/editContactOverlay.dtd)
locale/@AB_CD@/messenger/editContactOverlay.properties (%chrome/messenger/editContactOverlay.properties)
- locale/@AB_CD@/messenger/extensionsOverlay.properties (%chrome/messenger/extensionsOverlay.properties)
locale/@AB_CD@/messenger/mailEditorOverlay.dtd (%chrome/messenger/mailEditorOverlay.dtd)
locale/@AB_CD@/messenger/msgSynchronize.dtd (%chrome/messenger/msgSynchronize.dtd)
locale/@AB_CD@/messenger/offline.properties (%chrome/messenger/offline.properties)
locale/@AB_CD@/messenger/junkMailInfo.dtd (%chrome/messenger/junkMailInfo.dtd)
locale/@AB_CD@/messenger/viewLog.dtd (%chrome/messenger/viewLog.dtd)
locale/@AB_CD@/messenger/FilterListDialog.dtd (%chrome/messenger/FilterListDialog.dtd)
locale/@AB_CD@/messenger/CustomHeaders.dtd (%chrome/messenger/CustomHeaders.dtd)
locale/@AB_CD@/messenger/FilterEditor.dtd (%chrome/messenger/FilterEditor.dtd)