[Pkg-mozext-commits] [requestpolicy] 200/280: guarantee successful Framescript -> Overlay comm.
David Prévot
taffit at moszumanska.debian.org
Sat May 2 20:30:26 UTC 2015
This is an automated email from the git hooks/post-receive script.
taffit pushed a commit to branch master
in repository requestpolicy.
commit 7486a573d7a6fca1c41a2a37d60d1d06872d48e6
Author: Martin Kimmerle <dev at 256k.de>
Date: Mon Feb 2 05:34:25 2015 +0100
guarantee successful Framescript -> Overlay comm.
This commit ensures that Framescript to Overlay communication
is always successful, even if the Framescript starts up faster
than the Overlay. Fixes #594.
---
src/content/lib/environment.jsm | 6 +-
.../lib/framescript-to-overlay-communication.jsm | 165 +++++++++++++++++++++
src/content/lib/manager-for-message-listeners.jsm | 15 +-
src/content/ui/frame.dom-content-loaded.js | 85 ++++++-----
src/content/ui/frame.js | 26 ++--
src/content/ui/overlay.js | 8 +
6 files changed, 255 insertions(+), 50 deletions(-)
diff --git a/src/content/lib/environment.jsm b/src/content/lib/environment.jsm
index 200e3b7..cbe6396 100644
--- a/src/content/lib/environment.jsm
+++ b/src/content/lib/environment.jsm
@@ -513,16 +513,18 @@ function FrameScriptEnvironment(aMM, aName="frame script environment") {
Environment.call(self, _outerEnv, aName);
+ self.mm = aMM;
+
self.addStartupFunction(LEVELS.INTERFACE, function() {
// shut down the framescript on the message manager's
// `unload`. That event will occur when the browsing context
// (e.g. the tab) has been closed.
- self.shutdownOnUnload(aMM);
+ self.shutdownOnUnload(self.mm);
});
// a "MessageListener"-Manager for this environment
XPCOMUtils.defineLazyGetter(self, "mlManager", function() {
- return new ManagerForMessageListeners(self, aMM);
+ return new ManagerForMessageListeners(self, self.mm);
});
}
FrameScriptEnvironment.prototype = Object.create(Environment.prototype);
diff --git a/src/content/lib/framescript-to-overlay-communication.jsm b/src/content/lib/framescript-to-overlay-communication.jsm
new file mode 100644
index 0000000..f1ba449
--- /dev/null
+++ b/src/content/lib/framescript-to-overlay-communication.jsm
@@ -0,0 +1,165 @@
+/*
+ * ***** BEGIN LICENSE BLOCK *****
+ *
+ * RequestPolicy - A Firefox extension for control over cross-site requests.
+ * Copyright (c) 2008-2012 Justin Samuel
+ * Copyright (c) 2014-2015 Martin Kimmerle
+ *
+ * This program is free software: you can redistribute it and/or modify it under
+ * the terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * ***** END LICENSE BLOCK *****
+ */
+
+const Ci = Components.interfaces;
+const Cc = Components.classes;
+const Cu = Components.utils;
+
+let EXPORTED_SYMBOLS = ["FramescriptToOverlayCommunication"];
+
+let globalScope = this;
+
+Cu.import("chrome://requestpolicy/content/lib/script-loader.jsm");
+ScriptLoader.importModules([
+ "lib/environment",
+ "lib/logger",
+ "lib/utils/constants"
+], globalScope);
+
+
+/**
+ * The states of the communication channel to with the overlay.
+ * @enum {number}
+ */
+let States = Object.freeze({
+ "WAITING": 0,
+ "RUNNING": 1,
+ "STOPPED": 2
+});
+
+
+/**
+ * Sometimes the framescript loads and starts up faster than the
+ * Overlay of the corresponding window. This is due to the async
+ * nature of framescripts. As a result, the overlay does not
+ * receive those early messages from the framescript.
+ *
+ * This class helps to ensure that any message to the overlay is
+ * actually being received. Instances take functions ("runnables")
+ * that will be called as soon as the overlay is ready. If the
+ * overlay is already in the "RUNNING" state, the function will be
+ * called immediately.
+ *
+ * @param {Environment} aEnv - The environment to which the
+ * communication channel's lifetime will be bound to.
+ * @constructor
+ */
+function FramescriptToOverlayCommunication(aEnv) {
+ let self = this;
+
+ /**
+ * A queue of runnables that wait for the overlay to be ready.
+ * As it's a queue, the functions `pop` and `shift` have to be
+ * used.
+ * @type {Array.<function>}
+ */
+ self.waitingRunnables = [];
+
+ /**
+ * The state of the communication
+ * @type {States}
+ */
+ self.state = States.WAITING;
+
+ /**
+ * @type {Environment}
+ */
+ self.env = aEnv;
+
+ self.env.addStartupFunction(Environment.LEVELS.INTERFACE,
+ startCommNowOrLater.bind(null, self));
+ self.env.addShutdownFunction(Environment.LEVELS.INTERFACE,
+ stopCommunication.bind(null, self));
+}
+
+function dump(self, msg) {
+ Logger.dump(self.env.uid + ": " + msg);
+}
+
+/**
+ * Check whether the Overlay is ready. If it is, start the
+ * communication. If not, wait for the overlay to be ready.
+ *
+ * @param {FramescriptToOverlayCommunication} self
+ */
+function startCommNowOrLater(self) {
+ let answers = self.env.mm.sendSyncMessage(C.MM_PREFIX + "isOverlayReady");
+ if (answers.length > 0 && answers[0] === true) {
+ startCommunication(self);
+ } else {
+ // The Overlay is not ready yet, so listen for the message.
+ // Add the listener immediately.
+ self.env.mlManager.addListener("overlayIsReady",
+ startCommunication.bind(null, self),
+ true);
+ }
+}
+
+/**
+ * The overlay is ready.
+ *
+ * @param {FramescriptToOverlayCommunication} self
+ */
+function startCommunication(self) {
+ if (self.state === States.WAITING) {
+ //dump(self, "The Overlay is ready!");
+ self.state = States.RUNNING;
+
+ while (self.waitingRunnables.length !== 0) {
+ let runnable = self.waitingRunnables.shift();
+ //dump(self, "Lazily running function.");
+ runnable.call(null);
+ }
+ }
+}
+
+/**
+ * @param {FramescriptToOverlayCommunication} self
+ */
+function stopCommunication(self) {
+ self.state = States.STOPPED;
+}
+
+/**
+ * Add a function that will be called now or later.
+ *
+ * @param {function} aRunnable
+ */
+FramescriptToOverlayCommunication.prototype.run = function(aRunnable) {
+ let self = this;
+ switch (self.state) {
+ case States.RUNNING:
+ //dump(self, "Immediately running function.");
+ aRunnable.call(null);
+ break;
+
+ case States.WAITING:
+ //dump(self, "Remembering runnable.");
+ self.waitingRunnables.push(aRunnable);
+ break;
+
+ default:
+ //dump(self, "Ignoring runnable.");
+ break;
+ }
+};
diff --git a/src/content/lib/manager-for-message-listeners.jsm b/src/content/lib/manager-for-message-listeners.jsm
index 61a16cd..35a2830 100644
--- a/src/content/lib/manager-for-message-listeners.jsm
+++ b/src/content/lib/manager-for-message-listeners.jsm
@@ -97,8 +97,19 @@ function ManagerForMessageListeners(aEnv, aMM) {
}
+/**
+ * Add a listener. The class will then take care about adding
+ * and removing that message listener.
+ *
+ * @param {string} aMessageName
+ * @param {function} aCallback
+ * @param {boolean} aAddImmediately - Whether the listener should be
+ * added immediately, i.e. without waiting for the environment
+ * to start up.
+ */
ManagerForMessageListeners.prototype.addListener = function(aMessageName,
- aCallback) {
+ aCallback,
+ aAddImmediately) {
let self = this;
if (typeof aCallback !== 'function') {
Logger.warning(Logger.TYPE_ERROR, "The callback for a message listener" +
@@ -120,7 +131,7 @@ ManagerForMessageListeners.prototype.addListener = function(aMessageName,
callback: aCallback,
listening: false
};
- if (self.addNewListenersImmediately) {
+ if (aAddImmediately === true || self.addNewListenersImmediately) {
Logger.dump('Immediately adding message listener for "' +
listener.messageName + '". Environment: "' +
self.environment.name + '"');
diff --git a/src/content/ui/frame.dom-content-loaded.js b/src/content/ui/frame.dom-content-loaded.js
index f829793..9809156 100644
--- a/src/content/ui/frame.dom-content-loaded.js
+++ b/src/content/ui/frame.dom-content-loaded.js
@@ -32,9 +32,11 @@ let ManagerForDOMContentLoaded = (function() {
// Notify the main thread that a link has been clicked.
// Note: The <a> element is `currentTarget`! See:
// https://developer.mozilla.org/en-US/docs/Web/API/Event.currentTarget
- mm.sendSyncMessage(C.MM_PREFIX + "notifyLinkClicked",
- {origin: event.currentTarget.ownerDocument.URL,
- dest: event.currentTarget.href});
+ overlayComm.run(function() {
+ mm.sendSyncMessage(C.MM_PREFIX + "notifyLinkClicked",
+ {origin: event.currentTarget.ownerDocument.URL,
+ dest: event.currentTarget.href});
+ });
}
@@ -67,32 +69,33 @@ let ManagerForDOMContentLoaded = (function() {
onDocumentLoaded(doc);
- let answers = mm.sendSyncMessage(C.MM_PREFIX + "notifyDocumentLoaded",
- {documentURI: doc.documentURI});
- if (answers.length === 0) {
- Logger.warning(Logger.TYPE_ERROR, 'There seems to be no message ' +
- 'listener for "notifyDocumentLoaded".');
- } else {
- // take only one answer. If there are more answers, they are ignored
- // ==> there must be only one listener for 'notifyDocumentLoaded'
- let answer = answers[0];
-
- var blockedURIs = answer.blockedURIs;
- //console.debug("Received " +
- // Object.getOwnPropertyNames(blockedURIs).length +
- // " blocked URIs.");
-
- // Indicating blocked visible objects isn't an urgent task, so this should
- // be done async.
- Utils.runAsync(function() {
- ManagerForBlockedContent.indicateBlockedVisibleObjects(doc, blockedURIs);
- });
- }
-
+ overlayComm.run(function() {
+ let answers = mm.sendSyncMessage(C.MM_PREFIX + "notifyDocumentLoaded",
+ {documentURI: doc.documentURI});
+ if (answers.length === 0) {
+ Logger.warning(Logger.TYPE_ERROR, 'There seems to be no message ' +
+ 'listener for "notifyDocumentLoaded".');
+ } else {
+ // take only one answer. If there are more answers, they are ignored
+ // ==> there must be only one listener for 'notifyDocumentLoaded'
+ let answer = answers[0];
+
+ var blockedURIs = answer.blockedURIs;
+ //console.debug("Received " +
+ // Object.getOwnPropertyNames(blockedURIs).length +
+ // " blocked URIs.");
+
+ // Indicating blocked visible objects isn't an urgent task, so this should
+ // be done async.
+ Utils.runAsync(function() {
+ ManagerForBlockedContent.indicateBlockedVisibleObjects(doc, blockedURIs);
+ });
+ }
- if (isActiveTopLevelDocument(doc)) {
- mm.sendAsyncMessage(C.MM_PREFIX + "notifyTopLevelDocumentLoaded");
- }
+ if (isActiveTopLevelDocument(doc)) {
+ mm.sendAsyncMessage(C.MM_PREFIX + "notifyTopLevelDocumentLoaded");
+ }
+ });
}
/**
@@ -117,7 +120,9 @@ let ManagerForDOMContentLoaded = (function() {
// other origins every time an iframe is loaded. Maybe, then, this should
// use a timeout like observerBlockedRequests does.
if (isActiveTopLevelDocument(iframe.ownerDocument)) {
- mm.sendAsyncMessage(C.MM_PREFIX + "notifyDOMFrameContentLoaded");
+ overlayComm.run(function() {
+ mm.sendAsyncMessage(C.MM_PREFIX + "notifyDOMFrameContentLoaded");
+ });
}
}
@@ -188,8 +193,10 @@ let ManagerForDOMContentLoaded = (function() {
"Another extension disabled docShell.allowMetaRedirects.");
}
- mm.sendAsyncMessage(C.MM_PREFIX + "handleMetaRefreshes",
- {documentURI: documentURI, metaRefreshes: metaRefreshes});
+ overlayComm.run(function() {
+ mm.sendAsyncMessage(C.MM_PREFIX + "handleMetaRefreshes",
+ {documentURI: documentURI, metaRefreshes: metaRefreshes});
+ });
}
// Find all anchor tags and add click events (which also fire when enter
@@ -272,17 +279,21 @@ let ManagerForDOMContentLoaded = (function() {
function wrapWindowFunctions(aWindow) {
wrapWindowFunction(aWindow, "open",
function(url, windowName, windowFeatures) {
- mm.sendSyncMessage(C.MM_PREFIX + "notifyLinkClicked",
- {origin: aWindow.document.documentURI,
- dest: url});
+ overlayComm.run(function() {
+ mm.sendSyncMessage(C.MM_PREFIX + "notifyLinkClicked",
+ {origin: aWindow.document.documentURI,
+ dest: url});
+ });
});
wrapWindowFunction(aWindow, "openDialog",
function() {
// openDialog(url, name, features, arg1, arg2, ...)
- mm.sendSyncMessage(C.MM_PREFIX + "notifyLinkClicked",
- {origin: aWindow.document.documentURI,
- dest: arguments[0]});
+ overlayComm.run(function() {
+ mm.sendSyncMessage(C.MM_PREFIX + "notifyLinkClicked",
+ {origin: aWindow.document.documentURI,
+ dest: arguments[0]});
+ });
});
}
function unwrapWindowFunctions(aWindow) {
diff --git a/src/content/ui/frame.js b/src/content/ui/frame.js
index 4ddc90e..b661ec3 100644
--- a/src/content/ui/frame.js
+++ b/src/content/ui/frame.js
@@ -45,13 +45,16 @@ Components.utils.import("resource://gre/modules/devtools/Console.jsm");
ScriptLoader.importModules([
"lib/utils/constants",
"lib/logger",
- "lib/environment"
+ "lib/environment",
+ "lib/framescript-to-overlay-communication"
], mod);
- let {C, Logger, Environment, FrameScriptEnvironment} = mod;
+ let {C, Logger, Environment, FrameScriptEnvironment,
+ FramescriptToOverlayCommunication} = mod;
let framescriptEnv = new FrameScriptEnvironment(mm);
let mlManager = framescriptEnv.mlManager;
+ let overlayComm = new FramescriptToOverlayCommunication(framescriptEnv);
// Create a scope for the sub-scripts, which also can
@@ -72,7 +75,8 @@ Components.utils.import("resource://gre/modules/devtools/Console.jsm");
"Environment": Environment,
"framescriptEnv": framescriptEnv,
- "mlManager": mlManager
+ "mlManager": mlManager,
+ "overlayComm": overlayComm
};
function loadSubScripts() {
@@ -128,9 +132,11 @@ Components.utils.import("resource://gre/modules/devtools/Console.jsm");
// I believe an empty href always gets filled in with the current URL so
// it will never actually be empty. However, I don't know this for certain.
if (event.target.nodeName.toLowerCase() == "a" && event.target.href) {
- sendSyncMessage(C.MM_PREFIX + "notifyLinkClicked",
- {origin: event.target.ownerDocument.URL,
- dest: event.target.href});
+ overlayComm.run(function() {
+ sendSyncMessage(C.MM_PREFIX + "notifyLinkClicked",
+ {origin: event.target.ownerDocument.URL,
+ dest: event.target.href});
+ });
return;
}
// Form submit button clicked. This can either be directly (e.g. mouseclick,
@@ -139,9 +145,11 @@ Components.utils.import("resource://gre/modules/devtools/Console.jsm");
if (event.target.nodeName.toLowerCase() == "input" &&
event.target.type.toLowerCase() == "submit" &&
event.target.form && event.target.form.action) {
- sendSyncMessage(C.MM_PREFIX + "registerFormSubmitted",
- {origin: event.target.ownerDocument.URL,
- dest: event.target.form.action});
+ overlayComm.run(function() {
+ sendSyncMessage(C.MM_PREFIX + "registerFormSubmitted",
+ {origin: event.target.ownerDocument.URL,
+ dest: event.target.form.action});
+ });
return;
}
};
diff --git a/src/content/ui/overlay.js b/src/content/ui/overlay.js
index bd6cf87..b9d6643 100644
--- a/src/content/ui/overlay.js
+++ b/src/content/ui/overlay.js
@@ -143,6 +143,14 @@ requestpolicy.overlay = (function() {
OverlayEnvironment.shutdownOnUnload(window);
OverlayEnvironment.startup();
+
+ // Tell the framescripts that the overlay is ready. The
+ // listener must be added immediately.
+ mlManager.addListener("isOverlayReady", function() {
+ return true;
+ });
+ window.messageManager.broadcastAsyncMessage(C.MM_PREFIX +
+ "overlayIsReady", true);
}
} catch (e) {
Logger.severe(Logger.TYPE_ERROR,
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-mozext/requestpolicy.git
More information about the Pkg-mozext-commits
mailing list