Commit 8f88ce31 authored by Hiroshige Hayashizaki's avatar Hiroshige Hayashizaki Committed by Commit Bot

[Import Maps] Introduce PendingImportMap and report exceptions

This CL introduce PendingImportMap to align import map implementation
with spec structure and PendingScript processing.
(When external import maps are introduced, PendingImportMap will
control pending fetches and import map registration order)

This CL also plumbs parse errors via PendingImportMap
to ModulatorImplBase::RegisterImportMap() and fires
window error events. Previously, console errors were
output but no error events were fired.

(This CL also adds a check for import maps moved between documents,
but currently it is no-op because we can't move inline import maps
between documents before registration)

Bug: 990561
Change-Id: Ic3f818bcc52b698dde5456a70bbe3516eee73f5b
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1760841
Commit-Queue: Hiroshige Hayashizaki <hiroshige@chromium.org>
Reviewed-by: default avatarKouhei Ueno <kouhei@chromium.org>
Cr-Commit-Position: refs/heads/master@{#692320}
parent 68dff7cb
...@@ -48,6 +48,8 @@ blink_core_sources("script") { ...@@ -48,6 +48,8 @@ blink_core_sources("script") {
"module_script.h", "module_script.h",
"parsed_specifier.cc", "parsed_specifier.cc",
"parsed_specifier.h", "parsed_specifier.h",
"pending_import_map.cc",
"pending_import_map.h",
"pending_script.cc", "pending_script.cc",
"pending_script.h", "pending_script.h",
"script.h", "script.h",
......
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
#include <memory> #include <memory>
#include <utility> #include <utility>
#include "third_party/blink/public/mojom/devtools/console_message.mojom-blink.h" #include "third_party/blink/public/mojom/devtools/console_message.mojom-blink.h"
#include "third_party/blink/renderer/bindings/core/v8/script_value.h"
#include "third_party/blink/renderer/core/script/layered_api.h" #include "third_party/blink/renderer/core/script/layered_api.h"
#include "third_party/blink/renderer/core/script/modulator.h" #include "third_party/blink/renderer/core/script/modulator.h"
#include "third_party/blink/renderer/core/script/parsed_specifier.h" #include "third_party/blink/renderer/core/script/parsed_specifier.h"
...@@ -146,35 +147,30 @@ KURL NormalizeValue(const String& key, ...@@ -146,35 +147,30 @@ KURL NormalizeValue(const String& key,
// Parse |text| as an import map. Errors (e.g. json parsing error, invalid // Parse |text| as an import map. Errors (e.g. json parsing error, invalid
// keys/values, etc.) are basically ignored, except that they are reported to // keys/values, etc.) are basically ignored, except that they are reported to
// the console |logger|. // the console |logger|.
// TODO(hiroshige): Handle errors in a spec-conformant way once specified. ImportMap* ImportMap::Parse(const Modulator& modulator,
// https://github.com/WICG/import-maps/issues/100
ImportMap* ImportMap::Create(const Modulator& modulator_for_built_in_modules,
const String& input, const String& input,
const KURL& base_url, const KURL& base_url,
ConsoleLogger& logger) { ConsoleLogger& logger,
ScriptValue* error_to_rethrow) {
DCHECK(error_to_rethrow);
// <spec step="1">Let parsed be the result of parsing JSON into Infra values // <spec step="1">Let parsed be the result of parsing JSON into Infra values
// given input.</spec> // given input.</spec>
std::unique_ptr<JSONValue> parsed = ParseJSON(input); std::unique_ptr<JSONValue> parsed = ParseJSON(input);
if (!parsed) { if (!parsed) {
logger.AddConsoleMessage(mojom::ConsoleMessageSource::kOther, *error_to_rethrow =
mojom::ConsoleMessageLevel::kError, modulator.CreateSyntaxError("Failed to parse import map: invalid JSON");
"Failed to parse import map: invalid JSON"); return MakeGarbageCollected<ImportMap>(modulator, SpecifierMap());
// TODO(hiroshige): Return null.
return MakeGarbageCollected<ImportMap>(modulator_for_built_in_modules,
SpecifierMap());
} }
// <spec step="2">If parsed is not a map, then throw a TypeError indicating // <spec step="2">If parsed is not a map, then throw a TypeError indicating
// that the top-level value must be a JSON object.</spec> // that the top-level value must be a JSON object.</spec>
std::unique_ptr<JSONObject> parsed_map = JSONObject::From(std::move(parsed)); std::unique_ptr<JSONObject> parsed_map = JSONObject::From(std::move(parsed));
if (!parsed_map) { if (!parsed_map) {
logger.AddConsoleMessage(mojom::ConsoleMessageSource::kOther, *error_to_rethrow =
mojom::ConsoleMessageLevel::kError, modulator.CreateTypeError("Failed to parse import map: not an object");
"Failed to parse import map: not an object"); return MakeGarbageCollected<ImportMap>(modulator, SpecifierMap());
// TODO(hiroshige): Return null.
return MakeGarbageCollected<ImportMap>(modulator_for_built_in_modules,
SpecifierMap());
} }
// <spec step="3">Let sortedAndNormalizedImports be an empty map.</spec> // <spec step="3">Let sortedAndNormalizedImports be an empty map.</spec>
...@@ -187,13 +183,10 @@ ImportMap* ImportMap::Create(const Modulator& modulator_for_built_in_modules, ...@@ -187,13 +183,10 @@ ImportMap* ImportMap::Create(const Modulator& modulator_for_built_in_modules,
// object.</spec> // object.</spec>
JSONObject* imports = parsed_map->GetJSONObject("imports"); JSONObject* imports = parsed_map->GetJSONObject("imports");
if (!imports) { if (!imports) {
logger.AddConsoleMessage(mojom::ConsoleMessageSource::kOther, *error_to_rethrow = modulator.CreateTypeError(
mojom::ConsoleMessageLevel::kError,
"Failed to parse import map: \"imports\" " "Failed to parse import map: \"imports\" "
"top-level key must be a JSON object."); "top-level key must be a JSON object.");
// TODO(hiroshige): Return null. return MakeGarbageCollected<ImportMap>(modulator, SpecifierMap());
return MakeGarbageCollected<ImportMap>(modulator_for_built_in_modules,
SpecifierMap());
} }
// <spec step="4.2">Set sortedAndNormalizedImports to the result of sorting // <spec step="4.2">Set sortedAndNormalizedImports to the result of sorting
...@@ -210,7 +203,7 @@ ImportMap* ImportMap::Create(const Modulator& modulator_for_built_in_modules, ...@@ -210,7 +203,7 @@ ImportMap* ImportMap::Create(const Modulator& modulator_for_built_in_modules,
// <spec step="8">Return the import map whose imports are // <spec step="8">Return the import map whose imports are
// sortedAndNormalizedImports and whose scopes scopes are // sortedAndNormalizedImports and whose scopes scopes are
// sortedAndNormalizedScopes.</spec> // sortedAndNormalizedScopes.</spec>
return MakeGarbageCollected<ImportMap>(modulator_for_built_in_modules, return MakeGarbageCollected<ImportMap>(modulator,
sorted_and_normalized_imports); sorted_and_normalized_imports);
} }
......
...@@ -18,6 +18,7 @@ class ConsoleLogger; ...@@ -18,6 +18,7 @@ class ConsoleLogger;
class JSONObject; class JSONObject;
class Modulator; class Modulator;
class ParsedSpecifier; class ParsedSpecifier;
class ScriptValue;
// Import maps. // Import maps.
// https://wicg.github.io/import-maps/ // https://wicg.github.io/import-maps/
...@@ -25,10 +26,11 @@ class ParsedSpecifier; ...@@ -25,10 +26,11 @@ class ParsedSpecifier;
class CORE_EXPORT ImportMap final class CORE_EXPORT ImportMap final
: public GarbageCollectedFinalized<ImportMap> { : public GarbageCollectedFinalized<ImportMap> {
public: public:
static ImportMap* Create(const Modulator& modulator_for_built_in_modules, static ImportMap* Parse(const Modulator&,
const String& text, const String& text,
const KURL& base_url, const KURL& base_url,
ConsoleLogger& logger); ConsoleLogger& logger,
ScriptValue* error_to_rethrow);
ImportMap(const Modulator&, const HashMap<String, Vector<KURL>>& imports); ImportMap(const Modulator&, const HashMap<String, Vector<KURL>>& imports);
......
...@@ -174,8 +174,12 @@ class CORE_EXPORT Modulator : public GarbageCollectedFinalized<Modulator>, ...@@ -174,8 +174,12 @@ class CORE_EXPORT Modulator : public GarbageCollectedFinalized<Modulator>,
const ReferrerScriptInfo&, const ReferrerScriptInfo&,
ScriptPromiseResolver*) = 0; ScriptPromiseResolver*) = 0;
virtual ScriptValue CreateTypeError(const String& message) const = 0;
virtual ScriptValue CreateSyntaxError(const String& message) const = 0;
// Import maps. https://github.com/WICG/import-maps // Import maps. https://github.com/WICG/import-maps
virtual void RegisterImportMap(const ImportMap*) = 0; virtual void RegisterImportMap(const ImportMap*,
ScriptValue error_to_rethrow) = 0;
virtual bool IsAcquiringImportMaps() const = 0; virtual bool IsAcquiringImportMaps() const = 0;
virtual void ClearIsAcquiringImportMaps() = 0; virtual void ClearIsAcquiringImportMaps() = 0;
virtual const ImportMap* GetImportMapForTest() const = 0; virtual const ImportMap* GetImportMapForTest() const = 0;
......
...@@ -237,22 +237,52 @@ KURL ModulatorImplBase::ResolveModuleSpecifier(const String& specifier, ...@@ -237,22 +237,52 @@ KURL ModulatorImplBase::ResolveModuleSpecifier(const String& specifier,
} }
} }
void ModulatorImplBase::RegisterImportMap(const ImportMap* import_map) { ScriptValue ModulatorImplBase::CreateTypeError(const String& message) const {
if (import_map_) { ScriptState::Scope scope(script_state_);
// Only one import map is allowed. ScriptValue error(script_state_, V8ThrowException::CreateTypeError(
// TODO(crbug.com/927119): Implement merging. script_state_->GetIsolate(), message));
GetExecutionContext()->AddConsoleMessage( return error;
mojom::ConsoleMessageSource::kOther, mojom::ConsoleMessageLevel::kError, }
"Multiple import maps are not yet supported. https://crbug.com/927119");
ScriptValue ModulatorImplBase::CreateSyntaxError(const String& message) const {
ScriptState::Scope scope(script_state_);
ScriptValue error(script_state_, V8ThrowException::CreateSyntaxError(
script_state_->GetIsolate(), message));
return error;
}
// <specdef href="https://wicg.github.io/import-maps/#register-an-import-map">
void ModulatorImplBase::RegisterImportMap(const ImportMap* import_map,
ScriptValue error_to_rethrow) {
DCHECK(import_map);
DCHECK(BuiltInModuleInfraEnabled());
// <spec step="7">If import map parse result’s error to rethrow is not null,
// then:</spec>
if (!error_to_rethrow.IsEmpty()) {
// <spec step="7.1">Report the exception given import map parse result’s
// error to rethrow. ...</spec>
if (!IsScriptingDisabled()) {
ScriptState::Scope scope(script_state_);
ModuleRecord::ReportException(script_state_, error_to_rethrow.V8Value());
}
// <spec step="7.2">Return.</spec>
return; return;
} }
if (!BuiltInModuleInfraEnabled()) { // <spec step="8">Update element’s node document's import map with import map
// parse result’s import map.</spec>
//
// TODO(crbug.com/927119): Implement merging. Currently only one import map is
// allowed.
if (import_map_) {
GetExecutionContext()->AddConsoleMessage( GetExecutionContext()->AddConsoleMessage(
mojom::ConsoleMessageSource::kOther, mojom::ConsoleMessageLevel::kError, mojom::ConsoleMessageSource::kOther, mojom::ConsoleMessageLevel::kError,
"Import maps are disabled when Layered API Infra is disabled."); "Multiple import maps are not yet supported. https://crbug.com/927119");
return; return;
} }
import_map_ = import_map; import_map_ = import_map;
} }
...@@ -405,8 +435,7 @@ ScriptValue ModulatorImplBase::ExecuteModule( ...@@ -405,8 +435,7 @@ ScriptValue ModulatorImplBase::ExecuteModule(
v8::Local<v8::Module> record = module_script->V8Module(); v8::Local<v8::Module> record = module_script->V8Module();
CHECK(!record.IsEmpty()); CHECK(!record.IsEmpty());
// <spec step="7.2">Set evaluationStatus to ModuleRecord::Evaluate(). // <spec step="7.2">Set evaluationStatus to record.Evaluate(). ...</spec>
// ...</spec>
error = ModuleRecord::Evaluate(script_state_, record, error = ModuleRecord::Evaluate(script_state_, record,
module_script->SourceURL()); module_script->SourceURL());
......
...@@ -80,7 +80,10 @@ class ModulatorImplBase : public Modulator { ...@@ -80,7 +80,10 @@ class ModulatorImplBase : public Modulator {
const ReferrerScriptInfo&, const ReferrerScriptInfo&,
ScriptPromiseResolver*) override; ScriptPromiseResolver*) override;
const ImportMap* GetImportMapForTest() const final { return import_map_; } const ImportMap* GetImportMapForTest() const final { return import_map_; }
void RegisterImportMap(const ImportMap*) final;
ScriptValue CreateTypeError(const String& message) const override;
ScriptValue CreateSyntaxError(const String& message) const override;
void RegisterImportMap(const ImportMap*, ScriptValue error_to_rethrow) final;
bool IsAcquiringImportMaps() const final { return acquiring_import_maps_; } bool IsAcquiringImportMaps() const final { return acquiring_import_maps_; }
void ClearIsAcquiringImportMaps() final { acquiring_import_maps_ = false; } void ClearIsAcquiringImportMaps() final { acquiring_import_maps_ = false; }
ModuleImportMeta HostGetImportMetaProperties( ModuleImportMeta HostGetImportMetaProperties(
......
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/core/script/pending_import_map.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/script/import_map.h"
#include "third_party/blink/renderer/core/script/modulator.h"
#include "third_party/blink/renderer/core/script/script_element_base.h"
#include "third_party/blink/renderer/platform/weborigin/kurl.h"
namespace blink {
PendingImportMap* PendingImportMap::CreateInline(ScriptElementBase& element,
const String& import_map_text,
const KURL& base_url) {
Document& element_document = element.GetDocument();
Document* context_document = element_document.ContextDocument();
Modulator* modulator =
Modulator::From(ToScriptStateForMainWorld(context_document->GetFrame()));
ScriptValue error_to_rethrow;
ImportMap* import_map =
ImportMap::Parse(*modulator, import_map_text, base_url, *context_document,
&error_to_rethrow);
return MakeGarbageCollected<PendingImportMap>(
element, import_map, error_to_rethrow, *context_document);
}
PendingImportMap::PendingImportMap(ScriptElementBase& element,
ImportMap* import_map,
ScriptValue error_to_rethrow,
const Document& original_context_document)
: element_(&element),
import_map_(import_map),
original_context_document_(&original_context_document) {
if (!error_to_rethrow.IsEmpty()) {
ScriptState::Scope scope(error_to_rethrow.GetScriptState());
error_to_rethrow_.Set(error_to_rethrow.GetIsolate(),
error_to_rethrow.V8Value());
}
}
// <specdef href="https://wicg.github.io/import-maps/#register-an-import-map">
// This is parallel to PendingScript::ExecuteScriptBlock().
void PendingImportMap::RegisterImportMap() const {
// <spec step="1">If element’s the script’s result is null, then fire an event
// named error at element, and return.</spec>
if (!import_map_) {
element_->DispatchErrorEvent();
return;
}
// <spec step="2">Let import map parse result be element’s the script’s
// result.</spec>
//
// This is |this|.
// <spec step="3">Assert: element’s the script’s type is "importmap".</spec>
//
// <spec step="4">Assert: import map parse result is an import map parse
// result.</spec>
//
// These are ensured by C++ type.
// <spec step="5">Let settings object be import map parse result’s settings
// object.</spec>
//
// <spec step="6">If element’s node document’s relevant settings object is not
// equal to settings object, then return. ...</spec>
Document* context_document = element_->GetDocument().ContextDocument();
if (!context_document)
return;
if (original_context_document_ != context_document)
return;
// Steps 7 and 8.
LocalFrame* frame = context_document->GetFrame();
if (!frame)
return;
Modulator* modulator = Modulator::From(ToScriptStateForMainWorld(frame));
ScriptState* script_state = modulator->GetScriptState();
ScriptState::Scope scope(script_state);
ScriptValue error(script_state,
error_to_rethrow_.NewLocal(script_state->GetIsolate()));
modulator->RegisterImportMap(import_map_, error);
// <spec step="9">If element is from an external file, then fire an event
// named load at element.</spec>
//
// TODO(hiroshige): Implement this when external import maps are implemented.
}
void PendingImportMap::Trace(Visitor* visitor) {
visitor->Trace(element_);
visitor->Trace(import_map_);
visitor->Trace(error_to_rethrow_);
visitor->Trace(original_context_document_);
}
} // namespace blink
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef THIRD_PARTY_BLINK_RENDERER_CORE_SCRIPT_PENDING_IMPORT_MAP_H_
#define THIRD_PARTY_BLINK_RENDERER_CORE_SCRIPT_PENDING_IMPORT_MAP_H_
#include "third_party/blink/renderer/core/core_export.h"
#include "third_party/blink/renderer/platform/bindings/trace_wrapper_v8_reference.h"
#include "third_party/blink/renderer/platform/heap/garbage_collected.h"
#include "third_party/blink/renderer/platform/heap/member.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
#include "v8/include/v8.h"
namespace blink {
class Document;
class ImportMap;
class KURL;
class ScriptElementBase;
class ScriptValue;
// PendingImportMap serves as a container for an import map after "prepare a
// script" until it is registered. PendingImportMap is similar to PendingScript.
//
// After PendingImportMap is ready, PendingImportMap works mostly as
// https://wicg.github.io/import-maps/#import-map-parse-result and
// |element_|'s script's result is |this|,
// except for "null import map parse result" corresponds to
// non-null PendingImportMap with |import_map_| == nullptr.
//
// Note: Currently we only support inline import maps and PendingImportMap is
// always ready.
class CORE_EXPORT PendingImportMap final
: public GarbageCollectedFinalized<PendingImportMap> {
public:
// https://wicg.github.io/import-maps/#create-an-import-map-parse-result
// for inline import maps.
static PendingImportMap* CreateInline(ScriptElementBase&,
const String& import_map_text,
const KURL& base_url);
PendingImportMap(ScriptElementBase&,
ImportMap*,
ScriptValue error_to_rethrow,
const Document& original_context_document);
void RegisterImportMap() const;
virtual void Trace(Visitor* visitor);
private:
Member<ScriptElementBase> element_;
// https://wicg.github.io/import-maps/#import-map-parse-result-import-map
Member<ImportMap> import_map_;
// https://wicg.github.io/import-maps/#import-map-parse-result-error-to-rethrow
// The error is TypeError if the string is non-null, or null otherwise.
TraceWrapperV8Reference<v8::Value> error_to_rethrow_;
// https://wicg.github.io/import-maps/#import-map-parse-result-settings-object
// The context document at the time when PrepareScript() is executed.
// This is only used to check whether the script element is moved between
// documents and thus doesn't retain a strong reference.
WeakMember<const Document> original_context_document_;
DISALLOW_COPY_AND_ASSIGN(PendingImportMap);
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_CORE_SCRIPT_PENDING_IMPORT_MAP_H_
...@@ -46,6 +46,7 @@ ...@@ -46,6 +46,7 @@
#include "third_party/blink/renderer/core/script/js_module_script.h" #include "third_party/blink/renderer/core/script/js_module_script.h"
#include "third_party/blink/renderer/core/script/modulator.h" #include "third_party/blink/renderer/core/script/modulator.h"
#include "third_party/blink/renderer/core/script/module_pending_script.h" #include "third_party/blink/renderer/core/script/module_pending_script.h"
#include "third_party/blink/renderer/core/script/pending_import_map.h"
#include "third_party/blink/renderer/core/script/script.h" #include "third_party/blink/renderer/core/script/script.h"
#include "third_party/blink/renderer/core/script/script_element_base.h" #include "third_party/blink/renderer/core/script/script_element_base.h"
#include "third_party/blink/renderer/core/script/script_runner.h" #include "third_party/blink/renderer/core/script/script_runner.h"
...@@ -172,50 +173,6 @@ enum class ShouldFireErrorEvent { ...@@ -172,50 +173,6 @@ enum class ShouldFireErrorEvent {
kShouldFire, kShouldFire,
}; };
ShouldFireErrorEvent ParseAndRegisterImportMap(ScriptElementBase& element) {
Document& element_document = element.GetDocument();
Document* context_document = element_document.ContextDocument();
DCHECK(context_document);
Modulator* modulator =
Modulator::From(ToScriptStateForMainWorld(context_document->GetFrame()));
DCHECK(modulator);
// If import maps are not enabled, we do nothing and return here, and also
// do not fire error events.
if (!modulator->BuiltInModuleInfraEnabled())
return ShouldFireErrorEvent::kDoNotFire;
if (!modulator->IsAcquiringImportMaps()) {
element_document.AddConsoleMessage(ConsoleMessage::Create(
mojom::ConsoleMessageSource::kJavaScript,
mojom::ConsoleMessageLevel::kError,
"An import map is added after module script load was triggered."));
return ShouldFireErrorEvent::kShouldFire;
}
// TODO(crbug.com/922212): Implemenet external import maps.
if (element.HasSourceAttribute()) {
element_document.AddConsoleMessage(
ConsoleMessage::Create(mojom::ConsoleMessageSource::kJavaScript,
mojom::ConsoleMessageLevel::kError,
"External import maps are not yet supported."));
return ShouldFireErrorEvent::kShouldFire;
}
UseCounter::Count(*context_document, WebFeature::kImportMap);
KURL base_url = element_document.BaseURL();
const String import_map_text = element.TextFromChildren();
ImportMap* import_map = ImportMap::Create(*modulator, import_map_text,
base_url, element_document);
if (!import_map)
return ShouldFireErrorEvent::kShouldFire;
modulator->RegisterImportMap(import_map);
return ShouldFireErrorEvent::kDoNotFire;
}
} // namespace } // namespace
// <specdef href="https://html.spec.whatwg.org/C/#prepare-a-script"> // <specdef href="https://html.spec.whatwg.org/C/#prepare-a-script">
...@@ -389,6 +346,14 @@ bool ScriptLoader::PrepareScript(const TextPosition& script_start_position, ...@@ -389,6 +346,14 @@ bool ScriptLoader::PrepareScript(const TextPosition& script_start_position,
if (!context_document->CanExecuteScripts(kAboutToExecuteScript)) if (!context_document->CanExecuteScripts(kAboutToExecuteScript))
return false; return false;
// Set |is_import_map| only if BuiltInModuleInfraEnabled().
if (is_import_map) {
Modulator* modulator = Modulator::From(
ToScriptStateForMainWorld(context_document->GetFrame()));
if (!modulator->BuiltInModuleInfraEnabled())
is_import_map = false;
}
// <spec step="12">If the script element has a nomodule content attribute and // <spec step="12">If the script element has a nomodule content attribute and
// the script's type is "classic", then return. The script is not // the script's type is "classic", then return. The script is not
// executed.</spec> // executed.</spec>
...@@ -424,18 +389,6 @@ bool ScriptLoader::PrepareScript(const TextPosition& script_start_position, ...@@ -424,18 +389,6 @@ bool ScriptLoader::PrepareScript(const TextPosition& script_start_position,
if (!IsScriptForEventSupported()) if (!IsScriptForEventSupported())
return false; return false;
// Process the import map.
if (is_import_map) {
if (ParseAndRegisterImportMap(*element_) ==
ShouldFireErrorEvent::kShouldFire) {
element_document.GetTaskRunner(TaskType::kDOMManipulation)
->PostTask(FROM_HERE,
WTF::Bind(&ScriptElementBase::DispatchErrorEvent,
WrapPersistent(element_.Get())));
}
return false;
}
// This FeaturePolicy is still in the process of being added to the spec. // This FeaturePolicy is still in the process of being added to the spec.
if (ShouldBlockSyncScriptForFeaturePolicy(element_.Get(), GetScriptType(), if (ShouldBlockSyncScriptForFeaturePolicy(element_.Get(), GetScriptType(),
parser_inserted_)) { parser_inserted_)) {
...@@ -523,6 +476,26 @@ bool ScriptLoader::PrepareScript(const TextPosition& script_start_position, ...@@ -523,6 +476,26 @@ bool ScriptLoader::PrepareScript(const TextPosition& script_start_position,
// TODO(hiroshige): Use a consistent Document everywhere. // TODO(hiroshige): Use a consistent Document everywhere.
auto* fetch_client_settings_object_fetcher = context_document->Fetcher(); auto* fetch_client_settings_object_fetcher = context_document->Fetcher();
// https://wicg.github.io/import-maps/#integration-prepare-a-script
// If the script’s type is "importmap" and the element’s node document’s
// acquiring import maps is false, then queue a task to fire an event named
// error at the element, and return. [spec text]
if (is_import_map) {
Modulator* modulator = Modulator::From(
ToScriptStateForMainWorld(context_document->GetFrame()));
if (!modulator->IsAcquiringImportMaps()) {
element_document.AddConsoleMessage(ConsoleMessage::Create(
mojom::ConsoleMessageSource::kJavaScript,
mojom::ConsoleMessageLevel::kError,
"An import map is added after module script load was triggered."));
element_document.GetTaskRunner(TaskType::kDOMManipulation)
->PostTask(FROM_HERE,
WTF::Bind(&ScriptElementBase::DispatchErrorEvent,
WrapPersistent(element_.Get())));
return false;
}
}
// <spec step="24">If the element has a src content attribute, then:</spec> // <spec step="24">If the element has a src content attribute, then:</spec>
if (element_->HasSourceAttribute()) { if (element_->HasSourceAttribute()) {
// <spec step="24.1">Let src be the value of the element's src // <spec step="24.1">Let src be the value of the element's src
...@@ -559,6 +532,19 @@ bool ScriptLoader::PrepareScript(const TextPosition& script_start_position, ...@@ -559,6 +532,19 @@ bool ScriptLoader::PrepareScript(const TextPosition& script_start_position,
} }
// <spec step="24.6">Switch on the script's type:</spec> // <spec step="24.6">Switch on the script's type:</spec>
if (is_import_map) {
// TODO(crbug.com/922212): Implement external import maps.
element_document.AddConsoleMessage(ConsoleMessage::Create(
mojom::ConsoleMessageSource::kJavaScript,
mojom::ConsoleMessageLevel::kError,
"External import maps are not yet supported."));
element_document.GetTaskRunner(TaskType::kDOMManipulation)
->PostTask(FROM_HERE,
WTF::Bind(&ScriptElementBase::DispatchErrorEvent,
WrapPersistent(element_.Get())));
return false;
}
if (GetScriptType() == mojom::ScriptType::kClassic) { if (GetScriptType() == mojom::ScriptType::kClassic) {
// - "classic": // - "classic":
...@@ -628,6 +614,25 @@ bool ScriptLoader::PrepareScript(const TextPosition& script_start_position, ...@@ -628,6 +614,25 @@ bool ScriptLoader::PrepareScript(const TextPosition& script_start_position,
KURL base_url = element_document.BaseURL(); KURL base_url = element_document.BaseURL();
// <spec step="25.2">Switch on the script's type:</spec> // <spec step="25.2">Switch on the script's type:</spec>
if (is_import_map) {
UseCounter::Count(*context_document, WebFeature::kImportMap);
// https://wicg.github.io/import-maps/#integration-prepare-a-script
// 1. Let import map parse result be the result of create an import map
// parse result, given source text, base URL and settings object. [spec
// text]
PendingImportMap* pending_import_map = PendingImportMap::CreateInline(
*element_, element_->TextFromChildren(), base_url);
// Because we currently support inline import maps only, the pending
// import map is ready immediately and thus we call `register an import
// map` synchronously here.
pending_import_map->RegisterImportMap();
return false;
}
switch (GetScriptType()) { switch (GetScriptType()) {
// <spec step="25.2.A">"classic"</spec> // <spec step="25.2.A">"classic"</spec>
case mojom::ScriptType::kClassic: { case mojom::ScriptType::kClassic: {
......
...@@ -125,7 +125,17 @@ void DummyModulator::ResolveDynamically(const String&, ...@@ -125,7 +125,17 @@ void DummyModulator::ResolveDynamically(const String&,
NOTREACHED(); NOTREACHED();
} }
void DummyModulator::RegisterImportMap(const ImportMap*) { ScriptValue DummyModulator::CreateTypeError(const String& message) const {
NOTREACHED();
return ScriptValue();
}
ScriptValue DummyModulator::CreateSyntaxError(const String& message) const {
NOTREACHED();
return ScriptValue();
}
void DummyModulator::RegisterImportMap(const ImportMap*,
ScriptValue error_to_rethrow) {
NOTREACHED(); NOTREACHED();
} }
......
...@@ -61,7 +61,10 @@ class DummyModulator : public Modulator { ...@@ -61,7 +61,10 @@ class DummyModulator : public Modulator {
const KURL&, const KURL&,
const ReferrerScriptInfo&, const ReferrerScriptInfo&,
ScriptPromiseResolver*) override; ScriptPromiseResolver*) override;
void RegisterImportMap(const ImportMap*) override; ScriptValue CreateTypeError(const String& message) const override;
ScriptValue CreateSyntaxError(const String& message) const override;
void RegisterImportMap(const ImportMap*,
ScriptValue error_to_rethrow) override;
bool IsAcquiringImportMaps() const override; bool IsAcquiringImportMaps() const override;
void ClearIsAcquiringImportMaps() override; void ClearIsAcquiringImportMaps() override;
ModuleImportMeta HostGetImportMetaProperties( ModuleImportMeta HostGetImportMetaProperties(
......
This is a testharness.js-based test. This is a testharness.js-based test.
FAIL Invalid JSON assert_throws: function "() => parseFromString('{ imports: {} }', 'https://base.example/')" did not throw PASS Invalid JSON
FAIL Mismatching the top-level schema / should throw for top-level non-objects assert_throws: function "() => parseFromString(input, baseURL)" did not throw PASS Mismatching the top-level schema / should throw for top-level non-objects
FAIL Mismatching the top-level schema / should throw if imports is a non-object assert_throws: function "() => parseFromString(input, baseURL)" did not throw PASS Mismatching the top-level schema / should throw if imports is a non-object
FAIL Mismatching the top-level schema / should throw if scopes is a non-object assert_throws: function "() => parseFromString(input, baseURL)" did not throw FAIL Mismatching the top-level schema / should throw if scopes is a non-object assert_throws: function "() => parseFromString(input, baseURL)" did not throw
FAIL Mismatching the top-level schema / should ignore unspecified top-level entries assert_object_equals: expected property "0" missing FAIL Mismatching the top-level schema / should ignore unspecified top-level entries assert_object_equals: expected property "0" missing
PASS Mismatching the specifier map schema / should ignore entries where the address is not a string, array, or null PASS Mismatching the specifier map schema / should ignore entries where the address is not a string, array, or null
......
...@@ -80,16 +80,28 @@ function parseFromString(mapString, mapBaseURL) { ...@@ -80,16 +80,28 @@ function parseFromString(mapString, mapBaseURL) {
iframe.contentDocument.write(` iframe.contentDocument.write(`
<base href="${mapBaseURL}"> <base href="${mapBaseURL}">
<script> <script>
let isError = false; var scriptError;
function onError() { var windowError;
isError = true; function onScriptError(event) {
scriptError = event.error;
} }
function onWindowError(event) {
windowError = event.error;
return false;
}
window.addEventListener('error', onWindowError);
</sc` + `ript> </sc` + `ript>
<script type="importmap" onerror="onError()"> <script type="importmap" onerror="onScriptError(event)">
${mapString} ${mapString}
</sc` + `ript> </sc` + `ript>
`); `);
iframe.contentDocument.close(); iframe.contentDocument.close();
// Rethrow window's error event.
if (iframe.contentWindow.windowError) {
throw iframe.contentWindow.windowError;
}
return iframe; return iframe;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment