This commit is contained in:
gorhill 2017-01-08 14:36:08 -05:00
parent a927725bd9
commit a303c7800e
3 changed files with 77 additions and 124 deletions

View file

@ -1,7 +1,7 @@
/******************************************************************************* /*******************************************************************************
uBlock Origin - a browser extension to block requests. uBlock Origin - a browser extension to block requests.
Copyright (C) 2014-2016 The uBlock Origin authors Copyright (C) 2014-2017 The uBlock Origin authors
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -161,7 +161,7 @@ var contentObserver = {
popupMessageName: hostName + ':shouldLoadPopup', popupMessageName: hostName + ':shouldLoadPopup',
ignoredPopups: new WeakMap(), ignoredPopups: new WeakMap(),
uniqueSandboxId: 1, uniqueSandboxId: 1,
canE10S: Services.vc.compare(Services.appinfo.platformVersion, '44') > 0, modernFirefox: Services.vc.compare(Services.appinfo.platformVersion, '44') > 0,
get componentRegistrar() { get componentRegistrar() {
return Components.manager.QueryInterface(Ci.nsIComponentRegistrar); return Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
@ -189,31 +189,40 @@ var contentObserver = {
register: function() { register: function() {
Services.obs.addObserver(this, 'document-element-inserted', true); Services.obs.addObserver(this, 'document-element-inserted', true);
Services.obs.addObserver(this, 'content-document-global-created', true);
this.componentRegistrar.registerFactory( // https://bugzilla.mozilla.org/show_bug.cgi?id=1232354
this.classID, // For modern versions of Firefox, the frameId/parentFrameId
this.classDescription, // information can be found in channel.loadInfo of the HTTP observer.
this.contractID, if ( this.modernFirefox !== true ) {
this this.componentRegistrar.registerFactory(
); this.classID,
this.categoryManager.addCategoryEntry( this.classDescription,
'content-policy', this.contractID,
this.contractID, this
this.contractID, );
false, this.categoryManager.addCategoryEntry(
true 'content-policy',
); this.contractID,
this.contractID,
false,
true
);
}
}, },
unregister: function() { unregister: function() {
Services.obs.removeObserver(this, 'document-element-inserted'); Services.obs.removeObserver(this, 'document-element-inserted');
Services.obs.removeObserver(this, 'content-document-global-created');
this.componentRegistrar.unregisterFactory(this.classID, this); if ( this.modernFirefox !== true ) {
this.categoryManager.deleteCategoryEntry( this.componentRegistrar.unregisterFactory(this.classID, this);
'content-policy', this.categoryManager.deleteCategoryEntry(
this.contractID, 'content-policy',
false this.contractID,
); false
);
}
}, },
getFrameId: function(win) { getFrameId: function(win) {
@ -223,48 +232,6 @@ var contentObserver = {
.outerWindowID; .outerWindowID;
}, },
handlePopup: function(location, origin, context) {
let openeeContext = context.contentWindow || context;
if (
typeof openeeContext.opener !== 'object' ||
openeeContext.opener === null ||
openeeContext.opener === context ||
this.ignoredPopups.has(openeeContext)
) {
return;
}
// https://github.com/gorhill/uBlock/issues/452
// Use location of top window, not that of a frame, as this
// would cause tab id lookup (necessary for popup blocking) to
// always fail.
// https://github.com/gorhill/uBlock/issues/1305
// Opener could be a dead object, using it would cause a throw.
// Repro case:
// - Open http://delishows.to/show/chicago-med/season/1/episode/6
// - Click anywhere in the background
let openerURL = null;
try {
let opener = openeeContext.opener.top || openeeContext.opener;
openerURL = opener.location && opener.location.href;
} catch(ex) {
}
// If no valid opener URL found, use the origin URL.
if ( openerURL === null ) {
openerURL = origin.asciiSpec;
}
let messageManager = getMessageManager(openeeContext);
if ( messageManager === null ) {
return;
}
if ( typeof messageManager.sendRpcMessage === 'function' ) {
// https://bugzil.la/1092216
messageManager.sendRpcMessage(this.popupMessageName, openerURL);
} else {
// Compatibility for older versions
messageManager.sendSyncMessage(this.popupMessageName, openerURL);
}
},
// https://bugzil.la/612921 // https://bugzil.la/612921
shouldLoad: function(type, location, origin, context) { shouldLoad: function(type, location, origin, context) {
// For whatever reason, sometimes the global scope is completely // For whatever reason, sometimes the global scope is completely
@ -278,17 +245,6 @@ var contentObserver = {
return this.ACCEPT; return this.ACCEPT;
} }
if ( type === this.MAIN_FRAME ) {
this.handlePopup(location, origin, context);
}
// https://bugzilla.mozilla.org/show_bug.cgi?id=1232354
// For modern versions of Firefox, the frameId/parentFrameId
// information can be found in channel.loadInfo of the HTTP observer.
if ( this.canE10S ) {
return this.ACCEPT;
}
if ( !location.schemeIs('http') && !location.schemeIs('https') ) { if ( !location.schemeIs('http') && !location.schemeIs('https') ) {
return this.ACCEPT; return this.ACCEPT;
} }
@ -504,17 +460,44 @@ var contentObserver = {
}, },
ignorePopup: function(e) { ignorePopup: function(e) {
if ( e.isTrusted === false ) { if ( e.isTrusted === false ) { return; }
return;
}
let contObs = contentObserver; let contObs = contentObserver;
contObs.ignoredPopups.set(this, true); contObs.ignoredPopups.set(this, true);
this.removeEventListener('keydown', contObs.ignorePopup, true); this.removeEventListener('keydown', contObs.ignorePopup, true);
this.removeEventListener('mousedown', contObs.ignorePopup, true); this.removeEventListener('mousedown', contObs.ignorePopup, true);
}, },
lookupPopupOpenerURL: function(popup) {
var opener, openerURL;
for (;;) {
opener = popup.opener;
if ( !opener ) { break; }
if ( opener.top ) { opener = opener.top; }
if ( opener === popup ) { break; }
if ( !opener.location ) { break; }
if ( !this.reGoodPopupURLs.test(opener.location.href) ) { break; }
openerURL = opener.location.href;
// https://github.com/uBlockOrigin/uAssets/issues/255
// - Mind chained about:blank popups.
if ( openerURL !== 'about:blank' ) { break; }
popup = opener;
}
return openerURL;
},
reGoodPopupURLs: /^(?:about:blank|blob:|data:|https?:|javascript:)/,
observe: function(subject, topic) {
// https://github.com/gorhill/uBlock/issues/2290
if ( topic === 'content-document-global-created' ) {
if ( subject !== subject.top ) { return; }
if ( this.ignoredPopups.has(subject) ) { return; }
let openerURL = this.lookupPopupOpenerURL(subject);
if ( !openerURL ) { return; }
let messager = getMessageManager(subject);
if ( !messager ) { return; }
messager.sendAsyncMessage(this.popupMessageName, openerURL);
return;
}
observe: function(doc) {
// For whatever reason, sometimes the global scope is completely // For whatever reason, sometimes the global scope is completely
// uninitialized at this point. Repro steps: // uninitialized at this point. Repro steps:
// - Launch FF with uBlock enabled // - Launch FF with uBlock enabled
@ -522,14 +505,11 @@ var contentObserver = {
// - Enable uBlock // - Enable uBlock
// - Services and all other global variables are undefined // - Services and all other global variables are undefined
// Hopefully will eventually understand why this happens. // Hopefully will eventually understand why this happens.
if ( Services === undefined ) { if ( Services === undefined ) { return; }
return;
}
let doc = subject;
let win = doc.defaultView || null; let win = doc.defaultView || null;
if ( win === null ) { if ( win === null ) { return; }
return;
}
if ( win.opener && this.ignoredPopups.has(win) === false ) { if ( win.opener && this.ignoredPopups.has(win) === false ) {
win.addEventListener('keydown', this.ignorePopup, true); win.addEventListener('keydown', this.ignorePopup, true);
@ -543,17 +523,13 @@ var contentObserver = {
// TODO: We may have to exclude more types, for now let's be // TODO: We may have to exclude more types, for now let's be
// conservative and focus only on the one issue reported, i.e. let's // conservative and focus only on the one issue reported, i.e. let's
// not test against 'text/html'. // not test against 'text/html'.
if ( doc.contentType.startsWith('image/') ) { if ( doc.contentType.startsWith('image/') ) { return; }
return;
}
let loc = win.location; let loc = win.location;
if ( loc.protocol !== 'http:' && loc.protocol !== 'https:' && loc.protocol !== 'file:' ) { if ( loc.protocol !== 'http:' && loc.protocol !== 'https:' && loc.protocol !== 'file:' ) {
if ( loc.protocol === 'chrome:' && loc.host === hostName ) { if ( loc.protocol === 'chrome:' && loc.host === hostName ) {
this.initContentScripts(win); this.initContentScripts(win);
} }
// What about data: and about:blank? // What about data: and about:blank?
return; return;
} }

View file

@ -1,7 +1,7 @@
/******************************************************************************* /*******************************************************************************
uBlock Origin - a browser extension to block requests. uBlock Origin - a browser extension to block requests.
Copyright (C) 2014-2106 The uBlock Origin authors Copyright (C) 2014-2107 The uBlock Origin authors
This program is free software: you can redistribute it and/or modify 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 it under the terms of the GNU General Public License as published by
@ -2361,26 +2361,14 @@ vAPI.net.registerListeners = function() {
null; null;
} }
var shouldLoadPopupListenerMessageName = location.host + ':shouldLoadPopup', var shouldLoadPopupListenerMessageName = location.host + ':shouldLoadPopup';
shouldLoadPopupListenerMap = new Map(), var shouldLoadPopupListener = function(e) {
shouldLoadPopupListenerMapToD = 0; if ( typeof vAPI.tabs.onPopupCreated !== 'function' ) { return; }
var shouldLoadPopupListener = function(openerURL, target) { var target = e.target,
var popupTabId = tabWatcher.tabIdFromTarget(target), openerURL = e.data,
popupURL = target.currentURI && target.currentURI.asciiSpec || '', popupTabId = tabWatcher.tabIdFromTarget(target),
openerTabId, openerTabId,
uri; uri;
if ( shouldLoadPopupListenerMapToD > Date.now() ) {
openerTabId = shouldLoadPopupListenerMap.get(popupURL);
}
// https://github.com/uBlockOrigin/uAssets/issues/255
// Handle chained popups.
if ( openerTabId !== undefined ) {
shouldLoadPopupListenerMap.set(popupURL, openerTabId);
shouldLoadPopupListenerMapToD = Date.now() + 10000;
vAPI.tabs.onPopupCreated(popupTabId, openerTabId);
return;
}
for ( var browser of tabWatcher.browsers() ) { for ( var browser of tabWatcher.browsers() ) {
uri = browser.currentURI; uri = browser.currentURI;
@ -2398,24 +2386,14 @@ vAPI.net.registerListeners = function() {
openerTabId = tabWatcher.tabIdFromTarget(browser); openerTabId = tabWatcher.tabIdFromTarget(browser);
if ( openerTabId === popupTabId ) { continue; } if ( openerTabId === popupTabId ) { continue; }
shouldLoadPopupListenerMap = new Map();
shouldLoadPopupListenerMapToD = Date.now() + 10000;
shouldLoadPopupListenerMap.set(popupURL, openerTabId);
vAPI.tabs.onPopupCreated(popupTabId, openerTabId); vAPI.tabs.onPopupCreated(popupTabId, openerTabId);
break; break;
} }
}; };
var shouldLoadPopupListenerAsync = function(e) {
if ( typeof vAPI.tabs.onPopupCreated !== 'function' ) {
return;
}
// We are handling a synchronous message: do not block.
vAPI.setTimeout(shouldLoadPopupListener.bind(null, e.data, e.target), 1);
};
vAPI.messaging.globalMessageManager.addMessageListener( vAPI.messaging.globalMessageManager.addMessageListener(
shouldLoadPopupListenerMessageName, shouldLoadPopupListenerMessageName,
shouldLoadPopupListenerAsync shouldLoadPopupListener
); );
var shouldLoadListenerMessageName = location.host + ':shouldLoad'; var shouldLoadListenerMessageName = location.host + ':shouldLoad';
@ -2501,7 +2479,7 @@ vAPI.net.registerListeners = function() {
cleanupTasks.push(function() { cleanupTasks.push(function() {
vAPI.messaging.globalMessageManager.removeMessageListener( vAPI.messaging.globalMessageManager.removeMessageListener(
shouldLoadPopupListenerMessageName, shouldLoadPopupListenerMessageName,
shouldLoadPopupListenerAsync shouldLoadPopupListener
); );
vAPI.messaging.globalMessageManager.removeMessageListener( vAPI.messaging.globalMessageManager.removeMessageListener(

View file

@ -174,10 +174,9 @@ housekeep itself.
vAPI.tabs.onPopupCreated = function(targetTabId, openerTabId) { vAPI.tabs.onPopupCreated = function(targetTabId, openerTabId) {
var popup = popupCandidates[targetTabId]; var popup = popupCandidates[targetTabId];
if ( popup !== undefined ) { if ( popup === undefined ) {
return; popupCandidates[targetTabId] = new PopupCandidate(targetTabId, openerTabId);
} }
popupCandidates[targetTabId] = new PopupCandidate(targetTabId, openerTabId);
popupCandidateTest(targetTabId); popupCandidateTest(targetTabId);
}; };