window.open returns undefined in chrome extension

17,789

Solution 1

Note: This answer is obsolete. window.open() in a Chrome extension always returns either null (when the popup is blocked) or a window object. The information below only applies to very old (2012) versions of Chrome.


Content scripts do not have any access to a page's global window object. For content scripts, the following applies:

  • The window variable does not refer to the page's global object. Instead, it refers to a new context, a "layer" over the page. The page's DOM is fully accessible. #execution-environment

Given a document consisting of   <iframe id="frameName" src="http://domain/"></iframe>:

  • Access to the contents of a frame is restricted by the Same origin policy of the page; the permissions of your extension does not relax the policy.
  • frames[0] and frames['frameName'], (normally referring to the the frame's containing global window object) is undefined.
  • var iframe = document.getElementById('frameName');
    • iframe.contentDocument returns a document object of the containing frame, because content scripts have access to the DOM of a page. This property is null when the Same origin policy applies.
    • iframe.contentDocument.defaultView (refers to the window object associated with the document) is undefined.
    • iframe.contentWindow is undefined.

As you can see, window.open() does not return a Window instance (neither does window.opener, and so forth).


Alternatives

  • Inject the code in the page, so that it runs in the context of the page. Note: Only use this method if the page you're operating on can be trusted. To communicate between the injected script and the content script, you could use:

    var login_url = 'http://example.com/';
    var event_name = 'robwuniq' + Math.random().toString(16); // Unique name
    document.addEventListener(event_name, function localName() {
        document.removeEventListener(event_name, localName); // Clean-up
        // Your logic:
        Backbone.history.navigate('index', true);
    });
    // Method 2b: Inject code which runs in the context of the page
    var actualCode = '(' + function(login_url, event_name) {
        var loginWin = window.open(login_url, 'LoginWindow', "width=655,height=490");
        console.log(loginWin);
        // Check every 100 ms if the popup is closed.
        var finishedInterval = setInterval(function() {
            console.log('checking if loginWin closed');
            if (loginWin.closed) {
                clearInterval(finishedInterval);
                console.log('popup is now closed');
                // Notify content script
                var event = document.createEvent('Events');
                event.initEvent(event_name, false, false);
                document.dispatchEvent(event);
            }
        }, 1000);
    } + ')(' + JSON.stringify(login_url+'') + ', "' + event_name + '")';
    var script = document.createElement('script');
    script.textContent = actualCode;
    (document.head||document.documentElement).appendChild(script);
    script.parentNode.removeChild(script);
    
  • Launch the window from the background page using window.open(). This returns a window object which has a reliable closed property. See the next bullet point for more details on the communication flow.

  • From the content script, pass a message to the background page. In the background page, use chrome.windows.create to open a window. In the callback, assign an chrome.tabs.onRemoved and/or chrome.tabs.onUpdated event. When these event listeners are triggered, they should remove themselves, and notify the original caller (content script) using the sendResponse function of chrome.extension.onMessage.

Solution 2

In my case, Chrome was blocking the popup and the user had to unblock by clicking the "blocked popup" icon in the upper-right corner of the window. (They can also enable/disable exceptions under "Content settings..." in Chrome settings.)

I would suggest adding some code after window.open() so that the user knows what to do. For example:

if (!loginWin)
    alert("You must first unblock popups and try again for this to work!");
Share:
17,789
user1566788
Author by

user1566788

Updated on July 21, 2022

Comments

  • user1566788
    user1566788 almost 2 years

    I have content script based Chrome extension. I initiate the sign in process through a popup window in the content script.

    I open a popup window using the below code and then wait till its closed.

    However, I get an 'undefined' from window.open method. Does anybody know why this happens?

    loginwin is undefined in below code although the popup window opens up fine with the specified login_url. The code below is called from my content script.

    var loginWin = window.open(login_url, 'LoginWindow', "width=655,height=490");
    console.log(loginWin);
    // Check every 100 ms if the popup is closed.
    var finishedInterval = setInterval(function() {
        console.log('checking if loginWin closed');
        if (loginWin.closed) {
            clearInterval(finishedInterval);
            console.log('popup is now closed');
            Backbone.history.navigate('index', true);
        }
    }, 1000);