| Index: lib/popupBlocker.js |
| =================================================================== |
| --- a/lib/popupBlocker.js |
| +++ b/lib/popupBlocker.js |
| @@ -25,14 +25,69 @@ |
| let {checkWhitelisted} = require("whitelisting"); |
| let {logRequest} = require("devtools"); |
| -let tabsLoading = {}; |
| +let loadingPopups = Object.create(null); |
| -function checkPotentialPopup(tabId, url, sourcePage, documentHost, specificOnly) |
| +function hasLoadingPopups() |
| { |
| - let urlObj = new URL(url || "about:blank"); |
| + return Object.keys(loadingPopups).length > 0; |
|
Wladimir Palant
2016/02/12 15:43:43
With this being called from onCreatedNavigationTar
Sebastian Noack
2016/02/12 15:52:13
V8 is optimized for usage of Object.keys() like he
|
| +} |
| + |
| +function forgetPopup(tabId) |
| +{ |
| + delete loadingPopups[tabId]; |
| + |
| + if (!hasLoadingPopups()) |
| + { |
| + chrome.webRequest.onBeforeRequest.removeListener(onBeforeRequest); |
| + chrome.webNavigation.onCompleted.removeListener(onCompleted); |
| + chrome.tabs.onRemoved.removeListener(forgetPopup); |
| + } |
| +} |
| + |
| +function getFrame(tabId, processId, frameId, callback) |
| +{ |
| + chrome.webNavigation.getFrame( |
| + { |
| + tabId: tabId, |
| + processId: processId, |
| + frameId: frameId |
| + }, |
| + details => |
| + { |
| + let {url, parentFrameId} = details; |
| + let frame = {url: new URL(url), parent: null}; |
| + |
| + if (parentFrameId != -1) |
| + { |
| + getFrame( |
| + tabId, processId, parentFrameId, |
| + parentFrame => |
| + { |
| + frame.parent = parentFrame; |
| + callback(frame); |
| + } |
| + ); |
| + } |
| + else |
| + { |
| + callback(frame); |
| + } |
| + } |
| + ); |
| +} |
| + |
| +function checkPotentialPopup(tabId, popup) |
| +{ |
| + let urlObj = new URL(popup.url || "about:blank"); |
| let urlString = stringifyURL(urlObj); |
| + let documentHost = extractHostFromFrame(popup.sourceFrame); |
| let thirdParty = isThirdParty(urlObj, documentHost); |
| + let specificOnly = !!checkWhitelisted( |
| + popup.sourcePage, popup.sourceFrame, |
| + RegExpFilter.typeMap.GENERICBLOCK |
| + ); |
| + |
| let filter = defaultMatcher.matchesAny( |
| urlString, RegExpFilter.typeMap.POPUP, |
| documentHost, thirdParty, null, specificOnly |
| @@ -42,48 +97,72 @@ |
| chrome.tabs.remove(tabId); |
| logRequest( |
| - sourcePage, urlString, "POPUP", documentHost, |
| - thirdParty, null, specificOnly, filter |
| + popup.sourcePage, urlString, "POPUP", |
| + documentHost, thirdParty, null, |
| + specificOnly, filter |
| ); |
| } |
| +function onBeforeRequest(details) |
| +{ |
| + let popup = loadingPopups[details.tabId]; |
| + if (popup) |
| + { |
| + popup.url = details.url; |
| + if (popup.sourceFrame) |
| + checkPotentialPopup(details.tabId, popup); |
| + } |
| +} |
| + |
| +function onCompleted(details) |
| +{ |
| + if (details.frameId == 0 && details.url != "about:blank") |
| + forgetPopup(details.tabId); |
|
Wladimir Palant
2016/02/12 15:43:43
Will this cause a memory leak if a website opens a
Sebastian Noack
2016/02/12 15:52:13
Yes, just as with the previous implementation. But
|
| +} |
| + |
| +function onSourceFrameRetrieved(tabId, frame) |
| +{ |
| + let popup = loadingPopups[tabId]; |
| + if (popup) |
| + { |
| + if (checkWhitelisted(popup.sourcePage, frame)) |
| + { |
| + forgetPopup(tabId); |
| + } |
| + else |
| + { |
| + popup.sourceFrame = frame; |
| + checkPotentialPopup(tabId, popup); |
| + } |
| + } |
| +} |
| + |
| chrome.webNavigation.onCreatedNavigationTarget.addListener(details => |
| { |
| - let sourcePage = new ext.Page({id: details.sourceTabId}); |
| - let sourceFrame = ext.getFrame(details.sourceTabId, details.sourceFrameId); |
| + if (!hasLoadingPopups()) |
| + { |
| + chrome.webRequest.onBeforeRequest.addListener( |
| + onBeforeRequest, |
| + { |
| + urls: ["<all_urls>"], |
| + types: ["main_frame"] |
| + } |
| + ); |
| - if (checkWhitelisted(sourcePage, sourceFrame)) |
| - return; |
| - |
| - let documentHost = extractHostFromFrame(sourceFrame); |
| - if (!documentHost) |
| - return; |
| - |
| - let specificOnly = !!checkWhitelisted(sourcePage, sourceFrame, |
| - RegExpFilter.typeMap.GENERICBLOCK); |
| - |
| - tabsLoading[details.tabId] = { |
| - page: sourcePage, |
| - documentHost: documentHost, |
| - specificOnly: specificOnly |
| - }; |
| - checkPotentialPopup(details.tabId, details.url, sourcePage, |
| - documentHost, specificOnly); |
| -}); |
| - |
| -chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => |
| -{ |
| - if (!(tabId in tabsLoading)) |
| - return; |
| - |
| - if ("url" in changeInfo) |
| - { |
| - let source = tabsLoading[tabId]; |
| - checkPotentialPopup(tabId, tab.url, source.page, |
| - source.documentHost, |
| - source.specificOnly); |
| + chrome.webNavigation.onCompleted.addListener(onCompleted); |
| + chrome.tabs.onRemoved.addListener(forgetPopup); |
| } |
| - if ("status" in changeInfo && changeInfo.status == "complete" && tab.url != "about:blank") |
| - delete tabsLoading[tabId]; |
| + loadingPopups[details.tabId] = { |
| + url: details.url, |
| + sourcePage: new ext.Page({id: details.sourceTabId}), |
| + sourceFrame: null |
| + }; |
| + |
| + getFrame( |
| + details.sourceTabId, |
| + details.sourceProcessId, |
| + details.sourceFrameId, |
| + onSourceFrameRetrieved.bind(null, details.tabId) |
| + ); |
| }); |