summaryrefslogtreecommitdiffstats
path: root/toolkit/components/webextensions/ExtensionManagement.jsm
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/webextensions/ExtensionManagement.jsm')
-rw-r--r--toolkit/components/webextensions/ExtensionManagement.jsm321
1 files changed, 0 insertions, 321 deletions
diff --git a/toolkit/components/webextensions/ExtensionManagement.jsm b/toolkit/components/webextensions/ExtensionManagement.jsm
deleted file mode 100644
index 324c5b71b..000000000
--- a/toolkit/components/webextensions/ExtensionManagement.jsm
+++ /dev/null
@@ -1,321 +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/. */
-
-"use strict";
-
-this.EXPORTED_SYMBOLS = ["ExtensionManagement"];
-
-const Ci = Components.interfaces;
-const Cc = Components.classes;
-const Cu = Components.utils;
-const Cr = Components.results;
-
-Cu.import("resource://gre/modules/AppConstants.jsm");
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "ExtensionUtils",
- "resource://gre/modules/ExtensionUtils.jsm");
-
-XPCOMUtils.defineLazyGetter(this, "console", () => ExtensionUtils.getConsole());
-
-XPCOMUtils.defineLazyGetter(this, "UUIDMap", () => {
- let {UUIDMap} = Cu.import("resource://gre/modules/Extension.jsm", {});
- return UUIDMap;
-});
-
-/*
- * This file should be kept short and simple since it's loaded even
- * when no extensions are running.
- */
-
-// Keep track of frame IDs for content windows. Mostly we can just use
-// the outer window ID as the frame ID. However, the API specifies
-// that top-level windows have a frame ID of 0. So we need to keep
-// track of which windows are top-level. This code listens to messages
-// from ExtensionContent to do that.
-var Frames = {
- // Window IDs of top-level content windows.
- topWindowIds: new Set(),
-
- init() {
- if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
- return;
- }
-
- Services.mm.addMessageListener("Extension:TopWindowID", this);
- Services.mm.addMessageListener("Extension:RemoveTopWindowID", this, true);
- },
-
- isTopWindowId(windowId) {
- return this.topWindowIds.has(windowId);
- },
-
- // Convert an outer window ID to a frame ID. An outer window ID of 0
- // is invalid.
- getId(windowId) {
- if (this.isTopWindowId(windowId)) {
- return 0;
- }
- if (windowId == 0) {
- return -1;
- }
- return windowId;
- },
-
- // Convert an outer window ID for a parent window to a frame
- // ID. Outer window IDs follow the same convention that
- // |window.top.parent === window.top|. The API works differently,
- // giving a frame ID of -1 for the the parent of a top-level
- // window. This function handles the conversion.
- getParentId(parentWindowId, windowId) {
- if (parentWindowId == windowId) {
- // We have a top-level window.
- return -1;
- }
-
- // Not a top-level window. Just return the ID as normal.
- return this.getId(parentWindowId);
- },
-
- receiveMessage({name, data}) {
- switch (name) {
- case "Extension:TopWindowID":
- // FIXME: Need to handle the case where the content process
- // crashes. Right now we leak its top window IDs.
- this.topWindowIds.add(data.windowId);
- break;
-
- case "Extension:RemoveTopWindowID":
- this.topWindowIds.delete(data.windowId);
- break;
- }
- },
-};
-Frames.init();
-
-var APIs = {
- apis: new Map(),
-
- register(namespace, schema, script) {
- if (this.apis.has(namespace)) {
- throw new Error(`API namespace already exists: ${namespace}`);
- }
-
- this.apis.set(namespace, {schema, script});
- },
-
- unregister(namespace) {
- if (!this.apis.has(namespace)) {
- throw new Error(`API namespace does not exist: ${namespace}`);
- }
-
- this.apis.delete(namespace);
- },
-};
-
-function getURLForExtension(id, path = "") {
- let uuid = UUIDMap.get(id, false);
- if (!uuid) {
- Cu.reportError(`Called getURLForExtension on unmapped extension ${id}`);
- return null;
- }
- return `moz-extension://${uuid}/${path}`;
-}
-
-// This object manages various platform-level issues related to
-// moz-extension:// URIs. It lives here so that it can be used in both
-// the parent and child processes.
-//
-// moz-extension URIs have the form moz-extension://uuid/path. Each
-// extension has its own UUID, unique to the machine it's installed
-// on. This is easier and more secure than using the extension ID,
-// since it makes it slightly harder to fingerprint for extensions if
-// each user uses different URIs for the extension.
-var Service = {
- initialized: false,
-
- // Map[uuid -> extension].
- // extension can be an Extension (parent process) or BrowserExtensionContent (child process).
- uuidMap: new Map(),
-
- init() {
- let aps = Cc["@mozilla.org/addons/policy-service;1"].getService(Ci.nsIAddonPolicyService);
- aps = aps.wrappedJSObject;
- this.aps = aps;
- aps.setExtensionURILoadCallback(this.extensionURILoadableByAnyone.bind(this));
- aps.setExtensionURIToAddonIdCallback(this.extensionURIToAddonID.bind(this));
- },
-
- // Called when a new extension is loaded.
- startupExtension(uuid, uri, extension) {
- if (!this.initialized) {
- this.initialized = true;
- this.init();
- }
-
- // Create the moz-extension://uuid mapping.
- let handler = Services.io.getProtocolHandler("moz-extension");
- handler.QueryInterface(Ci.nsISubstitutingProtocolHandler);
- handler.setSubstitution(uuid, uri);
-
- this.uuidMap.set(uuid, extension);
- this.aps.setAddonHasPermissionCallback(extension.id, extension.hasPermission.bind(extension));
- this.aps.setAddonLoadURICallback(extension.id, this.checkAddonMayLoad.bind(this, extension));
- this.aps.setAddonLocalizeCallback(extension.id, extension.localize.bind(extension));
- this.aps.setAddonCSP(extension.id, extension.manifest.content_security_policy);
- this.aps.setBackgroundPageUrlCallback(uuid, this.generateBackgroundPageUrl.bind(this, extension));
- },
-
- // Called when an extension is unloaded.
- shutdownExtension(uuid) {
- let extension = this.uuidMap.get(uuid);
- this.uuidMap.delete(uuid);
- this.aps.setAddonHasPermissionCallback(extension.id, null);
- this.aps.setAddonLoadURICallback(extension.id, null);
- this.aps.setAddonLocalizeCallback(extension.id, null);
- this.aps.setAddonCSP(extension.id, null);
- this.aps.setBackgroundPageUrlCallback(uuid, null);
-
- let handler = Services.io.getProtocolHandler("moz-extension");
- handler.QueryInterface(Ci.nsISubstitutingProtocolHandler);
- handler.setSubstitution(uuid, null);
- },
-
- // Return true if the given URI can be loaded from arbitrary web
- // content. The manifest.json |web_accessible_resources| directive
- // determines this.
- extensionURILoadableByAnyone(uri) {
- let uuid = uri.host;
- let extension = this.uuidMap.get(uuid);
- if (!extension || !extension.webAccessibleResources) {
- return false;
- }
-
- let path = uri.QueryInterface(Ci.nsIURL).filePath;
- if (path.length > 0 && path[0] == "/") {
- path = path.substr(1);
- }
- return extension.webAccessibleResources.matches(path);
- },
-
- // Checks whether a given extension can load this URI (typically via
- // an XML HTTP request). The manifest.json |permissions| directive
- // determines this.
- checkAddonMayLoad(extension, uri) {
- return extension.whiteListedHosts.matchesIgnoringPath(uri);
- },
-
- generateBackgroundPageUrl(extension) {
- let background_scripts = extension.manifest.background &&
- extension.manifest.background.scripts;
- if (!background_scripts) {
- return;
- }
- let html = "<!DOCTYPE html>\n<body>\n";
- for (let script of background_scripts) {
- script = script.replace(/"/g, "&quot;");
- html += `<script src="${script}"></script>\n`;
- }
- html += "</body>\n</html>\n";
- return "data:text/html;charset=utf-8," + encodeURIComponent(html);
- },
-
- // Finds the add-on ID associated with a given moz-extension:// URI.
- // This is used to set the addonId on the originAttributes for the
- // nsIPrincipal attached to the URI.
- extensionURIToAddonID(uri) {
- let uuid = uri.host;
- let extension = this.uuidMap.get(uuid);
- return extension ? extension.id : undefined;
- },
-};
-
-// API Levels Helpers
-
-// Find the add-on associated with this document via the
-// principal's originAttributes. This value is computed by
-// extensionURIToAddonID, which ensures that we don't inject our
-// API into webAccessibleResources or remote web pages.
-function getAddonIdForWindow(window) {
- return Cu.getObjectPrincipal(window).originAttributes.addonId;
-}
-
-const API_LEVELS = Object.freeze({
- NO_PRIVILEGES: 0,
- CONTENTSCRIPT_PRIVILEGES: 1,
- FULL_PRIVILEGES: 2,
-});
-
-// Finds the API Level ("FULL_PRIVILEGES", "CONTENTSCRIPT_PRIVILEGES", "NO_PRIVILEGES")
-// with a given a window object.
-function getAPILevelForWindow(window, addonId) {
- const {NO_PRIVILEGES, CONTENTSCRIPT_PRIVILEGES, FULL_PRIVILEGES} = API_LEVELS;
-
- // Non WebExtension URLs and WebExtension URLs from a different extension
- // has no access to APIs.
- if (!addonId || getAddonIdForWindow(window) != addonId) {
- return NO_PRIVILEGES;
- }
-
- // Extension pages running in the content process always defaults to
- // "content script API level privileges".
- if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
- return CONTENTSCRIPT_PRIVILEGES;
- }
-
- let docShell = window.QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIDocShell);
-
- // Handling of ExtensionPages running inside sub-frames.
- if (docShell.sameTypeParent) {
- let parentWindow = docShell.sameTypeParent.QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIDOMWindow);
-
- // The option page iframe embedded in the about:addons tab should have
- // full API level privileges. (see Bug 1256282 for rationale)
- let parentDocument = parentWindow.document;
- let parentIsSystemPrincipal = Services.scriptSecurityManager
- .isSystemPrincipal(parentDocument.nodePrincipal);
- if (parentDocument.location.href == "about:addons" && parentIsSystemPrincipal) {
- return FULL_PRIVILEGES;
- }
-
- // The addon iframes embedded in a addon page from with the same addonId
- // should have the same privileges of the sameTypeParent.
- // (see Bug 1258347 for rationale)
- let parentSameAddonPrivileges = getAPILevelForWindow(parentWindow, addonId);
- if (parentSameAddonPrivileges > NO_PRIVILEGES) {
- return parentSameAddonPrivileges;
- }
-
- // In all the other cases, WebExtension URLs loaded into sub-frame UI
- // will have "content script API level privileges".
- // (see Bug 1214658 for rationale)
- return CONTENTSCRIPT_PRIVILEGES;
- }
-
- // WebExtension URLs loaded into top frames UI could have full API level privileges.
- return FULL_PRIVILEGES;
-}
-
-this.ExtensionManagement = {
- startupExtension: Service.startupExtension.bind(Service),
- shutdownExtension: Service.shutdownExtension.bind(Service),
-
- registerAPI: APIs.register.bind(APIs),
- unregisterAPI: APIs.unregister.bind(APIs),
-
- getFrameId: Frames.getId.bind(Frames),
- getParentFrameId: Frames.getParentId.bind(Frames),
-
- getURLForExtension,
-
- // exported API Level Helpers
- getAddonIdForWindow,
- getAPILevelForWindow,
- API_LEVELS,
-
- APIs,
-};