Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I check which tab opened a (popup) window?

The problem

I've had this issue for months now, but the concept is pretty straightforward: I want to block some Malicious Site™ from programmatically opening tabs or popup windows.

With the chrome.tabs API, I can listen with onCreated when a new tab is created, and I can easily check who (i.e. which tab) opened that particular tab accessing the openerTabId property of the Tab object passed to the callback function.

Now, I would like to do the exact same thing when a new window is created: I would like to know which tab opened the window (if any, because it could have been opened by the user too), check its URL to see if it is the Malicious Site™, and act accordingly (i.e. block the popup). I tried doing it in the exact same way: request the array of tabs in the new window and check their openerTabId property, but unfortunately such property is not defined! I searched the documentation and Googled for hours, but sadly it looks like there's no simple way to check who opened a window.

A very clumsy solution

Stated the above, the only way I was able to do something even remotely close to what I really want, is the following:

  1. Every time a new window is created, its ID is added to an array called windowWatchlist.
  2. Every time a tab is updated (NB: updated, not created), a script is injected inside it to check its document.referrer, which should contain the URL of the site which opened the tab: if the referrer URL contains the address of the Malicious Site™ I want to block popups from, the window is then closed and removed from the windowWatchlist.
  3. Every time a window is closed, if its ID is in the windowWatchlist, it gets removed from it.

Here's the code (which runs in my background.js script):

// Called on chrome.windows.onCreated
function watchPopupWindow(window) {
    windowWatchlist.push(window.id);
    console.log('Added window #' + window.id + ' to watchlist.');
}

// Called on chrome.windows.onRemoved
function unwatchPopupWindow(windowID) {
    var index = windowWatchlist.indexOf(windowID);

    // If the windowID is in the watchlist:
    if (index != -1) {
        // Remove it:
        windowWatchlist.splice(index, 1);
        console.log('Removed window #' + windowID + ' from watchlist.');
    }
}

// Called on chrome.tabs.onUpdated
function blockPopupWindow(tabID, info, tab) {
    // If this tab is in a window which is in the watchlist:
    if (windowWatchlist.indexOf(tab.windowId) != -1 && info.url && info.url != 'about:blank') {
        // Check the referrer of this tab:
        chrome.tabs.executeScript(tabID, {code: 'document.referrer;'}, function(ref) {
            // If the referrer is the malicious site to block:
            if (ref && ref[0] && ref[0].indexOf("http://MALICIOUS-SITE.XXX") != -1) {
                // Close the popup window:
                chrome.windows.remove(tab.windowId, function() {
                    console.log('Blocked popup window #' + tab.windowId + '.');

                    if (chrome.runtime.lastError)
                        console.error(chrome.runtime.lastError.message);
                });;
            }
        });
    }
}

var windowWatchlist = [];

chrome.windows.onCreated.addListener(watchPopupWindow, {windowTypes: ['popup']});
chrome.windows.onRemoved.addListener(unwatchPopupWindow, {windowTypes: ['popup']});
chrome.tabs.onUpdated.addListener(blockPopupWindow);

Now, you may be wondering: why do you need all this mess only to check a referrer? Couldn't you just check the tabs contained in the window when the window is opened and check their referrer directly inside the callback of chrome.window.onCreated? That's a clever question, and the answer is simple: the problem is that I cannot check the referrer of the tabs right when they are created, because they almost always need some time to load, and the referrer isn't loaded until the page starts loading inside the tab. Therefore, I need to check when a tab is updated, see if its window is in my watchlist, and then check its referrer. This is why chrome.tabs.onUpdated is needed, since it fires its listeners whenever a tab changes state (e.g. tab.status changes from "loading" to "complete").

Why this solution doesn't work

The reason why I call this solution "clumsy" and the reason why it doesn't really work should be already clear to anyone with some experience of JavaScript and web developing: document.referrer isn't reliable at all, and is very often undefined or (in case of multiple redirects) not the right one. This makes my script fail about 90% of the times, because it is unable to determine whether the popup window was opened by the Malicious Site™ or not.

Moreover, the Malicious Site™ often opens popups with URL about:blank or no URL at all, and only when they are loaded, injects data into them, making them basically impossible to detect, even with chrome.tabs.onUpdated which doesn't fire any listener in this situation.

I could decide to block any popup with URL about:blank or undefined, and this is what I'm doing right now indeed, but is a pretty bad compromise, since that I end up closing popups opened by any site which uses this method, and not only the Malicious Site™ I want to block.

In conclusion

My question is simple, but I don't know about its solution: does anyone know any other more reliable method which could be used to detect which tab opened a new window? Nothing comes to my mind, maybe something could be possible using the chrome.webRequest API? I don't really know. For months I've been accepting the fact that a simple solution just wasn't possible, and helplessly waited for an update or something, but I never actually thought about asking here, because the problem looked above the competence of an average Chrome Extension programmer, but hopefully I was wrong.


UPDATE: The solution to inject a script inside the site and replace the window.open function with something else isn't viable: if an <iframe> is loaded without a src attribute, but with an already written DOM inside the srcdoc attribute, Chrome will not execute a content script inside it, even if the call to chrome.tabs.executeScript is made with allFrames: true, and even if the content script is declared inside the extension's manifest.

like image 879
Marco Bonelli Avatar asked Sep 25 '17 13:09

Marco Bonelli


People also ask

How do I know if a popup window is closed?

Here is the code: var win = window. open('http://www.google.com', 'google','width=800,height=600,status=0,toolbar=0'); var timer = setInterval(function() { if(win. closed) { clearInterval(timer); alert('closed'); } }, 1000);

How to write popup window in JavaScript?

var newWindow = window. open("", null, "height=200,width=400,status=yes,toolbar=no,menubar=no,location=no"); newWindow. document. write("<select>"); newWindow.


1 Answers

I came across the same problem and found the webNavigation.onCreatedNavigationTarget event that yields the source tab/frame id when a new window is opened.

Solution found from this post: Is it possible to determine a tab's opener within a Google Chrome extension?

like image 132
MRousse Avatar answered Sep 20 '22 20:09

MRousse