Bug 40934 - Support <script defer> as specified by HTML5
Summary: Support <script defer> as specified by HTML5
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: New Bugs (show other bugs)
Version: 528+ (Nightly build)
Hardware: Other OS X 10.5
: P2 Normal
Assignee: Tony Gentilcore
URL:
Keywords:
Depends on: 40968 43055 43532 43736 43875 45103
Blocks: 20710 43391
  Show dependency treegraph
 
Reported: 2010-06-21 12:09 PDT by Tony Gentilcore
Modified: 2010-09-02 08:46 PDT (History)
10 users (show)

See Also:


Attachments
Patch (11.38 KB, patch)
2010-06-21 12:58 PDT, Tony Gentilcore
no flags Details | Formatted Diff | Diff
Patch (16.40 KB, patch)
2010-06-29 13:27 PDT, Tony Gentilcore
no flags Details | Formatted Diff | Diff
Patch (13.33 KB, patch)
2010-06-29 18:41 PDT, Tony Gentilcore
no flags Details | Formatted Diff | Diff
Patch (13.37 KB, patch)
2010-06-29 19:02 PDT, Tony Gentilcore
no flags Details | Formatted Diff | Diff
Patch (15.98 KB, patch)
2010-07-01 12:26 PDT, Tony Gentilcore
no flags Details | Formatted Diff | Diff
Patch (18.58 KB, patch)
2010-07-01 15:37 PDT, Tony Gentilcore
no flags Details | Formatted Diff | Diff
Patch (27.71 KB, patch)
2010-07-29 21:25 PDT, Tony Gentilcore
no flags Details | Formatted Diff | Diff
Fix tab in ChangeLog (27.72 KB, patch)
2010-07-29 21:31 PDT, Tony Gentilcore
no flags Details | Formatted Diff | Diff
Patch (28.36 KB, patch)
2010-07-30 10:36 PDT, Tony Gentilcore
no flags Details | Formatted Diff | Diff
Work in progress (60.99 KB, patch)
2010-08-09 20:09 PDT, Tony Gentilcore
no flags Details | Formatted Diff | Diff
Testcase (78 bytes, text/html)
2010-08-17 09:41 PDT, Tony Gentilcore
no flags Details
Patch (29.75 KB, patch)
2010-08-31 10:10 PDT, Tony Gentilcore
no flags Details | Formatted Diff | Diff
Naming fixes (29.79 KB, patch)
2010-08-31 14:01 PDT, Tony Gentilcore
no flags Details | Formatted Diff | Diff
Use enum for parser state (42.62 KB, patch)
2010-08-31 18:34 PDT, Tony Gentilcore
no flags Details | Formatted Diff | Diff
Fix Qt build (43.90 KB, patch)
2010-08-31 20:09 PDT, Tony Gentilcore
no flags Details | Formatted Diff | Diff
Patch for landing (44.60 KB, patch)
2010-09-01 09:11 PDT, Tony Gentilcore
no flags Details | Formatted Diff | Diff
Patch for landing (44.65 KB, patch)
2010-09-01 15:03 PDT, Tony Gentilcore
no flags Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Tony Gentilcore 2010-06-21 12:09:12 PDT
Support <script defer> as specified by HTML5
Comment 1 Tony Gentilcore 2010-06-21 12:58:49 PDT
Created attachment 59276 [details]
Patch
Comment 2 Tony Gentilcore 2010-06-21 13:01:19 PDT
Adam, hopefully I've addressed all your original comments from my big patch in:
https://bugs.webkit.org/show_bug.cgi?id=20710

This patch only does defer so it is much more manageable. I'll mail one for async separately.
Comment 3 Adam Barth 2010-06-21 13:51:58 PDT
Comment on attachment 59276 [details]
Patch

Eric should review this one.
Comment 4 Eric Seidel (no email) 2010-06-21 14:14:31 PDT
Comment on attachment 59276 [details]
Patch

First of all, thank you! It's wonderful to have you working on this!

This is a complicated patch.  It may take me a couple passes to review it, but I'll go through it once with comments now.

LayoutTests/http/tests/loading/resources/defer-script.js:1
 +  window.status += " defer ";
I think I probably would have used some other logging mechanism. :)

LayoutTests/http/tests/loading/script-defer-expected.txt:6
 +  main frame - didFinishLoadForFrame
I'm not sure you want to have this in the loading directory, I don't think you need/want these callbacks.  Maybe you do, but it makes the tests possibly more complicated than needed.

LayoutTests/http/tests/loading/script-defer.html:8
 +    window.status += (' DCL ')
function log(text) {
   document.getElementById("preTag").innerHTML += text + "\n";
}
is the easy hack for logging.  It's also just as easy to create text nodes and br nodes in a normal div, like the "console" div and log() function for script-tests do.

LayoutTests/http/tests/loading/script-defer.html:17
 +      var results = "PASS";
You can actually use the shouldBe, log, etc. from script-tests w/o being a template-based script-test by including fast/js/js-test-pre.js.  I think that's exposed in some manner to http tests, but I could be wrong.

Honestly all the window.status stuff is fine. :)  It was just a little strange at first to read.

WebCore/html/HTML5DocumentParser.cpp:306
 +      m_endWasDelayed = !m_scriptRunner->executeScriptsWaitingForParsing();
This seems very strange.  You mean we'd end up calling end() twice?  Since we delayed the end inside end?

WebCore/html/HTML5DocumentParser.cpp:306
 +      m_endWasDelayed = !m_scriptRunner->executeScriptsWaitingForParsing();
Maybe we should have a wrapper function for calling end instead?  runAnyDeferredScriptsAndEnd() or similar?

WebCore/html/HTML5ScriptRunner.cpp:233
 +              watchForLoad(m_parsingBlockingScript);
Feels very strange to me to pull this watchForLoad call out from right next to where we make the request.  Seems like it would be easy to end up watching twice or not at all.  If we fail to "watch" a script, it will end up with no clients and can end up purging its data from the cache any time we hit the run-loop.

WebCore/html/HTML5ScriptRunner.cpp:241
 +  void HTML5ScriptRunner::requestScript(PendingScript& pendingScript, Element* scriptElement)
I think this is a great change.  It is definitely better to pass in the m_parsingBlockingScript as a reference than to use it directly!

WebCore/html/HTML5ScriptRunner.cpp:279
 +      m_scriptsToExecuteAfterParsing.append(pendingScript);
I think the lack of watchForLoad is going to bite us here.  I think instead the load function needs to know to ignore the notification.  That will be slightly sad as we may have to remove more ASSERTS (which are the only thing which makes this sort of async programming possible).

WebCore/html/HTML5ScriptRunner.cpp:294
 +              requestDeferredScript(script);
This is nice and clean. :)

WebCore/html/HTML5ScriptRunner.h:114
 +      Deque<PendingScript> m_scriptsToExecuteAfterParsing;
I think this design seems totally workable.  I wonder if we want the ScriptRunner to be in charge of running defer scripts, or should something else like the Document do so (since it happens after parsing is done?)  How are async scripts going to wire in?  Do those need to be run by the ScriptRunner or Document or something else?

We may need to split the scripting logic out of Document ont something else.  Maybe the ScriptRunner should be owned by the document directly and just used by the parser?  Or maybe a new ScriptRunner-type-class needs to exist for the Documetns' script running needs?

Those big design questions don't need to be answered by this patch, but should enter into your thinking in terms of planning async and defer. :)

r- because I think this will end up with defer scripts being purged from the cache before being run (thus causing flakiness and ASSERTs in debug mode).
Comment 5 Tony Gentilcore 2010-06-21 16:29:34 PDT
(In reply to comment #4)
> (From update of attachment 59276 [details])
> First of all, thank you! It's wonderful to have you working on this!
> 
> This is a complicated patch.  It may take me a couple passes to review it, but I'll go through it once with comments now.

Thanks for the quick and detailed review. I have a few questions inline below before I update the patch.

> 
> LayoutTests/http/tests/loading/resources/defer-script.js:1
>  +  window.status += " defer ";
> I think I probably would have used some other logging mechanism. :)

I'll switch to what you suggested below.

> 
> LayoutTests/http/tests/loading/script-defer-expected.txt:6
>  +  main frame - didFinishLoadForFrame
> I'm not sure you want to have this in the loading directory, I don't think you need/want these callbacks.  Maybe you do, but it makes the tests possibly more complicated than needed.
> 

Which directory should these go in? Maybe http/tests/misc/?

> LayoutTests/http/tests/loading/script-defer.html:8
>  +    window.status += (' DCL ')
> function log(text) {
>    document.getElementById("preTag").innerHTML += text + "\n";
> }
> is the easy hack for logging.  It's also just as easy to create text nodes and br nodes in a normal div, like the "console" div and log() function for script-tests do.
> 

Good idea I'll do that.

> LayoutTests/http/tests/loading/script-defer.html:17
>  +      var results = "PASS";
> You can actually use the shouldBe, log, etc. from script-tests w/o being a template-based script-test by including fast/js/js-test-pre.js.  I think that's exposed in some manner to http tests, but I could be wrong.
> 
> Honestly all the window.status stuff is fine. :)  It was just a little strange at first to read.

I like your suggestion, will switch to that instead.

> 
> WebCore/html/HTML5DocumentParser.cpp:306
>  +      m_endWasDelayed = !m_scriptRunner->executeScriptsWaitingForParsing();
> This seems very strange.  You mean we'd end up calling end() twice?  Since we delayed the end inside end?

Yes, with this approach we'd call end() twice. The root problem is that we don't have one method that implements:
http://dev.w3.org/html5/spec/Overview.html#the-end

It is currently spread across Document, DocumentParser, TreeBuilder, and FrameLoader.

The key bit I'm trying to add is 8.2.6.3. It could either go here, in DocumentParser, or it could go in Document as I originally coded it in https://bugs.webkit.org/attachment.cgi?id=56542&action=prettypatch.

> 
> WebCore/html/HTML5DocumentParser.cpp:306
>  +      m_endWasDelayed = !m_scriptRunner->executeScriptsWaitingForParsing();
> Maybe we should have a wrapper function for calling end instead?  runAnyDeferredScriptsAndEnd() or similar?

Yes, I can do that. Does that address your above concern about calling end() twice?

> 
> WebCore/html/HTML5ScriptRunner.cpp:233
>  +              watchForLoad(m_parsingBlockingScript);
> Feels very strange to me to pull this watchForLoad call out from right next to where we make the request.  Seems like it would be easy to end up watching twice or not at all.  If we fail to "watch" a script, it will end up with no clients and can end up purging its data from the cache any time we hit the run-loop.

I believe PendingScript makes this safe. It holds a CachedResourceHandle which guarantees that it will not be purged even after it has loaded with no watcher. Also, we can't watch twice because of the ASSERT(!pendingScript.watchingForLoad()) in watchForLoad(). Let me know if you think I'm missing something.

Perhaps you can suggest a test case I can add that would prove this case?

> 
> WebCore/html/HTML5ScriptRunner.cpp:241
>  +  void HTML5ScriptRunner::requestScript(PendingScript& pendingScript, Element* scriptElement)
> I think this is a great change.  It is definitely better to pass in the m_parsingBlockingScript as a reference than to use it directly!

Would it be worthwhile to break this out into a separate patch to reduce the size of this one?

> 
> WebCore/html/HTML5ScriptRunner.cpp:279
>  +      m_scriptsToExecuteAfterParsing.append(pendingScript);
> I think the lack of watchForLoad is going to bite us here.  I think instead the load function needs to know to ignore the notification.  That will be slightly sad as we may have to remove more ASSERTS (which are the only thing which makes this sort of async programming possible).

Yes, I don't want to remove those ASSERTs. I believe that the PendingScript's CachedResourceHandle prevents us from having to worry about this. If I missed something, then I probably need to figure out a way to have a different host that can be notified when deferred scripts load. Perhaps a separate DeferredScriptRunnerHost.

> 
> WebCore/html/HTML5ScriptRunner.cpp:294
>  +              requestDeferredScript(script);
> This is nice and clean. :)
> 
> WebCore/html/HTML5ScriptRunner.h:114
>  +      Deque<PendingScript> m_scriptsToExecuteAfterParsing;
> I think this design seems totally workable.  I wonder if we want the ScriptRunner to be in charge of running defer scripts, or should something else like the Document do so (since it happens after parsing is done?)  How are async scripts going to wire in?  Do those need to be run by the ScriptRunner or Document or something else?

Async scripts can be run after the DocumentParser is destroyed. So I was planning to track the number of outstanding async scripts on the Document. Similar to this:
https://bugs.webkit.org/attachment.cgi?id=57565&action=prettypatch

I could track both deferred and async scripts on the document, but you initially didn't like the idea of having code on the document that isn't used after it is loaded.

> 
> We may need to split the scripting logic out of Document ont something else.  Maybe the ScriptRunner should be owned by the document directly and just used by the parser?  Or maybe a new ScriptRunner-type-class needs to exist for the Documetns' script running needs?

Letting FrameLoader or Document own an AsyncScriptRunner and DeferredScriptRunner and DocumentParser own a SyncScriptRunner would be one way to go. It is hard for me to tell if the additional classes are warranted just yet.

> 
> Those big design questions don't need to be answered by this patch, but should enter into your thinking in terms of planning async and defer. :)
> 
> r- because I think this will end up with defer scripts being purged from the cache before being run (thus causing flakiness and ASSERTs in debug mode).
Comment 6 Eric Seidel (no email) 2010-06-21 16:47:36 PDT
(In reply to comment #5)
> Which directory should these go in? Maybe http/tests/misc/?

If I remember correctly the "loading" directory may automatically get these callbacks turned on (which is a horrible design IMO).  If you want the callbacks then sure, leave them there.  Otherwise anywhere you like is fine.  Maybe http/tests/local/HTMLScriptElement  Since we don't actually need to load the main page off the network.  I'm not sure if local/ is reserved for a special purpose or if it's just for tests which don't actually need to be sent over HTTP, just use http resources.

> I believe PendingScript makes this safe. It holds a CachedResourceHandle which guarantees that it will not be purged even after it has loaded with no watcher. Also, we can't watch twice because of the ASSERT(!pendingScript.watchingForLoad()) in watchForLoad(). Let me know if you think I'm missing something.

Nope!  That's what I thought too.  See http://trac.webkit.org/changeset/61374 for more context.  CachedResource will mark itself purgeable if it has no clients.  CachedResourceHandle seems to just keep the actual CachedResource alive, but that doesn't mean it will still have any data attached to it. :)

> Perhaps you can suggest a test case I can add that would prove this case?

Get a DEFER to finish loading, but not run yet due to the parser being blocked on something else.
Load a bunch of data or somehow convince the cache to start purging stuff.
Try and run the script, it will have been marked purged and thus have no data (which will do nothing in Release mode, or hit an ASSERT inside ScriptSource in debug mode.)

> > WebCore/html/HTML5ScriptRunner.cpp:241
> >  +  void HTML5ScriptRunner::requestScript(PendingScript& pendingScript, Element* scriptElement)
> > I think this is a great change.  It is definitely better to pass in the m_parsingBlockingScript as a reference than to use it directly!
> 
> Would it be worthwhile to break this out into a separate patch to reduce the size of this one?

Totally up to you.  I'm happy to r=me that change if you want to post it separately.  This isn't a very big change, just complicated due to the end() symantecs which are delicate (and hence guarded by a zillion ASSERTS).

> Yes, I don't want to remove those ASSERTs. I believe that the PendingScript's CachedResourceHandle prevents us from having to worry about this. If I missed something, then I probably need to figure out a way to have a different host that can be notified when deferred scripts load. Perhaps a separate DeferredScriptRunnerHost.

Well, it would be easy to have some separate thing signed up as the client.  Even something which implemented an empty notifyFinished().

> I could track both deferred and async scripts on the document, but you initially didn't like the idea of having code on the document that isn't used after it is loaded.

Yup.  I worry our poor Document class is a bit of a dumping ground.  I still need to go back and read the spec on DEFER vs. ASYNC.
Comment 7 Tony Gentilcore 2010-06-29 13:27:32 PDT
Created attachment 60047 [details]
Patch
Comment 8 Tony Gentilcore 2010-06-29 13:28:18 PDT
I believe this addresses all of your previous comments. Should be ready for another pass.
Comment 9 Eric Seidel (no email) 2010-06-29 13:35:10 PDT
Attachment 60047 [details] did not build on mac:
Build output: http://webkit-commit-queue.appspot.com/results/3352240
Comment 10 Tony Gentilcore 2010-06-29 13:37:43 PDT
So odd that VisualStudio doesn't warn/error about initialization order. I'll upload a fixed patch shortly.
Comment 11 Eric Seidel (no email) 2010-06-29 13:46:43 PDT
Comment on attachment 60047 [details]
Patch

End should be final.  How are we getting past the first end check to need a second?

Are you sure you need that code or does:

    bool shouldDelayEnd() const { return inWrite() || isWaitingForScripts() || inScriptExecution() || isScheduledForResume(); }

already catch your case?
Comment 12 Eric Seidel (no email) 2010-06-29 13:49:14 PDT
Comment on attachment 60047 [details]
Patch

I'm slightly onfused why we need RefCounted PendingScript?

Wouldn't the blocking script just move off the queue  to become the pending blocking script when it's turn has come?
Comment 13 Tony Gentilcore 2010-06-29 14:18:39 PDT
(In reply to comment #12)
> (From update of attachment 60047 [details])
> I'm slightly onfused why we need RefCounted PendingScript?
> 
> Wouldn't the blocking script just move off the queue  to become the pending blocking script when it's turn has come?

Yes, you are right, RefPtr functionality is not needed here, it was just convenient. The problem is that PendingScript is Noncopyable so I can't just assign it.

Here are some options:
1. Remove Noncopyable and implement the = operator. It seemed desirable to keep it non copyable, what do you think?
2. Make it an OwnPtr. Then I need a create() method that returns a PassOwnPtr and one that returns a PassRefPtr. Perhaps that isn't so bad?

What do you recommend?
Comment 14 Adam Barth 2010-06-29 14:22:37 PDT
A class either inherits from RefCounted or it doesn't.  If it does, then you can't use OwnPtr.  You have to use RefPtr.  If it doesn't, you can't use RefPtr and you have to use OwnPtr (or hold it directly).  (There are some exceptions where folks implement the RefCounted API without inheriting from RefCounted, but this doesn't sound like one of them.)

Having create methods for both RefPtr and OwnPtr doesn't make any sense.
Comment 15 Tony Gentilcore 2010-06-29 14:25:25 PDT
(In reply to comment #14)
> A class either inherits from RefCounted or it doesn't.  If it does, then you can't use OwnPtr.  You have to use RefPtr.  If it doesn't, you can't use RefPtr and you have to use OwnPtr (or hold it directly).  (There are some exceptions where folks implement the RefCounted API without inheriting from RefCounted, but this doesn't sound like one of them.)
> 
> Having create methods for both RefPtr and OwnPtr doesn't make any sense.

I'm kind of glad the API doesn't allow that. It would have been weird. So maybe the best option is to make PendingScript copyable and not use smart ptrs at all?
Comment 16 Adam Barth 2010-06-29 14:47:00 PDT
I haven't looked at the details at all, but that could well be a good solution.
Comment 17 Tony Gentilcore 2010-06-29 18:39:17 PDT
(In reply to comment #11)
> (From update of attachment 60047 [details])
> End should be final.  How are we getting past the first end check to need a second?
> 
> Are you sure you need that code or does:
> 
>     bool shouldDelayEnd() const { return inWrite() || isWaitingForScripts() || inScriptExecution() || isScheduledForResume(); }
> 
> already catch your case?

A deferred script needs to be run after parsing, but before DOMContentLoaded. See:
http://dev.w3.org/html5/spec/Overview.html#the-end

When HTMLDocumentParser::end() calls m_treeBuilder->finished(), the Document fires DOMContentLoaded. So I need to hook in before that point.

However, shouldDelayEnd() doesn't handle my case because I need to wait for that to be completely finished. For example, I couldn't call executeScriptsWaitingForParsingAndEnd() if shouldDelayEnd() would return true because a synchronous script is still loading.

This all feels weird at the moment because there is no single place that implements "#the-end". Those steps are mostly spread through Document and FrameLoader. An alternate approach is what I did in the original patch (way back when) which was to add a phase to Document.

Adding the phase here feels cleaner to me than adding it to Document. If you have a suggestion for a third alternative, I'm happy to pursue that. It would be really nice to eventually have one method that performs "#the-end".
Comment 18 Tony Gentilcore 2010-06-29 18:41:07 PDT
Created attachment 60076 [details]
Patch
Comment 19 Eric Seidel (no email) 2010-06-29 18:48:07 PDT
Attachment 60076 [details] did not build on mac:
Build output: http://webkit-commit-queue.appspot.com/results/3372026
Comment 20 Tony Gentilcore 2010-06-29 19:02:12 PDT
Created attachment 60078 [details]
Patch
Comment 21 Eric Seidel (no email) 2010-07-01 00:48:51 PDT
Comment on attachment 60078 [details]
Patch

WebCore/html/HTMLDocumentParser.cpp:292
 +      if (m_scriptRunner && !m_scriptRunner->executeScriptsWaitingForParsing()) {
This is kinda subtle.  I might have broken the running of m_scriptRunner->executeScriptsWaitingForParsing into a separate inner if:

if (m_scriptRunner) {
    bool continueParsing = m_scriptRunner->executeScriptsWaitingForParsing();
    if (!continueParsing) {


WebCore/html/HTMLScriptRunner.cpp:237
 +              watchForLoad(m_parsingBlockingScript);
Is this always right?  What about the case where parsing finshed, but a stylesheet hasn't loaded.  That seems like it will end up recursing under watchForLoad() because the m_parsingBlockingScript will have already loaded but can't run because we're blocked on CSS.  Or?

WebCore/html/HTMLScriptRunner.cpp:245
 +  void HTMLScriptRunner::requestScript(PendingScript& pendingScript, Element* scriptElement)
I still think this is a fantastic change to this function to make it not affect the instance, only its args.


otherwise it looks fine.  r- for the possible slow CSS problem.
Comment 22 Tony Gentilcore 2010-07-01 12:26:56 PDT
Created attachment 60271 [details]
Patch
Comment 23 Tony Gentilcore 2010-07-01 12:29:08 PDT
(In reply to comment #21)
> (From update of attachment 60078 [details])
> WebCore/html/HTMLDocumentParser.cpp:292
>  +      if (m_scriptRunner && !m_scriptRunner->executeScriptsWaitingForParsing()) {
> This is kinda subtle.  I might have broken the running of m_scriptRunner->executeScriptsWaitingForParsing into a separate inner if:
> 
> if (m_scriptRunner) {
>     bool continueParsing = m_scriptRunner->executeScriptsWaitingForParsing();
>     if (!continueParsing) {

Good call. That is much more readable :-)

> 
> 
> WebCore/html/HTMLScriptRunner.cpp:237
>  +              watchForLoad(m_parsingBlockingScript);
> Is this always right?  What about the case where parsing finshed, but a stylesheet hasn't loaded.  That seems like it will end up recursing under watchForLoad() because the m_parsingBlockingScript will have already loaded but can't run because we're blocked on CSS.  Or?
> 

It should be impossible to move into #the-end 8.2.6.3 phase until after all stylesheets have loaded. I've added a new LayoutTest to verify this case. Please let me know if I'm still missing something or if that isn't what you had in mind. I'd also be interested in adding more tests if you can think of other tricky scenarios.

> WebCore/html/HTMLScriptRunner.cpp:245
>  +  void HTMLScriptRunner::requestScript(PendingScript& pendingScript, Element* scriptElement)
> I still think this is a fantastic change to this function to make it not affect the instance, only its args.
> 
> 
> otherwise it looks fine.  r- for the possible slow CSS problem.
Comment 24 Eric Seidel (no email) 2010-07-01 13:18:41 PDT
I think it's clearer if we ASSERT that stylesheets are done in end() instead of assuming.
Comment 25 Tony Gentilcore 2010-07-01 15:35:11 PDT
(In reply to comment #24)
> I think it's clearer if we ASSERT that stylesheets are done in end() instead of assuming.

You were right! There was a bug, but it was slightly different.

It isn't valid to ASSERT that stylesheets are done in end(). Stylesheets don't block the end of parsing, they only block parser-inserted script execution. The suggested ASSERT would trigger for this document "<html><link rel=stylesheet href=slow.css></html>". Currently executeScriptsWaitingForParsing() calls executeParsingBlockingScripts() which calls executePendingScript() which performs an ASSERT(m_document->haveStylesheetsLoaded()). So we are guaranteed that we can never run a script (deferred or otherwise) if there is a pending stylesheet.

Anyway, I added another new test that writes a slow stylesheet from the first deferred script and ensures that the second deferred happens after the stylesheet is applied. It uncovered the bug. In HTMLScriptRunner::executeScriptsWaitingForParsing(), executeParsingBlockingScripts() may return false under two conditions:
1. The script isn't loaded yet
2. A blocking stylesheet isn't loaded yet
It is only valid to call watchForLoad() if it returns false for #1. So I guarded watchForLoad() with a check for #1.
Comment 26 Tony Gentilcore 2010-07-01 15:37:44 PDT
Created attachment 60302 [details]
Patch
Comment 27 Eric Seidel (no email) 2010-07-01 15:49:51 PDT
Comment on attachment 60302 [details]
Patch

Why can't we ASSERT right here:
33     while (!m_scriptsToExecuteAfterParsing.isEmpty()) {
 234         ASSERT(!haveParsingBlockingScript());

that stylesheets are done?

I want to make it impossible for us to think we're running the very last script only to have us block on stylesheet loads.  Or worse, have us try to run the very last script, have it block, and then call waitForLoad which imemdiately calls us back re-entrantly.
Comment 28 Eric Seidel (no email) 2010-07-01 15:50:37 PDT
I guess waitForLoad() asserts that the cachedScript isn't loaded...
Comment 29 Tony Gentilcore 2010-07-01 16:02:46 PDT
(In reply to comment #27)
> (From update of attachment 60302 [details])
> Why can't we ASSERT right here:
> 33     while (!m_scriptsToExecuteAfterParsing.isEmpty()) {
>  234         ASSERT(!haveParsingBlockingScript());
> 
> that stylesheets are done?

My latest test case triggers the condition where stylesheets are not done at that line (a doc.write of a stylesheet from a deferred script).

In that case, the first deferred script becomes the parsing blocking script, and executeParsingBlockingScripts()'s call to isPendingScriptReady() sets us into m_hasScritpsWaitingForStylesheets state. When the stylesheet loads, the m_parsingBlockingScript executes and the remaining deferred scripts are run (if any).

> 
> I want to make it impossible for us to think we're running the very last script only to have us block on stylesheet loads.  Or worse, have us try to run the very last script, have it block, and then call waitForLoad which imemdiately calls us back re-entrantly.

My new guard of !m_parsingBlockingScript.cachedScript()->isLoaded() should prevent this. Am I missing something still?
Comment 30 Tony Gentilcore 2010-07-07 10:21:09 PDT
(In reply to comment #29)
> (In reply to comment #27)
> > (From update of attachment 60302 [details] [details])
> > Why can't we ASSERT right here:
> > 33     while (!m_scriptsToExecuteAfterParsing.isEmpty()) {
> >  234         ASSERT(!haveParsingBlockingScript());
> > 
> > that stylesheets are done?
> 
> My latest test case triggers the condition where stylesheets are not done at that line (a doc.write of a stylesheet from a deferred script).
> 
> In that case, the first deferred script becomes the parsing blocking script, and executeParsingBlockingScripts()'s call to isPendingScriptReady() sets us into m_hasScritpsWaitingForStylesheets state. When the stylesheet loads, the m_parsingBlockingScript executes and the remaining deferred scripts are run (if any).
> 
> > 
> > I want to make it impossible for us to think we're running the very last script only to have us block on stylesheet loads.  Or worse, have us try to run the very last script, have it block, and then call waitForLoad which imemdiately calls us back re-entrantly.
> 
> My new guard of !m_parsingBlockingScript.cachedScript()->isLoaded() should prevent this. Am I missing something still?

I don't want to let this drop. Does that explanation convince or do you think I'm still missing a bug?
Comment 31 Eric Seidel (no email) 2010-07-07 13:35:22 PDT
Comment on attachment 60302 [details]
Patch

Totally agreed!  You shouldn't let this drop. :)  Nor should I have.WebCore/html/HTMLScriptRunner.cpp:281
 +      m_scriptsToExecuteAfterParsing.append(pendingScript);
Why don't we want to check m_parsingBlockingScript.cachedScript() here?  It seems strange to have the watchForLoad stuff outside of requestScript.

requestScript coudl return a success bool which we could use to judge if we should waitForLoad if it's not loaded or something.  requestScript could simply return a pointer to the cachedScript() too.

It seems that we don't want to put the pendingScript on the "executeAfterParsing" list if it failed to be requested?

Do we need to test when the before-load event fires for deferred scripts?  Currently it fires synchronously as they're parsed.  is that correct?


This is just complicated stuff.  Your job as the patch submitter is to convince me it's right. :)  And my job as the reviewer is to use my (hopefully helpful) knowledge of the code to understand what could go wrong.

I think this is very close, and we could probably land it as-is.  But I'm curious as to your answers to the above questions first.
Comment 32 Tony Gentilcore 2010-07-29 21:25:56 PDT
Created attachment 63027 [details]
Patch
Comment 33 Tony Gentilcore 2010-07-29 21:27:45 PDT
As per our offline conversation, I've added a lot of new tests. Please let me know if they happen to trigger any more ideas for tricky scenarios we could throw at it.

The code change is just that bit about returning a boolean from requestScript and only adding to the queue if a request was actually made.
Comment 34 Tony Gentilcore 2010-07-29 21:31:30 PDT
Created attachment 63029 [details]
Fix tab in ChangeLog
Comment 35 Eric Seidel (no email) 2010-07-30 04:44:44 PDT
Comment on attachment 63029 [details]
Fix tab in ChangeLog

LayoutTests/fast/dom/HTMLScriptElement/defer-double-defer-write.html:14
 +      onbeforeload="doubleWrite(4)"
Why does this onbeforeload run at a different time than the one in the next test?  This one seems to run after the 3, the next test it runs immediately?  Is it racey?

LayoutTests/fast/dom/HTMLScriptElement/defer-double-write.html:14
 +      onbeforeload="doubleWrite(3)"
Here is the other onbeforeload your'e expecting to run at a different time than the other one.  I'm not sure how tehse two tests are different.

Oh, I see, the first one uses a "double defer".  But I still wouldn't expect the onbeforeload time to change?LayoutTests/fast/dom/HTMLScriptElement/defer-inline-script.html:11
 +  <script defer>
Excellent.  This test coudl probably be simpler with just a little document.write(). :)  The script-testing stuff is useful, but you don't really seem to be uing it here. :)

LayoutTests/fast/dom/HTMLScriptElement/defer-onbeforeload.html:8
 +  Checks that deferred scripts fire onbeforeload immediately and that it is cancellable.
This test seems to have conflicting expectations with teh first one...

LayoutTests/fast/dom/HTMLScriptElement/defer-onbeforeload.html:10
 +  <script src="resources/shouldnotexecute.js" onbeforeload="return false;" defer></script>
Seems you'd want to log here, no?

LayoutTests/fast/dom/HTMLScriptElement/defer-script-invalid-url.html:10
 +  <script src="http://invalid:999999/" defer></script>
Do we need/want to check timing expectations of onerror?  That might be racey though.

LayoutTests/fast/dom/HTMLScriptElement/resources/shouldnotexecute.js:1
 +  debug("should not execute");
Might put FAIL in there.

LayoutTests/http/tests/misc/resources/script-debug-body-background.js:1
 +  debug('Body background: ' + getComputedStyle(document.body)['background-color']);
This should ideally contain the expectation in it.  Since you're using the full-blown js-test-pre.js harness, you could use shouldBe here or shouldBeEqualToString

LayoutTests/http/tests/misc/script-defer-write-slow-stylesheet.html:18
 +  <script src="http://127.0.0.1:8000/misc/resources/script-write-slow-stylesheet.js" defer="defer"></script>
So this is testing that writing a stylesheet from a defer script will delay DOMContentLoaded, right?

WebCore/html/HTMLScriptRunner.cpp:251
 +      AtomicString srcValue = scriptElement->getAttribute(srcAttr);
const AtomicString& srcValue should be sufficient (and prevents one uneeded ref).

WebCore/html/HTMLScriptRunner.cpp:257
 +      pendingScript.element = scriptElement;
Do we need to end up clearing this in the failure case?  I guess not, since we weren't doing so before?

WebCore/html/HTMLScriptRunner.cpp:276
 +      if (madeRequest && !m_parsingBlockingScript.cachedScript()->isLoaded())
I think I would have made the m_parsingBlockingScript.cachedScript() dependance explicit here:

if (madeRequest) {
    ASSERT(m_parsingBlockingScript.cachedScript());
    if (!m_parsingBlockingScript.cachedScript()->isLoaded())
         watchForLoad(m_parsingBlockingScript);
}

Since requestScript can return w/o a cachedScript(), we should ASSERT that the bool and cachedScript() always agree somewhere.

WebCore/html/HTMLScriptRunner.cpp:283
 +      bool madeRequest = requestScript(pendingScript, scriptElement);
This is a success bool, it seems.

I'm not sure the name "madeRequest" helps me here.  My first thought was "oh, do I need to call it again?"

Maybe "requestSuccessfull"?  Or just throw it in the if block itself w/o a local variable would be OK too.

(This is a nit, and not a big deal.)

In general this looks fine.  I think we could go one more round for nits.
Comment 36 Tony Gentilcore 2010-07-30 10:34:04 PDT
(In reply to comment #35)
> (From update of attachment 63029 [details])
> LayoutTests/fast/dom/HTMLScriptElement/defer-double-defer-write.html:14
>  +      onbeforeload="doubleWrite(4)"
> Why does this onbeforeload run at a different time than the one in the next test?  This one seems to run after the 3, the next test it runs immediately?  Is it racey?

It isn't racey. The onbeforeload runs at the right time. Notice that it doesn't write any numbers, it only writes deferred scripts which don't write the numbers until later. I've added a write directly to the onbeforeload handler to make that clear and verify that it runs at the right time.

> 
> LayoutTests/fast/dom/HTMLScriptElement/defer-double-write.html:14
>  +      onbeforeload="doubleWrite(3)"
> Here is the other onbeforeload your'e expecting to run at a different time than the other one.  I'm not sure how tehse two tests are different.
> 
> Oh, I see, the first one uses a "double defer".  But I still wouldn't expect the onbeforeload time to change?

See above comment. Should be clear that the onbeforeload time is the same now.

> LayoutTests/fast/dom/HTMLScriptElement/defer-inline-script.html:11
>  +  <script defer>
> Excellent.  This test coudl probably be simpler with just a little document.write(). :)  The script-testing stuff is useful, but you don't really seem to be uing it here. :)

Done.

> 
> LayoutTests/fast/dom/HTMLScriptElement/defer-onbeforeload.html:8
>  +  Checks that deferred scripts fire onbeforeload immediately and that it is cancellable.
> This test seems to have conflicting expectations with teh first one...

See first comment.

> 
> LayoutTests/fast/dom/HTMLScriptElement/defer-onbeforeload.html:10
>  +  <script src="resources/shouldnotexecute.js" onbeforeload="return false;" defer></script>
> Seems you'd want to log here, no?

Good idea. Done.

> 
> LayoutTests/fast/dom/HTMLScriptElement/defer-script-invalid-url.html:10
>  +  <script src="http://invalid:999999/" defer></script>
> Do we need/want to check timing expectations of onerror?  That might be racey though.

There is currently a bug about onerror not being fired for invalid URLs. I have a local patch started to fix that, but don't want to complicate these expectations for now. http://crbug.com/39423

> 
> LayoutTests/fast/dom/HTMLScriptElement/resources/shouldnotexecute.js:1
>  +  debug("should not execute");
> Might put FAIL in there.

Done.

> 
> LayoutTests/http/tests/misc/resources/script-debug-body-background.js:1
>  +  debug('Body background: ' + getComputedStyle(document.body)['background-color']);
> This should ideally contain the expectation in it.  Since you're using the full-blown js-test-pre.js harness, you could use shouldBe here or shouldBeEqualToString

Done.

> 
> LayoutTests/http/tests/misc/script-defer-write-slow-stylesheet.html:18
>  +  <script src="http://127.0.0.1:8000/misc/resources/script-write-slow-stylesheet.js" defer="defer"></script>
> So this is testing that writing a stylesheet from a defer script will delay DOMContentLoaded, right?

Yes. I've updated the description to reflect that.

> 
> WebCore/html/HTMLScriptRunner.cpp:251
>  +      AtomicString srcValue = scriptElement->getAttribute(srcAttr);
> const AtomicString& srcValue should be sufficient (and prevents one uneeded ref).

Oops. Good catch. Fixed.

> 
> WebCore/html/HTMLScriptRunner.cpp:257
>  +      pendingScript.element = scriptElement;
> Do we need to end up clearing this in the failure case?  I guess not, since we weren't doing so before?

No, because it ASSERTs that it is clear in the first line of this method.

> 
> WebCore/html/HTMLScriptRunner.cpp:276
>  +      if (madeRequest && !m_parsingBlockingScript.cachedScript()->isLoaded())
> I think I would have made the m_parsingBlockingScript.cachedScript() dependance explicit here:
> 
> if (madeRequest) {
>     ASSERT(m_parsingBlockingScript.cachedScript());
>     if (!m_parsingBlockingScript.cachedScript()->isLoaded())
>          watchForLoad(m_parsingBlockingScript);
> }
> 
> Since requestScript can return w/o a cachedScript(), we should ASSERT that the bool and cachedScript() always agree somewhere.

Good call. I did that here and in requestDeferredScript().

> 
> WebCore/html/HTMLScriptRunner.cpp:283
>  +      bool madeRequest = requestScript(pendingScript, scriptElement);
> This is a success bool, it seems.
> 
> I'm not sure the name "madeRequest" helps me here.  My first thought was "oh, do I need to call it again?"
> 
> Maybe "requestSuccessfull"?  Or just throw it in the if block itself w/o a local variable would be OK too.

I got rid of the variable.

> 
> (This is a nit, and not a big deal.)
> 
> In general this looks fine.  I think we could go one more round for nits.
Comment 37 Tony Gentilcore 2010-07-30 10:36:11 PDT
Created attachment 63078 [details]
Patch
Comment 38 Tony Gentilcore 2010-08-02 12:52:15 PDT
> In general this looks fine.  I think we could go one more round for nits.

Do you want to do one more round or should I take the previous r+?
Comment 39 Eric Seidel (no email) 2010-08-02 13:41:59 PDT
Comment on attachment 63078 [details]
Patch

LGTM!
Comment 40 WebKit Commit Bot 2010-08-04 08:11:16 PDT
Comment on attachment 63078 [details]
Patch

Rejecting patch 63078 from commit-queue.

Failed to run "['WebKitTools/Scripts/run-webkit-tests', '--no-launch-safari', '--exit-after-n-failures=1', '--ignore-tests', 'compositing', '--quiet']" exit_code: 1
Running build-dumprendertree
Compiling Java tests
make: Nothing to be done for `default'.
Running tests from /Users/eseidel/Projects/CommitQueue/LayoutTests
Testing 20784 test cases.
fast/dom/HTMLScriptElement/defer-script-invalid-url.html -> failed

Exiting early after 1 failures. 6694 tests run.
132.45s total testing time

6693 test cases (99%) succeeded
1 test case (<1%) had incorrect layout

Full output: http://queues.webkit.org/results/3572870
Comment 41 Tony Gentilcore 2010-08-04 13:39:51 PDT
Committed r64674: <http://trac.webkit.org/changeset/64674>
Comment 42 Tony Gentilcore 2010-08-04 13:54:17 PDT
> fast/dom/HTMLScriptElement/defer-script-invalid-url.html -> failed

This test was working as intended but displaying a "blocked external resource request" message at the top.

I changed http://invalid:99999 to http://localhost:99999 and landed.
Comment 43 WebKit Review Bot 2010-08-04 14:12:03 PDT
http://trac.webkit.org/changeset/64674 might have broken GTK Linux 64-bit Debug
Comment 44 Tony Gentilcore 2010-08-05 10:14:37 PDT
Rolled back due to incompatibility with HTML5 tree builder which was enabled last night.

I'm going to rework this so that the insertion point is undefined when deferred scripts are run (which means no doc.write from deferred scripts). That is the behavior specced by HTML5 and it should be fine as IE's original definition of defer was basically a contract that the script won't do any doc.writes.
Comment 45 Tony Gentilcore 2010-08-05 17:11:21 PDT
Updating with a quick survey of document.write in deferred script behavior:

IE5-IE9: Blow away document
FF3.6: Blow away document
Minefield: Drop document.write

The HTML5 spec, says to set the insertion point to undefined before running deferred scripts, but it isn't immediately clear to me whether that means writes should blow away the doc or be dropped. I'm reading more carefully now.
Comment 46 Tony Gentilcore 2010-08-09 20:09:23 PDT
Created attachment 63968 [details]
Work in progress
Comment 47 Tony Gentilcore 2010-08-17 09:41:16 PDT
Created attachment 64600 [details]
Testcase

This is the test case I had in mind.

First close() detaches, then write() implicitly reopens the document.

In the previous attempts, we bailed out of pumpTokenizer() after the first token, so I'd expect this would have resulted in a document with only "<p>" (but haven't verified).

Your patch should continue the loop through all tokens and result in a document with "<p>1<p>2".

This also means your comment that the parser doesn't touch webcore after detach isn't strictly true.
Comment 48 Tony Gentilcore 2010-08-17 09:43:05 PDT
(In reply to comment #47)
> Created an attachment (id=64600) [details]
> Testcase

Oops, wrong tab == wrong bug :-(
Comment 49 Tony Gentilcore 2010-08-31 10:10:47 PDT
Created attachment 66066 [details]
Patch
Comment 50 Eric Seidel (no email) 2010-08-31 12:01:42 PDT
Comment on attachment 66066 [details]
Patch

> WebCore/html/parser/HTMLDocumentParser.cpp:346
> +    stop();
I'm not sure stop() is the right name here.  Since it doesn't set m_parserStopped.

> WebCore/html/parser/HTMLDocumentParser.h:137
> +    bool m_active; // http://www.whatwg.org/specs/web-apps/current-work/#active-parser
Why not just use m_parserStopped?
Comment 51 Tony Gentilcore 2010-08-31 14:01:02 PDT
(In reply to comment #50)
> (From update of attachment 66066 [details])
> > WebCore/html/parser/HTMLDocumentParser.cpp:346
> > +    stop();
> I'm not sure stop() is the right name here.  Since it doesn't set m_parserStopped.

Very good point. More details below, but I'm trying out prepareToEnd().

> 
> > WebCore/html/parser/HTMLDocumentParser.h:137
> > +    bool m_active; // http://www.whatwg.org/specs/web-apps/current-work/#active-parser
> Why not just use m_parserStopped?

m_parserStopped is set too late -- closer to the end of 10.2.6 #the-end. When it is set, it does things like prevent further inserts/appends which isn't correct when running deferred scripts. I need something that indicates we're in the deferred scripts phase so that notifiyFinished() can direct the load and inScriptExecution() won't prevent implicit open()s. So I've changed it to m_runningDeferredScripts, please let me know what you think.
Comment 52 Tony Gentilcore 2010-08-31 14:01:21 PDT
Created attachment 66099 [details]
Naming fixes
Comment 53 Eric Seidel (no email) 2010-08-31 14:13:09 PDT
OK.  Seems we're building a state machine with bools.

What about this:

enum ParserState {
   InitialState,
   ParsingState,
   RunningDeferredScriptsState,
   StoppedState,
   DetachedState,
};

That's an approximation, but maybe the idea is sound?
Comment 54 Tony Gentilcore 2010-08-31 14:41:16 PDT
(In reply to comment #53)
> OK.  Seems we're building a state machine with bools.
> 
> What about this:
> 
> enum ParserState {
>    InitialState,
>    ParsingState,
>    RunningDeferredScriptsState,
>    StoppedState,
>    DetachedState,
> };
> 
> That's an approximation, but maybe the idea is sound?

Yeah, that sounds very reasonable. Before I go down that path, just wanted to bounce one alternative by you.

The state will need to be tracked on the Document's readyState as well. It currently has an m_bParsing attribute and a really shady implementation of readyState(). I could instead do this on Document:

enum ReadyState {
  Loading,
  Interactive,
  Complete
}

Then DocumentParser could use m_document->readyState() == Interactive as RunningDeferredScriptsState and everything else would be untouched.
Comment 55 Eric Seidel (no email) 2010-08-31 14:49:00 PDT
I don't know what that would look like, but sounds sane.

I think we eventually need a state machine in the parser too, or maybe they're the same thing.  it's unclear to me.
Comment 56 Tony Gentilcore 2010-08-31 14:50:59 PDT
(In reply to comment #55)
> I don't know what that would look like, but sounds sane.
> 
> I think we eventually need a state machine in the parser too, or maybe they're the same thing.  it's unclear to me.

OK. I'll explore and see which one turns out cleaner.

As another point, it would be kind of odd to have a RunningDeferredScriptsState in DocumentParser when not all subclasses would utilize that state.
Comment 57 Tony Gentilcore 2010-08-31 18:34:58 PDT
Created attachment 66157 [details]
Use enum for parser state
Comment 58 Early Warning System Bot 2010-08-31 18:46:34 PDT
Attachment 66157 [details] did not build on qt:
Build output: http://queues.webkit.org/results/3917003
Comment 59 Tony Gentilcore 2010-08-31 20:09:52 PDT
Created attachment 66162 [details]
Fix Qt build
Comment 60 Adam Barth 2010-08-31 20:21:37 PDT
Comment on attachment 66162 [details]
Fix Qt build

View in context: https://bugs.webkit.org/attachment.cgi?id=66162&action=prettypatch

This looks great.  A few minor comments below.  The bitfield one is the main thing.

> WebCore/ChangeLog:16
> +2010-08-31  Tony Gentilcore  <tonyg@chromium.org>
> +
> +        Reviewed by NOBODY (OOPS!).
> +
> +        Support <script defer> as specified by HTML5
> +        https://bugs.webkit.org/show_bug.cgi?id=40934
> +
> +        Tests: fast/dom/HTMLScriptElement/defer-double-defer-write.html
> +               fast/dom/HTMLScriptElement/defer-double-write.html
> +               fast/dom/HTMLScriptElement/defer-inline-script.html
> +               fast/dom/HTMLScriptElement/defer-onbeforeload.html
> +               fast/dom/HTMLScriptElement/defer-script-invalid-url.html
> +               fast/dom/HTMLScriptElement/defer-write.html
> +               fast/dom/HTMLScriptElement/two-defer-writes.html
> +               http/tests/misc/script-defer-after-slow-stylesheet.html
> +               http/tests/misc/script-defer.html
So much boilerplate in the ChangeLog but no actual text.  :)

> WebCore/dom/DocumentParser.h:69
> +    bool isParsing() const { return m_state == ParsingState; }
> +    bool isActive() const { return m_state & (ParsingState | StoppingState); }
> +    bool isStopping() const { return m_state == StoppingState; }
> +    bool isDetached() const { return m_state == DetachedState; }
Can you ASSERT m_document (as appropriate) in these states?

> WebCore/dom/DocumentParser.h:100
> +    enum ParserState {
> +        ParsingState = 0x1,
> +        StoppingState = 0x2,
> +        StoppedState = 0x4,
> +        DetachedState = 0x8
> +    };
Woah.  Do we really need a bit field here?  It seems like an enum should be enough.
Comment 61 Tony Gentilcore 2010-08-31 20:54:28 PDT
> So much boilerplate in the ChangeLog but no actual text.  :)
Sorry, I keep losing my comments with all the iterations here. I'll add them back before landing.

> Can you ASSERT m_document (as appropriate) in these states?
Unfortunately that wouldn't be very clean as these are just checking the state so they may be called at any time. I'd have to do something like ASSERT(m_state == ParsingState || m_state == StoppingState || m_state == StoppedState || !m_document); Is that what you have in mind?
 
> Woah.  Do we really need a bit field here?  It seems like an enum should be enough.
I thought it would be more efficient, but it looks like the compiler is smart enough to optimize away the two states in isActive(). So I've removed the bitfield.

Thanks for the review. Will land in the morning.
Comment 62 Adam Barth 2010-08-31 21:46:55 PDT
> > Woah.  Do we really need a bit field here?  It seems like an enum should be enough.
> I thought it would be more efficient, but it looks like the compiler is smart enough to optimize away the two states in isActive(). So I've removed the bitfield.

Thanks.
Comment 63 Eric Seidel (no email) 2010-08-31 22:20:01 PDT
Comment on attachment 66162 [details]
Fix Qt build

View in context: https://bugs.webkit.org/attachment.cgi?id=66162&action=prettypatch

> WebCore/dom/Document.cpp:1827
> +        if (m_frame->loader()->isLoadingMainResource() || (parser && parser->isParsing() && parser->isExecutingScript()))
Why the extra check needed here?

> WebCore/dom/DocumentParser.h:61
>      virtual bool processingData() const { return false; }
We should just remove this now since clearly it doesn't work anymore...  (A matter for a separate bug.)

> WebCore/dom/DocumentParser.h:67
> +    bool isActive() const { return m_state & (ParsingState | StoppingState); }
I would have used a <  I thought you moved off of bitfields?

> WebCore/dom/DocumentParser.h:100
> +    };
Ah, I see, not yet moved off of a bitfield in this patch.

> WebCore/dom/XMLDocumentParser.cpp:135
> +    if (!isActive() || m_sawXSLTransform)
!isActive() seems a strange check.  why not just isStopped() and have "stopped" check m_state >= Stopped?

> WebCore/html/parser/HTMLDocumentParser.cpp:163
> +    if (m_scriptRunner) {
> +        if (!m_scriptRunner->executeScriptsWaitingForParsing())
Why not combine these into one line?

!isActive() feels a little strange.  Thanks again for all your work on this!
Comment 64 Tony Gentilcore 2010-09-01 08:47:49 PDT
> > +        if (m_frame->loader()->isLoadingMainResource() || (parser && parser->isParsing() && parser->isExecutingScript()))
> Why the extra check needed here?
When this condition is satisfied, the method bails out early thus preventing an implicit open from a write() in a deferred script. In an earlier version of the patch, I had added the check to isExecutingScript() so that it only returns true when executing a parsing-blocking script, not a deferred script. But now that the method is accessible on parser, this seems cleaner. Let me know if you want it moved.

> >      virtual bool processingData() const { return false; }
> We should just remove this now since clearly it doesn't work anymore...  (A matter for a separate bug.)
Yep, I can put together a subsequent patch.

> > +    bool isActive() const { return m_state & (ParsingState | StoppingState); }
> I would have used a <  I thought you moved off of bitfields?
Done.

> > +    if (!isActive() || m_sawXSLTransform)
> !isActive() seems a strange check.  why not just isStopped() and have "stopped" check m_state >= Stopped?
Good call. Done.

> > +    if (m_scriptRunner) {
> > +        if (!m_scriptRunner->executeScriptsWaitingForParsing())
> Why not combine these into one line?
That was the original. You asked to break it up in Comment #21. Going back to one line now ;-)
Comment 65 Tony Gentilcore 2010-09-01 09:11:09 PDT
Created attachment 66219 [details]
Patch for landing
Comment 66 WebKit Commit Bot 2010-09-01 10:31:01 PDT
Comment on attachment 66219 [details]
Patch for landing

Rejecting patch 66219 from commit-queue.

Failed to run "['WebKitTools/Scripts/run-webkit-tests', '--no-launch-safari', '--exit-after-n-failures=1', '--wait-for-httpd', '--ignore-tests', 'compositing,media', '--quiet']" exit_code: 1
Running build-dumprendertree
Compiling Java tests
make: Nothing to be done for `default'.
Running tests from /Users/eseidel/Projects/CommitQueue/LayoutTests
Testing 20902 test cases.
fast/dom/HTMLScriptElement/defer-inline-script.html -> failed

Exiting early after 1 failures. 6743 tests run.
164.54s total testing time

6742 test cases (99%) succeeded
1 test case (<1%) had incorrect layout

Full output: http://queues.webkit.org/results/3974018
Comment 67 Tony Gentilcore 2010-09-01 15:03:40 PDT
Created attachment 66281 [details]
Patch for landing
Comment 68 WebKit Commit Bot 2010-09-02 02:06:27 PDT
Comment on attachment 66281 [details]
Patch for landing

Clearing flags on attachment: 66281

Committed r66649: <http://trac.webkit.org/changeset/66649>
Comment 69 WebKit Commit Bot 2010-09-02 02:06:36 PDT
All reviewed patches have been landed.  Closing bug.
Comment 70 WebKit Review Bot 2010-09-02 03:38:45 PDT
http://trac.webkit.org/changeset/66649 might have broken Qt Linux Release
Comment 71 Csaba Osztrogonác 2010-09-02 06:07:25 PDT
Reopened, because it was rolled out by http://trac.webkit.org/changeset/66660

Qt bot got the following (or similar) error for ~2000 tests:
"This page contains the following errors:
error on line 1 at column 2: 
Below is a rendering of the page up to the first error."
Comment 72 Tony Gentilcore 2010-09-02 08:43:31 PDT
> Qt bot got the following (or similar) error for ~2000 tests:
> "This page contains the following errors:
> error on line 1 at column 2: 
> Below is a rendering of the page up to the first error."

Oops, it looks like I swapped startParsing() and stopParsing() in the XMLDocumentParserQt.cpp :-(

Fixed and landing w/o the cq. I'll watch it as it lands this time.
Comment 73 Tony Gentilcore 2010-09-02 08:46:12 PDT
Committed r66670: <http://trac.webkit.org/changeset/66670>