gecko-dev/toolkit/components/extensions/ext-backgroundPage.js
Kris Maglione ccbc83560c Bug 1239822: Part 2a - [webext] Explicitly destroy windowless browsers on unload. r=billm
--HG--
extra : commitid : JXAapKEd31v
extra : rebase_source : 729fa88d1d0e8c91a47745af00fb321866eb1063
2016-01-15 13:30:15 -08:00

142 lines
4.4 KiB
JavaScript

"use strict";
var { interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/Services.jsm");
// WeakMap[Extension -> BackgroundPage]
var backgroundPagesMap = new WeakMap();
// Responsible for the background_page section of the manifest.
function BackgroundPage(options, extension) {
this.extension = extension;
this.scripts = options.scripts || [];
this.page = options.page || null;
this.contentWindow = null;
this.chromeWebNav = null;
this.webNav = null;
this.context = null;
}
BackgroundPage.prototype = {
build() {
let chromeWebNav = Services.appShell.createWindowlessBrowser(true);
this.chromeWebNav = chromeWebNav;
let url;
if (this.page) {
url = this.extension.baseURI.resolve(this.page);
} else {
// TODO: Chrome uses "_generated_background_page.html" for this.
url = this.extension.baseURI.resolve("_blank.html");
}
if (!this.extension.isExtensionURL(url)) {
this.extension.manifestError("Background page must be a file within the extension");
url = this.extension.baseURI.resolve("_blank.html");
}
let uri = Services.io.newURI(url, null, null);
let system = Services.scriptSecurityManager.getSystemPrincipal();
let interfaceRequestor = chromeWebNav.QueryInterface(Ci.nsIInterfaceRequestor);
let chromeShell = interfaceRequestor.getInterface(Ci.nsIDocShell);
chromeShell.createAboutBlankContentViewer(system);
let chromeDoc = chromeWebNav.document;
const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul";
let browser = chromeDoc.createElementNS(XUL_NS, "browser");
browser.setAttribute("type", "content");
browser.setAttribute("disableglobalhistory", "true");
chromeDoc.body.appendChild(browser);
let frameLoader = browser.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader;
let docShell = frameLoader.docShell;
this.context = new ExtensionPage(this.extension, {type: "background", docShell, uri});
GlobalManager.injectInDocShell(docShell, this.extension, this.context);
let webNav = docShell.QueryInterface(Ci.nsIWebNavigation);
this.webNav = webNav;
webNav.loadURI(url, 0, null, null, null);
let window = webNav.document.defaultView;
this.contentWindow = window;
this.context.contentWindow = window;
// TODO: Right now we run onStartup after the background page
// finishes. See if this is what Chrome does.
let loadListener = event => {
if (event.target != window.document) {
return;
}
event.currentTarget.removeEventListener("load", loadListener, true);
if (this.scripts) {
let doc = window.document;
for (let script of this.scripts) {
let url = this.extension.baseURI.resolve(script);
if (!this.extension.isExtensionURL(url)) {
this.extension.manifestError("Background scripts must be files within the extension");
continue;
}
let tag = doc.createElement("script");
tag.setAttribute("src", url);
tag.async = false;
doc.body.appendChild(tag);
}
}
if (this.extension.onStartup) {
this.extension.onStartup();
}
};
browser.addEventListener("load", loadListener, true);
},
shutdown() {
// Navigate away from the background page to invalidate any
// setTimeouts or other callbacks.
this.webNav.loadURI("about:blank", 0, null, null, null);
this.webNav = null;
this.chromeWebNav.loadURI("about:blank", 0, null, null, null);
this.chromeWebNav.close();
this.chromeWebNav = null;
},
};
/* eslint-disable mozilla/balanced-listeners */
extensions.on("manifest_background", (type, directive, extension, manifest) => {
let bgPage = new BackgroundPage(manifest.background, extension);
bgPage.build();
backgroundPagesMap.set(extension, bgPage);
});
extensions.on("shutdown", (type, extension) => {
if (backgroundPagesMap.has(extension)) {
backgroundPagesMap.get(extension).shutdown();
backgroundPagesMap.delete(extension);
}
});
/* eslint-enable mozilla/balanced-listeners */
extensions.registerSchemaAPI("extension", null, (extension, context) => {
return {
extension: {
getBackgroundPage: function() {
return backgroundPagesMap.get(extension).contentWindow;
},
},
runtime: {
getBackgroundPage: function(callback) {
runSafe(context, callback, backgroundPagesMap.get(extension).contentWindow);
},
},
};
});