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") {
"module_script.h",
"parsed_specifier.cc",
"parsed_specifier.h",
"pending_import_map.cc",
"pending_import_map.h",
"pending_script.cc",
"pending_script.h",
"script.h",
......
......@@ -7,6 +7,7 @@
#include <memory>
#include <utility>
#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/modulator.h"
#include "third_party/blink/renderer/core/script/parsed_specifier.h"
......@@ -146,35 +147,30 @@ KURL NormalizeValue(const String& key,
// 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
// the console |logger|.
// TODO(hiroshige): Handle errors in a spec-conformant way once specified.
// https://github.com/WICG/import-maps/issues/100
ImportMap* ImportMap::Create(const Modulator& modulator_for_built_in_modules,
const String& input,
const KURL& base_url,
ConsoleLogger& logger) {
ImportMap* ImportMap::Parse(const Modulator& modulator,
const String& input,
const KURL& base_url,
ConsoleLogger& logger,
ScriptValue* error_to_rethrow) {
DCHECK(error_to_rethrow);
// <spec step="1">Let parsed be the result of parsing JSON into Infra values
// given input.</spec>
std::unique_ptr<JSONValue> parsed = ParseJSON(input);
if (!parsed) {
logger.AddConsoleMessage(mojom::ConsoleMessageSource::kOther,
mojom::ConsoleMessageLevel::kError,
"Failed to parse import map: invalid JSON");
// TODO(hiroshige): Return null.
return MakeGarbageCollected<ImportMap>(modulator_for_built_in_modules,
SpecifierMap());
*error_to_rethrow =
modulator.CreateSyntaxError("Failed to parse import map: invalid JSON");
return MakeGarbageCollected<ImportMap>(modulator, SpecifierMap());
}
// <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>
std::unique_ptr<JSONObject> parsed_map = JSONObject::From(std::move(parsed));
if (!parsed_map) {
logger.AddConsoleMessage(mojom::ConsoleMessageSource::kOther,
mojom::ConsoleMessageLevel::kError,
"Failed to parse import map: not an object");
// TODO(hiroshige): Return null.
return MakeGarbageCollected<ImportMap>(modulator_for_built_in_modules,
SpecifierMap());
*error_to_rethrow =
modulator.CreateTypeError("Failed to parse import map: not an object");
return MakeGarbageCollected<ImportMap>(modulator, SpecifierMap());
}
// <spec step="3">Let sortedAndNormalizedImports be an empty map.</spec>
......@@ -187,13 +183,10 @@ ImportMap* ImportMap::Create(const Modulator& modulator_for_built_in_modules,
// object.</spec>
JSONObject* imports = parsed_map->GetJSONObject("imports");
if (!imports) {
logger.AddConsoleMessage(mojom::ConsoleMessageSource::kOther,
mojom::ConsoleMessageLevel::kError,
"Failed to parse import map: \"imports\" "
"top-level key must be a JSON object.");
// TODO(hiroshige): Return null.
return MakeGarbageCollected<ImportMap>(modulator_for_built_in_modules,
SpecifierMap());
*error_to_rethrow = modulator.CreateTypeError(
"Failed to parse import map: \"imports\" "
"top-level key must be a JSON object.");
return MakeGarbageCollected<ImportMap>(modulator, SpecifierMap());
}
// <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,
// <spec step="8">Return the import map whose imports are
// sortedAndNormalizedImports and whose scopes scopes are
// sortedAndNormalizedScopes.</spec>
return MakeGarbageCollected<ImportMap>(modulator_for_built_in_modules,
return MakeGarbageCollected<ImportMap>(modulator,
sorted_and_normalized_imports);
}
......
......@@ -18,6 +18,7 @@ class ConsoleLogger;
class JSONObject;
class Modulator;
class ParsedSpecifier;
class ScriptValue;
// Import maps.
// https://wicg.github.io/import-maps/
......@@ -25,10 +26,11 @@ class ParsedSpecifier;
class CORE_EXPORT ImportMap final
: public GarbageCollectedFinalized<ImportMap> {
public:
static ImportMap* Create(const Modulator& modulator_for_built_in_modules,
const String& text,
const KURL& base_url,
ConsoleLogger& logger);
static ImportMap* Parse(const Modulator&,
const String& text,
const KURL& base_url,
ConsoleLogger& logger,
ScriptValue* error_to_rethrow);
ImportMap(const Modulator&, const HashMap<String, Vector<KURL>>& imports);
......
......@@ -174,8 +174,12 @@ class CORE_EXPORT Modulator : public GarbageCollectedFinalized<Modulator>,
const ReferrerScriptInfo&,
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
virtual void RegisterImportMap(const ImportMap*) = 0;
virtual void RegisterImportMap(const ImportMap*,
ScriptValue error_to_rethrow) = 0;
virtual bool IsAcquiringImportMaps() const = 0;
virtual void ClearIsAcquiringImportMaps() = 0;
virtual const ImportMap* GetImportMapForTest() const = 0;
......
......@@ -237,22 +237,52 @@ KURL ModulatorImplBase::ResolveModuleSpecifier(const String& specifier,
}
}
void ModulatorImplBase::RegisterImportMap(const ImportMap* import_map) {
if (import_map_) {
// Only one import map is allowed.
// TODO(crbug.com/927119): Implement merging.
GetExecutionContext()->AddConsoleMessage(
mojom::ConsoleMessageSource::kOther, mojom::ConsoleMessageLevel::kError,
"Multiple import maps are not yet supported. https://crbug.com/927119");
ScriptValue ModulatorImplBase::CreateTypeError(const String& message) const {
ScriptState::Scope scope(script_state_);
ScriptValue error(script_state_, V8ThrowException::CreateTypeError(
script_state_->GetIsolate(), message));
return error;
}
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;
}
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(
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;
}
import_map_ = import_map;
}
......@@ -405,8 +435,7 @@ ScriptValue ModulatorImplBase::ExecuteModule(
v8::Local<v8::Module> record = module_script->V8Module();
CHECK(!record.IsEmpty());
// <spec step="7.2">Set evaluationStatus to ModuleRecord::Evaluate().
// ...</spec>
// <spec step="7.2">Set evaluationStatus to record.Evaluate(). ...</spec>
error = ModuleRecord::Evaluate(script_state_, record,
module_script->SourceURL());
......
......@@ -80,7 +80,10 @@ class ModulatorImplBase : public Modulator {
const ReferrerScriptInfo&,
ScriptPromiseResolver*) override;
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_; }
void ClearIsAcquiringImportMaps() final { acquiring_import_maps_ = false; }
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 @@
#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/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_element_base.h"
#include "third_party/blink/renderer/core/script/script_runner.h"
......@@ -172,50 +173,6 @@ enum class ShouldFireErrorEvent {
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
// <specdef href="https://html.spec.whatwg.org/C/#prepare-a-script">
......@@ -389,6 +346,14 @@ bool ScriptLoader::PrepareScript(const TextPosition& script_start_position,
if (!context_document->CanExecuteScripts(kAboutToExecuteScript))
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
// the script's type is "classic", then return. The script is not
// executed.</spec>
......@@ -424,18 +389,6 @@ bool ScriptLoader::PrepareScript(const TextPosition& script_start_position,
if (!IsScriptForEventSupported())
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.
if (ShouldBlockSyncScriptForFeaturePolicy(element_.Get(), GetScriptType(),
parser_inserted_)) {
......@@ -523,6 +476,26 @@ bool ScriptLoader::PrepareScript(const TextPosition& script_start_position,
// TODO(hiroshige): Use a consistent Document everywhere.
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>
if (element_->HasSourceAttribute()) {
// <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,
}
// <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) {
// - "classic":
......@@ -628,6 +614,25 @@ bool ScriptLoader::PrepareScript(const TextPosition& script_start_position,
KURL base_url = element_document.BaseURL();
// <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()) {
// <spec step="25.2.A">"classic"</spec>
case mojom::ScriptType::kClassic: {
......
......@@ -125,7 +125,17 @@ void DummyModulator::ResolveDynamically(const String&,
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();
}
......
......@@ -61,7 +61,10 @@ class DummyModulator : public Modulator {
const KURL&,
const ReferrerScriptInfo&,
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;
void ClearIsAcquiringImportMaps() override;
ModuleImportMeta HostGetImportMetaProperties(
......
This is a testharness.js-based test.
FAIL Invalid JSON assert_throws: function "() => parseFromString('{ imports: {} }', 'https://base.example/')" did not throw
FAIL Mismatching the top-level schema / should throw for top-level non-objects assert_throws: function "() => parseFromString(input, baseURL)" did not throw
FAIL Mismatching the top-level schema / should throw if imports is a non-object assert_throws: function "() => parseFromString(input, baseURL)" did not throw
PASS Invalid JSON
PASS Mismatching the top-level schema / should throw for top-level non-objects
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 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
......
......@@ -80,16 +80,28 @@ function parseFromString(mapString, mapBaseURL) {
iframe.contentDocument.write(`
<base href="${mapBaseURL}">
<script>
let isError = false;
function onError() {
isError = true;
var scriptError;
var windowError;
function onScriptError(event) {
scriptError = event.error;
}
function onWindowError(event) {
windowError = event.error;
return false;
}
window.addEventListener('error', onWindowError);
</sc` + `ript>
<script type="importmap" onerror="onError()">
<script type="importmap" onerror="onScriptError(event)">
${mapString}
</sc` + `ript>
`);
iframe.contentDocument.close();
// Rethrow window's error event.
if (iframe.contentWindow.windowError) {
throw iframe.contentWindow.windowError;
}
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