Chromium Code Reviews| Index: third_party/WebKit/Source/core/html/parser/HTMLDocumentParser.cpp |
| diff --git a/third_party/WebKit/Source/core/html/parser/HTMLDocumentParser.cpp b/third_party/WebKit/Source/core/html/parser/HTMLDocumentParser.cpp |
| index d3d8d1ed36c91d0f28aba5ba2016f8c71dde1c23..bbbab42d5624b4970af129f4a7e4f993f62f15c4 100644 |
| --- a/third_party/WebKit/Source/core/html/parser/HTMLDocumentParser.cpp |
| +++ b/third_party/WebKit/Source/core/html/parser/HTMLDocumentParser.cpp |
| @@ -151,7 +151,9 @@ HTMLDocumentParser::HTMLDocumentParser(Document& document, |
| m_pumpSessionNestingLevel(0), |
| m_pumpSpeculationsSessionNestingLevel(0), |
| m_isParsingAtLineNumber(false), |
| - m_triedLoadingLinkHeaders(false) { |
| + m_triedLoadingLinkHeaders(false), |
| + m_addedPendingStylesheetInBody(false), |
| + m_isWaitingForStylesheets(false) { |
| ASSERT(shouldUseThreading() || (m_token && m_tokenizer)); |
| // Threading is not allowed in prefetch mode. |
| DCHECK(!document.isPrefetchOnly() || !shouldUseThreading()); |
| @@ -256,7 +258,7 @@ bool HTMLDocumentParser::isParsingFragment() const { |
| } |
| void HTMLDocumentParser::pumpTokenizerIfPossible() { |
| - if (isStopped() || isWaitingForScripts()) |
| + if (isStopped() || isPaused()) |
| return; |
| pumpTokenizer(); |
| @@ -293,7 +295,7 @@ bool HTMLDocumentParser::canTakeNextToken() { |
| // continuing. |
| if (m_treeBuilder->hasParserBlockingScript()) |
| runScriptsForPausedTreeBuilder(); |
| - if (isStopped() || isWaitingForScripts()) |
| + if (isStopped() || isPaused()) |
| return false; |
| // FIXME: It's wrong for the HTMLDocumentParser to reach back to the |
| @@ -381,7 +383,7 @@ void HTMLDocumentParser::notifyPendingTokenizedChunks() { |
| for (auto& chunk : pendingChunks) |
| m_speculations.append(std::move(chunk)); |
| - if (!isWaitingForScripts() && !isScheduledForResume()) { |
| + if (!isPaused() && !isScheduledForResume()) { |
| if (m_tasksWereSuspended) |
| m_parserScheduler->forceResumeAfterYield(); |
| else |
| @@ -399,19 +401,20 @@ void HTMLDocumentParser::validateSpeculations( |
| ASSERT(chunk); |
| // TODO(kouhei): We should simplify codepath here by disallowing |
| // validateSpeculations |
| - // while isWaitingForScripts, and m_lastChunkBeforeScript can simply be |
| + // while isPaused, and m_lastChunkBeforePause can simply be |
| // pushed to m_speculations. |
| - if (isWaitingForScripts()) { |
| - // We're waiting on a network script, just save the chunk, we'll get a |
| - // second validateSpeculations call after the script completes. This call |
| - // should have been made immediately after runScriptsForPausedTreeBuilder |
| - // which may have started a network load and left us waiting. |
| - ASSERT(!m_lastChunkBeforeScript); |
| - m_lastChunkBeforeScript = std::move(chunk); |
| + if (isPaused()) { |
| + // We're waiting on a network script or stylesheet, just save the chunk, |
| + // we'll get a second validateSpeculations call after the script or |
| + // stylesheet completes. This call should have been made immediately after |
| + // runScriptsForPausedTreeBuilder in the script case which may have started |
| + // a network load and left us waiting. |
| + DCHECK(!m_lastChunkBeforePause); |
| + m_lastChunkBeforePause = std::move(chunk); |
| return; |
| } |
| - ASSERT(!m_lastChunkBeforeScript); |
| + DCHECK(!m_lastChunkBeforePause); |
| std::unique_ptr<HTMLTokenizer> tokenizer = std::move(m_tokenizer); |
| std::unique_ptr<HTMLToken> token = std::move(m_token); |
| @@ -488,12 +491,12 @@ size_t HTMLDocumentParser::processTokenizedChunkFromBackgroundParser( |
| SECURITY_DCHECK(m_pumpSpeculationsSessionNestingLevel == 1); |
| SECURITY_DCHECK(!inPumpSession()); |
| ASSERT(!isParsingFragment()); |
| - ASSERT(!isWaitingForScripts()); |
| + DCHECK(!isPaused()); |
| ASSERT(!isStopped()); |
| ASSERT(shouldUseThreading()); |
| ASSERT(!m_tokenizer); |
| ASSERT(!m_token); |
| - ASSERT(!m_lastChunkBeforeScript); |
| + DCHECK(!m_lastChunkBeforePause); |
| std::unique_ptr<TokenizedChunk> chunk(std::move(popChunk)); |
| std::unique_ptr<CompactHTMLTokenStream> tokens = std::move(chunk->tokens); |
| @@ -558,6 +561,11 @@ size_t HTMLDocumentParser::processTokenizedChunkFromBackgroundParser( |
| break; |
| } |
| + if (m_isWaitingForStylesheets && it + 1 == tokens->end()) { |
|
kouhei (in TOK)
2017/01/12 11:58:13
Can we merge this to the if condition above?
It is
Pat Meenan
2017/01/12 13:26:46
I'm not 100% sure that there aren't any conditions
|
| + validateSpeculations(std::move(chunk)); |
| + break; |
| + } |
| + |
| if (it->type() == HTMLToken::EndOfFile) { |
| // The EOF is assumed to be the last token of this bunch. |
| ASSERT(it + 1 == tokens->end()); |
| @@ -586,8 +594,8 @@ void HTMLDocumentParser::pumpPendingSpeculations() { |
| // m_tokenizer and m_token don't have state that invalidates m_speculations. |
| ASSERT(!m_tokenizer); |
| ASSERT(!m_token); |
| - ASSERT(!m_lastChunkBeforeScript); |
| - ASSERT(!isWaitingForScripts()); |
| + DCHECK(!m_lastChunkBeforePause); |
| + DCHECK(!isPaused()); |
| ASSERT(!isStopped()); |
| ASSERT(!isScheduledForResume()); |
| ASSERT(!inPumpSession()); |
| @@ -621,7 +629,7 @@ void HTMLDocumentParser::pumpPendingSpeculations() { |
| // isScheduledForResume() may be set here as a result of |
| // processTokenizedChunkFromBackgroundParser running arbitrary javascript |
| // which invokes nested event loops. (e.g. inspector breakpoints) |
| - if (!isParsing() || isWaitingForScripts() || isScheduledForResume()) |
| + if (!isParsing() || isPaused() || isScheduledForResume()) |
| break; |
| if (m_speculations.isEmpty() || |
| @@ -709,7 +717,7 @@ void HTMLDocumentParser::pumpTokenizer() { |
| m_treeBuilder->flush(FlushAlways); |
| RELEASE_ASSERT(!isStopped()); |
| - if (isWaitingForScripts()) { |
| + if (isPaused()) { |
| ASSERT(m_tokenizer->getState() == HTMLTokenizer::DataState); |
| ASSERT(m_preloader); |
| @@ -748,6 +756,9 @@ void HTMLDocumentParser::constructTreeFromHTMLToken() { |
| m_treeBuilder->constructTree(&atomicToken); |
| + if (m_addedPendingStylesheetInBody) |
| + m_isWaitingForStylesheets = true; |
| + |
| // FIXME: constructTree may synchronously cause Document to be detached. |
| if (!m_token) |
| return; |
| @@ -762,6 +773,8 @@ void HTMLDocumentParser::constructTreeFromCompactHTMLToken( |
| const CompactHTMLToken& compactToken) { |
| AtomicHTMLToken token(compactToken); |
| m_treeBuilder->constructTree(&token); |
| + if (m_addedPendingStylesheetInBody) |
| + m_isWaitingForStylesheets = true; |
| } |
| bool HTMLDocumentParser::hasInsertionPoint() { |
| @@ -792,7 +805,7 @@ void HTMLDocumentParser::insert(const SegmentedString& source) { |
| m_input.insertAtCurrentInsertionPoint(excludedLineNumberSource); |
| pumpTokenizerIfPossible(); |
| - if (isWaitingForScripts()) { |
| + if (isPaused()) { |
| // Check the document.write() output with a separate preload scanner as |
| // the main scanner can't deal with insertions. |
| if (!m_insertionPreloadScanner) |
| @@ -905,14 +918,14 @@ void HTMLDocumentParser::append(const String& inputSource) { |
| } |
| if (m_preloadScanner) { |
| - if (m_input.current().isEmpty() && !isWaitingForScripts()) { |
| + if (m_input.current().isEmpty() && !isPaused()) { |
| // We have parsed until the end of the current input and so are now moving |
| // ahead of the preload scanner. Clear the scanner so we know to scan |
| // starting from the current input point if we block again. |
| m_preloadScanner.reset(); |
| } else { |
| m_preloadScanner->appendToEnd(source); |
| - if (isWaitingForScripts()) |
| + if (isPaused()) |
| scanAndPreload(m_preloadScanner.get()); |
| } |
| } |
| @@ -1063,14 +1076,14 @@ bool HTMLDocumentParser::isWaitingForScripts() const { |
| m_reentryPermit->parserPauseFlag(); |
| } |
| -void HTMLDocumentParser::resumeParsingAfterScriptExecution() { |
| +void HTMLDocumentParser::resumeParsingAfterPause() { |
| ASSERT(!isExecutingScript()); |
| - ASSERT(!isWaitingForScripts()); |
| + DCHECK(!isPaused()); |
| if (m_haveBackgroundParser) { |
| - if (m_lastChunkBeforeScript) { |
| - validateSpeculations(std::move(m_lastChunkBeforeScript)); |
| - DCHECK(!m_lastChunkBeforeScript); |
| + if (m_lastChunkBeforePause) { |
| + validateSpeculations(std::move(m_lastChunkBeforePause)); |
| + DCHECK(!m_lastChunkBeforePause); |
| pumpPendingSpeculations(); |
| } |
| return; |
| @@ -1103,19 +1116,41 @@ void HTMLDocumentParser::notifyScriptLoaded(PendingScript* pendingScript) { |
| } |
| m_scriptRunner->executeScriptsWaitingForLoad(pendingScript); |
| - if (!isWaitingForScripts()) |
| - resumeParsingAfterScriptExecution(); |
| + if (!isPaused()) |
| + resumeParsingAfterPause(); |
| } |
| void HTMLDocumentParser::executeScriptsWaitingForResources() { |
| DCHECK(document()->isScriptExecutionReady()); |
| + if (m_isWaitingForStylesheets) |
| + m_isWaitingForStylesheets = false; |
| + |
| // Document only calls this when the Document owns the DocumentParser so this |
| // will not be called in the DocumentFragment case. |
| DCHECK(m_scriptRunner); |
| m_scriptRunner->executeScriptsWaitingForResources(); |
| - if (!isWaitingForScripts()) |
| - resumeParsingAfterScriptExecution(); |
| + if (!isPaused()) |
| + resumeParsingAfterPause(); |
| +} |
| + |
| +void HTMLDocumentParser::didAddPendingStylesheetInBody() { |
| + // When in-body CSS doesn't block painting, the parser needs to pause so that |
| + // the DOM doesn't include any elements that may depend on the CSS for style. |
| + // The stylesheet can be added and removed during the parsing of a single |
| + // token so don't actually set the bit to block parsing here, just track |
| + // the state of the added sheet in case it does persist beyond a single |
| + // token. |
| + if (RuntimeEnabledFeatures::cssInBodyDoesNotBlockPaintEnabled()) |
| + m_addedPendingStylesheetInBody = true; |
| +} |
| + |
| +void HTMLDocumentParser::didLoadAllStylesheets() { |
| + // Just toggle the stylesheet flag here (mostly for synchronous sheets). |
| + // The document will also call into executeScriptsWaitingForResources |
| + // which is when the parser will re-start, otherwise it will attempt to |
| + // resume twice which could cause state machine issues. |
| + m_addedPendingStylesheetInBody = false; |
| } |
| void HTMLDocumentParser::parseDocumentFragment( |