aboutsummaryrefslogtreecommitdiffstats
path: root/platform
diff options
context:
space:
mode:
authorgorhill <rhill@raymondhill.net>2015-09-30 09:33:38 -0400
committergorhill <rhill@raymondhill.net>2015-09-30 09:33:38 -0400
commit8d294869fe252b54c08a14fc9b94722c2ace56ef (patch)
tree4b7e65aa6271d10c111cd138ac84cc2ef61643b9 /platform
parentcc17a77b0ac69ba73299148e2a710dcbd5069a21 (diff)
downloaduBlock-8d294869fe252b54c08a14fc9b94722c2ace56ef.zip
uBlock-8d294869fe252b54c08a14fc9b94722c2ace56ef.tar.gz
uBlock-8d294869fe252b54c08a14fc9b94722c2ace56ef.tar.bz2
this fixes #756
Diffstat (limited to 'platform')
-rw-r--r--platform/firefox/frameModule.js42
-rw-r--r--platform/firefox/frameScript.js33
-rw-r--r--platform/firefox/vapi-background.js96
-rw-r--r--platform/firefox/vapi-client.js37
4 files changed, 169 insertions, 39 deletions
diff --git a/platform/firefox/frameModule.js b/platform/firefox/frameModule.js
index a11720e..98fc107 100644
--- a/platform/firefox/frameModule.js
+++ b/platform/firefox/frameModule.js
@@ -30,6 +30,7 @@ const {Services} = Cu.import('resource://gre/modules/Services.jsm', null);
const {XPCOMUtils} = Cu.import('resource://gre/modules/XPCOMUtils.jsm', null);
const hostName = Services.io.newURI(Components.stack.filename, null, null).host;
+const rpcEmitterName = hostName + ':child-process-message';
//Cu.import('resource://gre/modules/devtools/Console.jsm');
@@ -239,9 +240,25 @@ const contentObserver = {
wantXHRConstructor: false
});
+ if ( Services.cpmm ) {
+ sandbox.rpc = function(details) {
+ var svc = Services;
+ if ( svc === undefined ) { return; }
+ var cpmm = svc.cpmm;
+ if ( !cpmm ) { return; }
+ var r = cpmm.sendSyncMessage(rpcEmitterName, details);
+ if ( Array.isArray(r) ) {
+ return r[0];
+ }
+ };
+ } else {
+ sandbox.rpc = function() {};
+ }
+
sandbox.injectScript = function(script) {
- if ( Services !== undefined ) {
- Services.scriptloader.loadSubScript(script, sandbox);
+ var svc = Services;
+ if ( svc !== undefined ) {
+ svc.scriptloader.loadSubScript(script, sandbox);
} else {
// Sandbox appears void.
// I've seen this happens, need to investigate why.
@@ -258,9 +275,10 @@ const contentObserver = {
sandbox.removeMessageListener();
sandbox.addMessageListener =
sandbox.injectScript =
+ sandbox.outerShutdown =
sandbox.removeMessageListener =
- sandbox.sendAsyncMessage =
- sandbox.outerShutdown = function(){};
+ sandbox.rpc =
+ sandbox.sendAsyncMessage = function(){};
sandbox.vAPI = {};
messager = null;
};
@@ -412,13 +430,19 @@ const LocationChangeListener = function(docShell) {
var requestor = docShell.QueryInterface(Ci.nsIInterfaceRequestor);
var ds = requestor.getInterface(Ci.nsIWebProgress);
+ if ( !ds ) {
+ return;
+ }
var mm = requestor.getInterface(Ci.nsIContentFrameMessageManager);
-
- if ( ds && mm && typeof mm.sendAsyncMessage === 'function' ) {
- this.docShell = ds;
- this.messageManager = mm;
- ds.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_LOCATION);
+ if ( !mm ) {
+ return;
+ }
+ if ( typeof mm.sendAsyncMessage !== 'function' ) {
+ return;
}
+ this.docShell = ds;
+ this.messageManager = mm;
+ ds.addProgressListener(this, Ci.nsIWebProgress.NOTIFY_LOCATION);
};
LocationChangeListener.prototype.QueryInterface = XPCOMUtils.generateQI([
diff --git a/platform/firefox/frameScript.js b/platform/firefox/frameScript.js
index fdee50c..17cd84f 100644
--- a/platform/firefox/frameScript.js
+++ b/platform/firefox/frameScript.js
@@ -19,13 +19,11 @@
Home: https://github.com/gorhill/uBlock
*/
-/* global addMessageListener, removeMessageListener, docShell */
-
/******************************************************************************/
-var locationChangeListener; // Keep alive while frameScript is alive
+// https://developer.mozilla.org/en-US/Firefox/Multiprocess_Firefox/Frame_script_environment
-(function() {
+(function(context) {
'use strict';
@@ -52,25 +50,36 @@ let injectContentScripts = function(win) {
};
let onLoadCompleted = function() {
- removeMessageListener('ublock0-load-completed', onLoadCompleted);
- injectContentScripts(content);
+ context.removeMessageListener('ublock0-load-completed', onLoadCompleted);
+ injectContentScripts(context.content);
};
+context.addMessageListener('ublock0-load-completed', onLoadCompleted);
-addMessageListener('ublock0-load-completed', onLoadCompleted);
+let shutdown = function(ev) {
+ if ( ev.target !== context ) {
+ return;
+ }
+ context.removeMessageListener('ublock0-load-completed', onLoadCompleted);
+ context.removeEventListener('unload', shutdown);
+ context.locationChangeListener = null;
+ LocationChangeListener = null;
+ contentObserver = null;
+};
+context.addEventListener('unload', shutdown);
-if ( docShell ) {
+if ( context.docShell ) {
let Ci = Components.interfaces;
- let wp = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
- .getInterface(Ci.nsIWebProgress);
+ let wp = context.docShell.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIWebProgress);
let dw = wp.DOMWindow;
if ( dw === dw.top ) {
- locationChangeListener = new LocationChangeListener(docShell);
+ context.locationChangeListener = new LocationChangeListener(context.docShell);
}
}
/******************************************************************************/
-})();
+})(this);
/******************************************************************************/
diff --git a/platform/firefox/vapi-background.js b/platform/firefox/vapi-background.js
index bf531c0..8bc110b 100644
--- a/platform/firefox/vapi-background.js
+++ b/platform/firefox/vapi-background.js
@@ -70,7 +70,7 @@ vAPI.localStorage.setDefaultBool('forceLegacyToolbarButton', false);
var cleanupTasks = [];
// This must be updated manually, every time a new task is added/removed
-var expectedNumberOfCleanups = 7;
+var expectedNumberOfCleanups = 8;
window.addEventListener('unload', function() {
if ( typeof vAPI.app.onShutdown === 'function' ) {
@@ -90,10 +90,11 @@ window.addEventListener('unload', function() {
}
// frameModule needs to be cleared too
+ var frameModuleURL = vAPI.getURL('frameModule.js');
var frameModule = {};
- Cu.import(vAPI.getURL('frameModule.js'), frameModule);
+ Cu.import(frameModuleURL, frameModule);
frameModule.contentObserver.unregister();
- Cu.unload(vAPI.getURL('frameModule.js'));
+ Cu.unload(frameModuleURL);
});
/******************************************************************************/
@@ -987,15 +988,15 @@ var tabWatcher = (function() {
};
// https://developer.mozilla.org/en-US/docs/Web/Events/TabOpen
- var onOpen = function({target}) {
- var tabId = tabIdFromTarget(target);
- var browser = browserFromTabId(tabId);
- vAPI.tabs.onNavigation({
- frameId: 0,
- tabId: tabId,
- url: browser.currentURI.asciiSpec,
- });
- };
+ //var onOpen = function({target}) {
+ // var tabId = tabIdFromTarget(target);
+ // var browser = browserFromTabId(tabId);
+ // vAPI.tabs.onNavigation({
+ // frameId: 0,
+ // tabId: tabId,
+ // url: browser.currentURI.asciiSpec,
+ // });
+ //};
// https://developer.mozilla.org/en-US/docs/Web/Events/TabShow
var onShow = function({target}) {
@@ -1208,7 +1209,7 @@ vAPI.messaging = {
return Cc['@mozilla.org/globalmessagemanager;1']
.getService(Ci.nsIMessageListenerManager);
},
- frameScript: vAPI.getURL('frameScript.js'),
+ frameScriptURL: vAPI.getURL('frameScript.js'),
listeners: {},
defaultHandler: null,
NOOPFUNC: function(){},
@@ -1438,7 +1439,7 @@ vAPI.messaging.setup = function(defaultHandler) {
this.onMessage
);
- this.globalMessageManager.loadFrameScript(this.frameScript, true);
+ this.globalMessageManager.loadFrameScript(this.frameScriptURL, true);
cleanupTasks.push(function() {
var gmm = vAPI.messaging.globalMessageManager;
@@ -1452,7 +1453,7 @@ vAPI.messaging.setup = function(defaultHandler) {
})
);
- gmm.removeDelayedFrameScript(vAPI.messaging.frameScript);
+ gmm.removeDelayedFrameScript(vAPI.messaging.frameScriptURL);
gmm.removeMessageListener(
location.host + ':background',
vAPI.messaging.onMessage
@@ -1472,6 +1473,60 @@ vAPI.messaging.broadcast = function(message) {
};
/******************************************************************************/
+/******************************************************************************/
+
+// Synchronous messaging: Firefox allows this. Chromium does not allow this.
+
+// Sometimes there is no way around synchronous messaging, as long as:
+// - the code at the other end execute fast and return quickly.
+// - it's not abused.
+// Original rationale is <https://github.com/gorhill/uBlock/issues/756>.
+// Synchronous messaging is a good solution for this case because:
+// - It's done only *once* per page load. (Keep in mind there is already a
+// sync message sent for each single network request on a page and it's not
+// an issue, because the code executed is trivial, which is the key -- see
+// shouldLoadListener below).
+// - The code at the other end is fast.
+// Though vAPI.rpcReceiver was brought forth because of this one case, I
+// generalized the concept for whatever future need for synchronous messaging
+// which might arise.
+
+// https://developer.mozilla.org/en-US/Firefox/Multiprocess_Firefox/Message_Manager/Message_manager_overview#Content_frame_message_manager
+
+vAPI.rpcReceiver = (function() {
+ var calls = Object.create(null);
+ var childProcessMessageName = location.host + ':child-process-message';
+
+ var onChildProcessMessage = function(ev) {
+ var msg = ev.data;
+ if ( !msg ) { return; }
+ var fn = calls[msg.fnName];
+ if ( typeof fn === 'function' ) {
+ return fn(msg);
+ }
+ };
+
+ if ( Services.ppmm ) {
+ Services.ppmm.addMessageListener(
+ childProcessMessageName,
+ onChildProcessMessage
+ );
+ }
+
+ cleanupTasks.push(function() {
+ if ( Services.ppmm ) {
+ Services.ppmm.removeMessageListener(
+ childProcessMessageName,
+ onChildProcessMessage
+ );
+ }
+ });
+
+ return calls;
+})();
+
+/******************************************************************************/
+/******************************************************************************/
var httpObserver = {
classDescription: 'net-channel-event-sinks for ' + location.host,
@@ -1865,7 +1920,11 @@ vAPI.net.registerListeners = function() {
var tabId = tabWatcher.tabIdFromTarget(e.target);
var sourceTabId = null;
- // Popup candidate
+ // Popup candidate: this code path is taken only for when a new top
+ // document loads, i.e. only once per document load. TODO: evaluate for
+ // popup filtering in an asynchrous manner -- it's not really required
+ // to evaluate on the spot. Still, there is currently no harm given
+ // this code path is typically taken only once per page load.
if ( details.openerURL ) {
for ( var browser of tabWatcher.browsers() ) {
var URI = browser.currentURI;
@@ -1899,8 +1958,9 @@ vAPI.net.registerListeners = function() {
}
}
- //console.log('shouldLoadListener:', details.url);
-
+ // We are being called synchronously from the content process, so we
+ // must return ASAP. The code below merely record the details of the
+ // request into a ring buffer for later retrieval by the HTTP observer.
var pendingReq = httpObserver.createPendingRequest(details.url);
pendingReq.frameId = details.frameId;
pendingReq.parentFrameId = details.parentFrameId;
diff --git a/platform/firefox/vapi-client.js b/platform/firefox/vapi-client.js
index bd6facd..07da7c8 100644
--- a/platform/firefox/vapi-client.js
+++ b/platform/firefox/vapi-client.js
@@ -31,6 +31,13 @@
/******************************************************************************/
+// Not all sandbox are given an rpc function, so assign a dummy one it is
+// missing -- this avoids the need for constantly testing before use.
+
+self.rpc = self.rpc || function(){};
+
+/******************************************************************************/
+
var vAPI = self.vAPI = self.vAPI || {};
vAPI.firefox = true;
vAPI.sessionId = String.fromCharCode(Date.now() % 26 + 97) +
@@ -67,6 +74,30 @@ vAPI.shutdown = (function() {
/******************************************************************************/
+(function() {
+ var hostname = location.hostname;
+ if ( !hostname ) {
+ return;
+ }
+ var filters = self.rpc({
+ fnName: 'getScriptTagFilters',
+ url: location.href,
+ hostname: hostname
+ });
+ if ( typeof filters !== 'string' || filters === '' ) {
+ return;
+ }
+ var reFilters = new RegExp(filters);
+ document.addEventListener('beforescriptexecute', function(ev) {
+ if ( reFilters.test(ev.target.textContent) ) {
+ ev.preventDefault();
+ ev.stopPropagation();
+ }
+ });
+})();
+
+/******************************************************************************/
+
vAPI.messaging = {
channels: {},
pending: {},
@@ -254,6 +285,12 @@ MessagingChannel.prototype.send = function(message, callback) {
MessagingChannel.prototype.sendTo = function(message, toTabId, toChannel, callback) {
var messaging = vAPI.messaging;
+ if ( !messaging ) {
+ if ( typeof callback === 'function' ) {
+ callback();
+ }
+ return;
+ }
// Too large a gap between the last request and the last response means
// the main process is no longer reachable: memory leaks and bad
// performance become a risk -- especially for long-lived, dynamic