зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1317697: Split ExtensionContent.jsm into a stub process script. r=mixedpuppy
MozReview-Commit-ID: 4vn0ERZiBQd --HG-- rename : toolkit/components/extensions/ExtensionContent.jsm => toolkit/components/extensions/extension-process-script.js extra : rebase_source : cc473732c152fa2ac47202a8c5634e4a68a30763 extra : absorb_source : 4a9b52534bee64e907e61f3bb229b0ad7849c097
This commit is contained in:
Родитель
8410d7c300
Коммит
e1feed9d51
|
@ -11,7 +11,6 @@ var {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
|||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/ExtensionContent.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "E10SUtils",
|
||||
"resource:///modules/E10SUtils.jsm");
|
||||
|
@ -1045,9 +1044,9 @@ var UserContextIdNotifier = {
|
|||
|
||||
UserContextIdNotifier.init();
|
||||
|
||||
ExtensionContent.init(this);
|
||||
Services.obs.notifyObservers(this, "tab-content-frameloader-created");
|
||||
|
||||
addEventListener("unload", () => {
|
||||
ExtensionContent.uninit(this);
|
||||
RefreshBlocker.uninit();
|
||||
});
|
||||
|
||||
|
|
|
@ -336,7 +336,7 @@ TabActor.prototype = {
|
|||
get webextensionsContentScriptGlobals() {
|
||||
// Ignore xpcshell runtime which spawn TabActors without a window.
|
||||
if (this.window) {
|
||||
return ExtensionContent.getContentScriptGlobalsForWindow(this.window);
|
||||
return ExtensionContent.getContentScriptGlobals(this.window);
|
||||
}
|
||||
|
||||
return [];
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
|
||||
var { classes: Cc, interfaces: Ci, utils: Cu } = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/ExtensionContent.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
|
@ -167,7 +166,4 @@ addMessageListener("RemoteLogins:fillForm", function(message) {
|
|||
LoginManagerContent.receiveMessage(message, content);
|
||||
});
|
||||
|
||||
ExtensionContent.init(this);
|
||||
addEventListener("unload", () => {
|
||||
ExtensionContent.uninit(this);
|
||||
});
|
||||
Services.obs.notifyObservers(this, "tab-content-frameloader-created");
|
||||
|
|
|
@ -67,7 +67,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "Schemas",
|
|||
XPCOMUtils.defineLazyModuleGetter(this, "Task",
|
||||
"resource://gre/modules/Task.jsm");
|
||||
|
||||
Cu.import("resource://gre/modules/ExtensionContent.jsm");
|
||||
Cu.import("resource://gre/modules/ExtensionManagement.jsm");
|
||||
Cu.import("resource://gre/modules/ExtensionParent.jsm");
|
||||
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
|
||||
|
|
|
@ -1099,14 +1099,6 @@ ExtensionChild = {
|
|||
// Map<innerWindowId, ExtensionPageContextChild>
|
||||
extensionContexts: new Map(),
|
||||
|
||||
initOnce() {
|
||||
// This initializes the default message handler for messages targeted at
|
||||
// an addon process, in case the addon process receives a message before
|
||||
// its Messenger has been instantiated. For example, if a content script
|
||||
// sends a message while there is no background page.
|
||||
MessageChannel.setupMessageManagers([Services.cpmm]);
|
||||
},
|
||||
|
||||
init(global) {
|
||||
if (!ExtensionManagement.isExtensionProcess) {
|
||||
throw new Error("Cannot init extension page global in current process");
|
||||
|
@ -1127,7 +1119,7 @@ ExtensionChild = {
|
|||
* The extension for which the context should be created.
|
||||
* @param {nsIDOMWindow} contentWindow The global of the page.
|
||||
*/
|
||||
createExtensionContext(extension, contentWindow) {
|
||||
initExtensionContext(extension, contentWindow) {
|
||||
if (!ExtensionManagement.isExtensionProcess) {
|
||||
throw new Error("Cannot create an extension page context in current process");
|
||||
}
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -11,7 +11,6 @@ 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");
|
||||
|
||||
|
@ -27,6 +26,12 @@ XPCOMUtils.defineLazyGetter(this, "UUIDMap", () => {
|
|||
return UUIDMap;
|
||||
});
|
||||
|
||||
const {appinfo} = Services;
|
||||
const isParentProcess = appinfo.processType === appinfo.PROCESS_TYPE_DEFAULT;
|
||||
if (isParentProcess) {
|
||||
Services.ppmm.loadProcessScript("chrome://extensions/content/extension-process-script.js", true);
|
||||
}
|
||||
|
||||
var ExtensionManagement;
|
||||
|
||||
/*
|
||||
|
@ -264,9 +269,9 @@ ExtensionManagement = {
|
|||
|
||||
get isExtensionProcess() {
|
||||
if (this.useRemoteWebExtensions) {
|
||||
return Services.appinfo.remoteType === E10SUtils.EXTENSION_REMOTE_TYPE;
|
||||
return appinfo.remoteType === E10SUtils.EXTENSION_REMOTE_TYPE;
|
||||
}
|
||||
return Services.appinfo.processType === Services.appinfo.PROCESS_TYPE_DEFAULT;
|
||||
return isParentProcess;
|
||||
},
|
||||
|
||||
startupExtension: Service.startupExtension.bind(Service),
|
||||
|
|
|
@ -265,11 +265,9 @@ GlobalManager = {
|
|||
|
||||
_onExtensionBrowser(type, browser, additionalData = {}) {
|
||||
browser.messageManager.loadFrameScript(`data:,
|
||||
Components.utils.import("resource://gre/modules/ExtensionContent.jsm");
|
||||
ExtensionContent.init(this);
|
||||
addEventListener("unload", function() {
|
||||
ExtensionContent.uninit(this);
|
||||
});
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
Services.obs.notifyObservers(this, "tab-content-frameloader-created", "");
|
||||
`, false);
|
||||
|
||||
let viewType = browser.getAttribute("webextension-view-type");
|
||||
|
|
|
@ -55,12 +55,9 @@ let BASE_MANIFEST = Object.freeze({
|
|||
|
||||
|
||||
function frameScript() {
|
||||
Components.utils.import("resource://gre/modules/ExtensionContent.jsm");
|
||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
ExtensionContent.init(this);
|
||||
this.addEventListener("unload", () => { // eslint-disable-line mozilla/balanced-listeners
|
||||
ExtensionContent.uninit(this);
|
||||
});
|
||||
Services.obs.notifyObservers(this, "tab-content-frameloader-created");
|
||||
}
|
||||
|
||||
const FRAME_SCRIPT = `data:text/javascript,(${encodeURI(frameScript)}).call(this)`;
|
||||
|
|
|
@ -2535,6 +2535,7 @@ this.Schemas = {
|
|||
if (schemas) {
|
||||
this.schemaJSON = schemas;
|
||||
}
|
||||
|
||||
Services.cpmm.addMessageListener("Schema:Add", this);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,604 @@
|
|||
/* 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 script contains the minimum, skeleton content process code that we need
|
||||
* in order to lazily load other extension modules when they are first
|
||||
* necessary. Anything which is not likely to be needed immediately, or shortly
|
||||
* after startup, in *every* browser process live outside of this file.
|
||||
*/
|
||||
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionManagement",
|
||||
"resource://gre/modules/ExtensionManagement.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "MatchGlobs",
|
||||
"resource://gre/modules/MatchPattern.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "MatchPattern",
|
||||
"resource://gre/modules/MatchPattern.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "MessageChannel",
|
||||
"resource://gre/modules/MessageChannel.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "WebNavigationFrames",
|
||||
"resource://gre/modules/WebNavigationFrames.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionChild",
|
||||
"resource://gre/modules/ExtensionChild.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionContent",
|
||||
"resource://gre/modules/ExtensionContent.jsm");
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionUtils",
|
||||
"resource://gre/modules/ExtensionUtils.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyGetter(this, "console", () => ExtensionUtils.getConsole());
|
||||
XPCOMUtils.defineLazyGetter(this, "getInnerWindowID", () => ExtensionUtils.getInnerWindowID);
|
||||
|
||||
const isContentProcess = Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT;
|
||||
|
||||
|
||||
class ScriptMatcher {
|
||||
constructor(extension, options) {
|
||||
this.extension = extension;
|
||||
this.options = options;
|
||||
|
||||
this._script = null;
|
||||
|
||||
this.allFrames = options.all_frames;
|
||||
this.matchAboutBlank = options.match_about_blank;
|
||||
this.frameId = options.frame_id;
|
||||
this.runAt = options.run_at;
|
||||
|
||||
this.matches = new MatchPattern(options.matches);
|
||||
this.excludeMatches = new MatchPattern(options.exclude_matches || null);
|
||||
// TODO: MatchPattern should pre-mangle host-only patterns so that we
|
||||
// don't need to call a separate match function.
|
||||
this.matchesHost = new MatchPattern(options.matchesHost || null);
|
||||
this.includeGlobs = options.include_globs && new MatchGlobs(options.include_globs);
|
||||
this.excludeGlobs = new MatchGlobs(options.exclude_globs);
|
||||
}
|
||||
|
||||
toString() {
|
||||
return `[Script {js: [${this.options.js}], matchAboutBlank: ${this.matchAboutBlank}, runAt: ${this.runAt}, matches: ${this.options.matches}}]`;
|
||||
}
|
||||
|
||||
get script() {
|
||||
if (!this._script) {
|
||||
this._script = new ExtensionContent.Script(this.extension.realExtension,
|
||||
this.options);
|
||||
}
|
||||
return this._script;
|
||||
}
|
||||
|
||||
preload() {
|
||||
let {script} = this;
|
||||
|
||||
script.loadCSS();
|
||||
script.compileScripts();
|
||||
}
|
||||
|
||||
matchesLoadInfo(uri, loadInfo) {
|
||||
if (!this.matchesURI(uri)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!this.allFrames && !loadInfo.isTopLevelLoad) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
matchesURI(uri) {
|
||||
if (!(this.matches.matches(uri) || this.matchesHost.matchesIgnoringPath(uri))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.excludeMatches.matches(uri)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.includeGlobs != null && !this.includeGlobs.matches(uri.spec)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.excludeGlobs.matches(uri.spec)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
matchesWindow(window) {
|
||||
if (!this.allFrames && this.frameId == null && window.parent !== window) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let uri = window.document.documentURIObject;
|
||||
let principal = window.document.nodePrincipal;
|
||||
|
||||
if (this.matchAboutBlank) {
|
||||
// When matching top-level about:blank documents,
|
||||
// allow loading into any with a NullPrincipal.
|
||||
if (uri.spec === "about:blank" && window === window.parent && principal.isNullPrincipal) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// When matching about:blank/srcdoc iframes, the checks below
|
||||
// need to be performed against the "owner" document's URI.
|
||||
if (["about:blank", "about:srcdoc"].includes(uri.spec)) {
|
||||
uri = principal.URI;
|
||||
}
|
||||
}
|
||||
|
||||
// Documents from data: URIs also inherit the principal.
|
||||
if (Services.netUtils.URIChainHasFlags(uri, Ci.nsIProtocolHandler.URI_INHERITS_SECURITY_CONTEXT)) {
|
||||
if (!this.matchAboutBlank) {
|
||||
return false;
|
||||
}
|
||||
uri = principal.URI;
|
||||
}
|
||||
|
||||
if (!this.matchesURI(uri)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.frameId != null && WebNavigationFrames.getFrameId(window) !== this.frameId) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If mozAddonManager is present on this page, don't allow
|
||||
// content scripts.
|
||||
if (window.navigator.mozAddonManager !== undefined) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
injectInto(window) {
|
||||
return this.script.injectInto(window);
|
||||
}
|
||||
}
|
||||
|
||||
function getMessageManager(contentWindow) {
|
||||
let docShell = contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDocShell)
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor);
|
||||
try {
|
||||
return docShell.getInterface(Ci.nsIContentFrameMessageManager);
|
||||
} catch (e) {
|
||||
// Some windows don't support this interface (hidden window).
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
var DocumentManager;
|
||||
var ExtensionManager;
|
||||
|
||||
class ExtensionGlobal {
|
||||
constructor(global) {
|
||||
this.global = global;
|
||||
|
||||
MessageChannel.addListener(global, "Extension:Capture", this);
|
||||
MessageChannel.addListener(global, "Extension:DetectLanguage", this);
|
||||
MessageChannel.addListener(global, "Extension:Execute", this);
|
||||
MessageChannel.addListener(global, "WebNavigation:GetFrame", this);
|
||||
MessageChannel.addListener(global, "WebNavigation:GetAllFrames", this);
|
||||
}
|
||||
|
||||
uninit() {
|
||||
}
|
||||
|
||||
get messageFilterStrict() {
|
||||
return {
|
||||
innerWindowID: getInnerWindowID(this.global.content),
|
||||
};
|
||||
}
|
||||
|
||||
receiveMessage({target, messageName, recipient, data}) {
|
||||
switch (messageName) {
|
||||
case "Extension:Capture":
|
||||
return ExtensionContent.handleExtensionCapture(this.global, data.width, data.height, data.options);
|
||||
case "Extension:DetectLanguage":
|
||||
return ExtensionContent.handleDetectLanguage(this.global, target);
|
||||
case "Extension:Execute":
|
||||
let extension = ExtensionManager.get(recipient.extensionId);
|
||||
let script = new ScriptMatcher(extension, data.options);
|
||||
|
||||
return ExtensionContent.handleExtensionExecute(this.global, target, data.options, script);
|
||||
case "WebNavigation:GetFrame":
|
||||
return ExtensionContent.handleWebNavigationGetFrame(this.global, data.options);
|
||||
case "WebNavigation:GetAllFrames":
|
||||
return ExtensionContent.handleWebNavigationGetAllFrames(this.global);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Responsible for creating ExtensionContexts and injecting content
|
||||
// scripts into them when new documents are created.
|
||||
DocumentManager = {
|
||||
globals: new Map(),
|
||||
|
||||
// Initialize listeners that we need regardless of whether extensions are
|
||||
// enabled.
|
||||
earlyInit() {
|
||||
Services.obs.addObserver(this, "tab-content-frameloader-created"); // eslint-disable-line mozilla/balanced-listeners
|
||||
},
|
||||
|
||||
// Initialize listeners that we need when any extension is enabled.
|
||||
init() {
|
||||
Services.obs.addObserver(this, "document-element-inserted");
|
||||
},
|
||||
uninit() {
|
||||
Services.obs.removeObserver(this, "document-element-inserted");
|
||||
},
|
||||
|
||||
// Initialize listeners that we need when any extension content script is
|
||||
// enabled.
|
||||
initMatchers() {
|
||||
if (isContentProcess) {
|
||||
Services.obs.addObserver(this, "http-on-opening-request");
|
||||
}
|
||||
},
|
||||
uninitMatchers() {
|
||||
if (isContentProcess) {
|
||||
Services.obs.removeObserver(this, "http-on-opening-request");
|
||||
}
|
||||
},
|
||||
|
||||
// Initialize listeners that we need when any about:blank content script is
|
||||
// enabled.
|
||||
//
|
||||
// Loads of about:blank are special, and do not trigger "document-element-inserted"
|
||||
// observers. So if we have any scripts that match about:blank, we also need
|
||||
// to observe "content-document-global-created".
|
||||
initAboutBlankMatchers() {
|
||||
Services.obs.addObserver(this, "content-document-global-created");
|
||||
},
|
||||
uninitAboutBlankMatchers() {
|
||||
Services.obs.removeObserver(this, "content-document-global-created");
|
||||
},
|
||||
|
||||
// Initialize a frame script global which extension contexts may be loaded
|
||||
// into.
|
||||
initGlobal(global) {
|
||||
// Note: {once: true} does not work as expected here.
|
||||
global.addEventListener("unload", event => { // eslint-disable-line mozilla/balanced-listeners
|
||||
this.uninitGlobal(global);
|
||||
});
|
||||
|
||||
this.globals.set(global, new ExtensionGlobal(global));
|
||||
if (ExtensionManagement.isExtensionProcess) {
|
||||
ExtensionChild.init(global);
|
||||
}
|
||||
},
|
||||
uninitGlobal(global) {
|
||||
if (ExtensionManagement.isExtensionProcess) {
|
||||
ExtensionChild.uninit(global);
|
||||
}
|
||||
this.globals.get(global).uninit();
|
||||
this.globals.delete(global);
|
||||
},
|
||||
|
||||
initExtension(extension) {
|
||||
if (this.extensionCount === 0) {
|
||||
this.init();
|
||||
}
|
||||
this.extensionCount++;
|
||||
|
||||
for (let script of extension.scripts) {
|
||||
this.addContentScript(script);
|
||||
}
|
||||
|
||||
this.injectExtensionScripts(extension);
|
||||
},
|
||||
uninitExtension(extension) {
|
||||
for (let script of extension.scripts) {
|
||||
this.removeContentScript(script);
|
||||
}
|
||||
|
||||
this.extensionCount--;
|
||||
if (this.extensionCount === 0) {
|
||||
this.uninit();
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
extensionCount: 0,
|
||||
matchAboutBlankCount: 0,
|
||||
|
||||
contentScripts: new Set(),
|
||||
|
||||
addContentScript(script) {
|
||||
if (this.contentScripts.size == 0) {
|
||||
this.initMatchers();
|
||||
}
|
||||
|
||||
if (script.matchAboutBlank) {
|
||||
if (this.matchAboutBlankCount == 0) {
|
||||
this.initAboutBlankMatchers();
|
||||
}
|
||||
this.matchAboutBlankCount++;
|
||||
}
|
||||
|
||||
this.contentScripts.add(script);
|
||||
},
|
||||
removeContentScript(script) {
|
||||
this.contentScripts.delete(script);
|
||||
|
||||
if (this.contentScripts.size == 0) {
|
||||
this.uninitMatchers();
|
||||
}
|
||||
|
||||
if (script.matchAboutBlank) {
|
||||
this.matchAboutBlankCount--;
|
||||
if (this.matchAboutBlankCount == 0) {
|
||||
this.uninitAboutBlankMatchers();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Listeners
|
||||
|
||||
observers: {
|
||||
async "content-document-global-created"(window) {
|
||||
// We only care about about:blank here, since it doesn't trigger
|
||||
// "document-element-inserted".
|
||||
if ((window.location && window.location.href !== "about:blank") ||
|
||||
// Make sure we only load into frames that belong to tabs, or other
|
||||
// special areas that we want to load content scripts into.
|
||||
!this.globals.has(getMessageManager(window))) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We can't tell for certain whether the final document will actually be
|
||||
// about:blank at this point, though, so wait for the DOM to finish
|
||||
// loading and check again before injecting scripts.
|
||||
await new Promise(resolve => window.addEventListener(
|
||||
"DOMContentLoaded", resolve, {once: true, capture: true}));
|
||||
|
||||
if (window.location.href === "about:blank") {
|
||||
this.injectWindowScripts(window);
|
||||
}
|
||||
},
|
||||
|
||||
"document-element-inserted"(document) {
|
||||
let window = document.defaultView;
|
||||
if (!document.location || !window ||
|
||||
// Make sure we only load into frames that belong to tabs, or other
|
||||
// special areas that we want to load content scripts into.
|
||||
!this.globals.has(getMessageManager(window))) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.injectWindowScripts(window);
|
||||
this.loadInto(window);
|
||||
},
|
||||
|
||||
"http-on-opening-request"(subject, topic, data) {
|
||||
// If this request is a docshell load, check whether any of our scripts
|
||||
// are likely to be loaded into it, and begin preloading the ones that
|
||||
// are.
|
||||
let {loadInfo} = subject.QueryInterface(Ci.nsIChannel);
|
||||
if (loadInfo) {
|
||||
let {externalContentPolicyType: type} = loadInfo;
|
||||
if (type === Ci.nsIContentPolicy.TYPE_DOCUMENT ||
|
||||
type === Ci.nsIContentPolicy.TYPE_SUBDOCUMENT) {
|
||||
this.preloadScripts(subject.URI, loadInfo);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"tab-content-frameloader-created"(global) {
|
||||
this.initGlobal(global);
|
||||
},
|
||||
},
|
||||
|
||||
observe(subject, topic, data) {
|
||||
this.observers[topic].call(this, subject, topic, data);
|
||||
},
|
||||
|
||||
// Script loading
|
||||
|
||||
injectExtensionScripts(extension) {
|
||||
for (let window of this.enumerateWindows()) {
|
||||
for (let script of extension.scripts) {
|
||||
if (script.matchesWindow(window)) {
|
||||
script.injectInto(window);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
injectWindowScripts(window) {
|
||||
for (let script of this.contentScripts) {
|
||||
if (script.matchesWindow(window)) {
|
||||
script.injectInto(window);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
preloadScripts(uri, loadInfo) {
|
||||
for (let script of this.contentScripts) {
|
||||
if (script.matchesLoadInfo(uri, loadInfo)) {
|
||||
script.preload();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
loadInto(window) {
|
||||
let extensionId = ExtensionManagement.getAddonIdForWindow(window);
|
||||
if (!extensionId) {
|
||||
return;
|
||||
}
|
||||
|
||||
let extension = ExtensionManager.get(extensionId);
|
||||
if (!extension) {
|
||||
throw new Error(`No registered extension for ID ${extensionId}`);
|
||||
}
|
||||
|
||||
let apiLevel = ExtensionManagement.getAPILevelForWindow(window, extensionId);
|
||||
const levels = ExtensionManagement.API_LEVELS;
|
||||
|
||||
if (apiLevel === levels.CONTENTSCRIPT_PRIVILEGES) {
|
||||
ExtensionContent.initExtensionContext(extension.realExtension, window);
|
||||
} else if (apiLevel === levels.FULL_PRIVILEGES) {
|
||||
ExtensionChild.initExtensionContext(extension.realExtension, window);
|
||||
} else {
|
||||
throw new Error(`Unexpected window with extension ID ${extensionId}`);
|
||||
}
|
||||
},
|
||||
|
||||
// Helpers
|
||||
|
||||
* enumerateWindows(docShell) {
|
||||
if (docShell) {
|
||||
let enum_ = docShell.getDocShellEnumerator(docShell.typeContent,
|
||||
docShell.ENUMERATE_FORWARDS);
|
||||
|
||||
for (let docShell of XPCOMUtils.IterSimpleEnumerator(enum_, Ci.nsIInterfaceRequestor)) {
|
||||
yield docShell.getInterface(Ci.nsIDOMWindow);
|
||||
}
|
||||
} else {
|
||||
for (let global of this.globals.keys()) {
|
||||
yield* this.enumerateWindows(global.docShell);
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* This class is a minimal stub extension object which loads and instantiates a
|
||||
* real extension object when non-basic functionality is needed.
|
||||
*/
|
||||
class StubExtension {
|
||||
constructor(data) {
|
||||
this.data = data;
|
||||
this.id = data.id;
|
||||
this.uuid = data.uuid;
|
||||
this.instanceId = data.instanceId;
|
||||
this.manifest = data.manifest;
|
||||
|
||||
this.scripts = data.content_scripts.map(scriptData => new ScriptMatcher(this, scriptData));
|
||||
|
||||
this._realExtension = null;
|
||||
|
||||
this.startup();
|
||||
}
|
||||
|
||||
startup() {
|
||||
// Extension.jsm takes care of this in the parent.
|
||||
if (isContentProcess) {
|
||||
let uri = Services.io.newURI(this.data.resourceURL);
|
||||
ExtensionManagement.startupExtension(this.uuid, uri, this);
|
||||
}
|
||||
}
|
||||
|
||||
shutdown() {
|
||||
if (isContentProcess) {
|
||||
ExtensionManagement.shutdownExtension(this.uuid);
|
||||
}
|
||||
if (this._realExtension) {
|
||||
this._realExtension.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
// Lazily create the real extension object when needed.
|
||||
get realExtension() {
|
||||
if (!this._realExtension) {
|
||||
this._realExtension = new ExtensionContent.BrowserExtensionContent(this.data);
|
||||
}
|
||||
return this._realExtension;
|
||||
}
|
||||
|
||||
// Forward functions needed by ExtensionManagement.
|
||||
hasPermission(...args) {
|
||||
return this.realExtension.hasPermission(...args);
|
||||
}
|
||||
localize(...args) {
|
||||
return this.realExtension.localize(...args);
|
||||
}
|
||||
get whiteListedHosts() {
|
||||
return this.realExtension.whiteListedHosts;
|
||||
}
|
||||
get webAccessibleResources() {
|
||||
return this.realExtension.webAccessibleResources;
|
||||
}
|
||||
}
|
||||
|
||||
ExtensionManager = {
|
||||
// Map[extensionId -> StubExtension]
|
||||
extensions: new Map(),
|
||||
|
||||
init() {
|
||||
MessageChannel.setupMessageManagers([Services.cpmm]);
|
||||
|
||||
Services.cpmm.addMessageListener("Extension:Startup", this);
|
||||
Services.cpmm.addMessageListener("Extension:Shutdown", this);
|
||||
Services.cpmm.addMessageListener("Extension:FlushJarCache", this);
|
||||
|
||||
let procData = Services.cpmm.initialProcessData || {};
|
||||
|
||||
for (let data of procData["Extension:Extensions"] || []) {
|
||||
let extension = new StubExtension(data);
|
||||
this.extensions.set(data.id, extension);
|
||||
DocumentManager.initExtension(extension);
|
||||
}
|
||||
|
||||
if (isContentProcess) {
|
||||
// Make sure we handle new schema data until Schemas.jsm is loaded.
|
||||
if (!procData["Extension:Schemas"]) {
|
||||
procData["Extension:Schemas"] = new Map();
|
||||
}
|
||||
this.schemaJSON = procData["Extension:Schemas"];
|
||||
|
||||
Services.cpmm.addMessageListener("Schema:Add", this);
|
||||
}
|
||||
},
|
||||
|
||||
get(extensionId) {
|
||||
return this.extensions.get(extensionId);
|
||||
},
|
||||
|
||||
receiveMessage({name, data}) {
|
||||
switch (name) {
|
||||
case "Extension:Startup": {
|
||||
let extension = new StubExtension(data);
|
||||
|
||||
this.extensions.set(data.id, extension);
|
||||
|
||||
DocumentManager.initExtension(extension);
|
||||
|
||||
Services.cpmm.sendAsyncMessage("Extension:StartupComplete");
|
||||
break;
|
||||
}
|
||||
|
||||
case "Extension:Shutdown": {
|
||||
let extension = this.extensions.get(data.id);
|
||||
this.extensions.delete(data.id);
|
||||
|
||||
extension.shutdown();
|
||||
|
||||
DocumentManager.uninitExtension(extension);
|
||||
break;
|
||||
}
|
||||
|
||||
case "Extension:FlushJarCache": {
|
||||
ExtensionUtils.flushJarCache(data.path);
|
||||
Services.cpmm.sendAsyncMessage("Extension:FlushJarCacheComplete");
|
||||
break;
|
||||
}
|
||||
|
||||
case "Schema:Add": {
|
||||
this.schemaJSON.set(data.url, data.schema);
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
DocumentManager.earlyInit();
|
||||
ExtensionManager.init();
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
toolkit.jar:
|
||||
% content extensions %content/extensions/
|
||||
content/extensions/extension-process-script.js
|
||||
content/extensions/ext-alarms.js
|
||||
content/extensions/ext-backgroundPage.js
|
||||
content/extensions/ext-browser-content.js
|
||||
|
|
|
@ -21,9 +21,14 @@ add_task(function* test_contentscript_cache() {
|
|||
"js": ["content_script.js"],
|
||||
"run_at": "document_start",
|
||||
}],
|
||||
|
||||
permissions: ["<all_urls>", "tabs"],
|
||||
},
|
||||
|
||||
background() {
|
||||
async background() {
|
||||
// Force our extension instance to be initialized for the current content process.
|
||||
await browser.tabs.insertCSS({code: ""});
|
||||
|
||||
browser.test.sendMessage("origin", location.origin);
|
||||
},
|
||||
|
||||
|
@ -62,7 +67,9 @@ add_task(function* test_contentscript_cache() {
|
|||
let {ExtensionManager} = Components.utils.import("resource://gre/modules/ExtensionContent.jsm", {});
|
||||
let ext = ExtensionManager.extensions.get(extensionId);
|
||||
|
||||
assert.equal(ext.staticScripts.size, 0, "Should have no cached scripts in the parent process");
|
||||
if (ext) {
|
||||
assert.equal(ext.staticScripts.size, 0, "Should have no cached scripts in the parent process");
|
||||
}
|
||||
|
||||
sendAsyncMessage("done");
|
||||
});
|
||||
|
|
|
@ -50,7 +50,7 @@ add_task(function* test_contentscript_context() {
|
|||
// Get the content script context and check that it points to the correct window.
|
||||
|
||||
let {DocumentManager} = SpecialPowers.Cu.import("resource://gre/modules/ExtensionContent.jsm", {});
|
||||
let context = DocumentManager.getContentScriptContext(extension, win);
|
||||
let context = DocumentManager.getContext(extension.id, win);
|
||||
ok(context != null, "Got content script context");
|
||||
|
||||
is(SpecialPowers.unwrap(context.contentWindow), win, "Context's contentWindow property is correct");
|
||||
|
|
|
@ -60,7 +60,7 @@ add_task(function* test_contentscript_devtools_sandbox_metadata() {
|
|||
"resource://gre/modules/ExtensionContent.jsm", {}
|
||||
);
|
||||
|
||||
let res = ExtensionContent.getContentScriptGlobalsForWindow(win);
|
||||
let res = ExtensionContent.getContentScriptGlobals(win);
|
||||
is(res.length, 1, "Got the expected array of globals");
|
||||
let metadata = SpecialPowers.Cu.getSandboxMetadata(res[0]) || {};
|
||||
|
||||
|
@ -70,7 +70,7 @@ add_task(function* test_contentscript_devtools_sandbox_metadata() {
|
|||
yield extension.unload();
|
||||
info("extension unloaded");
|
||||
|
||||
res = ExtensionContent.getContentScriptGlobalsForWindow(win);
|
||||
res = ExtensionContent.getContentScriptGlobals(win);
|
||||
is(res.length, 0, "No content scripts globals found once the extension is unloaded");
|
||||
|
||||
win.close();
|
||||
|
|
|
@ -37,19 +37,14 @@ function docShellToWindow(docShell) {
|
|||
* A generator function which iterates over a docShell tree, given a root docShell.
|
||||
*
|
||||
* @param {nsIDocShell} docShell - the root docShell object
|
||||
* @returns {Iterator<DocShell>} the FrameDetail JSON object which represents the docShell.
|
||||
*/
|
||||
function* iterateDocShellTree(docShell) {
|
||||
let docShellsEnum = docShell.getDocShellEnumerator(
|
||||
Ci.nsIDocShellTreeItem.typeContent,
|
||||
Ci.nsIDocShell.ENUMERATE_FORWARDS
|
||||
);
|
||||
docShell.typeContent, docShell.ENUMERATE_FORWARDS);
|
||||
|
||||
while (docShellsEnum.hasMoreElements()) {
|
||||
yield docShellsEnum.getNext();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Загрузка…
Ссылка в новой задаче