7

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);
user1566788
  • 585
  • 2
  • 6
  • 14

2 Answers2

6

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.
Community
  • 1
  • 1
Rob W
  • 341,306
  • 83
  • 791
  • 678
  • @Rob...Thanks a lot for your detailed response. I have not tried your solution yet. I will try it during this weekend and mark your answer as accepted. – user1566788 Aug 06 '12 at 15:16
  • Thanks Rob, it works fine. This is the approach I followed, content_script sends a message to background script. Background scripts opens up a window and then waits for it close. The server then redirects the user to a login success page. The login success page closes the window. Once it closes, a message is sent back to content script. One thing I could not figure out was how would I know whether login process went through fine or not? Because user could have closed login window without logging in. I can requery the server for this, but somehow it does not sound neat. – user1566788 Aug 08 '12 at 02:54
  • You could insert a content script in the pop-up using `chrome.extension.executeScript` and `chrome.tabs.onUpdated` (filter: `status==='complete'`). Then, look in the document for an unique node which identifies a logged in status. – Rob W Aug 08 '12 at 09:37
  • Great awnser, just a quick question (which I properly could read up on my self if i read thought all those pages), the reason for bullet 3 is only communication between the background page and the pop-up right?... If your just building a "Print view" over a page there should be no apparent reason to not just stick with solution 2 right?... Is that correct understooed, or is there any other drawbacks to that one? – Jens Sep 27 '12 at 07:56
  • The third bullet point addresses the question's requirement that the window's state (has it been closed?) has to be monitored. If you only want to open a window, use either `window.open();` *or* [`chrome.windows.create`](http://code.google.com/chrome/extensions/windows.html#method-create). – Rob W Sep 27 '12 at 09:25
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!");
ScottyB
  • 2,167
  • 1
  • 30
  • 46