Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Synchronous loading of about:blank

67 views
Skip to first unread message

Henri Sivonen

unread,
Dec 18, 2009, 9:22:12 AM12/18/09
to
I'm looking at a bug where the problem seems to be that about:blank
isn't being loaded synchronously into an iframe upon BindToTree but
should be.

So far I've found out that the start of the load is deferred until the
end of the doc update (which still is "synchronous" as far as
observability from <script> goes), but after examining layers and layers
of URI loading code, I haven't been able to locate what mechanism
normally makes sure that about:blank loads synchronously.

What's the normal mechanism for making about:blank loading synchronous
and what might cause the mechanism to behave accidentally asynchronously?

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

Boris Zbarsky

unread,
Dec 18, 2009, 11:48:02 AM12/18/09
to
On 12/18/09 6:22 AM, Henri Sivonen wrote:
> What's the normal mechanism for making about:blank loading synchronous

It's not "loading" per se. What happens in the sync case is that we
just create the document directly; if there's an existing load it
continues to race with the synthetic document.

The synthetic document is created by
nsDocShell::CreateAboutBlankContentViewer.

> and what might cause the mechanism to behave accidentally asynchronously?

Absolutely nothing that I can think of. I suppose something might cause
it to fail, though...

-Boris

Henri Sivonen

unread,
Dec 21, 2009, 9:49:59 AM12/21/09
to
In article <-Z6dnddj2u_eL7bW...@mozilla.org>,
Boris Zbarsky <bzba...@mit.edu> wrote:

> On 12/18/09 6:22 AM, Henri Sivonen wrote:
> > What's the normal mechanism for making about:blank loading synchronous
>
> It's not "loading" per se. What happens in the sync case is that we
> just create the document directly; if there's an existing load it
> continues to race with the synthetic document.
>
> The synthetic document is created by
> nsDocShell::CreateAboutBlankContentViewer.

Thanks.

This method doesn't run at all when the old parser is used on the test
case. nsContentDLF::CreateBlankDocument doesn't run, either.

This is probably a big part of the bug. :-)

The test case is
http://mxr.mozilla.org/mozilla-central/source/content/html/document/test/
test_bug404320.html

I haven't figured out why the old parser passes the test, nonetheless.

Boris Zbarsky

unread,
Dec 21, 2009, 10:16:27 PM12/21/09
to
On 12/21/09 9:49 AM, Henri Sivonen wrote:
> The test case is
> http://mxr.mozilla.org/mozilla-central/source/content/html/document/test/
> test_bug404320.html
>
> I haven't figured out why the old parser passes the test, nonetheless.

Does the about:blank load that gets kicked off in the iframe just happen
to complete before the script runs? In particular, the expected
sequence of events on that testcase should be something like.... start
the first script loading, start the second script and the stylesheet
preloading, when the first and second scripts load start the iframe load
(when that tag is parsed), then wait for the stylesheet load before
running the test script itself.

Not sure what the order is in the new parser world. Could add printfs
to see, right?

In any case, the contentDocument getter should be calling
CreateAboutBlankContentViewer if there's nothing loaded in the iframe
(via nsDocShell::EnsureContentViewer, called from
nsDocShell::GetInterface for the nsIDOMDocument case, called from
nsGlobalWindow::GetDocument, called from
nsGenericHTMLFrameElement::GetContentDocument). Is that not happening
in the HTML5 case? If so, is there already an mContentViewer in
nsDocShell::EnsureContentViewer?

-Boris

Henri Sivonen

unread,
Jan 4, 2010, 7:37:48 AM1/4/10
to
In article <UZydnYksPN6Rp63W...@mozilla.org>,
Boris Zbarsky <bzba...@mit.edu> wrote:

> On 12/21/09 9:49 AM, Henri Sivonen wrote:
> > The test case is
> > http://mxr.mozilla.org/mozilla-central/source/content/html/document/test/
> > test_bug404320.html
> >
> > I haven't figured out why the old parser passes the test, nonetheless.
>
> Does the about:blank load that gets kicked off in the iframe just happen
> to complete before the script runs?

This seems to be the case. In particular, OnStop for the about:blank
stream causes synchronous tree building in the old parser. The document
is short enough that the sink doesn't interrupt.

With the HTML5 parser, no stream event causes synchronous tree building:
there's always another trip through the event loop.

(I didn't research this all the way, because the above observation seems
to be enough to explain why the HTML5 code path doesn't work.)

> In any case, the contentDocument getter should be calling
> CreateAboutBlankContentViewer if there's nothing loaded in the iframe
> (via nsDocShell::EnsureContentViewer, called from
> nsDocShell::GetInterface for the nsIDOMDocument case, called from
> nsGlobalWindow::GetDocument, called from
> nsGenericHTMLFrameElement::GetContentDocument). Is that not happening
> in the HTML5 case? If so, is there already an mContentViewer in
> nsDocShell::EnsureContentViewer?

There's already an mContentViewer each time
nsDocShell::EnsureContentViewer() gets called during the execution of
the test case with the HTML5 parser.

I think the safest fix is to special-case about:blank in the frame
loader so that it doesn't reach the normal asynchronous load path but
instead causes a deliberate call to nsDocShell::EnsureContentViewer()
and returns early.

Boris Zbarsky

unread,
Jan 4, 2010, 11:21:53 AM1/4/10
to
On 1/4/10 7:37 AM, Henri Sivonen wrote:
> This seems to be the case. In particular, OnStop for the about:blank
> stream causes synchronous tree building in the old parser.

Er... it does? Why?

> The document is short enough that the sink doesn't interrupt.

I'm not sure I follow. If the sink doesn't interrupt, how do we get an
OnStop for the about:blank? That event can only come from the event loop...

Or do you mean that when the OnStop happens for the about:blank we
immediately create the entire about:blank DOM, including in particular
the <body> for the about:blank document?

> With the HTML5 parser, no stream event causes synchronous tree building:
> there's always another trip through the event loop.

OK, I see.

> There's already an mContentViewer each time
> nsDocShell::EnsureContentViewer() gets called during the execution of
> the test case with the HTML5 parser.

Ah. So the about:blank content viewer has been embedded, but it hasn't
had its <body> node created yet, so the test fails?

> I think the safest fix is to special-case about:blank in the frame
> loader so that it doesn't reach the normal asynchronous load path but
> instead causes a deliberate call to nsDocShell::EnsureContentViewer()
> and returns early.

Even if we wanted to do this (which I'm not convinced of, by the way),
that might not be good enough. If the <iframe> were loading something
other than about:blank, and the script tried to touch the document.body
as it does in the testcase, would it fail with the body being null every
so often with the existing parser? If so, then your proposal to
special-case about:blank might work...

-Boris

Henri Sivonen

unread,
Jan 5, 2010, 4:25:06 AM1/5/10
to
In article <U4ydnSwYHOM_iN_W...@mozilla.org>,
Boris Zbarsky <bzba...@mit.edu> wrote:

> On 1/4/10 7:37 AM, Henri Sivonen wrote:
> > This seems to be the case. In particular, OnStop for the about:blank
> > stream causes synchronous tree building in the old parser.
>
> Er... it does? Why?

nsParser::OnStopRequest() can call ResumeParse().

> > The document is short enough that the sink doesn't interrupt.
>
> I'm not sure I follow. If the sink doesn't interrupt, how do we get an
> OnStop for the about:blank? That event can only come from the event loop...

OnStopRequest itself comes from the event loop. I meant that parsing
happens synchronously in response to that call in the old parser.

The HTML5 parser sends more runnables around, so there's more chance for
other tasks to run before the HTML5 parser is done. This doesn't explain
why the old parser succeeds with at least one trip through the event
loop, but it makes it reasonable to assume that the HTML5 parser is
going to continue failing with its more numerous trips through the event
loop without some kind of special synchronous single-threaded
about:blank mode (which might as well be special-cased as parserless
tree building instead).

(FWIW, the HTML5 parser was using more runnables even before the
off-the-main-thread move, so this problem wasn't introduced at that
point.)

> > There's already an mContentViewer each time
> > nsDocShell::EnsureContentViewer() gets called during the execution of
> > the test case with the HTML5 parser.
>
> Ah. So the about:blank content viewer has been embedded, but it hasn't
> had its <body> node created yet, so the test fails?

I think that's the case.

> > I think the safest fix is to special-case about:blank in the frame
> > loader so that it doesn't reach the normal asynchronous load path but
> > instead causes a deliberate call to nsDocShell::EnsureContentViewer()
> > and returns early.
>
> Even if we wanted to do this (which I'm not convinced of, by the way),

Do you have additional reasons for not doing this? (In addition to the
reason immediately below here that is.)

> that might not be good enough. If the <iframe> were loading something
> other than about:blank, and the script tried to touch the document.body
> as it does in the testcase, would it fail with the body being null every
> so often with the existing parser? If so, then your proposal to
> special-case about:blank might work...

Does the Web depend on non-about:blank iframes having html/head/body
elements whenever the document can be observed by a script from the
outside?

It seems that when observed from the inside, the old parser doesn't make
body exist before it is parsed:
http://software.hixie.ch/utilities/js/live-dom-viewer/saved/341
(Also tested Safari 4, Opera 10.01 and IE8 and they, too, returned null
for document.body.)

Boris Zbarsky

unread,
Jan 6, 2010, 1:44:25 PM1/6/10
to
On 1/5/10 4:25 AM, Henri Sivonen wrote:
>> Even if we wanted to do this (which I'm not convinced of, by the way),
>
> Do you have additional reasons for not doing this? (In addition to the
> reason immediately below here that is.)

Well, that depends on what we're proposing becomes synchronous. An
actual load of about:blank needs to fire all sorts of events
asynchronously (load, pageshow, etc, etc). Creation of the DOM could be
appropriately synchronous as needed, I suppose....

Another option might be to just create an about:blank viewer in all
cases as soon as a window is created instead of doing it lazily. If no
one does anything with the window before the "real" thing loads, we'll
just throw it away, and if someone does do something with it it's no
different from now. I'd somewhat support this approach, actually.

> Does the Web depend on non-about:blank iframes having html/head/body
> elements whenever the document can be observed by a script from the
> outside?

I have no idea. I suspect not, since it's in fact not the case in
Gecko, but it's pretty hard to test this... :(

-Boris

Henri Sivonen

unread,
Jan 7, 2010, 6:46:41 AM1/7/10
to
In article <j6udnZ6Bxq6UR9nW...@mozilla.org>,
Boris Zbarsky <bzba...@mit.edu> wrote:

> On 1/5/10 4:25 AM, Henri Sivonen wrote:
> >> Even if we wanted to do this (which I'm not convinced of, by the way),
> >
> > Do you have additional reasons for not doing this? (In addition to the
> > reason immediately below here that is.)
>
> Well, that depends on what we're proposing becomes synchronous. An
> actual load of about:blank needs to fire all sorts of events
> asynchronously (load, pageshow, etc, etc). Creation of the DOM could be
> appropriately synchronous as needed, I suppose....

I was thinking the DOM creation would be synchronous whenever a docshell
is asked to load about:blank but the events would be asynchronous.

> Another option might be to just create an about:blank viewer in all
> cases as soon as a window is created instead of doing it lazily. If no
> one does anything with the window before the "real" thing loads, we'll
> just throw it away, and if someone does do something with it it's no
> different from now. I'd somewhat support this approach, actually.

How would this interact with e.g. the frame loader still wanting to tell
the docshell to load about:blank when a frame is created? I think the
docshell or frame loader couldn't make about:blank loads no-ops, because
navigation from another URL to about:blank still needs to be supported
(synchronously per HTML5).

Boris Zbarsky

unread,
Jan 7, 2010, 8:38:56 AM1/7/10
to
On 1/7/10 6:46 AM, Henri Sivonen wrote:
>> Well, that depends on what we're proposing becomes synchronous. An
>> actual load of about:blank needs to fire all sorts of events
>> asynchronously (load, pageshow, etc, etc). Creation of the DOM could be
>> appropriately synchronous as needed, I suppose....
>
> I was thinking the DOM creation would be synchronous whenever a docshell
> is asked to load about:blank but the events would be asynchronous.

We could try to fake this, but it would be a huge pain involving some
pretty fragile code... in particular, some of those async events
typically come before any DOM creation, and we'd need to hack those
codepaths.

>> Another option might be to just create an about:blank viewer in all
>> cases as soon as a window is created instead of doing it lazily. If no
>> one does anything with the window before the "real" thing loads, we'll
>> just throw it away, and if someone does do something with it it's no
>> different from now. I'd somewhat support this approach, actually.
>
> How would this interact with e.g. the frame loader still wanting to tell
> the docshell to load about:blank when a frame is created?

Just like it interacts now. I'm just suggesting taking something that
happens _sometimes_ now and making it happen always, eliminating race
issues.

> because navigation from another URL to about:blank still needs to be supported
> (synchronously per HTML5).

Well, let's get that part of HTML5 fixed. I see no reason to ever make
loads synchronous, and in particular I see no way to do it safely.

-Boris

Henri Sivonen

unread,
Jan 8, 2010, 2:52:33 AM1/8/10
to
In article <KZadnX_RK9dtftjW...@mozilla.org>,
Boris Zbarsky <bzba...@mit.edu> wrote:

> On 1/7/10 6:46 AM, Henri Sivonen wrote:
> >> Well, that depends on what we're proposing becomes synchronous. An
> >> actual load of about:blank needs to fire all sorts of events
> >> asynchronously (load, pageshow, etc, etc). Creation of the DOM could be
> >> appropriately synchronous as needed, I suppose....
> >
> > I was thinking the DOM creation would be synchronous whenever a docshell
> > is asked to load about:blank but the events would be asynchronous.
>
> We could try to fake this, but it would be a huge pain involving some
> pretty fragile code... in particular, some of those async events
> typically come before any DOM creation, and we'd need to hack those
> codepaths.

I noticed that this is more fragile than I had hoped. My super-naive
initial attempt
(https://bug533381.bugzilla.mozilla.org/attachment.cgi?id=419905) broke
the event ordering.

> >> Another option might be to just create an about:blank viewer in all
> >> cases as soon as a window is created instead of doing it lazily. If no
> >> one does anything with the window before the "real" thing loads, we'll
> >> just throw it away, and if someone does do something with it it's no
> >> different from now. I'd somewhat support this approach, actually.
> >
> > How would this interact with e.g. the frame loader still wanting to tell
> > the docshell to load about:blank when a frame is created?
>
> Just like it interacts now. I'm just suggesting taking something that
> happens _sometimes_ now and making it happen always, eliminating race
> issues.

I don't know the docshell well enough to understand what kind of change
this would mean in practice. In my patch above, I tried to make it so
that EnsureContentViewer() always gets called when navigating to
about:blank, but that wasn't good enough.

> > because navigation from another URL to about:blank still needs to be
> > supported
> > (synchronously per HTML5).
>
> Well, let's get that part of HTML5 fixed. I see no reason to ever make
> loads synchronous, and in particular I see no way to do it safely.

What should the spec say?

Boris Zbarsky

unread,
Jan 8, 2010, 12:53:11 PM1/8/10
to
On 1/8/10 2:52 AM, Henri Sivonen wrote:
> I noticed that this is more fragile than I had hoped. My super-naive
> initial attempt
> (https://bug533381.bugzilla.mozilla.org/attachment.cgi?id=419905) broke
> the event ordering.

Yep.

> I don't know the docshell well enough to understand what kind of change
> this would mean in practice. In my patch above, I tried to make it so
> that EnsureContentViewer() always gets called when navigating to
> about:blank, but that wasn't good enough.

That's actually a little odd, honestly... But you didn't just call
EnsureContentViewer: you also aborted the load.

I guess you actually do need to do that to get your thing working, since
otherwise you could still have a race where the new load has replaced
the previous viewer but hasn't parsed itself yet...


>> Well, let's get that part of HTML5 fixed. I see no reason to ever make
>> loads synchronous, and in particular I see no way to do it safely.
>
> What should the spec say?

I have no idea. Why is it saying the loads should be synchronous to
start with?

-Boris

Henri Sivonen

unread,
Jan 11, 2010, 9:47:18 AM1/11/10
to
In article <Z7KdndsVEYya7NrW...@mozilla.org>,
Boris Zbarsky <bzba...@mit.edu> wrote:

> On 1/8/10 2:52 AM, Henri Sivonen wrote:

> > I don't know the docshell well enough to understand what kind of change
> > this would mean in practice. In my patch above, I tried to make it so
> > that EnsureContentViewer() always gets called when navigating to
> > about:blank, but that wasn't good enough.
>
> That's actually a little odd, honestly... But you didn't just call
> EnsureContentViewer: you also aborted the load.
>
> I guess you actually do need to do that to get your thing working, since
> otherwise you could still have a race where the new load has replaced
> the previous viewer but hasn't parsed itself yet...

Yeah. Letting the parser start and zap the children of the document
would defeat the point of calling EnsureContentViewer() in the first
place.

> >> Well, let's get that part of HTML5 fixed. I see no reason to ever make
> >> loads synchronous, and in particular I see no way to do it safely.
> >
> > What should the spec say?
>
> I have no idea. Why is it saying the loads should be synchronous to
> start with?

Hixie says that everyone does it and pages depend on it:
http://krijnhoetmer.nl/irc-logs/whatwg/20100108#l-210

I don't have evidence of Web pages depending on it. I do have evidence
of Mochitests depending on it. However, I suspect that
https://bugzilla.mozilla.org/show_bug.cgi?id=510802 will turn out to be
this issue.

Boris Zbarsky

unread,
Jan 11, 2010, 12:43:57 PM1/11/10
to
On 1/11/10 9:47 AM, Henri Sivonen wrote:
>> I have no idea. Why is it saying the loads should be synchronous to
>> start with?
>
> Hixie says that everyone does it and pages depend on it:
> http://krijnhoetmer.nl/irc-logs/whatwg/20100108#l-210

So... in general, Hixie's claim here is demonstrably false. If I have
an iframe with http://example.com loaded in it, and I set the iframe's
location to about:blank, that load is NOT synchronous in Gecko. Trivial
demonstration:

<head>
<script>
function doTheTest() {
var win = window.frames[0];
win.location.href = "about:blank";
win.stop();
}
</script>
</head>
<body onload="doTheTest()">
<iframe src="data:text/plain,Text%20here"></iframe>
</body>

So in what sense is the load supposed to be synchronous, exactly? Can
you point me to the relevant part of the spec?

-Boris

Henri Sivonen

unread,
Jan 12, 2010, 5:36:14 AM1/12/10
to
In article <NLKdnQ6U16zA_tbW...@mozilla.org>,
Boris Zbarsky <bzba...@mit.edu> wrote:

> On 1/11/10 9:47 AM, Henri Sivonen wrote:
> >> I have no idea. Why is it saying the loads should be synchronous to
> >> start with?
> >
> > Hixie says that everyone does it and pages depend on it:
> > http://krijnhoetmer.nl/irc-logs/whatwg/20100108#l-210
>
> So... in general, Hixie's claim here is demonstrably false. If I have
> an iframe with http://example.com loaded in it, and I set the iframe's
> location to about:blank, that load is NOT synchronous in Gecko. Trivial
> demonstration:
>
> <head>
> <script>
> function doTheTest() {
> var win = window.frames[0];
> win.location.href = "about:blank";
> win.stop();
> }
> </script>
> </head>
> <body onload="doTheTest()">
> <iframe src="data:text/plain,Text%20here"></iframe>
> </body>

Opera and Safari disagree with Gecko on your test case. IE8 doesn't seem
to support loading data: URLs into an iframe. When I change the data:
URL to an http: URL, Opera and IE8 disagree with Gecko but Safari agrees.
http://hsivonen.iki.fi/test/bz-about-blank.html

> So in what sense is the load supposed to be synchronous, exactly? Can
> you point me to the relevant part of the spec?

http://www.whatwg.org/specs/web-apps/current-work/#navigating-across-docu
ments
Step 12.

(See also, http://www.whatwg.org/specs/web-apps/current-work/#fetch step
1.)

What this means exactly is on the vaguer side. As I understand it, the
HTML parser is supposed to parse the empty stream synchronously at that
point.

0 new messages