Detecting whether a newly-opened window will navigate away from about:blank

50 views
Skip to first unread message

Henri Sivonen

unread,
Mar 19, 2010, 6:45:46 AM3/19/10
to dev-pl...@lists.mozilla.org
Context:
https://bugzilla.mozilla.org/show_bug.cgi?id=546648

Consider the following in a Mochitest:
otherWindow = window.open("data:text/html,...no external scripts here", "_blank", ...);
SimpleTest.waitForFocus(doStuffWithOtherWindowsDOM, otherWindow);

It turns out that the above pattern is unsafe. waitForFocus is supposed to make it safe. However, when waitForFocus checks whether it needs to wait for the load event in the argument window, it finds an about:blank document in readyState "complete" and decides--due to readyState "complete"--not to wait for the load event. (Unlike with an iframe window object in an analogous situation, the document in the new window is not nsDocShell::EnsureContentViewer-created. Instead, the document has been created by parsing the contents of an about:blank channel synchronously!) What has made the above pattern previously safe has been that also data: URLs that don't have external scripts have also been parser synchronously. Thus, the failure wait for the load event hasn't lead to a test case failure, because the DOM for the data: URL document has been there immediately.

The HTML5 parser parses data: URLs asynchronously--like http: URLs (because it would be silly to introduce another code path merely for Mochitest compatibility). This exposes the failure of waitForFocus to actually wait for a load event. Making waitForFocus check if the document in the window is about:blank makes other test cases fall over, so that's not an ideal fix. (Note that about:blank itself still goes through the old parser for the time being even if the HTML5 parser is enabled in order to make about:blank parse synchronously.)

Is there a way (that waitForFocus could use) to ask a window object if it has been scheduled to navigate to another URL?

P.S. What mechanism prevents the initial channel-parsed about:blank from firing a load event? Why are top-level docshells different from nested docshells in the sense that top level docshells have a channel-parsed about:blank and nested docshells have a docshell-lazily-generated about:blank initially?

--
Henri Sivonen
hsiv...@iki.fi
http://hsivonen.iki.fi/

Henri Sivonen

unread,
Mar 19, 2010, 10:23:13 AM3/19/10
to dev-pl...@lists.mozilla.org
On Mar 19, 2010, at 12:45, Henri Sivonen wrote:

> Why are top-level docshells different from nested docshells in the sense that top level docshells have a channel-parsed about:blank and nested docshells have a docshell-lazily-generated about:blank initially?

It turns out that windows opened by window.open() from Web content behave like iframes in Web content (docshell-created about:blank with readyState uninitialized). However, the XUL-opened window in the test case behaves differently.

Neil

unread,
Mar 19, 2010, 6:18:55 PM3/19/10
to
Henri Sivonen wrote:

>P.S. What mechanism prevents the initial channel-parsed about:blank from firing a load event?
>

You mean on a brand new browser window? I think they do fire a load
event, but it happens before the chrome window's load event so that
nobody cares, unless they capture it specifically.

--
Warning: May contain traces of nuts.

Boris Zbarsky

unread,
Mar 21, 2010, 2:11:51 PM3/21/10
to
On 3/19/10 6:45 AM, Henri Sivonen wrote:
> It turns out that the above pattern is unsafe. waitForFocus is
> supposed to make it safe. However, when waitForFocus checks whether
> it needs to wait for the load event in the argument window, it finds
> an about:blank document in readyState "complete" and decides--due to
> readyState "complete"--not to wait for the load event.

Sounds like either waitForFocus is buggy or is being misused. Given the
readyState use, sounds like waitForFocus is buggy, if it's expected to
wait for any pending loads. It's not clear to me that it is.

> (Unlike with an iframe window object in an analogous situation, the document in
> the new window is not nsDocShell::EnsureContentViewer-created.
> Instead, the document has been created by parsing the contents of an
> about:blank channel synchronously!)

It's only synchronous in the sense that sync XHR is: when creating a new
toplevel XUL window, the event loop spins until the onload for that
window fires; this naturally includes loads in subframes, which includes
the about:blank in the one default tab.

This code pattern:

otherWindow = window.open("data:text/html,...", "_blank", ...);

Performs the following steps:

1) Opens a new XUL window (which contains an iframe pointing to
about:blank).
2) Waits for the onload of that XUL window to fire.
3) Starts the data: load in the iframe.
4) Returns.

> Is there a way (that waitForFocus could use) to ask a window object
> if it has been scheduled to navigate to another URL?

It's really not clear to me that waitForFocus is the right tool here.
In this case, you want an explicit load listener, since you know there
is no way the data: load could have completed yet.

If we really want to make waitForFocus handle this, it needs to examine
loadgroup activity or something.

> P.S. What mechanism prevents the initial channel-parsed about:blank
> from firing a load event?

Nothing that I know of. Where do you see it failing to do so?

> Why are top-level docshells different from
> nested docshells in the sense that top level docshells have a
> channel-parsed about:blank and nested docshells have a
> docshell-lazily-generated about:blank initially?

I think I covered this above; this is not about toplevel vs subframe but
about the XUL window-opening mechanism.

-Boris

Henri Sivonen

unread,
Mar 22, 2010, 3:23:56 AM3/22/10
to dev-pl...@lists.mozilla.org
On Mar 21, 2010, at 20:11, Boris Zbarsky wrote:

> On 3/19/10 6:45 AM, Henri Sivonen wrote:
>> It turns out that the above pattern is unsafe. waitForFocus is
>> supposed to make it safe. However, when waitForFocus checks whether
>> it needs to wait for the load event in the argument window, it finds
>> an about:blank document in readyState "complete" and decides--due to
>> readyState "complete"--not to wait for the load event.
>
> Sounds like either waitForFocus is buggy or is being misused. Given the readyState use, sounds like waitForFocus is buggy, if it's expected to wait for any pending loads. It's not clear to me that it is.

Given the behavior of XUL window opening, waitForFocus in definitely buggy. More holistically, one might alternatively consider the difference in XUL and HTML window.open behavior to be the bug.

Isn't readyState supposed to be "complete" only when all the subloads are complete, too, though?

>> (Unlike with an iframe window object in an analogous situation, the document in
>> the new window is not nsDocShell::EnsureContentViewer-created.
>> Instead, the document has been created by parsing the contents of an
>> about:blank channel synchronously!)
>
> It's only synchronous in the sense that sync XHR is: when creating a new toplevel XUL window, the event loop spins until the onload for that window fires; this naturally includes loads in subframes, which includes the about:blank in the one default tab.

Interesting. It didn't even occur to me that it would spin the event loop.

> This code pattern:
>
> otherWindow = window.open("data:text/html,...", "_blank", ...);
>
> Performs the following steps:
>
> 1) Opens a new XUL window (which contains an iframe pointing to
> about:blank).
> 2) Waits for the onload of that XUL window to fire.
> 3) Starts the data: load in the iframe.
> 4) Returns.

Evidently, starting the data: load doesn't go so far that it'd make the document in the window have document.documentURI set to the data: URL.

>> Is there a way (that waitForFocus could use) to ask a window object
>> if it has been scheduled to navigate to another URL?
>
> It's really not clear to me that waitForFocus is the right tool here. In this case, you want an explicit load listener, since you know there is no way the data: load could have completed yet.

Using an explicit load listener was my reaction, too, but the patch didn't pass review.

> If we really want to make waitForFocus handle this, it needs to examine loadgroup activity or something.

Is this something that's available to privileged JS?

>> P.S. What mechanism prevents the initial channel-parsed about:blank
>> from firing a load event?
>
> Nothing that I know of. Where do you see it failing to do so?

Since the event loop is spun, maybe I'm not seeing what I thought I was seeing.

Boris Zbarsky

unread,
Mar 22, 2010, 12:01:10 PM3/22/10
to
On 3/22/10 3:23 AM, Henri Sivonen wrote:
> Given the behavior of XUL window opening, waitForFocus in definitely buggy. More holistically, one might alternatively consider the difference in XUL and HTML window.open behavior to be the bug.

There is no such difference, to my knowledge. If you do a window.open()
from HTML, you will get the same behavior: the open() call returns after
the about:blank has fully loaded and right after starting the load of
the actual URI passed in, if any.

At least that's what will happen if an actual new window opens. If a
new tab opens (which may be what you end up with in Firefox if you're
not careful to size the window or use one of the features that disables
opening in a tab), we will return from the open() call before the
about:blank load in that new tab completes. Arguably a tabbrowser bug,
that.

I have considered (and I think you have tried) making subframes not
create an about:blank load at all by default, which would also give
consistent behavior: no in-flight about:blank load when open() returns.

> Isn't readyState supposed to be "complete" only when all the subloads are complete, too, though?

Yes, but it's a property of the _document_. In this case the document
currently in the window is fully loaded. There is a new load that has
been kicked off in that window, but it hasn't gotten as far as actually
creating a new document yet, so the previous, fully-loaded, document is
being examined.

>> 1) Opens a new XUL window (which contains an iframe pointing to
>> about:blank).
>> 2) Waits for the onload of that XUL window to fire.
>> 3) Starts the data: load in the iframe.
>> 4) Returns.
>
> Evidently, starting the data: load doesn't go so far that it'd make the document in the window have document.documentURI set to the data: URL.

That's correct. "Starts the data: load" is effectively the same as
making a |window.location.href = "data:..."| call in this case. It just
does an asyncOpen on the channel and returns immediately.

> Using an explicit load listener was my reaction, too, but the patch didn't pass review.

Yeah, I saw. I think the reviewer was wrong....

>> If we really want to make waitForFocus handle this, it needs to examine loadgroup activity or something.
>
> Is this something that's available to privileged JS?

Yes.

-Boris

johnjbarton

unread,
Mar 22, 2010, 12:23:23 PM3/22/10
to
On 3/19/2010 3:45 AM, Henri Sivonen wrote:
> Context:
> https://bugzilla.mozilla.org/show_bug.cgi?id=546648
>
> Consider the following in a Mochitest:
> otherWindow = window.open("data:text/html,...no external scripts here", "_blank", ...);
> SimpleTest.waitForFocus(doStuffWithOtherWindowsDOM, otherWindow);

I guess Bug 549539 provides an alternative. You can watch for
"content-document-global-created" and wait until you see the observe
data == URL you want to examine.

jjb

Boris Zbarsky

unread,
Mar 22, 2010, 12:25:50 PM3/22/10
to

And call waitForFocus at that point, yes.

-Boris

Henri Sivonen

unread,
Mar 23, 2010, 8:51:23 AM3/23/10
to dev-pl...@lists.mozilla.org
"Boris Zbarsky" <bzba...@mit.edu> wrote:

> On 3/22/10 3:23 AM, Henri Sivonen wrote:
>> Given the behavior of XUL window opening, waitForFocus in definitely
>> buggy. More holistically, one might alternatively consider the
>> difference in XUL and HTML window.open behavior to be the bug.
>
> There is no such difference, to my knowledge. If you do a
> window.open() from HTML, you will get the same behavior: the open() call returns
> after the about:blank has fully loaded and right after starting the load of
> the actual URI passed in, if any.
>
> At least that's what will happen if an actual new window opens. If a
> new tab opens (which may be what you end up with in Firefox if you're
> not careful to size the window or use one of the features that
> disables
> opening in a tab), we will return from the open() call before the
> about:blank load in that new tab completes. Arguably a tabbrowser
> bug, that.

Indeed, window.open() from HTML gets a parser-created about:blank if window.open() is set to open windows and gets a docshell-created about:blank if window.open() is set to open tabs.

I had window.open() set to open tabs when I tested.

Reply all
Reply to author
Forward
0 new messages