148 lines
5.2 KiB
JavaScript
148 lines
5.2 KiB
JavaScript
"use strict";
|
|
|
|
var {interfaces: Ci, utils: Cu} = Components;
|
|
|
|
Cu.import("resource://gre/modules/Services.jsm");
|
|
Cu.import("resource://gre/modules/Task.jsm");
|
|
|
|
XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
|
|
"resource://gre/modules/AddonManager.jsm");
|
|
XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
|
|
"resource://gre/modules/PrivateBrowsingUtils.jsm");
|
|
|
|
Cu.import("resource://gre/modules/ExtensionUtils.jsm");
|
|
const {
|
|
promiseDocumentLoaded,
|
|
promiseObserved,
|
|
} = ExtensionUtils;
|
|
|
|
const XUL_URL = "data:application/vnd.mozilla.xul+xml;charset=utf-8," + encodeURI(
|
|
`<?xml version="1.0"?>
|
|
<window id="documentElement"/>`);
|
|
|
|
// WeakMap[Extension -> BackgroundPage]
|
|
var backgroundPagesMap = new WeakMap();
|
|
|
|
// Responsible for the background_page section of the manifest.
|
|
function BackgroundPage(options, extension) {
|
|
this.extension = extension;
|
|
this.page = options.page || null;
|
|
this.isGenerated = !!options.scripts;
|
|
this.windowlessBrowser = null;
|
|
this.webNav = null;
|
|
}
|
|
|
|
BackgroundPage.prototype = {
|
|
build: Task.async(function* () {
|
|
let windowlessBrowser = Services.appShell.createWindowlessBrowser(true);
|
|
this.windowlessBrowser = windowlessBrowser;
|
|
|
|
let url;
|
|
if (this.page) {
|
|
url = this.extension.baseURI.resolve(this.page);
|
|
} else if (this.isGenerated) {
|
|
url = this.extension.baseURI.resolve("_generated_background_page.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 system = Services.scriptSecurityManager.getSystemPrincipal();
|
|
|
|
// The windowless browser is a thin wrapper around a docShell that keeps
|
|
// its related resources alive. It implements nsIWebNavigation and
|
|
// forwards its methods to the underlying docShell, but cannot act as a
|
|
// docShell itself. Calling `getInterface(nsIDocShell)` gives us the
|
|
// underlying docShell, and `QueryInterface(nsIWebNavigation)` gives us
|
|
// access to the webNav methods that are already available on the
|
|
// windowless browser, but contrary to appearances, they are not the same
|
|
// object.
|
|
let chromeShell = windowlessBrowser.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
.getInterface(Ci.nsIDocShell)
|
|
.QueryInterface(Ci.nsIWebNavigation);
|
|
|
|
if (PrivateBrowsingUtils.permanentPrivateBrowsing) {
|
|
let attrs = chromeShell.getOriginAttributes();
|
|
attrs.privateBrowsingId = 1;
|
|
chromeShell.setOriginAttributes(attrs);
|
|
}
|
|
|
|
chromeShell.useGlobalHistory = false;
|
|
chromeShell.createAboutBlankContentViewer(system);
|
|
chromeShell.loadURI(XUL_URL, 0, null, null, null);
|
|
|
|
|
|
yield promiseObserved("chrome-document-global-created",
|
|
win => win.document == chromeShell.document);
|
|
|
|
let chromeDoc = yield promiseDocumentLoaded(chromeShell.document);
|
|
|
|
let browser = chromeDoc.createElement("browser");
|
|
browser.setAttribute("type", "content");
|
|
browser.setAttribute("disableglobalhistory", "true");
|
|
chromeDoc.documentElement.appendChild(browser);
|
|
|
|
extensions.emit("extension-browser-inserted", browser);
|
|
browser.messageManager.sendAsyncMessage("Extension:InitExtensionView", {
|
|
viewType: "background",
|
|
url,
|
|
});
|
|
|
|
yield new Promise(resolve => {
|
|
browser.messageManager.addMessageListener("Extension:ExtensionViewLoaded", function onLoad() {
|
|
browser.messageManager.removeMessageListener("Extension:ExtensionViewLoaded", onLoad);
|
|
resolve();
|
|
});
|
|
});
|
|
|
|
// TODO(robwu): This is not webext-oop compatible.
|
|
this.webNav = browser.docShell.QueryInterface(Ci.nsIWebNavigation);
|
|
let window = this.webNav.document.defaultView;
|
|
|
|
|
|
// Set the add-on's main debugger global, for use in the debugger
|
|
// console.
|
|
if (this.extension.addonData.instanceID) {
|
|
AddonManager.getAddonByInstanceID(this.extension.addonData.instanceID)
|
|
.then(addon => addon.setDebugGlobal(window));
|
|
}
|
|
|
|
this.extension.emit("startup");
|
|
}),
|
|
|
|
shutdown() {
|
|
if (this.extension.addonData.instanceID) {
|
|
AddonManager.getAddonByInstanceID(this.extension.addonData.instanceID)
|
|
.then(addon => addon.setDebugGlobal(null));
|
|
}
|
|
|
|
// Navigate away from the background page to invalidate any
|
|
// setTimeouts or other callbacks.
|
|
if (this.webNav) {
|
|
this.webNav.loadURI("about:blank", 0, null, null, null);
|
|
this.webNav = null;
|
|
}
|
|
|
|
this.windowlessBrowser.loadURI("about:blank", 0, null, null, null);
|
|
this.windowlessBrowser.close();
|
|
this.windowlessBrowser = null;
|
|
},
|
|
};
|
|
|
|
/* eslint-disable mozilla/balanced-listeners */
|
|
extensions.on("manifest_background", (type, directive, extension, manifest) => {
|
|
let bgPage = new BackgroundPage(manifest.background, extension);
|
|
backgroundPagesMap.set(extension, bgPage);
|
|
return bgPage.build();
|
|
});
|
|
|
|
extensions.on("shutdown", (type, extension) => {
|
|
if (backgroundPagesMap.has(extension)) {
|
|
backgroundPagesMap.get(extension).shutdown();
|
|
backgroundPagesMap.delete(extension);
|
|
}
|
|
});
|
|
/* eslint-enable mozilla/balanced-listeners */
|