1
0
mirror of https://github.com/gorhill/uBlock.git synced 2024-10-06 09:37:12 +02:00

this adds popunder filtering support for Firefox-based browsers

This commit is contained in:
gorhill 2015-12-01 15:07:22 -05:00
parent 5f304b6751
commit 4fd71d4209
4 changed files with 187 additions and 221 deletions

View File

@ -157,7 +157,6 @@ var toChromiumTabId = function(tabId) {
vAPI.tabs.registerListeners = function() { vAPI.tabs.registerListeners = function() {
var onNavigationClient = this.onNavigation || noopFunc; var onNavigationClient = this.onNavigation || noopFunc;
var onPopupClient = this.onPopup || noopFunc;
var onUpdatedClient = this.onUpdated || noopFunc; var onUpdatedClient = this.onUpdated || noopFunc;
// https://developer.chrome.com/extensions/webNavigation // https://developer.chrome.com/extensions/webNavigation
@ -167,58 +166,6 @@ vAPI.tabs.registerListeners = function() {
// onDOMContentLoaded -> // onDOMContentLoaded ->
// onCompleted // onCompleted
var popupCandidates = Object.create(null);
var PopupCandidate = function(details) {
this.targetTabId = details.tabId.toString();
this.openerTabId = details.sourceTabId.toString();
this.targetURL = details.url;
this.selfDestructionTimer = null;
};
PopupCandidate.prototype.selfDestruct = function() {
if ( this.selfDestructionTimer !== null ) {
clearTimeout(this.selfDestructionTimer);
}
delete popupCandidates[this.targetTabId];
};
PopupCandidate.prototype.launchSelfDestruction = function() {
if ( this.selfDestructionTimer !== null ) {
clearTimeout(this.selfDestructionTimer);
}
this.selfDestructionTimer = setTimeout(this.selfDestruct.bind(this), 10000);
};
var popupCandidateCreate = function(details) {
var popup = popupCandidates[details.tabId];
// This really should not happen...
if ( popup !== undefined ) {
return;
}
return (popupCandidates[details.tabId] = new PopupCandidate(details));
};
var popupCandidateTest = function(details) {
var popup = popupCandidates[details.tabId];
if ( popup === undefined ) {
return;
}
popup.targetURL = details.url;
if ( onPopupClient(popup) !== true ) {
return;
}
popup.selfDestruct();
return true;
};
var popupCandidateDestroy = function(details) {
var popup = popupCandidates[details.tabId];
if ( popup instanceof PopupCandidate ) {
popup.launchSelfDestruction();
}
};
// The chrome.webRequest.onBeforeRequest() won't be called for everything // The chrome.webRequest.onBeforeRequest() won't be called for everything
// else than `http`/`https`. Thus, in such case, we will bind the tab as // else than `http`/`https`. Thus, in such case, we will bind the tab as
// early as possible in order to increase the likelihood of a context // early as possible in order to increase the likelihood of a context
@ -233,22 +180,18 @@ vAPI.tabs.registerListeners = function() {
details.frameId = 0; details.frameId = 0;
onNavigationClient(details); onNavigationClient(details);
} }
popupCandidateCreate(details); if ( typeof vAPI.tabs.onPopupCreated === 'function' ) {
popupCandidateTest(details); vAPI.tabs.onPopupCreated(details.tabId.toString(), details.sourceTabId.toString());
}
}; };
var onBeforeNavigate = function(details) { var onBeforeNavigate = function(details) {
if ( details.frameId !== 0 ) { if ( details.frameId !== 0 ) {
return; return;
} }
//console.debug('onBeforeNavigate: popup candidate tab id %d = "%s"', details.tabId, details.url);
popupCandidateTest(details);
}; };
var onUpdated = function(tabId, changeInfo, tab) { var onUpdated = function(tabId, changeInfo, tab) {
if ( changeInfo.url && popupCandidateTest({ tabId: tabId, url: changeInfo.url }) ) {
return;
}
onUpdatedClient(tabId, changeInfo, tab); onUpdatedClient(tabId, changeInfo, tab);
}; };
@ -257,11 +200,6 @@ vAPI.tabs.registerListeners = function() {
return; return;
} }
onNavigationClient(details); onNavigationClient(details);
//console.debug('onCommitted: popup candidate tab id %d = "%s"', details.tabId, details.url);
if ( popupCandidateTest(details) === true ) {
return;
}
popupCandidateDestroy(details);
}; };
chrome.webNavigation.onCreatedNavigationTarget.addListener(onCreatedNavigationTarget); chrome.webNavigation.onCreatedNavigationTarget.addListener(onCreatedNavigationTarget);

View File

@ -90,6 +90,7 @@ var contentObserver = {
SUB_FRAME: Ci.nsIContentPolicy.TYPE_SUBDOCUMENT, SUB_FRAME: Ci.nsIContentPolicy.TYPE_SUBDOCUMENT,
contentBaseURI: 'chrome://' + hostName + '/content/js/', contentBaseURI: 'chrome://' + hostName + '/content/js/',
cpMessageName: hostName + ':shouldLoad', cpMessageName: hostName + ':shouldLoad',
popupMessageName: hostName + ':shouldLoadPopup',
ignoredPopups: new WeakMap(), ignoredPopups: new WeakMap(),
uniqueSandboxId: 1, uniqueSandboxId: 1,
@ -153,6 +154,38 @@ var contentObserver = {
.outerWindowID; .outerWindowID;
}, },
handlePopup: function(location, 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.
let openerURL = openeeContext.opener.top &&
openeeContext.opener.top.location.href;
if ( openerURL === null ) {
return;
}
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
@ -170,26 +203,16 @@ var contentObserver = {
return this.ACCEPT; return this.ACCEPT;
} }
if ( type === this.MAIN_FRAME ) {
this.handlePopup(location, context);
}
if ( !location.schemeIs('http') && !location.schemeIs('https') ) { if ( !location.schemeIs('http') && !location.schemeIs('https') ) {
return this.ACCEPT; return this.ACCEPT;
} }
let openerURL = null;
if ( type === this.MAIN_FRAME ) { if ( type === this.MAIN_FRAME ) {
context = context.contentWindow || context; context = context.contentWindow || context;
if (
typeof context.opener === 'object' &&
context.opener !== null &&
context.opener !== context &&
this.ignoredPopups.has(context) === false
) {
// 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.
openerURL = context.opener.top && context.opener.top.location.href;
}
} else if ( type === this.SUB_FRAME ) { } else if ( type === this.SUB_FRAME ) {
context = context.contentWindow; context = context.contentWindow;
} else { } else {
@ -219,7 +242,6 @@ var contentObserver = {
let details = { let details = {
frameId: isTopLevel ? 0 : this.getFrameId(context), frameId: isTopLevel ? 0 : this.getFrameId(context),
openerURL: openerURL,
parentFrameId: parentFrameId, parentFrameId: parentFrameId,
rawtype: type, rawtype: type,
tabId: '', tabId: '',

View File

@ -945,17 +945,24 @@ vAPI.tabs._remove = (function() {
/******************************************************************************/ /******************************************************************************/
vAPI.tabs.remove = function(tabId) { vAPI.tabs.remove = (function() {
var browser = tabWatcher.browserFromTabId(tabId); var remove = function(tabId) {
if ( !browser ) { var browser = tabWatcher.browserFromTabId(tabId);
return; if ( !browser ) {
} return;
var tab = tabWatcher.tabFromBrowser(browser); }
if ( !tab ) { var tab = tabWatcher.tabFromBrowser(browser);
return; if ( !tab ) {
} return;
this._remove(tab, getTabBrowser(getOwnerWindow(browser))); }
}; this._remove(tab, getTabBrowser(getOwnerWindow(browser)));
};
// Do this asynchronously
return function(tabId) {
vAPI.setTimeout(remove.bind(this, tabId), 10);
};
})();
/******************************************************************************/ /******************************************************************************/
@ -1170,17 +1177,6 @@ var tabWatcher = (function() {
onClose({ target: target }); onClose({ target: target });
}; };
// 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,
// });
//};
// https://developer.mozilla.org/en-US/docs/Web/Events/TabShow // https://developer.mozilla.org/en-US/docs/Web/Events/TabShow
var onShow = function({target}) { var onShow = function({target}) {
tabIdFromTarget(target); tabIdFromTarget(target);
@ -1266,7 +1262,6 @@ var tabWatcher = (function() {
// not set when a tab is opened as a result of session restore -- it is // not set when a tab is opened as a result of session restore -- it is
// set *after* the event is fired in such case. // set *after* the event is fired in such case.
if ( tabContainer ) { if ( tabContainer ) {
//tabContainer.addEventListener('TabOpen', onOpen);
tabContainer.addEventListener('TabShow', onShow); tabContainer.addEventListener('TabShow', onShow);
tabContainer.addEventListener('TabClose', onClose); tabContainer.addEventListener('TabClose', onClose);
// when new window is opened TabSelect doesn't run on the selected tab? // when new window is opened TabSelect doesn't run on the selected tab?
@ -1293,7 +1288,6 @@ var tabWatcher = (function() {
tabContainer = tabBrowser.tabContainer; tabContainer = tabBrowser.tabContainer;
} }
if ( tabContainer ) { if ( tabContainer ) {
//tabContainer.removeEventListener('TabOpen', onOpen);
tabContainer.removeEventListener('TabShow', onShow); tabContainer.removeEventListener('TabShow', onShow);
tabContainer.removeEventListener('TabClose', onClose); tabContainer.removeEventListener('TabClose', onClose);
tabContainer.removeEventListener('TabSelect', onSelect); tabContainer.removeEventListener('TabSelect', onSelect);
@ -1847,7 +1841,6 @@ var httpObserver = {
this.frameId = 0; this.frameId = 0;
this.parentFrameId = 0; this.parentFrameId = 0;
this.rawtype = 0; this.rawtype = 0;
this.sourceTabId = null;
this.tabId = 0; this.tabId = 0;
this._key = ''; // key is url, from URI.spec this._key = ''; // key is url, from URI.spec
}, },
@ -1937,9 +1930,10 @@ var httpObserver = {
} }
aWindow = loadContext.associatedWindow; aWindow = loadContext.associatedWindow;
} catch (ex) { } catch (ex) {
//console.error(ex); //console.error(ex.toString());
} }
} }
var gBrowser;
try { try {
if ( !aWindow && channel.loadGroup && channel.loadGroup.notificationCallbacks ) { if ( !aWindow && channel.loadGroup && channel.loadGroup.notificationCallbacks ) {
aWindow = channel aWindow = channel
@ -1949,21 +1943,23 @@ var httpObserver = {
.associatedWindow; .associatedWindow;
} }
if ( aWindow ) { if ( aWindow ) {
return tabWatcher.tabIdFromTarget( gBrowser = aWindow
aWindow
.getInterface(Ci.nsIWebNavigation) .getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShell) .QueryInterface(Ci.nsIDocShell)
.rootTreeItem .rootTreeItem
.QueryInterface(Ci.nsIInterfaceRequestor) .QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow) .getInterface(Ci.nsIDOMWindow)
.gBrowser .gBrowser;
.getBrowserForContentWindow(aWindow)
);
} }
} catch (ex) { } catch (ex) {
//console.error(ex); //console.error(ex.toString());
} }
return vAPI.noTabId; if ( !gBrowser || !gBrowser._getTabForContentWindow ) {
return vAPI.noTabId;
}
// Using `_getTabForContentWindow` ensure older versions of Firefox
// work well.
return tabWatcher.tabIdFromTarget(gBrowser._getTabForContentWindow(aWindow));
}, },
// https://github.com/gorhill/uBlock/issues/959 // https://github.com/gorhill/uBlock/issues/959
@ -1981,24 +1977,6 @@ var httpObserver = {
}; };
}, },
handlePopup: function(URI, tabId, sourceTabId) {
if ( !sourceTabId ) {
return false;
}
if ( !URI.schemeIs('http') && !URI.schemeIs('https') ) {
return false;
}
var result = vAPI.tabs.onPopup({
targetTabId: tabId,
openerTabId: sourceTabId,
targetURL: URI.asciiSpec
});
return result === true;
},
handleRequest: function(channel, URI, details) { handleRequest: function(channel, URI, details) {
var type = this.typeMap[details.rawtype] || 'other'; var type = this.typeMap[details.rawtype] || 'other';
if ( this.onBeforeRequestTypes && this.onBeforeRequestTypes.has(type) === false ) { if ( this.onBeforeRequestTypes && this.onBeforeRequestTypes.has(type) === false ) {
@ -2048,7 +2026,7 @@ var httpObserver = {
}, },
handleResponseHeaders: function(channel, URI, channelData) { handleResponseHeaders: function(channel, URI, channelData) {
var type = this.typeMap[channelData[4]] || 'other'; var type = this.typeMap[channelData[3]] || 'other';
if ( this.onHeadersReceivedTypes && this.onHeadersReceivedTypes.has(type) === false ) { if ( this.onHeadersReceivedTypes && this.onHeadersReceivedTypes.has(type) === false ) {
return; return;
} }
@ -2071,8 +2049,8 @@ var httpObserver = {
hostname: hostname, hostname: hostname,
parentFrameId: channelData[1], parentFrameId: channelData[1],
responseHeaders: responseHeaders, responseHeaders: responseHeaders,
tabId: channelData[3], tabId: channelData[2],
type: this.typeMap[channelData[4]] || 'other', type: this.typeMap[channelData[3]] || 'other',
url: URI.asciiSpec url: URI.asciiSpec
}); });
@ -2137,6 +2115,19 @@ var httpObserver = {
} }
} }
// IMPORTANT:
// If this is a main frame, ensure that the proper tab id is being
// used: it can happen that the wrong tab id was looked up at
// `shouldLoadListener` time. Without this, the popup blocker may
// not work properly, and also a tab opened from a link may end up
// being wrongly reported as an embedded element.
if ( pendingRequest !== null && pendingRequest.rawtype === 6 ) {
var tabId = this.tabIdFromChannel(channel);
if ( tabId !== vAPI.noTabId ) {
pendingRequest.tabId = tabId;
}
}
// Behind-the-scene request... Really? // Behind-the-scene request... Really?
if ( pendingRequest === null ) { if ( pendingRequest === null ) {
pendingRequest = this.synthesizePendingRequest(channel, rawtype); pendingRequest = this.synthesizePendingRequest(channel, rawtype);
@ -2171,7 +2162,6 @@ var httpObserver = {
channel.setProperty(this.REQDATAKEY, [ channel.setProperty(this.REQDATAKEY, [
pendingRequest.frameId, pendingRequest.frameId,
pendingRequest.parentFrameId, pendingRequest.parentFrameId,
pendingRequest.sourceTabId,
pendingRequest.tabId, pendingRequest.tabId,
pendingRequest.rawtype pendingRequest.rawtype
]); ]);
@ -2196,16 +2186,11 @@ var httpObserver = {
var channelData = oldChannel.getProperty(this.REQDATAKEY); var channelData = oldChannel.getProperty(this.REQDATAKEY);
if ( this.handlePopup(URI, channelData[3], channelData[2]) ) {
result = this.ABORT;
return;
}
var details = { var details = {
frameId: channelData[0], frameId: channelData[0],
parentFrameId: channelData[1], parentFrameId: channelData[1],
tabId: channelData[3], tabId: channelData[2],
rawtype: channelData[4] rawtype: channelData[3]
}; };
if ( this.handleRequest(newChannel, URI, details) ) { if ( this.handleRequest(newChannel, URI, details) ) {
@ -2250,9 +2235,15 @@ vAPI.net.registerListeners = function() {
null; null;
} }
var shouldBlockPopup = function(details) { var shouldLoadPopupListenerMessageName = location.host + ':shouldLoadPopup';
var sourceTabId = null; var shouldLoadPopupListener = function(e) {
var uri; if ( typeof vAPI.tabs.onPopupCreated !== 'function' ) {
return;
}
var openerURL = e.data;
var popupTabId = tabWatcher.tabIdFromTarget(e.target);
var uri, openerTabId;
for ( var browser of tabWatcher.browsers() ) { for ( var browser of tabWatcher.browsers() ) {
uri = browser.currentURI; uri = browser.currentURI;
@ -2265,25 +2256,23 @@ vAPI.net.registerListeners = function() {
// believe this may have to do with those very temporary // believe this may have to do with those very temporary
// browser objects created when opening a new tab, i.e. related // browser objects created when opening a new tab, i.e. related
// to https://github.com/gorhill/uBlock/issues/212 // to https://github.com/gorhill/uBlock/issues/212
if ( !uri || uri.spec !== details.openerURL ) { if ( !uri || uri.spec !== openerURL ) {
continue; continue;
} }
sourceTabId = tabWatcher.tabIdFromTarget(browser); openerTabId = tabWatcher.tabIdFromTarget(browser);
if ( sourceTabId === details.tabId ) { if ( openerTabId !== popupTabId ) {
sourceTabId = null; vAPI.tabs.onPopupCreated(popupTabId, openerTabId);
continue; break;
} }
uri = Services.io.newURI(details.url, null, null);
httpObserver.handlePopup(uri, details.tabId, sourceTabId);
break;
} }
return sourceTabId;
}; };
vAPI.messaging.globalMessageManager.addMessageListener(
shouldLoadPopupListenerMessageName,
shouldLoadPopupListener
);
var shouldLoadListenerMessageName = location.host + ':shouldLoad'; var shouldLoadListenerMessageName = location.host + ':shouldLoad';
var shouldLoadListener = function(e) { var shouldLoadListener = function(e) {
// Non blocking: it is assumed that the http observer is fired after // Non blocking: it is assumed that the http observer is fired after
@ -2291,18 +2280,6 @@ vAPI.net.registerListeners = function() {
// a request would end up being categorized as a behind-the-scene // a request would end up being categorized as a behind-the-scene
// requests. // requests.
var details = e.data; var details = e.data;
var sourceTabId = null;
details.tabId = tabWatcher.tabIdFromTarget(e.target);
// 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 ) {
sourceTabId = shouldBlockPopup(details);
}
// We are being called synchronously from the content process, so we // We are being called synchronously from the content process, so we
// must return ASAP. The code below merely record the details of the // must return ASAP. The code below merely record the details of the
@ -2311,8 +2288,7 @@ vAPI.net.registerListeners = function() {
pendingReq.frameId = details.frameId; pendingReq.frameId = details.frameId;
pendingReq.parentFrameId = details.parentFrameId; pendingReq.parentFrameId = details.parentFrameId;
pendingReq.rawtype = details.rawtype; pendingReq.rawtype = details.rawtype;
pendingReq.sourceTabId = sourceTabId; pendingReq.tabId = tabWatcher.tabIdFromTarget(e.target);
pendingReq.tabId = details.tabId;
}; };
vAPI.messaging.globalMessageManager.addMessageListener( vAPI.messaging.globalMessageManager.addMessageListener(
@ -2378,6 +2354,11 @@ vAPI.net.registerListeners = function() {
httpObserver.register(); httpObserver.register();
cleanupTasks.push(function() { cleanupTasks.push(function() {
vAPI.messaging.globalMessageManager.removeMessageListener(
shouldLoadPopupListenerMessageName,
shouldLoadPopupListener
);
vAPI.messaging.globalMessageManager.removeMessageListener( vAPI.messaging.globalMessageManager.removeMessageListener(
shouldLoadListenerMessageName, shouldLoadListenerMessageName,
shouldLoadListener shouldLoadListener
@ -2655,7 +2636,7 @@ vAPI.toolbarButton = {
// palette might take a little longer to appear on some platforms, // palette might take a little longer to appear on some platforms,
// give it a small delay and try again. // give it a small delay and try again.
if ( toolbox.palette === null ) { if ( !toolbox.palette ) {
vAPI.setTimeout(onReadyStateComplete.bind(null, window, callback, tryCount), 200); vAPI.setTimeout(onReadyStateComplete.bind(null, window, callback, tryCount), 200);
return; return;
} }
@ -2707,7 +2688,7 @@ vAPI.toolbarButton = {
var navbar = document.getElementById('nav-bar'); var navbar = document.getElementById('nav-bar');
var toolbarButton = createToolbarButton(window); var toolbarButton = createToolbarButton(window);
if ( palette !== null && palette.querySelector('#' + tbb.id) === null ) { if ( palette && palette.querySelector('#' + tbb.id) === null ) {
palette.appendChild(toolbarButton); palette.appendChild(toolbarButton);
} }

View File

@ -136,6 +136,53 @@ housekeep itself.
var mostRecentRootDocURL = ''; var mostRecentRootDocURL = '';
var mostRecentRootDocURLTimestamp = 0; var mostRecentRootDocURLTimestamp = 0;
var popupCandidates = Object.create(null);
var PopupCandidate = function(targetTabId, openerTabId) {
this.targetTabId = targetTabId;
this.openerTabId = openerTabId;
this.selfDestructionTimer = null;
this.launchSelfDestruction();
};
PopupCandidate.prototype.destroy = function() {
if ( this.selfDestructionTimer !== null ) {
clearTimeout(this.selfDestructionTimer);
}
delete popupCandidates[this.targetTabId];
};
PopupCandidate.prototype.launchSelfDestruction = function() {
if ( this.selfDestructionTimer !== null ) {
clearTimeout(this.selfDestructionTimer);
}
this.selfDestructionTimer = vAPI.setTimeout(this.destroy.bind(this), 10000);
};
var popupCandidateTest = function(targetTabId) {
var candidates = popupCandidates, entry;
for ( var tabId in candidates ) {
entry = candidates[tabId];
if ( targetTabId !== tabId && targetTabId !== entry.openerTabId ) {
continue;
}
if ( vAPI.tabs.onPopupUpdated(tabId, entry.openerTabId) === true ) {
entry.destroy();
} else {
entry.launchSelfDestruction();
}
}
};
vAPI.tabs.onPopupCreated = function(targetTabId, openerTabId) {
var popup = popupCandidates[targetTabId];
if ( popup !== undefined ) {
return;
}
popupCandidates[targetTabId] = new PopupCandidate(targetTabId, openerTabId);
popupCandidateTest(targetTabId);
};
var gcPeriod = 10 * 60 * 1000; var gcPeriod = 10 * 60 * 1000;
// A pushed entry is removed from the stack unless it is committed with // A pushed entry is removed from the stack unless it is committed with
@ -253,34 +300,11 @@ housekeep itself.
} }
this.stack.push(new StackEntry(url)); this.stack.push(new StackEntry(url));
this.update(); this.update();
popupCandidateTest(this.tabId);
if ( this.commitTimer !== null ) { if ( this.commitTimer !== null ) {
clearTimeout(this.commitTimer); clearTimeout(this.commitTimer);
} }
this.commitTimer = vAPI.setTimeout(this.onCommit.bind(this), 1000); this.commitTimer = vAPI.setTimeout(this.onCommit.bind(this), 500);
};
// Called when a former push is a false positive:
// https://github.com/chrisaljoudi/uBlock/issues/516
TabContext.prototype.unpush = function(url) {
if ( vAPI.isBehindTheSceneTabId(this.tabId) ) {
return;
}
// We are not going to unpush if there is no other candidate, the
// point of unpush is to make space for a better candidate.
var i = this.stack.length;
if ( i === 1 ) {
return;
}
while ( i-- ) {
if ( this.stack[i].url !== url ) {
continue;
}
this.stack.splice(i, 1);
if ( i === this.stack.length ) {
this.update();
}
return;
}
}; };
// This tells that the url is definitely the one to be associated with the // This tells that the url is definitely the one to be associated with the
@ -368,13 +392,6 @@ housekeep itself.
return entry; return entry;
}; };
var unpush = function(tabId, url) {
var entry = tabContexts[tabId];
if ( entry !== undefined ) {
entry.unpush(url);
}
};
var exists = function(tabId) { var exists = function(tabId) {
return tabContexts[tabId] !== undefined; return tabContexts[tabId] !== undefined;
}; };
@ -407,7 +424,6 @@ housekeep itself.
return { return {
push: push, push: push,
unpush: unpush,
commit: commit, commit: commit,
lookup: lookup, lookup: lookup,
exists: exists, exists: exists,
@ -425,6 +441,7 @@ vAPI.tabs.onNavigation = function(details) {
if ( details.frameId !== 0 ) { if ( details.frameId !== 0 ) {
return; return;
} }
var tabContext = µb.tabContextManager.commit(details.tabId, details.url); var tabContext = µb.tabContextManager.commit(details.tabId, details.url);
var pageStore = µb.bindTabToPageStats(details.tabId, 'afterNavigate'); var pageStore = µb.bindTabToPageStats(details.tabId, 'afterNavigate');
@ -453,6 +470,7 @@ vAPI.tabs.onUpdated = function(tabId, changeInfo, tab) {
if ( !changeInfo.url ) { if ( !changeInfo.url ) {
return; return;
} }
µb.tabContextManager.commit(tabId, changeInfo.url); µb.tabContextManager.commit(tabId, changeInfo.url);
µb.bindTabToPageStats(tabId, 'tabUpdated'); µb.bindTabToPageStats(tabId, 'tabUpdated');
}; };
@ -490,14 +508,14 @@ vAPI.tabs.onClosed = function(tabId) {
// c: close opener // c: close opener
// d: close target // d: close target
vAPI.tabs.onPopup = (function() { vAPI.tabs.onPopupUpdated = (function() {
//console.debug('vAPI.tabs.onPopup: details = %o', details); //console.debug('vAPI.tabs.onPopup: details = %o', details);
// The same context object will be reused everytime. This also allows to // The same context object will be reused everytime. This also allows to
// remember whether a popup or popunder was matched. // remember whether a popup or popunder was matched.
var context = {}; var context = {};
var popupMatch = function(openerURL, targetURL, clickedURL) { var popupMatch = function(openerURL, targetURL, clickedURL, popunder) {
var openerHostname = µb.URI.hostnameFromURI(openerURL); var openerHostname = µb.URI.hostnameFromURI(openerURL);
var openerDomain = µb.URI.domainFromHostname(openerHostname); var openerDomain = µb.URI.domainFromHostname(openerHostname);
@ -514,6 +532,7 @@ vAPI.tabs.onPopup = (function() {
if ( openerHostname !== '' ) { if ( openerHostname !== '' ) {
// Check user switch first // Check user switch first
if ( if (
popunder !== true &&
targetURL !== clickedURL && targetURL !== clickedURL &&
µb.hnSwitches.evaluateZ('no-popups', openerHostname) µb.hnSwitches.evaluateZ('no-popups', openerHostname)
) { ) {
@ -554,14 +573,25 @@ vAPI.tabs.onPopup = (function() {
return ''; return '';
}; };
return function(details) { return function(targetTabId, openerTabId) {
var tabContext = µb.tabContextManager.lookup(details.openerTabId); // Opener details.
var tabContext = µb.tabContextManager.lookup(openerTabId);
var openerURL = ''; var openerURL = '';
if ( tabContext.tabId === details.openerTabId ) { if ( tabContext.tabId === openerTabId ) {
openerURL = tabContext.normalURL; openerURL = tabContext.rawURL;
if ( openerURL === '' ) {
return;
}
} }
if ( openerURL === '' ) {
return; // Popup details.
tabContext = µb.tabContextManager.lookup(targetTabId);
var targetURL = '';
if ( tabContext.tabId === targetTabId ) {
targetURL = tabContext.rawURL;
if ( targetURL === '' ) {
return;
}
} }
// https://github.com/gorhill/uBlock/issues/341 // https://github.com/gorhill/uBlock/issues/341
@ -570,8 +600,6 @@ vAPI.tabs.onPopup = (function() {
return; return;
} }
var targetURL = details.targetURL;
// If the page URL is that of our "blocked page" URL, extract the URL of // If the page URL is that of our "blocked page" URL, extract the URL of
// the page which was blocked. // the page which was blocked.
if ( targetURL.lastIndexOf(vAPI.getURL('document-blocked.html'), 0) === 0 ) { if ( targetURL.lastIndexOf(vAPI.getURL('document-blocked.html'), 0) === 0 ) {
@ -582,15 +610,12 @@ vAPI.tabs.onPopup = (function() {
} }
// Popup test. // Popup test.
var openerTabId = details.openerTabId;
var targetTabId = details.targetTabId;
var result = popupMatch(openerURL, targetURL, µb.mouseURL); var result = popupMatch(openerURL, targetURL, µb.mouseURL);
// Popunder test. // Popunder test.
if ( result === '' ) { if ( result === '' ) {
openerTabId = details.targetTabId; var tmp = openerTabId; openerTabId = targetTabId; targetTabId = tmp;
targetTabId = details.openerTabId; result = popupMatch(targetURL, openerURL, µb.mouseURL, true);
result = popupMatch(targetURL, openerURL, µb.mouseURL);
} }
// Log only for when there was a hit against an actual filter (allow or block). // Log only for when there was a hit against an actual filter (allow or block).