summaryrefslogtreecommitdiffstats
path: root/browser/modules/HiddenFrame.jsm
diff options
context:
space:
mode:
Diffstat (limited to 'browser/modules/HiddenFrame.jsm')
-rw-r--r--browser/modules/HiddenFrame.jsm86
1 files changed, 86 insertions, 0 deletions
diff --git a/browser/modules/HiddenFrame.jsm b/browser/modules/HiddenFrame.jsm
new file mode 100644
index 000000000..7676ae189
--- /dev/null
+++ b/browser/modules/HiddenFrame.jsm
@@ -0,0 +1,86 @@
+/* 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.EXPORTED_SYMBOLS = ["HiddenFrame"];
+
+const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
+
+Cu.import("resource://gre/modules/PromiseUtils.jsm");
+Cu.import("resource://gre/modules/Services.jsm");
+Cu.import("resource://gre/modules/Timer.jsm");
+
+const HTML_NS = "http://www.w3.org/1999/xhtml";
+const XUL_PAGE = "data:application/vnd.mozilla.xul+xml;charset=utf-8,<window%20id='win'/>";
+
+/**
+ * An hidden frame object. It takes care of creating an IFRAME and attaching it the
+ * |hiddenDOMWindow|.
+ */
+function HiddenFrame() {}
+
+HiddenFrame.prototype = {
+ _frame: null,
+ _deferred: null,
+ _retryTimerId: null,
+
+ get hiddenDOMDocument() {
+ return Services.appShell.hiddenDOMWindow.document;
+ },
+
+ get isReady() {
+ return this.hiddenDOMDocument.readyState === "complete";
+ },
+
+ /**
+ * Gets the |contentWindow| of the hidden frame. Creates the frame if needed.
+ * @returns Promise Returns a promise which is resolved when the hidden frame has finished
+ * loading.
+ */
+ get: function () {
+ if (!this._deferred) {
+ this._deferred = PromiseUtils.defer();
+ this._create();
+ }
+
+ return this._deferred.promise;
+ },
+
+ destroy: function () {
+ clearTimeout(this._retryTimerId);
+
+ if (this._frame) {
+ if (!Cu.isDeadWrapper(this._frame)) {
+ this._frame.removeEventListener("load", this, true);
+ this._frame.remove();
+ }
+
+ this._frame = null;
+ this._deferred = null;
+ }
+ },
+
+ handleEvent: function () {
+ let contentWindow = this._frame.contentWindow;
+ if (contentWindow.location.href === XUL_PAGE) {
+ this._frame.removeEventListener("load", this, true);
+ this._deferred.resolve(contentWindow);
+ } else {
+ contentWindow.location = XUL_PAGE;
+ }
+ },
+
+ _create: function () {
+ if (this.isReady) {
+ let doc = this.hiddenDOMDocument;
+ this._frame = doc.createElementNS(HTML_NS, "iframe");
+ this._frame.addEventListener("load", this, true);
+ doc.documentElement.appendChild(this._frame);
+ } else {
+ // Check again if |hiddenDOMDocument| is ready as soon as possible.
+ this._retryTimerId = setTimeout(this._create.bind(this), 0);
+ }
+ }
+};