diff options
author | gorhill <rhill@raymondhill.net> | 2015-09-30 09:33:38 -0400 |
---|---|---|
committer | gorhill <rhill@raymondhill.net> | 2015-09-30 09:33:38 -0400 |
commit | 8d294869fe252b54c08a14fc9b94722c2ace56ef (patch) | |
tree | 4b7e65aa6271d10c111cd138ac84cc2ef61643b9 /platform | |
parent | cc17a77b0ac69ba73299148e2a710dcbd5069a21 (diff) | |
download | uBlock-8d294869fe252b54c08a14fc9b94722c2ace56ef.zip uBlock-8d294869fe252b54c08a14fc9b94722c2ace56ef.tar.gz uBlock-8d294869fe252b54c08a14fc9b94722c2ace56ef.tar.bz2 |
this fixes #756
Diffstat (limited to 'platform')
-rw-r--r-- | platform/firefox/frameModule.js | 42 | ||||
-rw-r--r-- | platform/firefox/frameScript.js | 33 | ||||
-rw-r--r-- | platform/firefox/vapi-background.js | 96 | ||||
-rw-r--r-- | platform/firefox/vapi-client.js | 37 |
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 |