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 b7ffc14fe49f45a40f6278f6b44bea0e0dc5ddd4..f15b209b11fa9b963db109875c45d22560b2d39a 100644 |
--- a/third_party/WebKit/Source/core/dom/ScriptLoader.cpp |
+++ b/third_party/WebKit/Source/core/dom/ScriptLoader.cpp |
@@ -29,10 +29,13 @@ |
#include "bindings/core/v8/V8Binding.h" |
#include "core/HTMLNames.h" |
#include "core/SVGNames.h" |
+#include "core/dom/ClassicPendingScript.h" |
#include "core/dom/ClassicScript.h" |
#include "core/dom/Document.h" |
#include "core/dom/DocumentParserTiming.h" |
#include "core/dom/IgnoreDestructiveWriteCountIncrementer.h" |
+#include "core/dom/Modulator.h" |
+#include "core/dom/ModulePendingScript.h" |
#include "core/dom/Script.h" |
#include "core/dom/ScriptElementBase.h" |
#include "core/dom/ScriptRunner.h" |
@@ -42,13 +45,13 @@ |
#include "core/frame/LocalFrame.h" |
#include "core/frame/SubresourceIntegrity.h" |
#include "core/frame/csp/ContentSecurityPolicy.h" |
-#include "core/html/CrossOriginAttribute.h" |
#include "core/html/imports/HTMLImport.h" |
#include "core/html/parser/HTMLParserIdioms.h" |
+#include "core/loader/modulescript/ModuleScriptFetchRequest.h" |
+#include "core/loader/resource/ScriptResource.h" |
#include "platform/WebFrameScheduler.h" |
#include "platform/loader/fetch/AccessControlStatus.h" |
#include "platform/loader/fetch/FetchParameters.h" |
-#include "platform/loader/fetch/MemoryCache.h" |
#include "platform/loader/fetch/ResourceFetcher.h" |
#include "platform/network/mime/MIMETypeRegistry.h" |
#include "platform/weborigin/SecurityOrigin.h" |
@@ -107,6 +110,7 @@ DEFINE_TRACE(ScriptLoader) { |
visitor->Trace(element_); |
visitor->Trace(resource_); |
visitor->Trace(pending_script_); |
+ visitor->Trace(module_tree_client_); |
PendingScriptClient::Trace(visitor); |
} |
@@ -157,10 +161,12 @@ void ScriptLoader::DispatchLoadEvent() { |
SetHaveFiredLoadEvent(true); |
} |
-bool ScriptLoader::IsValidScriptTypeAndLanguage( |
+namespace { |
+ |
+bool IsValidClassicScriptTypeAndLanguage( |
const String& type, |
const String& language, |
- LegacyTypeSupport support_legacy_types) { |
+ ScriptLoader::LegacyTypeSupport support_legacy_types) { |
// FIXME: isLegacySupportedJavaScriptLanguage() is not valid HTML5. It is used |
// here to maintain backwards compatibility with existing layout tests. The |
// specific violations are: |
@@ -173,12 +179,10 @@ bool ScriptLoader::IsValidScriptTypeAndLanguage( |
MIMETypeRegistry::IsSupportedJavaScriptMIMEType("text/" + |
language) || |
MIMETypeRegistry::IsLegacySupportedJavaScriptLanguage(language); |
- } else if (RuntimeEnabledFeatures::moduleScriptsEnabled() && |
- type == "module") { |
- return true; |
} else if (MIMETypeRegistry::IsSupportedJavaScriptMIMEType( |
type.StripWhiteSpace()) || |
- (support_legacy_types == kAllowLegacyTypeInTypeAttribute && |
+ (support_legacy_types == |
+ ScriptLoader::kAllowLegacyTypeInTypeAttribute && |
MIMETypeRegistry::IsLegacySupportedJavaScriptLanguage(type))) { |
return true; |
} |
@@ -186,11 +190,40 @@ bool ScriptLoader::IsValidScriptTypeAndLanguage( |
return false; |
} |
-bool ScriptLoader::IsScriptTypeSupported( |
- LegacyTypeSupport support_legacy_types) const { |
+} // namespace |
+ |
+// Step 6 of https://html.spec.whatwg.org/#prepare-a-script |
+bool ScriptLoader::IsValidScriptTypeAndLanguage( |
+ const String& type, |
+ const String& language, |
+ LegacyTypeSupport support_legacy_types, |
+ ScriptType& out_script_type) { |
+ if (IsValidClassicScriptTypeAndLanguage(type, language, |
+ support_legacy_types)) { |
+ // - "If the script block's type string is an ASCII case-insensitive match |
+ // for any JavaScript MIME type, the script's type is "classic"." |
+ // TODO(hiroshige): Annotate and/or cleanup this step. |
+ out_script_type = ScriptType::kClassic; |
+ return true; |
+ } |
+ |
+ if (RuntimeEnabledFeatures::moduleScriptsEnabled() && type == "module") { |
+ // - "If the script block's type string is an ASCII case-insensitive match |
+ // for the string "module", the script's type is "module"." |
+ out_script_type = ScriptType::kModule; |
+ return true; |
+ } |
+ |
+ // - "If neither of the above conditions are true, then abort these steps |
+ // at this point. No script is executed." |
+ return false; |
+} |
+ |
+bool ScriptLoader::IsScriptTypeSupported(LegacyTypeSupport support_legacy_types, |
+ ScriptType& out_script_type) const { |
return IsValidScriptTypeAndLanguage(element_->TypeAttributeValue(), |
element_->LanguageAttributeValue(), |
- support_legacy_types); |
+ support_legacy_types, out_script_type); |
} |
// https://html.spec.whatwg.org/#prepare-a-script |
@@ -231,9 +264,9 @@ bool ScriptLoader::PrepareScript(const TextPosition& script_start_position, |
if (!element_->IsConnected()) |
return false; |
- // 6. |
- // TODO(hiroshige): Annotate and/or cleanup this step. |
- if (!IsScriptTypeSupported(support_legacy_types)) |
+ // 6. "Determine the script's type as follows:" |
+ // |script_type_| is set here. |
+ if (!IsScriptTypeSupported(support_legacy_types, script_type_)) |
return false; |
// 7. "If was-parser-inserted is true, |
@@ -264,66 +297,201 @@ bool ScriptLoader::PrepareScript(const TextPosition& script_start_position, |
if (!context_document->CanExecuteScripts(kAboutToExecuteScript)) |
return false; |
+ // 11. "If the script element has a nomodule content attribute |
+ // and the script's type is "classic", then abort these steps. |
+ // The script is not executed." |
+ // TODO(japhet): Implement this step. |
+ |
// 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 (!element_->CharsetAttributeValue().IsEmpty()) |
- encoding = element_->CharsetAttributeValue(); |
- else |
- encoding = element_document.characterSet(); |
- |
- // Steps 15--20 are handled in fetchScript(). |
+ // 14. is handled below. |
+ |
+ // 15. "Let CORS setting be the current state of the element's |
+ // crossorigin content attribute." |
+ CrossOriginAttributeValue cross_origin = |
+ GetCrossOriginAttributeValue(element_->CrossOriginAttributeValue()); |
+ |
+ // 16. is handled below. |
+ |
+ // 17. "If the script element has a nonce attribute, |
+ // then let cryptographic nonce be that attribute's value. |
+ // Otherwise, let cryptographic nonce be the empty string." |
+ String nonce; |
+ if (element_->IsNonceableElement()) |
+ nonce = element_->nonce(); |
+ |
+ // 18. is handled below. |
+ |
+ // 19. "Let parser state be "parser-inserted" |
+ // if the script element has been flagged as "parser-inserted", |
+ // and "not parser-inserted" otherwise." |
+ ParserDisposition parser_state = |
+ IsParserInserted() ? kParserInserted : kNotParserInserted; |
// 21. "If the element has a src content attribute, run these substeps:" |
if (element_->HasSourceAttribute()) { |
- FetchParameters::DeferOption defer = FetchParameters::kNoDefer; |
- if (!parser_inserted_ || element_->AsyncAttributeValue() || |
- element_->DeferAttributeValue()) |
- defer = FetchParameters::kLazyLoad; |
- if (document_write_intervention_ == |
- DocumentWriteIntervention::kFetchDocWrittenScriptDeferIdle) |
- defer = FetchParameters::kIdleLoad; |
- if (!FetchScript(element_->SourceAttributeValue(), encoding, defer)) |
+ // 21.1. Let src be the value of the element's src attribute. |
+ String src = |
+ StripLeadingAndTrailingHTMLSpaces(element_->SourceAttributeValue()); |
+ |
+ // 21.2. "If src is the empty string, queue a task to |
+ // fire an event named error at the element, and abort these steps." |
+ if (src.IsEmpty()) { |
+ // TODO(hiroshige): Make this asynchronous. Currently we fire the error |
+ // event synchronously to keep the existing behavior. |
+ DispatchErrorEvent(); |
return false; |
+ } |
+ |
+ // 21.3. "Set the element's from an external file flag." |
+ is_external_script_ = true; |
+ |
+ // 21.4. "Parse src relative to the element's node document." |
+ KURL url = element_document.CompleteURL(src); |
+ |
+ // 21.5. "If the previous step failed, queue a task to |
+ // fire an event named error at the element, and abort these steps." |
+ if (!url.IsValid()) { |
+ // TODO(hiroshige): Make this asynchronous. Currently we fire the error |
+ // event synchronously to keep the existing behavior. |
+ DispatchErrorEvent(); |
+ return false; |
+ } |
+ |
+ DCHECK(!resource_); |
+ DCHECK(!module_tree_client_); |
+ |
+ // 21.6. "Switch on the script's type:" |
+ if (GetScriptType() == ScriptType::kClassic) { |
+ // - "classic": |
+ |
+ // 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 (!element_->CharsetAttributeValue().IsEmpty()) |
+ encoding = element_->CharsetAttributeValue(); |
+ else |
+ encoding = element_document.characterSet(); |
+ |
+ // Step 16 is skipped because "module script credentials" is not used |
+ // for classic scripts. |
+ |
+ // 18. "If the script element has an integrity attribute, |
+ // then let integrity metadata be that attribute's value. |
+ // Otherwise, let integrity metadata be the empty string." |
+ String integrity_attr = element_->IntegrityAttributeValue(); |
+ IntegrityMetadataSet integrity_metadata; |
+ if (!integrity_attr.IsEmpty()) { |
+ SubresourceIntegrity::ParseIntegrityAttribute( |
+ integrity_attr, integrity_metadata, &element_document); |
+ } |
+ |
+ if (!FetchClassicScript(url, element_document.Fetcher(), nonce, |
+ integrity_metadata, parser_state, cross_origin, |
+ element_document.GetSecurityOrigin(), encoding)) { |
+ // TODO(hiroshige): Make this asynchronous. Currently we fire the error |
+ // event synchronously to keep the existing behavior. |
+ DispatchErrorEvent(); |
+ return false; |
+ } |
+ |
+ DCHECK(resource_); |
+ DCHECK(!module_tree_client_); |
+ } else { |
+ // - "module": |
+ |
+ // Steps 14 and 18 are skipped because they are not used in module |
+ // scripts. |
+ |
+ // 16. "Let module script credentials mode be determined by switching |
+ // on CORS setting:" |
+ WebURLRequest::FetchCredentialsMode credentials_mode = |
+ WebURLRequest::kFetchCredentialsModeOmit; |
+ switch (cross_origin) { |
+ case kCrossOriginAttributeNotSet: |
+ credentials_mode = WebURLRequest::kFetchCredentialsModeOmit; |
+ break; |
+ case kCrossOriginAttributeAnonymous: |
+ credentials_mode = WebURLRequest::kFetchCredentialsModeSameOrigin; |
+ break; |
+ case kCrossOriginAttributeUseCredentials: |
+ credentials_mode = WebURLRequest::kFetchCredentialsModeInclude; |
+ break; |
+ } |
+ |
+ DCHECK(RuntimeEnabledFeatures::moduleScriptsEnabled()); |
+ Modulator* modulator = Modulator::From( |
+ ToScriptStateForMainWorld(element_document.GetFrame())); |
+ FetchModuleScriptTree(url, modulator, nonce, parser_state, |
+ credentials_mode); |
+ |
+ DCHECK(!resource_); |
+ DCHECK(module_tree_client_); |
+ } |
+ |
+ // "When the chosen algorithm asynchronously completes, set |
+ // the script's script to the result. At that time, the script is ready." |
+ // When the script is ready, PendingScriptClient::pendingScriptFinished() |
+ // is used as the notification, and the action to take when |
+ // the script is ready is specified later, in |
+ // - ScriptLoader::PrepareScript(), or |
+ // - HTMLParserScriptRunner, |
+ // depending on the conditions in Step 23 of "prepare a script". |
} |
// 22. "If the element does not have a src content attribute, |
// run these substeps:" |
- |
- // 22.1. "Let source text be the value of the text IDL attribute." |
- // This step is done later: |
- // - in ScriptLoader::pendingScript() (Step 23, 6th Clause), |
- // as Element::textFromChildren() in ScriptLoader::scriptContent(), |
- // - in HTMLParserScriptRunner::processScriptElementInternal() |
- // (Duplicated code of Step 23, 6th Clause), |
- // as Element::textContent(), |
- // - in XMLDocumentParser::endElementNs() (Step 23, 5th Clause), |
- // as Element::textFromChildren() in ScriptLoader::scriptContent(), |
- // - PendingScript::getSource() (Indirectly used via |
- // HTMLParserScriptRunner::processScriptElementInternal(), |
- // Step 23, 5th Clause), |
- // as Element::textContent(). |
- // TODO(hiroshige): Make them merged or consistent. |
- |
- // 22.2. "Switch on the script's type:" |
- // TODO(hiroshige): Clarify how Step 22.2 is implemented for "classic". |
- // TODO(hiroshige): Implement Step 22.2 for "module". |
+ if (!element_->HasSourceAttribute()) { |
+ // 22.1. "Let source text be the value of the text IDL attribute." |
+ // This step is done later: |
+ // - in ScriptLoader::pendingScript() (Step 23, 6th Clause), |
+ // as Element::textFromChildren() in ScriptLoader::scriptContent(), |
+ // - in HTMLParserScriptRunner::processScriptElementInternal() |
+ // (Duplicated code of Step 23, 6th Clause), |
+ // as Element::textContent(), |
+ // - in XMLDocumentParser::endElementNs() (Step 23, 5th Clause), |
+ // as Element::textFromChildren() in ScriptLoader::scriptContent(), |
+ // - PendingScript::getSource() (Indirectly used via |
+ // HTMLParserScriptRunner::processScriptElementInternal(), |
+ // Step 23, 5th Clause), |
+ // as Element::textContent(). |
+ // TODO(hiroshige): Make them merged or consistent. |
+ |
+ // 22.2. "Switch on the script's type:" |
+ switch (GetScriptType()) { |
+ // - "classic": |
+ case ScriptType::kClassic: |
+ // TODO(hiroshige): Clarify how Step 22.2 is implemented for "classic". |
+ break; |
+ |
+ // - "module": |
+ case ScriptType::kModule: |
+ // Inline module scripts are not yet supported. |
+ // FOXME: (how) should we handle this? Should we fail more gracefully? |
+ fprintf( |
+ stderr, |
+ "Inline module script is not yet supported (Document URL: %s)\n", |
+ element_document.Url().GetString().Utf8().Data()); |
+ CHECK(false); |
+ break; |
+ } |
+ } |
// [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. |
- if (document_write_intervention_ == |
- DocumentWriteIntervention::kFetchDocWrittenScriptDeferIdle) { |
+ if (GetScriptType() == ScriptType::kClassic && |
+ document_write_intervention_ == |
+ DocumentWriteIntervention::kFetchDocWrittenScriptDeferIdle) { |
pending_script_ = CreatePendingScript(); |
pending_script_->WatchForLoad(this); |
return true; |
@@ -344,9 +512,14 @@ bool ScriptLoader::PrepareScript(const TextPosition& script_start_position, |
// 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 (element_->HasSourceAttribute() && element_->DeferAttributeValue() && |
- parser_inserted_ && !element_->AsyncAttributeValue()) { |
+ // - "If the script's type is "module", |
+ // and the element has been flagged as "parser-inserted", |
+ // and the element does not have an async attribute" |
+ if ((GetScriptType() == ScriptType::kClassic && |
+ element_->HasSourceAttribute() && element_->DeferAttributeValue() && |
+ parser_inserted_ && !element_->AsyncAttributeValue()) || |
+ (GetScriptType() == ScriptType::kModule && parser_inserted_ && |
+ !element_->AsyncAttributeValue())) { |
// This clause is implemented by the caller-side of prepareScript(): |
// - HTMLParserScriptRunner::requestDeferredScript(), and |
// - TODO(hiroshige): Investigate XMLDocumentParser::endElementNs() |
@@ -362,7 +535,8 @@ bool ScriptLoader::PrepareScript(const TextPosition& script_start_position, |
// 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 (element_->HasSourceAttribute() && parser_inserted_ && |
+ if (GetScriptType() == ScriptType::kClassic && |
+ element_->HasSourceAttribute() && parser_inserted_ && |
!element_->AsyncAttributeValue()) { |
// This clause is implemented by the caller-side of prepareScript(): |
// - HTMLParserScriptRunner::requestParsingBlockingScript() |
@@ -386,7 +560,11 @@ bool ScriptLoader::PrepareScript(const TextPosition& script_start_position, |
// Part of the condition check is done in |
// HTMLParserScriptRunner::processScriptElementInternal(). |
// TODO(hiroshige): Clean up the split condition check. |
- if (!element_->HasSourceAttribute() && parser_inserted_ && |
+ // We check that the type is "classic" here, because according to the spec |
+ // a "module" script doesn't reach the 5th Clause because the 4th Clause |
+ // catches all "module" scripts. |
+ if (GetScriptType() == ScriptType::kClassic && |
+ !element_->HasSourceAttribute() && parser_inserted_ && |
!element_document.IsScriptExecutionReady()) { |
// The former part of this clause is |
// implemented by the caller-side of prepareScript(): |
@@ -404,9 +582,15 @@ bool ScriptLoader::PrepareScript(const TextPosition& script_start_position, |
// 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" |
+ // - "If the script's type is "module", |
+ // 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 (element_->HasSourceAttribute() && !element_->AsyncAttributeValue() && |
- !non_blocking_) { |
+ if ((GetScriptType() == ScriptType::kClassic && |
+ element_->HasSourceAttribute() && !element_->AsyncAttributeValue() && |
+ !non_blocking_) || |
+ (GetScriptType() == ScriptType::kModule && |
+ !element_->AsyncAttributeValue() && !non_blocking_)) { |
// "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." |
@@ -427,8 +611,10 @@ bool ScriptLoader::PrepareScript(const TextPosition& script_start_position, |
// 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 (element_->HasSourceAttribute()) { |
+ // - "If the script's type is "module"" |
+ if ((GetScriptType() == ScriptType::kClassic && |
+ element_->HasSourceAttribute()) || |
+ GetScriptType() == ScriptType::kModule) { |
// "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." |
@@ -459,6 +645,8 @@ bool ScriptLoader::PrepareScript(const TextPosition& script_start_position, |
// This clause is executed only if the script's type is "classic" |
// and the element doesn't have a src attribute. |
+ DCHECK_EQ(GetScriptType(), ScriptType::kClassic); |
+ DCHECK(!is_external_script_); |
// Reset line numbering for nested writes. |
TextPosition position = element_document.IsInDocumentWrite() |
@@ -477,108 +665,62 @@ bool ScriptLoader::PrepareScript(const TextPosition& script_start_position, |
return true; |
} |
-// Steps 15--21 of https://html.spec.whatwg.org/#prepare-a-script |
-bool ScriptLoader::FetchScript(const String& source_url, |
- const String& encoding, |
- FetchParameters::DeferOption defer) { |
- Document* element_document = &(element_->GetDocument()); |
- if (!element_->IsConnected() || element_->GetDocument() != element_document) |
- return false; |
- |
- DCHECK(!resource_); |
- // 21. "If the element has a src content attribute, run these substeps:" |
- if (!StripLeadingAndTrailingHTMLSpaces(source_url).IsEmpty()) { |
- // 21.4. "Parse src relative to the element's node document." |
- ResourceRequest resource_request(element_document->CompleteURL(source_url)); |
- |
- // [Intervention] |
- if (document_write_intervention_ == |
- DocumentWriteIntervention::kFetchDocWrittenScriptDeferIdle) { |
- resource_request.SetHTTPHeaderField( |
- "Intervention", |
- "<https://www.chromestatus.com/feature/5718547946799104>"); |
- } |
+bool ScriptLoader::FetchClassicScript( |
+ const KURL& url, |
+ ResourceFetcher* fetcher, |
+ const String& nonce, |
+ const IntegrityMetadataSet& integrity_metadata, |
+ ParserDisposition parser_state, |
+ CrossOriginAttributeValue cross_origin, |
+ SecurityOrigin* security_origin, |
+ const String& encoding) { |
+ // https://html.spec.whatwg.org/#prepare-a-script |
+ // 21.6, "classic": |
+ // "Fetch a classic script given url, settings, ..." |
+ ResourceRequest resource_request(url); |
- FetchParameters params(resource_request, element_->InitiatorName()); |
- |
- // 15. "Let CORS setting be the current state of the element's |
- // crossorigin content attribute." |
- CrossOriginAttributeValue cross_origin = |
- GetCrossOriginAttributeValue(element_->CrossOriginAttributeValue()); |
+ // [Intervention] |
+ if (document_write_intervention_ == |
+ DocumentWriteIntervention::kFetchDocWrittenScriptDeferIdle) { |
+ resource_request.SetHTTPHeaderField( |
+ "Intervention", |
+ "<https://www.chromestatus.com/feature/5718547946799104>"); |
+ } |
- // 16. "Let module script credentials mode be determined by switching |
- // on CORS setting:" |
- // TODO(hiroshige): Implement this step for "module". |
+ FetchParameters params(resource_request, element_->InitiatorName()); |
- // 21.6, "classic": "Fetch a classic script given ... CORS setting |
- // ... and encoding." |
- if (cross_origin != kCrossOriginAttributeNotSet) { |
- params.SetCrossOriginAccessControl(element_document->GetSecurityOrigin(), |
- cross_origin); |
- } |
+ // "... cryptographic nonce, ..." |
+ params.SetContentSecurityPolicyNonce(nonce); |
- params.SetCharset(encoding); |
- |
- // 17. "If the script element has a nonce attribute, |
- // then let cryptographic nonce be that attribute's value. |
- // Otherwise, let cryptographic nonce be the empty string." |
- if (element_->IsNonceableElement()) |
- params.SetContentSecurityPolicyNonce(element_->nonce()); |
- |
- // 19. "Let parser state be "parser-inserted" |
- // if the script element has been flagged as "parser-inserted", |
- // and "not parser-inserted" otherwise." |
- params.SetParserDisposition(IsParserInserted() ? kParserInserted |
- : kNotParserInserted); |
- |
- params.SetDefer(defer); |
- |
- // 18. "If the script element has an integrity attribute, |
- // then let integrity metadata be that attribute's value. |
- // Otherwise, let integrity metadata be the empty string." |
- String integrity_attr = element_->IntegrityAttributeValue(); |
- if (!integrity_attr.IsEmpty()) { |
- IntegrityMetadataSet metadata_set; |
- SubresourceIntegrity::ParseIntegrityAttribute( |
- integrity_attr, metadata_set, element_document); |
- params.SetIntegrityMetadata(metadata_set); |
- } |
+ // "... integrity metadata, ..." |
+ params.SetIntegrityMetadata(integrity_metadata); |
- // 21.6. "Switch on the script's type:" |
+ // "... parser state, ..." |
+ params.SetParserDisposition(parser_state); |
- // - "classic": |
- // "Fetch a classic script given url, settings, cryptographic nonce, |
- // integrity metadata, parser state, CORS setting, and encoding." |
- resource_ = ScriptResource::Fetch(params, element_document->Fetcher()); |
+ // "... CORS setting, ..." |
+ if (cross_origin != kCrossOriginAttributeNotSet) { |
+ params.SetCrossOriginAccessControl(security_origin, cross_origin); |
+ } |
- // - "module": |
- // "Fetch a module script graph given url, settings, "script", |
- // cryptographic nonce, parser state, and |
- // module script credentials mode." |
- // TODO(kouhei, hiroshige): Implement this. |
+ // "... and encoding." |
+ params.SetCharset(encoding); |
- // "When the chosen algorithm asynchronously completes, set |
- // the script's script to the result. At that time, the script is ready." |
- // When the script is ready, PendingScriptClient::pendingScriptFinished() |
- // is used as the notification, and the action to take when |
- // the script is ready is specified later, in |
- // - ScriptLoader::prepareScript(), or |
- // - HTMLParserScriptRunner, |
- // depending on the conditions in Step 23 of "prepare a script". |
+ // This DeferOption logic is only for classic scripts, as we always set |
+ // |kLazyLoad| for module scripts in ModuleScriptLoader. |
+ FetchParameters::DeferOption defer = FetchParameters::kNoDefer; |
+ if (!parser_inserted_ || element_->AsyncAttributeValue() || |
+ element_->DeferAttributeValue()) |
+ defer = FetchParameters::kLazyLoad; |
+ if (document_write_intervention_ == |
+ DocumentWriteIntervention::kFetchDocWrittenScriptDeferIdle) |
+ defer = FetchParameters::kIdleLoad; |
+ params.SetDefer(defer); |
- // 21.3. "Set the element's from an external file flag." |
- is_external_script_ = true; |
- } |
+ resource_ = ScriptResource::Fetch(params, fetcher); |
- if (!resource_) { |
- // 21.2. "If src is the empty string, queue a task to |
- // fire an event named error at the element, and abort these steps." |
- // 21.5. "If the previous step failed, queue a task to |
- // fire an event named error at the element, and abort these steps." |
- // TODO(hiroshige): Make this asynchronous. |
- DispatchErrorEvent(); |
+ if (!resource_) |
return false; |
- } |
// [Intervention] |
if (created_during_document_write_ && |
@@ -591,9 +733,36 @@ bool ScriptLoader::FetchScript(const String& source_url, |
return true; |
} |
+void ScriptLoader::FetchModuleScriptTree( |
+ const KURL& url, |
+ Modulator* modulator, |
+ const String& nonce, |
+ ParserDisposition parser_state, |
+ WebURLRequest::FetchCredentialsMode credentials_mode) { |
+ // https://html.spec.whatwg.org/#prepare-a-script |
+ // 21.6, "module": |
+ // "Fetch a module script graph given url, settings, "script", |
+ // cryptographic nonce, parser state, and |
+ // module script credentials mode." |
+ ModuleScriptFetchRequest module_request(url, nonce, parser_state, |
+ credentials_mode); |
+ |
+ module_tree_client_ = ModulePendingScriptTreeClient::Create(); |
+ |
+ modulator->FetchTree(module_request, module_tree_client_); |
+} |
+ |
PendingScript* ScriptLoader::CreatePendingScript() { |
- CHECK(resource_); |
- return PendingScript::Create(element_, resource_); |
+ switch (GetScriptType()) { |
+ case ScriptType::kClassic: |
+ CHECK(resource_); |
+ return ClassicPendingScript::Create(element_, resource_); |
+ case ScriptType::kModule: |
+ CHECK(module_tree_client_); |
+ return ModulePendingScript::Create(element_, module_tree_client_); |
+ } |
+ NOTREACHED(); |
+ return nullptr; |
} |
bool ScriptLoader::ExecuteScript(const Script* script) { |
@@ -620,6 +789,7 @@ bool ScriptLoader::ExecuteScript(const Script* script) { |
// TODO(hiroshige): Move event dispatching code to doExecuteScript(). |
bool ScriptLoader::DoExecuteScript(const Script* script) { |
DCHECK(already_started_); |
+ CHECK_EQ(script->GetScriptType(), GetScriptType()); |
if (script->IsEmpty()) |
return true; |
@@ -662,10 +832,13 @@ bool ScriptLoader::DoExecuteScript(const Script* script) { |
// or the script's type is module", |
// then increment the ignore-destructive-writes counter of the |
// script element's node document. Let neutralized doc be that Document." |
- // TODO(hiroshige): Implement "module" case. |
IgnoreDestructiveWriteCountIncrementer |
ignore_destructive_write_count_incrementer( |
- is_external_script_ || is_imported_script ? context_document : 0); |
+ is_external_script_ || |
+ script->GetScriptType() == ScriptType::kModule || |
+ is_imported_script |
+ ? context_document |
+ : 0); |
// 4. "Let old script element be the value to which the script element's |
// node document's currentScript object was most recently set." |
@@ -676,18 +849,24 @@ bool ScriptLoader::DoExecuteScript(const Script* script) { |
// 1. "If the script element's root is not a shadow root, |
// then set the script element's node document's currentScript |
// attribute to the script element. Otherwise, set it to null." |
- context_document->PushCurrentScript(element_.Get()); |
+ // - "module": |
+ // 1. "Set the script element's node document's currentScript attribute |
+ // to null." |
+ ScriptElementBase* current_script = nullptr; |
+ if (script->GetScriptType() == ScriptType::kClassic) |
+ current_script = element_; |
+ context_document->PushCurrentScript(current_script); |
+ // - "classic": |
// 2. "Run the classic script given by the script's script." |
// Note: This is where the script is compiled and actually executed. |
- script->RunScript(frame, element_->GetDocument().GetSecurityOrigin()); |
- |
// - "module": |
- // TODO(hiroshige): Implement this. |
+ // 2. "Run the module script given by the script's script." |
+ script->RunScript(frame, element_->GetDocument().GetSecurityOrigin()); |
// 6. "Set the script element's node document's currentScript attribute |
// to old script element." |
- context_document->PopCurrentScript(element_.Get()); |
+ context_document->PopCurrentScript(current_script); |
return true; |
@@ -699,25 +878,27 @@ bool ScriptLoader::DoExecuteScript(const Script* script) { |
void ScriptLoader::Execute() { |
DCHECK(!will_be_parser_executed_); |
DCHECK(async_exec_type_ != ScriptRunner::kNone); |
- DCHECK(pending_script_->GetResource()); |
+ DCHECK(pending_script_->IsExternal()); |
bool error_occurred = false; |
Script* script = pending_script_->GetSource(KURL(), error_occurred); |
+ const bool wasCanceled = pending_script_->WasCanceled(); |
DetachPendingScript(); |
if (error_occurred) { |
DispatchErrorEvent(); |
- } else if (!resource_->WasCanceled()) { |
+ } else if (!wasCanceled) { |
if (ExecuteScript(script)) |
DispatchLoadEvent(); |
else |
DispatchErrorEvent(); |
} |
resource_ = nullptr; |
+ module_tree_client_ = nullptr; |
} |
void ScriptLoader::PendingScriptFinished(PendingScript* pending_script) { |
DCHECK(!will_be_parser_executed_); |
DCHECK_EQ(pending_script_, pending_script); |
- DCHECK_EQ(pending_script->GetResource(), resource_); |
+ DCHECK_EQ(pending_script_->GetScriptType(), GetScriptType()); |
// We do not need this script in the memory cache. The primary goals of |
// sending this fetch request are to let the third party server know |
@@ -725,7 +906,8 @@ void ScriptLoader::PendingScriptFinished(PendingScript* pending_script) { |
// cache for subsequent uses. |
if (document_write_intervention_ == |
DocumentWriteIntervention::kFetchDocWrittenScriptDeferIdle) { |
- GetMemoryCache()->Remove(pending_script_->GetResource()); |
+ DCHECK_EQ(pending_script_->GetScriptType(), ScriptType::kClassic); |
+ pending_script_->RemoveFromMemoryCache(); |
pending_script_->StopWatchingForLoad(); |
return; |
} |