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:
parent
5f304b6751
commit
4fd71d4209
@ -157,7 +157,6 @@ var toChromiumTabId = function(tabId) {
|
||||
|
||||
vAPI.tabs.registerListeners = function() {
|
||||
var onNavigationClient = this.onNavigation || noopFunc;
|
||||
var onPopupClient = this.onPopup || noopFunc;
|
||||
var onUpdatedClient = this.onUpdated || noopFunc;
|
||||
|
||||
// https://developer.chrome.com/extensions/webNavigation
|
||||
@ -167,58 +166,6 @@ vAPI.tabs.registerListeners = function() {
|
||||
// onDOMContentLoaded ->
|
||||
// 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
|
||||
// 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
|
||||
@ -233,22 +180,18 @@ vAPI.tabs.registerListeners = function() {
|
||||
details.frameId = 0;
|
||||
onNavigationClient(details);
|
||||
}
|
||||
popupCandidateCreate(details);
|
||||
popupCandidateTest(details);
|
||||
if ( typeof vAPI.tabs.onPopupCreated === 'function' ) {
|
||||
vAPI.tabs.onPopupCreated(details.tabId.toString(), details.sourceTabId.toString());
|
||||
}
|
||||
};
|
||||
|
||||
var onBeforeNavigate = function(details) {
|
||||
if ( details.frameId !== 0 ) {
|
||||
return;
|
||||
}
|
||||
//console.debug('onBeforeNavigate: popup candidate tab id %d = "%s"', details.tabId, details.url);
|
||||
popupCandidateTest(details);
|
||||
};
|
||||
|
||||
var onUpdated = function(tabId, changeInfo, tab) {
|
||||
if ( changeInfo.url && popupCandidateTest({ tabId: tabId, url: changeInfo.url }) ) {
|
||||
return;
|
||||
}
|
||||
onUpdatedClient(tabId, changeInfo, tab);
|
||||
};
|
||||
|
||||
@ -257,11 +200,6 @@ vAPI.tabs.registerListeners = function() {
|
||||
return;
|
||||
}
|
||||
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);
|
||||
|
@ -90,6 +90,7 @@ var contentObserver = {
|
||||
SUB_FRAME: Ci.nsIContentPolicy.TYPE_SUBDOCUMENT,
|
||||
contentBaseURI: 'chrome://' + hostName + '/content/js/',
|
||||
cpMessageName: hostName + ':shouldLoad',
|
||||
popupMessageName: hostName + ':shouldLoadPopup',
|
||||
ignoredPopups: new WeakMap(),
|
||||
uniqueSandboxId: 1,
|
||||
|
||||
@ -153,6 +154,38 @@ var contentObserver = {
|
||||
.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
|
||||
shouldLoad: function(type, location, origin, context) {
|
||||
// For whatever reason, sometimes the global scope is completely
|
||||
@ -170,26 +203,16 @@ var contentObserver = {
|
||||
return this.ACCEPT;
|
||||
}
|
||||
|
||||
if ( type === this.MAIN_FRAME ) {
|
||||
this.handlePopup(location, context);
|
||||
}
|
||||
|
||||
if ( !location.schemeIs('http') && !location.schemeIs('https') ) {
|
||||
return this.ACCEPT;
|
||||
}
|
||||
|
||||
let openerURL = null;
|
||||
|
||||
if ( type === this.MAIN_FRAME ) {
|
||||
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 ) {
|
||||
context = context.contentWindow;
|
||||
} else {
|
||||
@ -219,7 +242,6 @@ var contentObserver = {
|
||||
|
||||
let details = {
|
||||
frameId: isTopLevel ? 0 : this.getFrameId(context),
|
||||
openerURL: openerURL,
|
||||
parentFrameId: parentFrameId,
|
||||
rawtype: type,
|
||||
tabId: '',
|
||||
|
@ -945,7 +945,8 @@ vAPI.tabs._remove = (function() {
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
vAPI.tabs.remove = function(tabId) {
|
||||
vAPI.tabs.remove = (function() {
|
||||
var remove = function(tabId) {
|
||||
var browser = tabWatcher.browserFromTabId(tabId);
|
||||
if ( !browser ) {
|
||||
return;
|
||||
@ -955,7 +956,13 @@ vAPI.tabs.remove = function(tabId) {
|
||||
return;
|
||||
}
|
||||
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 });
|
||||
};
|
||||
|
||||
// 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
|
||||
var onShow = function({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
|
||||
// set *after* the event is fired in such case.
|
||||
if ( tabContainer ) {
|
||||
//tabContainer.addEventListener('TabOpen', onOpen);
|
||||
tabContainer.addEventListener('TabShow', onShow);
|
||||
tabContainer.addEventListener('TabClose', onClose);
|
||||
// when new window is opened TabSelect doesn't run on the selected tab?
|
||||
@ -1293,7 +1288,6 @@ var tabWatcher = (function() {
|
||||
tabContainer = tabBrowser.tabContainer;
|
||||
}
|
||||
if ( tabContainer ) {
|
||||
//tabContainer.removeEventListener('TabOpen', onOpen);
|
||||
tabContainer.removeEventListener('TabShow', onShow);
|
||||
tabContainer.removeEventListener('TabClose', onClose);
|
||||
tabContainer.removeEventListener('TabSelect', onSelect);
|
||||
@ -1847,7 +1841,6 @@ var httpObserver = {
|
||||
this.frameId = 0;
|
||||
this.parentFrameId = 0;
|
||||
this.rawtype = 0;
|
||||
this.sourceTabId = null;
|
||||
this.tabId = 0;
|
||||
this._key = ''; // key is url, from URI.spec
|
||||
},
|
||||
@ -1937,9 +1930,10 @@ var httpObserver = {
|
||||
}
|
||||
aWindow = loadContext.associatedWindow;
|
||||
} catch (ex) {
|
||||
//console.error(ex);
|
||||
//console.error(ex.toString());
|
||||
}
|
||||
}
|
||||
var gBrowser;
|
||||
try {
|
||||
if ( !aWindow && channel.loadGroup && channel.loadGroup.notificationCallbacks ) {
|
||||
aWindow = channel
|
||||
@ -1949,21 +1943,23 @@ var httpObserver = {
|
||||
.associatedWindow;
|
||||
}
|
||||
if ( aWindow ) {
|
||||
return tabWatcher.tabIdFromTarget(
|
||||
aWindow
|
||||
gBrowser = aWindow
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShell)
|
||||
.rootTreeItem
|
||||
.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindow)
|
||||
.gBrowser
|
||||
.getBrowserForContentWindow(aWindow)
|
||||
);
|
||||
.gBrowser;
|
||||
}
|
||||
} catch (ex) {
|
||||
//console.error(ex);
|
||||
//console.error(ex.toString());
|
||||
}
|
||||
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
|
||||
@ -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) {
|
||||
var type = this.typeMap[details.rawtype] || 'other';
|
||||
if ( this.onBeforeRequestTypes && this.onBeforeRequestTypes.has(type) === false ) {
|
||||
@ -2048,7 +2026,7 @@ var httpObserver = {
|
||||
},
|
||||
|
||||
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 ) {
|
||||
return;
|
||||
}
|
||||
@ -2071,8 +2049,8 @@ var httpObserver = {
|
||||
hostname: hostname,
|
||||
parentFrameId: channelData[1],
|
||||
responseHeaders: responseHeaders,
|
||||
tabId: channelData[3],
|
||||
type: this.typeMap[channelData[4]] || 'other',
|
||||
tabId: channelData[2],
|
||||
type: this.typeMap[channelData[3]] || 'other',
|
||||
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?
|
||||
if ( pendingRequest === null ) {
|
||||
pendingRequest = this.synthesizePendingRequest(channel, rawtype);
|
||||
@ -2171,7 +2162,6 @@ var httpObserver = {
|
||||
channel.setProperty(this.REQDATAKEY, [
|
||||
pendingRequest.frameId,
|
||||
pendingRequest.parentFrameId,
|
||||
pendingRequest.sourceTabId,
|
||||
pendingRequest.tabId,
|
||||
pendingRequest.rawtype
|
||||
]);
|
||||
@ -2196,16 +2186,11 @@ var httpObserver = {
|
||||
|
||||
var channelData = oldChannel.getProperty(this.REQDATAKEY);
|
||||
|
||||
if ( this.handlePopup(URI, channelData[3], channelData[2]) ) {
|
||||
result = this.ABORT;
|
||||
return;
|
||||
}
|
||||
|
||||
var details = {
|
||||
frameId: channelData[0],
|
||||
parentFrameId: channelData[1],
|
||||
tabId: channelData[3],
|
||||
rawtype: channelData[4]
|
||||
tabId: channelData[2],
|
||||
rawtype: channelData[3]
|
||||
};
|
||||
|
||||
if ( this.handleRequest(newChannel, URI, details) ) {
|
||||
@ -2250,9 +2235,15 @@ vAPI.net.registerListeners = function() {
|
||||
null;
|
||||
}
|
||||
|
||||
var shouldBlockPopup = function(details) {
|
||||
var sourceTabId = null;
|
||||
var uri;
|
||||
var shouldLoadPopupListenerMessageName = location.host + ':shouldLoadPopup';
|
||||
var shouldLoadPopupListener = function(e) {
|
||||
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() ) {
|
||||
uri = browser.currentURI;
|
||||
@ -2265,25 +2256,23 @@ vAPI.net.registerListeners = function() {
|
||||
// believe this may have to do with those very temporary
|
||||
// browser objects created when opening a new tab, i.e. related
|
||||
// to https://github.com/gorhill/uBlock/issues/212
|
||||
if ( !uri || uri.spec !== details.openerURL ) {
|
||||
if ( !uri || uri.spec !== openerURL ) {
|
||||
continue;
|
||||
}
|
||||
|
||||
sourceTabId = tabWatcher.tabIdFromTarget(browser);
|
||||
if ( sourceTabId === details.tabId ) {
|
||||
sourceTabId = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
uri = Services.io.newURI(details.url, null, null);
|
||||
|
||||
httpObserver.handlePopup(uri, details.tabId, sourceTabId);
|
||||
openerTabId = tabWatcher.tabIdFromTarget(browser);
|
||||
if ( openerTabId !== popupTabId ) {
|
||||
vAPI.tabs.onPopupCreated(popupTabId, openerTabId);
|
||||
break;
|
||||
}
|
||||
|
||||
return sourceTabId;
|
||||
}
|
||||
};
|
||||
|
||||
vAPI.messaging.globalMessageManager.addMessageListener(
|
||||
shouldLoadPopupListenerMessageName,
|
||||
shouldLoadPopupListener
|
||||
);
|
||||
|
||||
var shouldLoadListenerMessageName = location.host + ':shouldLoad';
|
||||
var shouldLoadListener = function(e) {
|
||||
// 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
|
||||
// requests.
|
||||
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
|
||||
// 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.parentFrameId = details.parentFrameId;
|
||||
pendingReq.rawtype = details.rawtype;
|
||||
pendingReq.sourceTabId = sourceTabId;
|
||||
pendingReq.tabId = details.tabId;
|
||||
pendingReq.tabId = tabWatcher.tabIdFromTarget(e.target);
|
||||
};
|
||||
|
||||
vAPI.messaging.globalMessageManager.addMessageListener(
|
||||
@ -2378,6 +2354,11 @@ vAPI.net.registerListeners = function() {
|
||||
httpObserver.register();
|
||||
|
||||
cleanupTasks.push(function() {
|
||||
vAPI.messaging.globalMessageManager.removeMessageListener(
|
||||
shouldLoadPopupListenerMessageName,
|
||||
shouldLoadPopupListener
|
||||
);
|
||||
|
||||
vAPI.messaging.globalMessageManager.removeMessageListener(
|
||||
shouldLoadListenerMessageName,
|
||||
shouldLoadListener
|
||||
@ -2655,7 +2636,7 @@ vAPI.toolbarButton = {
|
||||
|
||||
// palette might take a little longer to appear on some platforms,
|
||||
// give it a small delay and try again.
|
||||
if ( toolbox.palette === null ) {
|
||||
if ( !toolbox.palette ) {
|
||||
vAPI.setTimeout(onReadyStateComplete.bind(null, window, callback, tryCount), 200);
|
||||
return;
|
||||
}
|
||||
@ -2707,7 +2688,7 @@ vAPI.toolbarButton = {
|
||||
var navbar = document.getElementById('nav-bar');
|
||||
var toolbarButton = createToolbarButton(window);
|
||||
|
||||
if ( palette !== null && palette.querySelector('#' + tbb.id) === null ) {
|
||||
if ( palette && palette.querySelector('#' + tbb.id) === null ) {
|
||||
palette.appendChild(toolbarButton);
|
||||
}
|
||||
|
||||
|
119
src/js/tab.js
119
src/js/tab.js
@ -136,6 +136,53 @@ housekeep itself.
|
||||
var mostRecentRootDocURL = '';
|
||||
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;
|
||||
|
||||
// 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.update();
|
||||
popupCandidateTest(this.tabId);
|
||||
if ( this.commitTimer !== null ) {
|
||||
clearTimeout(this.commitTimer);
|
||||
}
|
||||
this.commitTimer = vAPI.setTimeout(this.onCommit.bind(this), 1000);
|
||||
};
|
||||
|
||||
// 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.commitTimer = vAPI.setTimeout(this.onCommit.bind(this), 500);
|
||||
};
|
||||
|
||||
// This tells that the url is definitely the one to be associated with the
|
||||
@ -368,13 +392,6 @@ housekeep itself.
|
||||
return entry;
|
||||
};
|
||||
|
||||
var unpush = function(tabId, url) {
|
||||
var entry = tabContexts[tabId];
|
||||
if ( entry !== undefined ) {
|
||||
entry.unpush(url);
|
||||
}
|
||||
};
|
||||
|
||||
var exists = function(tabId) {
|
||||
return tabContexts[tabId] !== undefined;
|
||||
};
|
||||
@ -407,7 +424,6 @@ housekeep itself.
|
||||
|
||||
return {
|
||||
push: push,
|
||||
unpush: unpush,
|
||||
commit: commit,
|
||||
lookup: lookup,
|
||||
exists: exists,
|
||||
@ -425,6 +441,7 @@ vAPI.tabs.onNavigation = function(details) {
|
||||
if ( details.frameId !== 0 ) {
|
||||
return;
|
||||
}
|
||||
|
||||
var tabContext = µb.tabContextManager.commit(details.tabId, details.url);
|
||||
var pageStore = µb.bindTabToPageStats(details.tabId, 'afterNavigate');
|
||||
|
||||
@ -453,6 +470,7 @@ vAPI.tabs.onUpdated = function(tabId, changeInfo, tab) {
|
||||
if ( !changeInfo.url ) {
|
||||
return;
|
||||
}
|
||||
|
||||
µb.tabContextManager.commit(tabId, changeInfo.url);
|
||||
µb.bindTabToPageStats(tabId, 'tabUpdated');
|
||||
};
|
||||
@ -490,14 +508,14 @@ vAPI.tabs.onClosed = function(tabId) {
|
||||
// c: close opener
|
||||
// d: close target
|
||||
|
||||
vAPI.tabs.onPopup = (function() {
|
||||
vAPI.tabs.onPopupUpdated = (function() {
|
||||
//console.debug('vAPI.tabs.onPopup: details = %o', details);
|
||||
|
||||
// The same context object will be reused everytime. This also allows to
|
||||
// remember whether a popup or popunder was matched.
|
||||
var context = {};
|
||||
|
||||
var popupMatch = function(openerURL, targetURL, clickedURL) {
|
||||
var popupMatch = function(openerURL, targetURL, clickedURL, popunder) {
|
||||
var openerHostname = µb.URI.hostnameFromURI(openerURL);
|
||||
var openerDomain = µb.URI.domainFromHostname(openerHostname);
|
||||
|
||||
@ -514,6 +532,7 @@ vAPI.tabs.onPopup = (function() {
|
||||
if ( openerHostname !== '' ) {
|
||||
// Check user switch first
|
||||
if (
|
||||
popunder !== true &&
|
||||
targetURL !== clickedURL &&
|
||||
µb.hnSwitches.evaluateZ('no-popups', openerHostname)
|
||||
) {
|
||||
@ -554,15 +573,26 @@ vAPI.tabs.onPopup = (function() {
|
||||
return '';
|
||||
};
|
||||
|
||||
return function(details) {
|
||||
var tabContext = µb.tabContextManager.lookup(details.openerTabId);
|
||||
return function(targetTabId, openerTabId) {
|
||||
// Opener details.
|
||||
var tabContext = µb.tabContextManager.lookup(openerTabId);
|
||||
var openerURL = '';
|
||||
if ( tabContext.tabId === details.openerTabId ) {
|
||||
openerURL = tabContext.normalURL;
|
||||
}
|
||||
if ( tabContext.tabId === openerTabId ) {
|
||||
openerURL = tabContext.rawURL;
|
||||
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
|
||||
// Allow popups if uBlock is turned off in opener's context.
|
||||
@ -570,8 +600,6 @@ vAPI.tabs.onPopup = (function() {
|
||||
return;
|
||||
}
|
||||
|
||||
var targetURL = details.targetURL;
|
||||
|
||||
// If the page URL is that of our "blocked page" URL, extract the URL of
|
||||
// the page which was blocked.
|
||||
if ( targetURL.lastIndexOf(vAPI.getURL('document-blocked.html'), 0) === 0 ) {
|
||||
@ -582,15 +610,12 @@ vAPI.tabs.onPopup = (function() {
|
||||
}
|
||||
|
||||
// Popup test.
|
||||
var openerTabId = details.openerTabId;
|
||||
var targetTabId = details.targetTabId;
|
||||
var result = popupMatch(openerURL, targetURL, µb.mouseURL);
|
||||
|
||||
// Popunder test.
|
||||
if ( result === '' ) {
|
||||
openerTabId = details.targetTabId;
|
||||
targetTabId = details.openerTabId;
|
||||
result = popupMatch(targetURL, openerURL, µb.mouseURL);
|
||||
var tmp = openerTabId; openerTabId = targetTabId; targetTabId = tmp;
|
||||
result = popupMatch(targetURL, openerURL, µb.mouseURL, true);
|
||||
}
|
||||
|
||||
// Log only for when there was a hit against an actual filter (allow or block).
|
||||
|
Loading…
Reference in New Issue
Block a user