diff options
Diffstat (limited to 'addon-sdk/source/lib/sdk/content/page-worker.js')
-rw-r--r-- | addon-sdk/source/lib/sdk/content/page-worker.js | 154 |
1 files changed, 154 insertions, 0 deletions
diff --git a/addon-sdk/source/lib/sdk/content/page-worker.js b/addon-sdk/source/lib/sdk/content/page-worker.js new file mode 100644 index 000000000..e9e741120 --- /dev/null +++ b/addon-sdk/source/lib/sdk/content/page-worker.js @@ -0,0 +1,154 @@ +/* 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"; + +const { frames } = require("../remote/child"); +const { Class } = require("../core/heritage"); +const { Disposable } = require('../core/disposable'); +const { data } = require("../self"); +const { once } = require("../dom/events"); +const { getAttachEventType } = require("./utils"); +const { Rules } = require('../util/rules'); +const { uuid } = require('../util/uuid'); +const { WorkerChild } = require("./worker-child"); +const { Cc, Ci, Cu } = require("chrome"); +const { observe } = require("../event/chrome"); +const { on } = require("../event/core"); + +const appShell = Cc["@mozilla.org/appshell/appShellService;1"].getService(Ci.nsIAppShellService); + +const { XPCOMUtils } = require("resource://gre/modules/XPCOMUtils.jsm"); + +const pages = new Map(); + +const DOC_INSERTED = "document-element-inserted"; + +function isValidURL(page, url) { + return !page.rules || page.rules.matchesAny(url); +} + +const ChildPage = Class({ + implements: [ Disposable ], + setup: function(frame, id, options) { + this.id = id; + this.frame = frame; + this.options = options; + + this.webNav = appShell.createWindowlessBrowser(false); + this.docShell.allowJavascript = this.options.allow.script; + + // Accessing the browser's window forces the initial about:blank document to + // be created before we start listening for notifications + this.contentWindow; + + this.webProgress.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_LOCATION); + + pages.set(this.id, this); + + this.contentURL = options.contentURL; + + if (options.include) { + this.rules = Rules(); + this.rules.add.apply(this.rules, [].concat(options.include)); + } + }, + + dispose: function() { + pages.delete(this.id); + this.webProgress.removeProgressListener(this); + this.webNav.close(); + this.webNav = null; + }, + + attachWorker: function() { + if (!isValidURL(this, this.contentWindow.location.href)) + return; + + this.options.id = uuid().toString(); + this.options.window = this.contentWindow; + this.frame.port.emit("sdk/frame/connect", this.id, { + id: this.options.id, + url: this.contentWindow.document.documentURIObject.spec + }); + new WorkerChild(this.options); + }, + + get docShell() { + return this.webNav.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDocShell); + }, + + get webProgress() { + return this.docShell.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebProgress); + }, + + get contentWindow() { + return this.docShell.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIDOMWindow); + }, + + get contentURL() { + return this.options.contentURL; + }, + set contentURL(url) { + this.options.contentURL = url; + + url = this.options.contentURL ? data.url(this.options.contentURL) : "about:blank"; + this.webNav.loadURI(url, Ci.nsIWebNavigation.LOAD_FLAGS_NONE, null, null, null); + }, + + onLocationChange: function(progress, request, location, flags) { + // Ignore inner-frame events + if (progress != this.webProgress) + return; + // Ignore events that don't change the document + if (flags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT) + return; + + let event = getAttachEventType(this.options); + // Attaching at the start of the load is handled by the + // document-element-inserted listener. + if (event == DOC_INSERTED) + return; + + once(this.contentWindow, event, () => { + this.attachWorker(); + }, false); + }, + + QueryInterface: XPCOMUtils.generateQI(["nsIWebProgressListener", "nsISupportsWeakReference"]) +}); + +on(observe(DOC_INSERTED), "data", ({ target }) => { + let page = Array.from(pages.values()).find(p => p.contentWindow.document === target); + if (!page) + return; + + if (getAttachEventType(page.options) == DOC_INSERTED) + page.attachWorker(); +}); + +frames.port.on("sdk/frame/create", (frame, id, options) => { + new ChildPage(frame, id, options); +}); + +frames.port.on("sdk/frame/set", (frame, id, params) => { + let page = pages.get(id); + if (!page) + return; + + if ("allowScript" in params) + page.docShell.allowJavascript = params.allowScript; + if ("contentURL" in params) + page.contentURL = params.contentURL; +}); + +frames.port.on("sdk/frame/destroy", (frame, id) => { + let page = pages.get(id); + if (!page) + return; + + page.destroy(); +}); |