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() {
|
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);
|
||||||
|
@ -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: '',
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
121
src/js/tab.js
121
src/js/tab.js
@ -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).
|
||||||
|
Loading…
Reference in New Issue
Block a user