summaryrefslogtreecommitdiffstats
path: root/addon-sdk/source/lib/sdk/content/page-worker.js
diff options
context:
space:
mode:
Diffstat (limited to 'addon-sdk/source/lib/sdk/content/page-worker.js')
-rw-r--r--addon-sdk/source/lib/sdk/content/page-worker.js154
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();
+});