mirror of https://github.com/gorhill/uBlock.git
this fixes #756
This commit is contained in:
parent
cc17a77b0a
commit
8d294869fe
|
@ -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' ) {
|
||||
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([
|
||||
|
|
|
@ -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)
|
||||
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);
|
||||
|
||||
/******************************************************************************/
|
||||
|
|
|
@ -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
|
||||
|
@ -1471,6 +1472,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 = {
|
||||
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
<script src="js/traffic.js"></script>
|
||||
<script src="js/contextmenu.js"></script>
|
||||
<script src="js/reverselookup.js"></script>
|
||||
<script src="js/rpcreceiver.js"></script>
|
||||
<script src="js/start.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -128,15 +128,7 @@ var netFilters = function(details) {
|
|||
//console.debug('document.querySelectorAll("%s") = %o', text, document.querySelectorAll(text));
|
||||
};
|
||||
|
||||
var onBeforeScriptExecuteHandler = function(ev) {
|
||||
if ( vAPI.reScriptTagRegex.test(ev.target.textContent) ) {
|
||||
ev.preventDefault();
|
||||
ev.stopPropagation();
|
||||
}
|
||||
};
|
||||
|
||||
var filteringHandler = function(details) {
|
||||
var value;
|
||||
var styleTagCount = vAPI.styles.length;
|
||||
|
||||
vAPI.skipCosmeticFiltering = !details || details.skipCosmeticFiltering;
|
||||
|
@ -147,11 +139,6 @@ var filteringHandler = function(details) {
|
|||
if ( details.netHide.length !== 0 ) {
|
||||
netFilters(details);
|
||||
}
|
||||
value = details.scriptTagRegex;
|
||||
if ( typeof value === 'string' && value.length !== 0 ) {
|
||||
vAPI.reScriptTagRegex = new RegExp(value);
|
||||
document.addEventListener('beforescriptexecute', onBeforeScriptExecuteHandler);
|
||||
}
|
||||
// The port will never be used again at this point, disconnecting allows
|
||||
// the browser to flush this script from memory.
|
||||
}
|
||||
|
|
|
@ -237,7 +237,7 @@ var FilterParser = function() {
|
|||
this.invalid = false;
|
||||
this.cosmetic = true;
|
||||
this.reParser = /^\s*([^#]*)(##|#@#)(.+)\s*$/;
|
||||
this.reScriptSelectorParser = /^script:contains\(\/.+?\/\)$/;
|
||||
this.reScriptContains = /^script:contains\(.+?\)$/;
|
||||
};
|
||||
|
||||
/******************************************************************************/
|
||||
|
@ -286,16 +286,20 @@ FilterParser.prototype.parse = function(s) {
|
|||
|
||||
// Script tag filters: pre-process them so that can be used with minimal
|
||||
// overhead in the content script.
|
||||
if (
|
||||
this.suffix.charAt(0) === 's' &&
|
||||
this.reScriptSelectorParser.test(this.suffix)
|
||||
) {
|
||||
// Example: focus.de##script:contains(/uabInject/)
|
||||
if ( this.suffix.charAt(0) === 's' && this.reScriptContains.test(this.suffix) ) {
|
||||
// Currently supported only as non-generic selector.
|
||||
if ( this.prefix.length === 0 ) {
|
||||
this.invalid = true;
|
||||
return this;
|
||||
}
|
||||
this.suffix = 'script//:' + this.suffix.slice(17, -2).replace(/\\/g, '\\');
|
||||
var suffix = this.suffix;
|
||||
this.suffix = 'script//:';
|
||||
if ( suffix.charAt(16) !== '/' || suffix.slice(-2) !== '/)' ) {
|
||||
this.suffix += suffix.slice(16, -1).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||
} else {
|
||||
this.suffix += suffix.slice(17, -2).replace(/\\/g, '\\');
|
||||
}
|
||||
}
|
||||
|
||||
this.unhide = matches[2].charAt(1) === '@' ? 1 : 0;
|
||||
|
@ -1257,7 +1261,6 @@ FilterContainer.prototype.retrieveDomainSelectors = function(request) {
|
|||
cosmeticHide: [],
|
||||
cosmeticDonthide: [],
|
||||
netHide: [],
|
||||
scriptTagRegex: this.retrieveScriptTagRegex(domain, hostname),
|
||||
netCollapse: µb.userSettings.collapseBlocked
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue