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 0ed9b645e97913e65ae42861cfb2848a1a67024c..c986b693356515ae49a093936b1a9e7fb8e84058 100644 |
--- a/third_party/WebKit/Source/core/html/parser/HTMLDocumentParser.cpp |
+++ b/third_party/WebKit/Source/core/html/parser/HTMLDocumentParser.cpp |
@@ -31,6 +31,7 @@ |
#include "core/dom/DocumentFragment.h" |
#include "core/dom/DocumentLifecycleObserver.h" |
#include "core/dom/Element.h" |
+#include "core/dom/StyleEngine.h" |
#include "core/frame/LocalFrame.h" |
#include "core/frame/Settings.h" |
#include "core/html/HTMLDocument.h" |
@@ -44,6 +45,7 @@ |
#include "core/inspector/InspectorTraceEvents.h" |
#include "core/loader/DocumentLoader.h" |
#include "core/loader/NavigationScheduler.h" |
+#include "platform/RuntimeEnabledFeatures.h" |
#include "platform/SharedBuffer.h" |
#include "platform/ThreadSafeFunctional.h" |
#include "platform/TraceEvent.h" |
@@ -266,7 +268,7 @@ bool HTMLDocumentParser::processingData() const |
void HTMLDocumentParser::pumpTokenizerIfPossible() |
{ |
- if (isStopped() || isWaitingForScripts()) |
+ if (isStopped() || isWaitingForBlockingResource()) |
return; |
pumpTokenizer(); |
@@ -314,6 +316,9 @@ bool HTMLDocumentParser::canTakeNextToken() |
return false; |
} |
+ if (isWaitingForStyles() || isWaitingForImports()) |
+ return false; |
+ |
// FIXME: It's wrong for the HTMLDocumentParser to reach back to the |
// LocalFrame, but this approach is how the old parser handled |
// stopping when the page assigns window.location. What really |
@@ -358,7 +363,7 @@ void HTMLDocumentParser::notifyPendingParsedChunks() |
for (auto& chunk : pendingChunks) |
m_speculations.append(chunk.release()); |
- if (!isWaitingForScripts() && !isScheduledForResume()) { |
+ if (!isWaitingForBlockingResource() && !isScheduledForResume()) { |
if (m_tasksWereSuspended) |
m_parserScheduler->forceResumeAfterYield(); |
else |
@@ -374,17 +379,17 @@ void HTMLDocumentParser::didReceiveEncodingDataFromBackgroundParser(const Docume |
void HTMLDocumentParser::validateSpeculations(PassOwnPtr<ParsedChunk> chunk) |
{ |
ASSERT(chunk); |
- if (isWaitingForScripts()) { |
- // We're waiting on a network script, just save the chunk, we'll get |
- // a second validateSpeculations call after the script completes. |
+ if (isWaitingForBlockingResource()) { |
+ // We're waiting on a blocking resource, just save the chunk, we'll get |
+ // a second validateSpeculations call after the resource 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 = chunk; |
+ // in the case of a script which may have started a network load and left us waiting. |
+ ASSERT(!m_lastChunkBeforeBlockingResource); |
+ m_lastChunkBeforeBlockingResource = chunk; |
return; |
} |
- ASSERT(!m_lastChunkBeforeScript); |
+ ASSERT(!m_lastChunkBeforeBlockingResource); |
OwnPtr<HTMLTokenizer> tokenizer = m_tokenizer.release(); |
OwnPtr<HTMLToken> token = m_token.release(); |
@@ -436,7 +441,7 @@ size_t HTMLDocumentParser::processParsedChunkFromBackgroundParser(PassOwnPtr<Par |
ASSERT_WITH_SECURITY_IMPLICATION(document()->activeParserCount() == 1); |
ASSERT(!isParsingFragment()); |
- ASSERT(!isWaitingForScripts()); |
+ ASSERT(!isWaitingForBlockingResource()); |
ASSERT(!isStopped()); |
#if !ENABLE(OILPAN) |
// ASSERT that this object is both attached to the Document and protected. |
@@ -445,7 +450,7 @@ size_t HTMLDocumentParser::processParsedChunkFromBackgroundParser(PassOwnPtr<Par |
ASSERT(shouldUseThreading()); |
ASSERT(!m_tokenizer); |
ASSERT(!m_token); |
- ASSERT(!m_lastChunkBeforeScript); |
+ ASSERT(!m_lastChunkBeforeBlockingResource); |
OwnPtr<ParsedChunk> chunk(popChunk); |
OwnPtr<CompactHTMLTokenStream> tokens = chunk->tokens.release(); |
@@ -464,7 +469,7 @@ size_t HTMLDocumentParser::processParsedChunkFromBackgroundParser(PassOwnPtr<Par |
return elementTokenCount; |
for (Vector<CompactHTMLToken>::const_iterator it = tokens->begin(); it != tokens->end(); ++it) { |
- ASSERT(!isWaitingForScripts()); |
+ ASSERT(!isWaitingForBlockingResource()); |
if (!chunk->startingScript && (it->type() == HTMLToken::StartTag || it->type() == HTMLToken::EndTag)) |
elementTokenCount++; |
@@ -497,6 +502,12 @@ size_t HTMLDocumentParser::processParsedChunkFromBackgroundParser(PassOwnPtr<Par |
break; |
} |
+ if (isWaitingForStyles() || isWaitingForImports()) { |
+ ASSERT(it + 1 == tokens->end()); // The token is assumed to be the last token of this bunch. |
+ validateSpeculations(chunk.release()); |
+ break; |
+ } |
+ |
if (it->type() == HTMLToken::EndOfFile) { |
ASSERT(it + 1 == tokens->end()); // The EOF is assumed to be the last token of this bunch. |
ASSERT(m_speculations.isEmpty()); // There should never be any chunks after the EOF. |
@@ -528,15 +539,15 @@ 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()); |
+ ASSERT(!m_lastChunkBeforeBlockingResource); |
+ ASSERT(!isWaitingForBlockingResource()); |
ASSERT(!isStopped()); |
ASSERT(!isScheduledForResume()); |
ASSERT(!inPumpSession()); |
// FIXME: Here should never be reached when there is a blocking script, |
// but it happens in unknown scenarios. See https://crbug.com/440901 |
- if (isWaitingForScripts()) { |
+ if (isWaitingForBlockingResource()) { |
m_parserScheduler->scheduleForResume(); |
return; |
} |
@@ -560,7 +571,7 @@ void HTMLDocumentParser::pumpPendingSpeculations() |
// Surprisingly, isScheduledForResume() may be set here as a result of |
// processParsedChunkFromBackgroundParser running arbitrary javascript |
// which invokes nested event loops. (e.g. inspector breakpoints) |
- if (!isParsing() || isWaitingForScripts() || isScheduledForResume()) |
+ if (!isParsing() || isWaitingForBlockingResource() || isScheduledForResume()) |
break; |
if (m_speculations.isEmpty() || m_parserScheduler->yieldIfNeeded(session, m_speculations.first()->startingScript)) |
@@ -649,7 +660,7 @@ void HTMLDocumentParser::pumpTokenizer() |
m_treeBuilder->flush(FlushAlways); |
RELEASE_ASSERT(!isStopped()); |
- if (isWaitingForScripts()) { |
+ if (isWaitingForBlockingResource()) { |
ASSERT(m_tokenizer->getState() == HTMLTokenizer::DataState); |
ASSERT(m_preloader); |
@@ -739,7 +750,7 @@ void HTMLDocumentParser::insert(const SegmentedString& source) |
m_input.insertAtCurrentInsertionPoint(excludedLineNumberSource); |
pumpTokenizerIfPossible(); |
- if (isWaitingForScripts()) { |
+ if (isWaitingForBlockingResource()) { |
// Check the document.write() output with a separate preload scanner as |
// the main scanner can't deal with insertions. |
if (!m_insertionPreloadScanner) { |
@@ -824,13 +835,13 @@ void HTMLDocumentParser::append(const String& inputSource) |
const SegmentedString source(inputSource); |
if (m_preloadScanner) { |
- if (m_input.current().isEmpty() && !isWaitingForScripts()) { |
+ if (m_input.current().isEmpty() && !isWaitingForBlockingResource()) { |
// 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.clear(); |
} else { |
m_preloadScanner->appendToEnd(source); |
- if (isWaitingForScripts()) |
+ if (isWaitingForBlockingResource()) |
m_preloadScanner->scan(m_preloader.get(), document()->baseElementURL()); |
} |
} |
@@ -987,14 +998,29 @@ bool HTMLDocumentParser::isWaitingForScripts() const |
return treeBuilderHasBlockingScript || scriptRunnerHasBlockingScript; |
} |
-void HTMLDocumentParser::resumeParsingAfterScriptExecution() |
+bool HTMLDocumentParser::isWaitingForStyles() const |
+{ |
+ return RuntimeEnabledFeatures::htmlParserBlocksOnCSSEnabled() && document() && document()->styleEngine().hasPendingSheets(); |
+} |
+ |
+bool HTMLDocumentParser::isWaitingForImports() const |
+{ |
+ return RuntimeEnabledFeatures::htmlParserBlocksOnCSSEnabled() && document() && !document()->haveImportsLoaded(); |
+} |
+ |
+bool HTMLDocumentParser::isWaitingForBlockingResource() const |
+{ |
+ return isWaitingForScripts() || isWaitingForStyles() || isWaitingForImports(); |
+} |
+ |
+void HTMLDocumentParser::resumeParsingAfterBlock() |
{ |
ASSERT(!isExecutingScript()); |
- ASSERT(!isWaitingForScripts()); |
+ ASSERT(!isWaitingForBlockingResource()); |
if (m_haveBackgroundParser) { |
- validateSpeculations(m_lastChunkBeforeScript.release()); |
- ASSERT(!m_lastChunkBeforeScript); |
+ validateSpeculations(m_lastChunkBeforeBlockingResource.release()); |
+ ASSERT(!m_lastChunkBeforeBlockingResource); |
// processParsedChunkFromBackgroundParser can cause this parser to be detached from the Document, |
// but we need to ensure it isn't deleted yet. |
RefPtrWillBeRawPtr<HTMLDocumentParser> protect(this); |
@@ -1033,8 +1059,8 @@ void HTMLDocumentParser::notifyScriptLoaded(Resource* cachedResource) |
} |
m_scriptRunner->executeScriptsWaitingForLoad(cachedResource); |
- if (!isWaitingForScripts()) |
- resumeParsingAfterScriptExecution(); |
+ if (!isWaitingForBlockingResource()) |
+ resumeParsingAfterBlock(); |
} |
void HTMLDocumentParser::executeScriptsWaitingForResources() |
@@ -1042,18 +1068,17 @@ void HTMLDocumentParser::executeScriptsWaitingForResources() |
// Document only calls this when the Document owns the DocumentParser |
// so this will not be called in the DocumentFragment case. |
ASSERT(m_scriptRunner); |
- // Ignore calls unless we have a script blocking the parser waiting on a |
- // stylesheet load. Otherwise we are currently parsing and this |
- // is a re-entrant call from encountering a </ style> tag. |
- if (!m_scriptRunner->hasScriptsWaitingForResources()) |
- return; |
- // pumpTokenizer can cause this parser to be detached from the Document, |
- // but we need to ensure it isn't deleted yet. |
- RefPtrWillBeRawPtr<HTMLDocumentParser> protect(this); |
- m_scriptRunner->executeScriptsWaitingForResources(); |
- if (!isWaitingForScripts()) |
- resumeParsingAfterScriptExecution(); |
+ if (m_scriptRunner->hasScriptsWaitingForResources()) { |
+ // pumpTokenizer can cause this parser to be detached from the Document, |
+ // but we need to ensure it isn't deleted yet. |
+ RefPtrWillBeRawPtr<HTMLDocumentParser> protect(this); |
+ m_scriptRunner->executeScriptsWaitingForResources(); |
+ if (!isWaitingForBlockingResource()) |
+ resumeParsingAfterBlock(); |
+ } else if (RuntimeEnabledFeatures::htmlParserBlocksOnCSSEnabled() && !inPumpSession() && !isWaitingForBlockingResource()) { |
+ resumeParsingAfterBlock(); |
+ } |
} |
void HTMLDocumentParser::parseDocumentFragment(const String& source, DocumentFragment* fragment, Element* contextElement, ParserContentPolicy parserContentPolicy) |