Changeset 122168 in webkit
- Timestamp:
- Jul 9, 2012 4:48:14 PM (12 years ago)
- Location:
- trunk
- Files:
-
- 3 added
- 8 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/LayoutTests/ChangeLog
r122167 r122168 1 2012-07-09 Eric Seidel <eric@webkit.org> 2 3 document.write of scripts that also document.write sometimes writes async 4 https://bugs.webkit.org/show_bug.cgi?id=89102 5 6 Reviewed by Adam Barth. 7 8 * fast/parser/cached-script-document-write-expected.txt: Added. 9 * fast/parser/cached-script-document-write.html: Added. 10 * fast/parser/resources/cached-script-document-write.js: Added. 11 1 12 2012-07-09 Filip Pizlo <fpizlo@apple.com> 2 13 -
trunk/Source/WebCore/ChangeLog
r122163 r122168 1 2012-07-09 Eric Seidel <eric@webkit.org> 2 3 document.write of scripts that also document.write sometimes writes async 4 https://bugs.webkit.org/show_bug.cgi?id=89102 5 6 Reviewed by Adam Barth. 7 8 When a script tag is first encountered, the TreeBuilder holds the element and returns 9 out to the outer HTMLDocumentParser parse loop. The HTMLDocumentParser then takes 10 the script element and passes it to the HTMLScriptRunner for execution. However, if the 11 script is an "external script" the HTMLScriptRunner may have to wait for that parser 12 blocking script to load, and may store the script in its own m_parserBlockingScript member. 13 14 While the HTMLScriptRunner has this not-yet-loaded-script the parser is also blocked. 15 Because the "paused" state of the parser was held as a separate bool on the TreeBuilder 16 we'd have to be careful to update it to reflect the current state of this pending script 17 on the HTMLScriptRunner. 18 19 This patch removes this separate "paused" bool and makes the HTMLDocumentParser responsible 20 for the "paused" state of the parser through the isWaitingForScripts() function which 21 knows how to check both the TreeBuilder and the ScriptRunner for possible parser-blocking scripts. 22 23 I suspect this change may actually fix a bunch of edge cases where we were not 24 checking for the HTMLScriptRunner's parser blocking script and thus incorrectly ending 25 the parser, or not starting the pre-load scanner, etc. 26 27 As part of this change I also renamed m_haveParsingBlockingScript in HTMLScriptRunner to match 28 the naming style used elsewhere in the parser, as well as removed all the "bool" return values 29 for these parse/execute functions as they are no longer useful (or correct). The correct way 30 is always to check HTMLDocumentParser::isWaitingForScripts(). 31 32 Test: fast/parser/cached-script-document-write.html 33 34 * html/parser/HTMLDocumentParser.cpp: 35 (WebCore::HTMLDocumentParser::pumpTokenizerIfPossible): 36 (WebCore::HTMLDocumentParser::runScriptsForPausedTreeBuilder): 37 (WebCore::HTMLDocumentParser::canTakeNextToken): 38 (WebCore::HTMLDocumentParser::isWaitingForScripts): 39 (WebCore::HTMLDocumentParser::resumeParsingAfterScriptExecution): 40 (WebCore::HTMLDocumentParser::notifyFinished): 41 (WebCore::HTMLDocumentParser::executeScriptsWaitingForStylesheets): 42 * html/parser/HTMLScriptRunner.cpp: 43 (WebCore::HTMLScriptRunner::~HTMLScriptRunner): 44 (WebCore::HTMLScriptRunner::executeParsingBlockingScript): 45 (WebCore::HTMLScriptRunner::execute): 46 (WebCore::HTMLScriptRunner::hasParserBlockingScript): 47 (WebCore::HTMLScriptRunner::executeParsingBlockingScripts): 48 (WebCore::HTMLScriptRunner::executeScriptsWaitingForLoad): 49 (WebCore::HTMLScriptRunner::executeScriptsWaitingForParsing): 50 (WebCore::HTMLScriptRunner::requestParsingBlockingScript): 51 (WebCore::HTMLScriptRunner::runScript): 52 * html/parser/HTMLScriptRunner.h: 53 (HTMLScriptRunner): 54 * html/parser/HTMLTreeBuilder.cpp: 55 (WebCore::HTMLTreeBuilder::HTMLTreeBuilder): 56 (WebCore::HTMLTreeBuilder::takeScriptToProcess): 57 (WebCore::HTMLTreeBuilder::processEndTag): 58 (WebCore::HTMLTreeBuilder::processTokenInForeignContent): 59 * html/parser/HTMLTreeBuilder.h: 60 (HTMLTreeBuilder): 61 (WebCore::HTMLTreeBuilder::hasParserBlockingScript): 62 1 63 2012-07-09 Ryosuke Niwa <rniwa@webkit.org> 2 64 -
trunk/Source/WebCore/html/parser/HTMLDocumentParser.cpp
r121858 r122168 166 166 void HTMLDocumentParser::pumpTokenizerIfPossible(SynchronousMode mode) 167 167 { 168 if (isStopped() || m_treeBuilder->isPaused())168 if (isStopped() || isWaitingForScripts()) 169 169 return; 170 170 … … 196 196 } 197 197 198 bool HTMLDocumentParser::runScriptsForPausedTreeBuilder() 199 { 200 ASSERT(m_treeBuilder->isPaused()); 201 198 void HTMLDocumentParser::runScriptsForPausedTreeBuilder() 199 { 202 200 TextPosition scriptStartPosition = TextPosition::belowRangePosition(); 203 201 RefPtr<Element> scriptElement = m_treeBuilder->takeScriptToProcess(scriptStartPosition); 204 202 // We will not have a scriptRunner when parsing a DocumentFragment. 205 if (!m_scriptRunner) 206 return true; 207 return m_scriptRunner->execute(scriptElement.release(), scriptStartPosition); 203 if (m_scriptRunner) 204 m_scriptRunner->execute(scriptElement.release(), scriptStartPosition); 208 205 } 209 206 … … 213 210 return false; 214 211 215 // The parser will pause itself when waiting on a script to load or run. 216 if (m_treeBuilder->isPaused()) { 212 if (isWaitingForScripts()) { 217 213 if (mode == AllowYield) 218 214 m_parserScheduler->checkForYieldBeforeScript(session); … … 223 219 224 220 // If we're paused waiting for a script, we try to execute scripts before continuing. 225 bool shouldContinueParsing = runScriptsForPausedTreeBuilder(); 226 m_treeBuilder->setPaused(!shouldContinueParsing); 227 if (!shouldContinueParsing || isStopped()) 221 runScriptsForPausedTreeBuilder(); 222 if (isWaitingForScripts() || isStopped()) 228 223 return false; 229 224 } … … 386 381 ASSERT(isStopping()); 387 382 ASSERT(!hasInsertionPoint()); 388 if (m_scriptRunner && !m_scriptRunner->executeScriptsWaitingForParsing()) 389 return; 383 if (m_scriptRunner) { 384 m_scriptRunner->executeScriptsWaitingForParsing(); 385 if (isWaitingForScripts()) 386 return; 387 } 390 388 end(); 391 389 } … … 463 461 bool HTMLDocumentParser::isWaitingForScripts() const 464 462 { 465 return m_treeBuilder->isPaused(); 463 // When the TreeBuilder encounters a </script> tag, it returns to the HTMLDocumentParser 464 // where the script is transfered from the treebuilder to the script runner. 465 // The script runner will hold the script until its loaded and run. During 466 // any of this time, we want to count ourselves as "waiting for a script" and thus 467 // run the preload scanner, as well as delay completion of parsing. 468 bool treeBuilderHasBlockingScript = m_treeBuilder->hasParserBlockingScript(); 469 bool scriptRunnerHasBlockingScript = m_scriptRunner && m_scriptRunner->hasParserBlockingScript(); 470 // Since the parser is paused while a script runner has a blocking script, it should 471 // never be possible to end up with both objects holding a blocking script. 472 ASSERT(!(treeBuilderHasBlockingScript && scriptRunnerHasBlockingScript)); 473 // If either object has a blocking script, the parser should be paused. 474 return treeBuilderHasBlockingScript || scriptRunnerHasBlockingScript; 466 475 } 467 476 … … 469 478 { 470 479 ASSERT(!isExecutingScript()); 471 ASSERT(! m_treeBuilder->isPaused());480 ASSERT(!isWaitingForScripts()); 472 481 473 482 m_insertionPreloadScanner.clear(); … … 510 519 } 511 520 512 ASSERT(m_treeBuilder->isPaused()); 513 // Note: We only ever wait on one script at a time, so we always know this 514 // is the one we were waiting on and can un-pause the tree builder. 515 m_treeBuilder->setPaused(false); 516 bool shouldContinueParsing = m_scriptRunner->executeScriptsWaitingForLoad(cachedResource); 517 m_treeBuilder->setPaused(!shouldContinueParsing); 518 if (shouldContinueParsing) 521 m_scriptRunner->executeScriptsWaitingForLoad(cachedResource); 522 if (!isWaitingForScripts()) 519 523 resumeParsingAfterScriptExecution(); 520 524 } … … 534 538 // but we need to ensure it isn't deleted yet. 535 539 RefPtr<HTMLDocumentParser> protect(this); 536 537 ASSERT(!m_scriptRunner->isExecutingScript()); 538 ASSERT(m_treeBuilder->isPaused()); 539 // Note: We only ever wait on one script at a time, so we always know this 540 // is the one we were waiting on and can un-pause the tree builder. 541 m_treeBuilder->setPaused(false); 542 bool shouldContinueParsing = m_scriptRunner->executeScriptsWaitingForStylesheets(); 543 m_treeBuilder->setPaused(!shouldContinueParsing); 544 if (shouldContinueParsing) 540 m_scriptRunner->executeScriptsWaitingForStylesheets(); 541 if (!isWaitingForScripts()) 545 542 resumeParsingAfterScriptExecution(); 546 543 } -
trunk/Source/WebCore/html/parser/HTMLDocumentParser.h
r121623 r122168 125 125 void pumpTokenizerIfPossible(SynchronousMode); 126 126 127 boolrunScriptsForPausedTreeBuilder();127 void runScriptsForPausedTreeBuilder(); 128 128 void resumeParsingAfterScriptExecution(); 129 129 -
trunk/Source/WebCore/html/parser/HTMLScriptRunner.cpp
r95901 r122168 58 58 { 59 59 // FIXME: Should we be passed a "done loading/parsing" callback sooner than destruction? 60 if (m_pars ingBlockingScript.cachedScript() && m_parsingBlockingScript.watchingForLoad())61 stopWatchingForLoad(m_pars ingBlockingScript);60 if (m_parserBlockingScript.cachedScript() && m_parserBlockingScript.watchingForLoad()) 61 stopWatchingForLoad(m_parserBlockingScript); 62 62 63 63 while (!m_scriptsToExecuteAfterParsing.isEmpty()) { … … 113 113 ASSERT(!m_scriptNestingLevel); 114 114 ASSERT(m_document->haveStylesheetsLoaded()); 115 ASSERT(isPendingScriptReady(m_pars ingBlockingScript));115 ASSERT(isPendingScriptReady(m_parserBlockingScript)); 116 116 117 117 InsertionPointRecord insertionPointRecord(m_host->inputStream()); 118 executePendingScriptAndDispatchEvent(m_pars ingBlockingScript);118 executePendingScriptAndDispatchEvent(m_parserBlockingScript); 119 119 } 120 120 … … 160 160 // This function should match 10.2.5.11 "An end tag whose tag name is 'script'" 161 161 // Script handling lives outside the tree builder to keep the each class simple. 162 boolHTMLScriptRunner::execute(PassRefPtr<Element> scriptElement, const TextPosition& scriptStartPosition)162 void HTMLScriptRunner::execute(PassRefPtr<Element> scriptElement, const TextPosition& scriptStartPosition) 163 163 { 164 164 ASSERT(scriptElement); 165 // FIXME: If scripting is disabled, always just return true;165 // FIXME: If scripting is disabled, always just return. 166 166 167 167 bool hadPreloadScanner = m_host->hasPreloadScanner(); … … 170 170 runScript(scriptElement.get(), scriptStartPosition); 171 171 172 if (ha veParsingBlockingScript()) {172 if (hasParserBlockingScript()) { 173 173 if (m_scriptNestingLevel) 174 return false; // Block the parser.Unwind to the outermost HTMLScriptRunner::execute before continuing parsing.174 return; // Unwind to the outermost HTMLScriptRunner::execute before continuing parsing. 175 175 // If preload scanner got created, it is missing the source after the current insertion point. Append it and scan. 176 176 if (!hadPreloadScanner && m_host->hasPreloadScanner()) 177 177 m_host->appendCurrentInputStreamToPreloadScannerAndScan(); 178 if (!executeParsingBlockingScripts()) 179 return false; // We still have a parsing blocking script, block the parser. 180 } 181 return true; // Scripts executed as expected, continue parsing. 182 } 183 184 bool HTMLScriptRunner::haveParsingBlockingScript() const 185 { 186 return !!m_parsingBlockingScript.element(); 187 } 188 189 bool HTMLScriptRunner::executeParsingBlockingScripts() 190 { 191 while (haveParsingBlockingScript()) { 192 // We only really need to check once. 193 if (!isPendingScriptReady(m_parsingBlockingScript)) 194 return false; 178 executeParsingBlockingScripts(); 179 } 180 } 181 182 bool HTMLScriptRunner::hasParserBlockingScript() const 183 { 184 return !!m_parserBlockingScript.element(); 185 } 186 187 void HTMLScriptRunner::executeParsingBlockingScripts() 188 { 189 while (hasParserBlockingScript() && isPendingScriptReady(m_parserBlockingScript)) 195 190 executeParsingBlockingScript(); 196 } 197 return true; 198 } 199 200 bool HTMLScriptRunner::executeScriptsWaitingForLoad(CachedResource* cachedScript) 191 } 192 193 void HTMLScriptRunner::executeScriptsWaitingForLoad(CachedResource* cachedScript) 201 194 { 202 195 ASSERT(!m_scriptNestingLevel); 203 ASSERT(ha veParsingBlockingScript());204 ASSERT_UNUSED(cachedScript, m_pars ingBlockingScript.cachedScript() == cachedScript);205 ASSERT(m_pars ingBlockingScript.cachedScript()->isLoaded());206 returnexecuteParsingBlockingScripts();207 } 208 209 boolHTMLScriptRunner::executeScriptsWaitingForStylesheets()196 ASSERT(hasParserBlockingScript()); 197 ASSERT_UNUSED(cachedScript, m_parserBlockingScript.cachedScript() == cachedScript); 198 ASSERT(m_parserBlockingScript.cachedScript()->isLoaded()); 199 executeParsingBlockingScripts(); 200 } 201 202 void HTMLScriptRunner::executeScriptsWaitingForStylesheets() 210 203 { 211 204 ASSERT(m_document); … … 215 208 ASSERT(!m_scriptNestingLevel); 216 209 ASSERT(m_document->haveStylesheetsLoaded()); 217 returnexecuteParsingBlockingScripts();218 } 219 220 boolHTMLScriptRunner::executeScriptsWaitingForParsing()210 executeParsingBlockingScripts(); 211 } 212 213 void HTMLScriptRunner::executeScriptsWaitingForParsing() 221 214 { 222 215 while (!m_scriptsToExecuteAfterParsing.isEmpty()) { 223 216 ASSERT(!m_scriptNestingLevel); 224 ASSERT(!ha veParsingBlockingScript());217 ASSERT(!hasParserBlockingScript()); 225 218 ASSERT(m_scriptsToExecuteAfterParsing.first().cachedScript()); 226 219 if (!m_scriptsToExecuteAfterParsing.first().cachedScript()->isLoaded()) { 227 220 watchForLoad(m_scriptsToExecuteAfterParsing.first()); 228 return false;221 return; 229 222 } 230 223 PendingScript first = m_scriptsToExecuteAfterParsing.takeFirst(); 231 224 executePendingScriptAndDispatchEvent(first); 225 // FIXME: What is this m_document check for? 232 226 if (!m_document) 233 return false; 234 } 235 return true; 227 return; 228 } 236 229 } 237 230 238 231 void HTMLScriptRunner::requestParsingBlockingScript(Element* element) 239 232 { 240 if (!requestPendingScript(m_pars ingBlockingScript, element))233 if (!requestPendingScript(m_parserBlockingScript, element)) 241 234 return; 242 235 243 ASSERT(m_pars ingBlockingScript.cachedScript());236 ASSERT(m_parserBlockingScript.cachedScript()); 244 237 245 238 // We only care about a load callback if cachedScript is not already 246 // in the cache. Callers will attempt to run the m_parsingBlockingScript239 // in the cache. Callers will attempt to run the m_parserBlockingScript 247 240 // if possible before returning control to the parser. 248 if (!m_pars ingBlockingScript.cachedScript()->isLoaded())249 watchForLoad(m_pars ingBlockingScript);241 if (!m_parserBlockingScript.cachedScript()->isLoaded()) 242 watchForLoad(m_parserBlockingScript); 250 243 } 251 244 … … 279 272 { 280 273 ASSERT(m_document); 281 ASSERT(!ha veParsingBlockingScript());274 ASSERT(!hasParserBlockingScript()); 282 275 { 283 276 InsertionPointRecord insertionPointRecord(m_host->inputStream()); … … 303 296 else if (scriptElement->readyToBeParserExecuted()) { 304 297 if (m_scriptNestingLevel == 1) { 305 m_pars ingBlockingScript.setElement(script);306 m_pars ingBlockingScript.setStartingPosition(scriptStartPosition);298 m_parserBlockingScript.setElement(script); 299 m_parserBlockingScript.setStartingPosition(scriptStartPosition); 307 300 } else { 308 301 ScriptSourceCode sourceCode(script->textContent(), documentURLForScriptExecution(m_document), scriptStartPosition); -
trunk/Source/WebCore/html/parser/HTMLScriptRunner.h
r95901 r122168 54 54 55 55 // Processes the passed in script and any pending scripts if possible. 56 boolexecute(PassRefPtr<Element> scriptToProcess, const TextPosition& scriptStartPosition);56 void execute(PassRefPtr<Element> scriptToProcess, const TextPosition& scriptStartPosition); 57 57 58 boolexecuteScriptsWaitingForLoad(CachedResource*);58 void executeScriptsWaitingForLoad(CachedResource*); 59 59 bool hasScriptsWaitingForStylesheets() const { return m_hasScriptsWaitingForStylesheets; } 60 boolexecuteScriptsWaitingForStylesheets();61 boolexecuteScriptsWaitingForParsing();60 void executeScriptsWaitingForStylesheets(); 61 void executeScriptsWaitingForParsing(); 62 62 63 bool hasParserBlockingScript() const; 63 64 bool isExecutingScript() const { return !!m_scriptNestingLevel; } 64 65 … … 70 71 void executeParsingBlockingScript(); 71 72 void executePendingScriptAndDispatchEvent(PendingScript&); 72 bool haveParsingBlockingScript() const; 73 bool executeParsingBlockingScripts(); 73 void executeParsingBlockingScripts(); 74 74 75 75 void requestParsingBlockingScript(Element*); … … 87 87 Document* m_document; 88 88 HTMLScriptRunnerHost* m_host; 89 PendingScript m_pars ingBlockingScript;89 PendingScript m_parserBlockingScript; 90 90 Deque<PendingScript> m_scriptsToExecuteAfterParsing; // http://www.whatwg.org/specs/web-apps/current-work/#list-of-scripts-that-will-execute-when-the-document-has-finished-parsing 91 91 unsigned m_scriptNestingLevel; -
trunk/Source/WebCore/html/parser/HTMLTreeBuilder.cpp
r122081 r122168 352 352 , m_tree(document, maximumDOMTreeDepth) 353 353 , m_reportErrors(reportErrors) 354 , m_isPaused(false)355 354 , m_insertionMode(InitialMode) 356 355 , m_originalInsertionMode(InitialMode) … … 370 369 , m_tree(fragment, scriptingPermission, maximumDOMTreeDepth) 371 370 , m_reportErrors(false) // FIXME: Why not report errors in fragments? 372 , m_isPaused(false)373 371 , m_insertionMode(InitialMode) 374 372 , m_originalInsertionMode(InitialMode) … … 426 424 PassRefPtr<Element> HTMLTreeBuilder::takeScriptToProcess(TextPosition& scriptStartPosition) 427 425 { 426 ASSERT(m_scriptToProcess); 428 427 // Unpause ourselves, callers may pause us again when processing the script. 429 428 // The HTML5 spec is written as though scripts are executed inside the tree 430 429 // builder. We pause the parser to exit the tree builder, and then resume 431 430 // before running scripts. 432 m_isPaused = false;433 431 scriptStartPosition = m_scriptToProcessStartPosition; 434 432 m_scriptToProcessStartPosition = uninitializedPositionValue1(); … … 2128 2126 if (token.name() == scriptTag) { 2129 2127 // Pause ourselves so that parsing stops until the script can be processed by the caller. 2130 m_isPaused = true;2131 2128 ASSERT(m_tree.currentElement()->hasTagName(scriptTag)); 2132 2129 m_scriptToProcess = m_tree.currentElement(); … … 2751 2748 2752 2749 if (token.name() == SVGNames::scriptTag && m_tree.currentNode()->hasTagName(SVGNames::scriptTag)) { 2753 m_isPaused = true;2754 2750 m_scriptToProcess = m_tree.currentElement(); 2755 2751 m_tree.openElements()->pop(); -
trunk/Source/WebCore/html/parser/HTMLTreeBuilder.h
r122081 r122168 71 71 void detach(); 72 72 73 void setPaused(bool paused) { m_isPaused = paused; }74 bool isPaused() const { return m_isPaused; }75 76 73 // The token really should be passed as a const& since it's never modified. 77 74 void constructTreeFromToken(HTMLToken&); 78 75 void constructTreeFromAtomicToken(AtomicHTMLToken&); 79 76 80 // Must be called when parser is paused before calling the parser again. 77 bool hasParserBlockingScript() const { return !!m_scriptToProcess; } 78 // Must be called to take the parser-blocking script before calling the parser again. 81 79 PassRefPtr<Element> takeScriptToProcess(TextPosition& scriptStartPosition); 82 80 … … 217 215 218 216 bool m_reportErrors; 219 bool m_isPaused;220 217 221 218 // http://www.whatwg.org/specs/web-apps/current-work/multipage/parsing.html#insertion-mode
Note: See TracChangeset
for help on using the changeset viewer.