Index: third_party/WebKit/Source/core/dom/ScriptLoader.cpp |
diff --git a/third_party/WebKit/Source/core/dom/ScriptLoader.cpp b/third_party/WebKit/Source/core/dom/ScriptLoader.cpp |
index 03c0cbd36f4a7092db7ad623438abc26bc307c5a..76354255f542dcc24ca203453e59e3ed879101cb 100644 |
--- a/third_party/WebKit/Source/core/dom/ScriptLoader.cpp |
+++ b/third_party/WebKit/Source/core/dom/ScriptLoader.cpp |
@@ -220,14 +220,20 @@ bool ScriptLoader::isScriptTypeSupported( |
supportLegacyTypes); |
} |
-// http://dev.w3.org/html5/spec/Overview.html#prepare-a-script |
+// https://html.spec.whatwg.org/#prepare-a-script |
bool ScriptLoader::prepareScript(const TextPosition& scriptStartPosition, |
LegacyTypeSupport supportLegacyTypes) { |
+ // 1. "If the script element is marked as having "already started", then |
+ // abort these steps at this point. The script is not executed." |
if (m_alreadyStarted) |
return false; |
ScriptLoaderClient* client = this->client(); |
+ // 2. "If the element has its "parser-inserted" flag set, then |
+ // set was-parser-inserted to true and unset the element's |
+ // "parser-inserted" flag. |
+ // Otherwise, set was-parser-inserted to false." |
bool wasParserInserted; |
if (m_parserInserted) { |
wasParserInserted = true; |
@@ -236,44 +242,75 @@ bool ScriptLoader::prepareScript(const TextPosition& scriptStartPosition, |
wasParserInserted = false; |
} |
+ // 3. "If was-parser-inserted is true and the element does not have an |
+ // async attribute, then set the element's "non-blocking" flag to true." |
if (wasParserInserted && !client->asyncAttributeValue()) |
m_nonBlocking = true; |
+ // 4. "If the element has no src attribute, and its child nodes, if any, |
+ // consist only of comment nodes and empty Text nodes, |
+ // then abort these steps at this point. The script is not executed." |
// FIXME: HTML5 spec says we should check that all children are either |
// comments or empty text nodes. |
if (!client->hasSourceAttribute() && !m_element->hasChildren()) |
return false; |
+ // 5. "If the element is not connected, then abort these steps. |
+ // The script is not executed." |
if (!m_element->isConnected()) |
return false; |
+ // 6. |
+ // TODO(hiroshige): Annotate and/or cleanup this step. |
if (!isScriptTypeSupported(supportLegacyTypes)) |
return false; |
+ // 7. "If was-parser-inserted is true, |
+ // then flag the element as "parser-inserted" again, |
+ // and set the element's "non-blocking" flag to false." |
if (wasParserInserted) { |
m_parserInserted = true; |
m_nonBlocking = false; |
} |
+ // 8. "Set the element's "already started" flag." |
m_alreadyStarted = true; |
+ // 9. "If the element is flagged as "parser-inserted", but the element's |
+ // node document is not the Document of the parser that created the element, |
+ // then abort these steps." |
// FIXME: If script is parser inserted, verify it's still in the original |
// document. |
Document& elementDocument = m_element->document(); |
Document* contextDocument = elementDocument.contextDocument(); |
+ if (!contextDocument) |
+ return false; |
- if (!contextDocument || !contextDocument->allowExecutingScripts(m_element)) |
+ // 10. "If scripting is disabled for the script element, then abort these |
+ // steps at this point. The script is not executed." |
+ if (!contextDocument->allowExecutingScripts(m_element)) |
return false; |
+ // 13. |
if (!isScriptForEventSupported()) |
return false; |
+ // 14. "If the script element has a charset attribute, |
+ // then let encoding be the result of |
+ // getting an encoding from the value of the charset attribute." |
+ // "If the script element does not have a charset attribute, |
+ // or if getting an encoding failed, let encoding |
+ // be the same as the encoding of the script element's node document." |
+ // TODO(hiroshige): Should we handle failure in getting an encoding? |
String encoding; |
if (!client->charsetAttributeValue().isEmpty()) |
encoding = client->charsetAttributeValue(); |
else |
encoding = elementDocument.characterSet(); |
+ // Steps 15--20 are handled in fetchScript(). |
+ |
+ // 21. "If the element has a src content attribute, run these substeps:" |
if (client->hasSourceAttribute()) { |
FetchRequest::DeferOption defer = FetchRequest::NoDefer; |
if (!m_parserInserted || client->asyncAttributeValue() || |
@@ -286,6 +323,7 @@ bool ScriptLoader::prepareScript(const TextPosition& scriptStartPosition, |
return false; |
} |
+ // [Intervention] |
// Since the asynchronous, low priority fetch for doc.written blocked |
// script is not for execution, return early from here. Watch for its |
// completion to be able to remove it from the memory cache. |
@@ -296,26 +334,109 @@ bool ScriptLoader::prepareScript(const TextPosition& scriptStartPosition, |
return true; |
} |
+ // 23. "Then, follow the first of the following options that describes the |
+ // situation:" |
+ |
+ // Three flags are used to instruct the caller of prepareScript() to execute |
+ // a part of Step 23, when |m_willBeParserExecuted| is true: |
+ // - |m_willBeParserExecuted| |
+ // - |m_willExecuteWhenDocumentFinishedParsing| |
+ // - |m_readyToBeParserExecuted| |
+ // TODO(hiroshige): Clean up the dependency. |
+ |
+ // 1st Clause: |
+ // - "If the script's type is "classic", and |
+ // the element has a src attribute, and the element has a defer attribute, |
+ // and the element has been flagged as "parser-inserted", |
+ // and the element does not have an async attribute" |
+ // TODO(hiroshige): Check the script's type and implement "module" case. |
if (client->hasSourceAttribute() && client->deferAttributeValue() && |
m_parserInserted && !client->asyncAttributeValue()) { |
+ // This clause is implemented by the caller-side of prepareScript(): |
+ // - HTMLParserScriptRunner::requestDeferredScript(), and |
+ // - TODO(hiroshige): Investigate XMLDocumentParser::endElementNs() |
m_willExecuteWhenDocumentFinishedParsing = true; |
m_willBeParserExecuted = true; |
- } else if (client->hasSourceAttribute() && m_parserInserted && |
- !client->asyncAttributeValue()) { |
+ |
+ return true; |
hiroshige
2017/02/13 22:59:54
I replaced if-then-else-if-else-if-... block with
|
+ } |
+ |
+ // 2nd Clause: |
+ // - "If the script's type is "classic", |
+ // and the element has a src attribute, |
+ // and the element has been flagged as "parser-inserted", |
+ // and the element does not have an async attribute" |
+ // TODO(hiroshige): Check the script's type. |
+ if (client->hasSourceAttribute() && m_parserInserted && |
+ !client->asyncAttributeValue()) { |
+ // This clause is implemented by the caller-side of prepareScript(): |
+ // - HTMLParserScriptRunner::requestParsingBlockingScript() |
+ // - TODO(hiroshige): Investigate XMLDocumentParser::endElementNs() |
m_willBeParserExecuted = true; |
- } else if (!client->hasSourceAttribute() && m_parserInserted && |
- !elementDocument.isScriptExecutionReady()) { |
+ |
+ return true; |
+ } |
+ |
+ // 5th Clause: |
+ // TODO(hiroshige): Reorder the clauses to match the spec. |
+ // - "If the element does not have a src attribute, |
+ // and the element has been flagged as "parser-inserted", |
+ // and either the parser that created the script is an XML parser |
+ // or it's an HTML parser whose script nesting level is not greater than |
+ // one, |
+ // and the Document of the HTML parser or XML parser that created |
+ // the script element has a style sheet that is blocking scripts" |
+ // The last part "... has a style sheet that is blocking scripts" |
+ // is implemented in Document::isScriptExecutionReady(). |
+ // Part of the condition check is done in |
+ // HTMLParserScriptRunner::processScriptElementInternal(). |
+ // TODO(hiroshige): Clean up the split condition check. |
+ if (!client->hasSourceAttribute() && m_parserInserted && |
+ !elementDocument.isScriptExecutionReady()) { |
+ // The former part of this clause is |
+ // implemented by the caller-side of prepareScript(): |
+ // - HTMLParserScriptRunner::requestParsingBlockingScript() |
+ // - TODO(hiroshige): Investigate XMLDocumentParser::endElementNs() |
m_willBeParserExecuted = true; |
+ // "Set the element's "ready to be parser-executed" flag." |
m_readyToBeParserExecuted = true; |
- } else if (client->hasSourceAttribute() && !client->asyncAttributeValue() && |
- !m_nonBlocking) { |
+ |
+ return true; |
+ } |
+ |
+ // 3rd Clause: |
+ // - "If the script's type is "classic", |
+ // and the element has a src attribute, |
+ // and the element does not have an async attribute, |
+ // and the element does not have the "non-blocking" flag set" |
+ // TODO(hiroshige): Check the script's type and implement "module" case. |
+ if (client->hasSourceAttribute() && !client->asyncAttributeValue() && |
+ !m_nonBlocking) { |
+ // "Add the element to the end of the list of scripts that will execute |
+ // in order as soon as possible associated with the node document of the |
+ // script element at the time the prepare a script algorithm started." |
m_pendingScript = PendingScript::create(m_element, m_resource.get()); |
m_asyncExecType = ScriptRunner::InOrder; |
+ // TODO(hiroshige): Here |contextDocument| is used as "node document" |
+ // while Step 14 uses |elementDocument| as "node document". Fix this. |
contextDocument->scriptRunner()->queueScriptForExecution(this, |
m_asyncExecType); |
// Note that watchForLoad can immediately call pendingScriptFinished. |
m_pendingScript->watchForLoad(this); |
- } else if (client->hasSourceAttribute()) { |
+ // The part "When the script is ready..." is implemented in |
+ // ScriptRunner::notifyScriptReady(). |
+ // TODO(hiroshige): Annotate it. |
+ |
+ return true; |
+ } |
+ |
+ // 4th Clause: |
+ // - "If the script's type is "classic", and the element has a src attribute" |
+ // TODO(hiroshige): Check the script's type and implement "module" case. |
+ if (client->hasSourceAttribute()) { |
+ // "The element must be added to the set of scripts that will execute |
+ // as soon as possible of the node document of the script element at the |
+ // time the prepare a script algorithm started." |
m_pendingScript = PendingScript::create(m_element, m_resource.get()); |
m_asyncExecType = ScriptRunner::Async; |
LocalFrame* frame = m_element->document().frame(); |
@@ -326,28 +447,43 @@ bool ScriptLoader::prepareScript(const TextPosition& scriptStartPosition, |
m_pendingScript.get(), ScriptStreamer::Async, frame->settings(), |
scriptState, frame->frameScheduler()->loadingTaskRunner()); |
} |
+ // TODO(hiroshige): Here |contextDocument| is used as "node document" |
+ // while Step 14 uses |elementDocument| as "node document". Fix this. |
contextDocument->scriptRunner()->queueScriptForExecution(this, |
m_asyncExecType); |
// Note that watchForLoad can immediately call pendingScriptFinished. |
m_pendingScript->watchForLoad(this); |
- } else { |
- // Reset line numbering for nested writes. |
- TextPosition position = elementDocument.isInDocumentWrite() |
- ? TextPosition() |
- : scriptStartPosition; |
- KURL scriptURL = (!elementDocument.isInDocumentWrite() && m_parserInserted) |
- ? elementDocument.url() |
- : KURL(); |
- if (!executeScript( |
- ScriptSourceCode(scriptContent(), scriptURL, position))) { |
- dispatchErrorEvent(); |
- return false; |
- } |
+ // The part "When the script is ready..." is implemented in |
+ // ScriptRunner::notifyScriptReady(). |
+ // TODO(hiroshige): Annotate it. |
+ |
+ return true; |
+ } |
+ |
+ // 6th Clause: |
+ // - "Otherwise" |
+ // "Immediately execute the script block, |
+ // even if other scripts are already executing." |
+ // Note: this block is also duplicated in |
+ // HTMLParserScriptRunner::processScriptElementInternal(). |
+ // TODO(hiroshige): Merge the duplicated code. |
+ |
+ // Reset line numbering for nested writes. |
+ TextPosition position = elementDocument.isInDocumentWrite() |
+ ? TextPosition() |
+ : scriptStartPosition; |
+ KURL scriptURL = (!elementDocument.isInDocumentWrite() && m_parserInserted) |
+ ? elementDocument.url() |
+ : KURL(); |
+ if (!executeScript(ScriptSourceCode(scriptContent(), scriptURL, position))) { |
+ dispatchErrorEvent(); |
+ return false; |
} |
return true; |
} |
+// Steps 15--21 of https://html.spec.whatwg.org/#prepare-a-script |
bool ScriptLoader::fetchScript(const String& sourceUrl, |
const String& encoding, |
FetchRequest::DeferOption defer) { |
@@ -632,16 +768,30 @@ bool ScriptLoader::ignoresLoadRequest() const { |
!element() || !element()->isConnected(); |
} |
+// Step 13 of https://html.spec.whatwg.org/#prepare-a-script |
bool ScriptLoader::isScriptForEventSupported() const { |
+ // 1. "Let for be the value of the for attribute." |
String eventAttribute = client()->eventAttributeValue(); |
+ // 2. "Let event be the value of the event attribute." |
String forAttribute = client()->forAttributeValue(); |
+ |
+ // "If the script element has an event attribute and a for attribute, and |
+ // the script's type is "classic", then run these substeps:" |
+ // TODO(hiroshige): Check the script's type. |
if (eventAttribute.isNull() || forAttribute.isNull()) |
return true; |
+ // 3. "Strip leading and trailing ASCII whitespace from event and for." |
forAttribute = forAttribute.stripWhiteSpace(); |
+ // 4. "If for is not an ASCII case-insensitive match for the string |
+ // "window", |
+ // then abort these steps at this point. The script is not executed." |
if (!equalIgnoringCase(forAttribute, "window")) |
return false; |
eventAttribute = eventAttribute.stripWhiteSpace(); |
+ // 5. "If event is not an ASCII case-insensitive match for either the |
+ // string "onload" or the string "onload()", |
+ // then abort these steps at this point. The script is not executed. |
return equalIgnoringCase(eventAttribute, "onload") || |
equalIgnoringCase(eventAttribute, "onload()"); |
} |