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..fac36933c429c751375108ddd4cef395a400837a 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,8 @@ bool HTMLDocumentParser::isParsingFragment() const { |
} |
void HTMLDocumentParser::pumpTokenizerIfPossible() { |
- if (isStopped() || isWaitingForScripts()) |
+ checkIfBodyStlyesheetAdded(); |
+ if (isStopped() || isPaused()) |
return; |
pumpTokenizer(); |
@@ -271,6 +274,10 @@ void HTMLDocumentParser::resumeParsingAfterYield() { |
ASSERT(shouldUseThreading()); |
ASSERT(m_haveBackgroundParser); |
+ checkIfBodyStlyesheetAdded(); |
+ if (isStopped() || isPaused()) |
+ return; |
+ |
pumpPendingSpeculations(); |
} |
@@ -283,6 +290,7 @@ void HTMLDocumentParser::runScriptsForPausedTreeBuilder() { |
// We will not have a scriptRunner when parsing a DocumentFragment. |
if (m_scriptRunner) |
m_scriptRunner->processScriptElement(scriptElement, scriptStartPosition); |
+ checkIfBodyStlyesheetAdded(); |
} |
bool HTMLDocumentParser::canTakeNextToken() { |
@@ -293,7 +301,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 +389,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 +407,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 +497,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); |
@@ -550,10 +559,11 @@ size_t HTMLDocumentParser::processTokenizedChunkFromBackgroundParser( |
fetchQueuedPreloads(); |
} |
- if (isWaitingForScripts()) { |
- // The </script> is assumed to be the last token of this bunch. |
+ if (isPaused()) { |
+ // The script or stylesheet should be the last token of this bunch. |
ASSERT(it + 1 == tokens->end()); |
- runScriptsForPausedTreeBuilder(); |
+ if (isWaitingForScripts()) |
+ runScriptsForPausedTreeBuilder(); |
validateSpeculations(std::move(chunk)); |
break; |
} |
@@ -586,8 +596,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 +631,8 @@ 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()) |
+ checkIfBodyStlyesheetAdded(); |
+ if (!isParsing() || isPaused() || isScheduledForResume()) |
break; |
if (m_speculations.isEmpty() || |
@@ -709,7 +720,7 @@ void HTMLDocumentParser::pumpTokenizer() { |
m_treeBuilder->flush(FlushAlways); |
RELEASE_ASSERT(!isStopped()); |
- if (isWaitingForScripts()) { |
+ if (isPaused()) { |
ASSERT(m_tokenizer->getState() == HTMLTokenizer::DataState); |
ASSERT(m_preloader); |
@@ -747,6 +758,7 @@ void HTMLDocumentParser::constructTreeFromHTMLToken() { |
token().clear(); |
m_treeBuilder->constructTree(&atomicToken); |
+ checkIfBodyStlyesheetAdded(); |
// FIXME: constructTree may synchronously cause Document to be detached. |
if (!m_token) |
@@ -762,6 +774,7 @@ void HTMLDocumentParser::constructTreeFromCompactHTMLToken( |
const CompactHTMLToken& compactToken) { |
AtomicHTMLToken token(compactToken); |
m_treeBuilder->constructTree(&token); |
+ checkIfBodyStlyesheetAdded(); |
} |
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,18 @@ bool HTMLDocumentParser::isWaitingForScripts() const { |
m_reentryPermit->parserPauseFlag(); |
} |
-void HTMLDocumentParser::resumeParsingAfterScriptExecution() { |
+void HTMLDocumentParser::resumeParsingAfterPause() { |
ASSERT(!isExecutingScript()); |
- ASSERT(!isWaitingForScripts()); |
+ DCHECK(!isPaused()); |
+ |
+ checkIfBodyStlyesheetAdded(); |
+ if (isPaused()) |
+ return; |
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 +1120,48 @@ 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::didLoadAllBodyStylesheets() { |
+ // 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::checkIfBodyStlyesheetAdded() { |
+ if (m_addedPendingStylesheetInBody) { |
+ m_addedPendingStylesheetInBody = false; |
+ m_isWaitingForStylesheets = true; |
+ } |
} |
void HTMLDocumentParser::parseDocumentFragment( |