Bug 1342012 - Initial browser support for dynamic import from module scripts r=smaug
authorJon Coppeard <jcoppeard@mozilla.com>
Thu, 06 Dec 2018 16:52:15 -0500
changeset 509500 8e6f86cd811a93cce70fc91ac6fbb93405aff803
parent 509499 2f619be4479861b2cf0781e8376fda79044920ef
child 509501 c73b0641fc8e233767085a1c5868e82601d4830d
push id10547
push userffxbld-merge
push dateMon, 21 Jan 2019 13:03:58 +0000
treeherdermozilla-beta@24ec1916bffe [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs1342012
milestone66.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1342012 - Initial browser support for dynamic import from module scripts r=smaug
dom/script/ModuleLoadRequest.cpp
dom/script/ModuleLoadRequest.h
dom/script/ModuleScript.cpp
dom/script/ModuleScript.h
dom/script/ScriptLoadRequest.cpp
dom/script/ScriptLoadRequest.h
dom/script/ScriptLoader.cpp
dom/script/ScriptLoader.h
modules/libpref/init/all.js
testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/__dir__.ini
testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/dynamic-imports.html.ini
testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/inline-event-handler.html.ini
testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/propagate-nonce-external-module.html.ini
testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/propagate-nonce-inline-module.html.ini
testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-external-classic.html.ini
testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-external-module.html.ini
testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-inline-classic.html.ini
testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-inline-module.html.ini
testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-classic.html.ini
testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-module.html.ini
testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-nonce-classic.html.ini
testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-nonce-module.html.ini
testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-of-promise-result.html.ini
--- a/dom/script/ModuleLoadRequest.cpp
+++ b/dom/script/ModuleLoadRequest.cpp
@@ -1,58 +1,118 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ModuleLoadRequest.h"
+
+#include "mozilla/HoldDropJSObjects.h"
+
 #include "ModuleScript.h"
 #include "ScriptLoader.h"
 
 namespace mozilla {
 namespace dom {
 
 #undef LOG
 #define LOG(args) \
   MOZ_LOG(ScriptLoader::gScriptLoaderLog, mozilla::LogLevel::Debug, args)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ModuleLoadRequest)
 NS_INTERFACE_MAP_END_INHERITING(ScriptLoadRequest)
 
-NS_IMPL_CYCLE_COLLECTION_INHERITED(ModuleLoadRequest, ScriptLoadRequest,
-                                   mBaseURL, mLoader, mModuleScript, mImports)
+NS_IMPL_CYCLE_COLLECTION_CLASS(ModuleLoadRequest)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ModuleLoadRequest,
+                                                ScriptLoadRequest)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mBaseURL, mLoader, mModuleScript, mImports)
+  tmp->ClearDynamicImport();
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ModuleLoadRequest,
+                                                  ScriptLoadRequest)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBaseURL, mLoader, mModuleScript, mImports)
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(ModuleLoadRequest,
+                                               ScriptLoadRequest)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mDynamicReferencingPrivate)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mDynamicSpecifier)
+  NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mDynamicPromise)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_IMPL_ADDREF_INHERITED(ModuleLoadRequest, ScriptLoadRequest)
 NS_IMPL_RELEASE_INHERITED(ModuleLoadRequest, ScriptLoadRequest)
 
-ModuleLoadRequest::ModuleLoadRequest(nsIURI* aURI,
-                                     ScriptFetchOptions* aFetchOptions,
-                                     const SRIMetadata& aIntegrity,
-                                     nsIURI* aReferrer, ScriptLoader* aLoader)
-    : ScriptLoadRequest(ScriptKind::eModule, aURI, aFetchOptions, aIntegrity,
-                        aReferrer),
-      mIsTopLevel(true),
-      mLoader(aLoader),
-      mVisitedSet(new VisitedURLSet()) {
-  mVisitedSet->PutEntry(aURI);
+static VisitedURLSet* NewVisitedSetForTopLevelImport(nsIURI* aURI) {
+  auto set = new VisitedURLSet();
+  set->PutEntry(aURI);
+  return set;
+}
+
+/* static */ ModuleLoadRequest* ModuleLoadRequest::CreateTopLevel(
+    nsIURI* aURI, ScriptFetchOptions* aFetchOptions,
+    const SRIMetadata& aIntegrity, nsIURI* aReferrer, ScriptLoader* aLoader) {
+  return new ModuleLoadRequest(aURI, aFetchOptions, aIntegrity, aReferrer,
+                               true,  /* is top level */
+                               false, /* is dynamic import */
+                               aLoader, NewVisitedSetForTopLevelImport(aURI));
+}
+
+/* static */ ModuleLoadRequest* ModuleLoadRequest::CreateStaticImport(
+    nsIURI* aURI, ModuleLoadRequest* aParent) {
+  auto request =
+      new ModuleLoadRequest(aURI, aParent->mFetchOptions, SRIMetadata(),
+                            aParent->mURI, false, /* is top level */
+                            false,                /* is dynamic import */
+                            aParent->mLoader, aParent->mVisitedSet);
+
+  request->mIsInline = false;
+  request->mScriptMode = aParent->mScriptMode;
+
+  return request;
 }
 
-ModuleLoadRequest::ModuleLoadRequest(nsIURI* aURI, ModuleLoadRequest* aParent)
-    : ScriptLoadRequest(ScriptKind::eModule, aURI, aParent->mFetchOptions,
-                        SRIMetadata(), aParent->mURI),
-      mIsTopLevel(false),
-      mLoader(aParent->mLoader),
-      mVisitedSet(aParent->mVisitedSet) {
-  MOZ_ASSERT(mVisitedSet->Contains(aURI));
+/* static */ ModuleLoadRequest* ModuleLoadRequest::CreateDynamicImport(
+    nsIURI* aURI, ModuleScript* aScript,
+    JS::Handle<JS::Value> aReferencingPrivate, JS::Handle<JSString*> aSpecifier,
+    JS::Handle<JSObject*> aPromise) {
+  MOZ_ASSERT(aSpecifier);
+  MOZ_ASSERT(aPromise);
+
+  auto request = new ModuleLoadRequest(
+      aURI, aScript->FetchOptions(), SRIMetadata(), aScript->BaseURL(),
+      true, /* is top level */
+      true, /* is dynamic import */
+      aScript->Loader(), NewVisitedSetForTopLevelImport(aURI));
 
-  mIsInline = false;
-  mScriptMode = aParent->mScriptMode;
+  request->mIsInline = false;
+  request->mScriptMode = ScriptMode::eAsync;
+  request->mDynamicReferencingPrivate = aReferencingPrivate;
+  request->mDynamicSpecifier = aSpecifier;
+  request->mDynamicPromise = aPromise;
+
+  HoldJSObjects(request);
+
+  return request;
 }
 
+ModuleLoadRequest::ModuleLoadRequest(
+    nsIURI* aURI, ScriptFetchOptions* aFetchOptions,
+    const SRIMetadata& aIntegrity, nsIURI* aReferrer, bool aIsTopLevel,
+    bool aIsDynamicImport, ScriptLoader* aLoader, VisitedURLSet* aVisitedSet)
+    : ScriptLoadRequest(ScriptKind::eModule, aURI, aFetchOptions, aIntegrity,
+                        aReferrer),
+      mIsTopLevel(aIsTopLevel),
+      mIsDynamicImport(aIsDynamicImport),
+      mLoader(aLoader),
+      mVisitedSet(aVisitedSet) {}
+
 void ModuleLoadRequest::Cancel() {
   ScriptLoadRequest::Cancel();
   mModuleScript = nullptr;
   mProgress = ScriptLoadRequest::Progress::eReady;
   CancelImports();
   mReady.RejectIfExists(NS_ERROR_DOM_ABORT_ERR, __func__);
 }
 
@@ -133,10 +193,16 @@ void ModuleLoadRequest::LoadFailed() {
 }
 
 void ModuleLoadRequest::LoadFinished() {
   mLoader->ProcessLoadedModuleTree(this);
 
   mLoader = nullptr;
 }
 
+void ModuleLoadRequest::ClearDynamicImport() {
+  mDynamicReferencingPrivate = JS::UndefinedValue();
+  mDynamicSpecifier = nullptr;
+  mDynamicPromise = nullptr;
+}
+
 }  // namespace dom
 }  // namespace mozilla
--- a/dom/script/ModuleLoadRequest.h
+++ b/dom/script/ModuleLoadRequest.h
@@ -31,46 +31,67 @@ class VisitedURLSet : public nsTHashtabl
 // multiple imports of the same module.
 
 class ModuleLoadRequest final : public ScriptLoadRequest {
   ~ModuleLoadRequest() = default;
 
   ModuleLoadRequest(const ModuleLoadRequest& aOther) = delete;
   ModuleLoadRequest(ModuleLoadRequest&& aOther) = delete;
 
+  ModuleLoadRequest(nsIURI* aURI, ScriptFetchOptions* aFetchOptions,
+                    const SRIMetadata& aIntegrity, nsIURI* aReferrer,
+                    bool aIsTopLevel, bool aIsDynamicImport,
+                    ScriptLoader* aLoader, VisitedURLSet* aVisitedSet);
+
  public:
   NS_DECL_ISUPPORTS_INHERITED
-  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ModuleLoadRequest, ScriptLoadRequest)
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(ModuleLoadRequest,
+                                                         ScriptLoadRequest)
 
   // Create a top-level module load request.
-  ModuleLoadRequest(nsIURI* aURI, ScriptFetchOptions* aFetchOptions,
-                    const SRIMetadata& aIntegrity, nsIURI* aReferrer,
-                    ScriptLoader* aLoader);
+  static ModuleLoadRequest* CreateTopLevel(nsIURI* aURI,
+                                           ScriptFetchOptions* aFetchOptions,
+                                           const SRIMetadata& aIntegrity,
+                                           nsIURI* aReferrer,
+                                           ScriptLoader* aLoader);
 
-  // Create a module load request for an imported module.
-  ModuleLoadRequest(nsIURI* aURI, ModuleLoadRequest* aParent);
+  // Create a module load request for a static module import.
+  static ModuleLoadRequest* CreateStaticImport(nsIURI* aURI,
+                                               ModuleLoadRequest* aParent);
+
+  // Create a module load request for dynamic module import.
+  static ModuleLoadRequest* CreateDynamicImport(
+      nsIURI* aURI, ModuleScript* aScript,
+      JS::Handle<JS::Value> aReferencingPrivate,
+      JS::Handle<JSString*> aSpecifier, JS::Handle<JSObject*> aPromise);
 
   bool IsTopLevel() const override { return mIsTopLevel; }
 
+  bool IsDynamicImport() const { return mIsDynamicImport; }
+
   void SetReady() override;
   void Cancel() override;
+  void ClearDynamicImport();
 
   void ModuleLoaded();
   void ModuleErrored();
   void DependenciesLoaded();
   void LoadFailed();
 
  private:
   void LoadFinished();
   void CancelImports();
 
  public:
   // Is this a request for a top level module script or an import?
   const bool mIsTopLevel;
 
+  // Is this the top level request for a dynamic module import?
+  const bool mIsDynamicImport;
+
   // The base URL used for resolving relative module imports.
   nsCOMPtr<nsIURI> mBaseURL;
 
   // Pointer to the script loader, used to trigger actions when the module load
   // finishes.
   RefPtr<ScriptLoader> mLoader;
 
   // Set to a module script object after a successful load or nullptr on
@@ -83,14 +104,19 @@ class ModuleLoadRequest final : public S
   MozPromiseHolder<GenericPromise> mReady;
 
   // Array of imported modules.
   nsTArray<RefPtr<ModuleLoadRequest>> mImports;
 
   // Set of module URLs visited while fetching the module graph this request is
   // part of.
   RefPtr<VisitedURLSet> mVisitedSet;
+
+  // For dynamic imports, the details to pass to FinishDynamicImport.
+  JS::Heap<JS::Value> mDynamicReferencingPrivate;
+  JS::Heap<JSString*> mDynamicSpecifier;
+  JS::Heap<JSObject*> mDynamicPromise;
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 #endif  // mozilla_dom_ModuleLoadRequest_h
--- a/dom/script/ModuleScript.cpp
+++ b/dom/script/ModuleScript.cpp
@@ -1,99 +1,122 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ModuleScript.h"
+
 #include "mozilla/HoldDropJSObjects.h"
+
+#include "jsfriendapi.h"
 #include "ScriptLoader.h"
 
 namespace mozilla {
 namespace dom {
 
 // A single module script.  May be used to satisfy multiple load requests.
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ModuleScript)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(ModuleScript)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ModuleScript)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mLoader)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mFetchOptions)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mBaseURL)
   tmp->UnlinkModuleRecord();
   tmp->mParseError.setUndefined();
   tmp->mErrorToRethrow.setUndefined();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ModuleScript)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLoader)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFetchOptions)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(ModuleScript)
   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mModuleRecord)
   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mParseError)
   NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mErrorToRethrow)
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(ModuleScript)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(ModuleScript)
 
-ModuleScript::ModuleScript(ScriptLoader* aLoader, nsIURI* aBaseURL)
-    : mLoader(aLoader), mBaseURL(aBaseURL), mSourceElementAssociated(false) {
+ModuleScript::ModuleScript(ScriptLoader* aLoader,
+                           ScriptFetchOptions* aFetchOptions, nsIURI* aBaseURL)
+    : mLoader(aLoader),
+      mFetchOptions(aFetchOptions),
+      mBaseURL(aBaseURL),
+      mSourceElementAssociated(false) {
   MOZ_ASSERT(mLoader);
+  MOZ_ASSERT(mFetchOptions);
   MOZ_ASSERT(mBaseURL);
   MOZ_ASSERT(!mModuleRecord);
   MOZ_ASSERT(!HasParseError());
   MOZ_ASSERT(!HasErrorToRethrow());
 }
 
 void ModuleScript::UnlinkModuleRecord() {
-  // Remove module's back reference to this object request if present.
+  // Remove the module record's pointer to this object if present and
+  // decrement our reference count. The reference is added by
+  // SetModuleRecord() below.
   if (mModuleRecord) {
     MOZ_ASSERT(JS::GetModulePrivate(mModuleRecord).toPrivate() == this);
     JS::SetModulePrivate(mModuleRecord, JS::UndefinedValue());
     mModuleRecord = nullptr;
+    Release();
   }
 }
 
 ModuleScript::~ModuleScript() {
   // The object may be destroyed without being unlinked first.
   UnlinkModuleRecord();
   DropJSObjects(this);
 }
 
 void ModuleScript::SetModuleRecord(JS::Handle<JSObject*> aModuleRecord) {
   MOZ_ASSERT(!mModuleRecord);
   MOZ_ASSERT(!HasParseError());
   MOZ_ASSERT(!HasErrorToRethrow());
 
   mModuleRecord = aModuleRecord;
 
-  // Make module's host defined field point to this module script object.
-  // This is cleared in the UnlinkModuleRecord().
+  // Make module's host defined field point to this object and
+  // increment our reference count. This is decremented by
+  // UnlinkModuleRecord() above.
   JS::SetModulePrivate(mModuleRecord, JS::PrivateValue(this));
   HoldJSObjects(this);
+  AddRef();
+}
+
+void HostFinalizeTopLevelScript(JSFreeOp* aFop, const JS::Value& aPrivate) {
+  auto script = static_cast<ModuleScript*>(aPrivate.toPrivate());
+  if (script) {
+    MOZ_ASSERT(JS::GetModulePrivate(script->mModuleRecord.unbarrieredGet()) ==
+               aPrivate);
+    script->UnlinkModuleRecord();
+  }
 }
 
 void ModuleScript::SetParseError(const JS::Value& aError) {
   MOZ_ASSERT(!aError.isUndefined());
   MOZ_ASSERT(!HasParseError());
   MOZ_ASSERT(!HasErrorToRethrow());
 
   UnlinkModuleRecord();
   mParseError = aError;
   HoldJSObjects(this);
 }
 
 void ModuleScript::SetErrorToRethrow(const JS::Value& aError) {
   MOZ_ASSERT(!aError.isUndefined());
-  MOZ_ASSERT(!HasErrorToRethrow());
 
   // This is only called after SetModuleRecord() or SetParseError() so we don't
   // need to call HoldJSObjects() here.
   MOZ_ASSERT(mModuleRecord || HasParseError());
 
   mErrorToRethrow = aError;
 }
 
--- a/dom/script/ModuleScript.h
+++ b/dom/script/ModuleScript.h
@@ -13,45 +13,52 @@
 
 class nsIURI;
 
 namespace mozilla {
 namespace dom {
 
 class ScriptLoader;
 
+void HostFinalizeTopLevelScript(JSFreeOp* aFop, const JS::Value& aPrivate);
+
 class ModuleScript final : public nsISupports {
   RefPtr<ScriptLoader> mLoader;
+  RefPtr<ScriptFetchOptions> mFetchOptions;
   nsCOMPtr<nsIURI> mBaseURL;
   JS::Heap<JSObject*> mModuleRecord;
   JS::Heap<JS::Value> mParseError;
   JS::Heap<JS::Value> mErrorToRethrow;
   bool mSourceElementAssociated;
 
   ~ModuleScript();
 
  public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ModuleScript)
 
-  ModuleScript(ScriptLoader* aLoader, nsIURI* aBaseURL);
+  ModuleScript(ScriptLoader* aLoader, ScriptFetchOptions* aFetchOptions,
+               nsIURI* aBaseURL);
 
   void SetModuleRecord(JS::Handle<JSObject*> aModuleRecord);
   void SetParseError(const JS::Value& aError);
   void SetErrorToRethrow(const JS::Value& aError);
   void SetSourceElementAssociated();
 
   ScriptLoader* Loader() const { return mLoader; }
+  ScriptFetchOptions* FetchOptions() const { return mFetchOptions; }
   JSObject* ModuleRecord() const { return mModuleRecord; }
   nsIURI* BaseURL() const { return mBaseURL; }
   JS::Value ParseError() const { return mParseError; }
   JS::Value ErrorToRethrow() const { return mErrorToRethrow; }
   bool HasParseError() const { return !mParseError.isUndefined(); }
   bool HasErrorToRethrow() const { return !mErrorToRethrow.isUndefined(); }
   bool SourceElementAssociated() const { return mSourceElementAssociated; }
 
   void UnlinkModuleRecord();
+
+  friend void HostFinalizeTopLevelScript(JSFreeOp*, const JS::Value&);
 };
 
 }  // namespace dom
 }  // namespace mozilla
 
 #endif  // mozilla_dom_ModuleScript_h
--- a/dom/script/ScriptLoadRequest.cpp
+++ b/dom/script/ScriptLoadRequest.cpp
@@ -1,15 +1,15 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
-#include "ModuleLoadRequest.h"
+#include "ScriptLoadRequest.h"
 
 #include "mozilla/HoldDropJSObjects.h"
 #include "mozilla/Unused.h"
 
 #include "nsICacheInfoChannel.h"
 #include "ScriptLoadRequest.h"
 #include "ScriptSettings.h"
 
@@ -47,16 +47,17 @@ NS_INTERFACE_MAP_END
 NS_IMPL_CYCLE_COLLECTING_ADDREF(ScriptLoadRequest)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(ScriptLoadRequest)
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(ScriptLoadRequest)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ScriptLoadRequest)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mFetchOptions)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mCacheInfo)
+  tmp->mScript = nullptr;
   tmp->DropBytecodeCacheReferences();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ScriptLoadRequest)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFetchOptions)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCacheInfo)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
@@ -100,16 +101,18 @@ ScriptLoadRequest::~ScriptLoadRequest() 
 
   // But play it safe in release builds and try to clean them up here
   // as a fail safe.
   MaybeCancelOffThreadScript();
 
   if (mScript) {
     DropBytecodeCacheReferences();
   }
+
+  DropJSObjects(this);
 }
 
 void ScriptLoadRequest::SetReady() {
   MOZ_ASSERT(mProgress != Progress::eReady);
   mProgress = Progress::eReady;
 }
 
 void ScriptLoadRequest::Cancel() {
@@ -202,16 +205,22 @@ bool ScriptLoadRequest::ShouldAcceptBinA
 void ScriptLoadRequest::ClearScriptSource() {
   if (IsTextSource()) {
     ScriptText().clearAndFree();
   } else if (IsBinASTSource()) {
     ScriptBinASTData().clearAndFree();
   }
 }
 
+void ScriptLoadRequest::SetScript(JSScript* aScript) {
+  MOZ_ASSERT(!mScript);
+  mScript = aScript;
+  HoldJSObjects(this);
+}
+
 //////////////////////////////////////////////////////////////
 // ScriptLoadRequestList
 //////////////////////////////////////////////////////////////
 
 ScriptLoadRequestList::~ScriptLoadRequestList() { Clear(); }
 
 void ScriptLoadRequestList::Clear() {
   while (!isEmpty()) {
--- a/dom/script/ScriptLoadRequest.h
+++ b/dom/script/ScriptLoadRequest.h
@@ -205,16 +205,18 @@ class ScriptLoadRequest
     MOZ_ASSERT(!Element());
     mFetchOptions->mElement = aElement;
   }
 
   bool ShouldAcceptBinASTEncoding() const;
 
   void ClearScriptSource();
 
+  void SetScript(JSScript* aScript);
+
   void MaybeCancelOffThreadScript();
   void DropBytecodeCacheReferences();
 
   using super::getNext;
   using super::isInList;
 
   const ScriptKind
       mKind;  // Whether this is a classic script or a module script.
--- a/dom/script/ScriptLoader.cpp
+++ b/dom/script/ScriptLoader.cpp
@@ -108,19 +108,19 @@ inline void ImplCycleCollectionTraverse(
 // ScriptLoader
 //////////////////////////////////////////////////////////////
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ScriptLoader)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTION(ScriptLoader, mNonAsyncExternalScriptInsertedRequests,
                          mLoadingAsyncRequests, mLoadedAsyncRequests,
-                         mDeferRequests, mXSLTRequests, mParserBlockingRequest,
-                         mBytecodeEncodingQueue, mPreloads,
-                         mPendingChildLoaders, mFetchedModules)
+                         mDeferRequests, mXSLTRequests, mDynamicImportRequests,
+                         mParserBlockingRequest, mBytecodeEncodingQueue,
+                         mPreloads, mPendingChildLoaders, mFetchedModules)
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(ScriptLoader)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(ScriptLoader)
 
 ScriptLoader::ScriptLoader(nsIDocument* aDocument)
     : mDocument(aDocument),
       mParserBlockingBlockerCount(0),
       mBlockerCount(0),
@@ -128,16 +128,17 @@ ScriptLoader::ScriptLoader(nsIDocument* 
       mEnabled(true),
       mDeferEnabled(false),
       mDocumentParsingDone(false),
       mBlockingDOMContentLoaded(false),
       mLoadEventFired(false),
       mGiveUpEncoding(false),
       mReporter(new ConsoleReportCollector()) {
   LOG(("ScriptLoader::ScriptLoader %p", this));
+  EnsureModuleHooksInitialized();
 }
 
 ScriptLoader::~ScriptLoader() {
   LOG(("ScriptLoader::~ScriptLoader %p", this));
 
   mObservers.Clear();
 
   if (mParserBlockingRequest) {
@@ -159,16 +160,21 @@ ScriptLoader::~ScriptLoader() {
     req->FireScriptAvailable(NS_ERROR_ABORT);
   }
 
   for (ScriptLoadRequest* req = mLoadedAsyncRequests.getFirst(); req;
        req = req->getNext()) {
     req->FireScriptAvailable(NS_ERROR_ABORT);
   }
 
+  for (ScriptLoadRequest* req = mDynamicImportRequests.getFirst(); req;
+       req = req->getNext()) {
+    FinishDynamicImport(req->AsModuleRequest(), NS_ERROR_ABORT);
+  }
+
   for (ScriptLoadRequest* req =
            mNonAsyncExternalScriptInsertedRequests.getFirst();
        req; req = req->getNext()) {
     req->FireScriptAvailable(NS_ERROR_ABORT);
   }
 
   // Unblock the kids, in case any of them moved to a different document
   // subtree in the meantime and therefore aren't actually going away.
@@ -466,17 +472,17 @@ nsresult ScriptLoader::CreateModuleScrip
           rv = NS_ERROR_OUT_OF_MEMORY;
         }
       }
     }
 
     MOZ_ASSERT(NS_SUCCEEDED(rv) == (module != nullptr));
 
     RefPtr<ModuleScript> moduleScript =
-        new ModuleScript(this, aRequest->mBaseURL);
+        new ModuleScript(this, aRequest->mFetchOptions, aRequest->mBaseURL);
     aRequest->mModuleScript = moduleScript;
 
     if (!module) {
       LOG(("ScriptLoadRequest (%p):   compilation failed (%d)", aRequest,
            unsigned(rv)));
 
       MOZ_ASSERT(aes.HasException());
       JS::Rooted<JS::Value> error(cx);
@@ -684,17 +690,18 @@ void ScriptLoader::StartFetchingModuleDe
                  &ModuleLoadRequest::DependenciesLoaded,
                  &ModuleLoadRequest::ModuleErrored);
 }
 
 RefPtr<GenericPromise> ScriptLoader::StartFetchingModuleAndDependencies(
     ModuleLoadRequest* aParent, nsIURI* aURI) {
   MOZ_ASSERT(aURI);
 
-  RefPtr<ModuleLoadRequest> childRequest = new ModuleLoadRequest(aURI, aParent);
+  RefPtr<ModuleLoadRequest> childRequest =
+      ModuleLoadRequest::CreateStaticImport(aURI, aParent);
 
   aParent->mImports.AppendElement(childRequest);
 
   if (LOG_ENABLED()) {
     nsAutoCString url1;
     aParent->mURI->GetAsciiSpec(url1);
 
     nsAutoCString url2;
@@ -720,16 +727,22 @@ RefPtr<GenericPromise> ScriptLoader::Sta
   return ready;
 }
 
 // 8.1.3.8.1 HostResolveImportedModule(referencingModule, specifier)
 JSObject* HostResolveImportedModule(JSContext* aCx,
                                     JS::Handle<JS::Value> aReferencingPrivate,
                                     JS::Handle<JSString*> aSpecifier) {
   // Let referencing module script be referencingModule.[[HostDefined]].
+  if (aReferencingPrivate.isUndefined()) {
+    JS_ReportErrorNumberUC(aCx, js::GetErrorMessage, nullptr,
+                           JSMSG_IMPORT_SCRIPT_NOT_FOUND);
+    return nullptr;
+  }
+
   auto script = static_cast<ModuleScript*>(aReferencingPrivate.toPrivate());
   MOZ_ASSERT(JS::GetModulePrivate(script->ModuleRecord()) ==
              aReferencingPrivate);
 
   // Let url be the result of resolving a module specifier given referencing
   // module script and specifier.
   nsAutoJSString string;
   if (!string.init(aCx, aSpecifier)) {
@@ -769,24 +782,124 @@ bool HostPopulateImportMeta(JSContext* a
     JS_ReportOutOfMemory(aCx);
     return false;
   }
 
   return JS_DefineProperty(aCx, aMetaObject, "url", urlString,
                            JSPROP_ENUMERATE);
 }
 
-static void EnsureModuleResolveHook(JSContext* aCx) {
-  JSRuntime* rt = JS_GetRuntime(aCx);
+bool HostImportModuleDynamically(JSContext* aCx,
+                                 JS::Handle<JS::Value> aReferencingPrivate,
+                                 JS::Handle<JSString*> aSpecifier,
+                                 JS::Handle<JSObject*> aPromise) {
+  if (aReferencingPrivate.isUndefined()) {
+    JS_ReportErrorNumberUC(aCx, js::GetErrorMessage, nullptr,
+                           JSMSG_IMPORT_SCRIPT_NOT_FOUND);
+    return false;
+  }
+
+  auto script = static_cast<ModuleScript*>(aReferencingPrivate.toPrivate());
+  MOZ_ASSERT(JS::GetModulePrivate(script->ModuleRecord()) ==
+             aReferencingPrivate);
+
+  // Attempt to resolve the module specifier.
+  nsAutoJSString string;
+  if (!string.init(aCx, aSpecifier)) {
+    return false;
+  }
+
+  nsCOMPtr<nsIURI> uri = ResolveModuleSpecifier(script, string);
+  if (!uri) {
+    JS_ReportErrorNumberUC(aCx, js::GetErrorMessage, nullptr,
+                           JSMSG_BAD_MODULE_SPECIFIER, string.get());
+    return false;
+  }
+
+  // Create a new top-level load request.
+  RefPtr<ModuleLoadRequest> request = ModuleLoadRequest::CreateDynamicImport(
+      uri, script, aReferencingPrivate, aSpecifier, aPromise);
+
+  script->Loader()->StartDynamicImport(request);
+  return true;
+}
+
+void ScriptLoader::StartDynamicImport(ModuleLoadRequest* aRequest) {
+  LOG(("ScriptLoadRequest (%p): Start dynamic import", aRequest));
+
+  mDynamicImportRequests.AppendElement(aRequest);
+
+  nsresult rv = StartLoad(aRequest);
+  if (NS_FAILED(rv)) {
+    FinishDynamicImport(aRequest, rv);
+  }
+}
+
+void ScriptLoader::FinishDynamicImport(ModuleLoadRequest* aRequest,
+                                       nsresult aResult) {
+  AutoJSAPI jsapi;
+  MOZ_ALWAYS_TRUE(jsapi.Init(aRequest->mDynamicPromise));
+  FinishDynamicImport(jsapi.cx(), aRequest, aResult);
+}
+
+void ScriptLoader::FinishDynamicImport(JSContext* aCx,
+                                       ModuleLoadRequest* aRequest,
+                                       nsresult aResult) {
+  LOG(("ScriptLoadRequest (%p): Finish dynamic import %d %d", aRequest,
+       unsigned(aResult), JS_IsExceptionPending(aCx)));
+
+  // Complete the dynamic import, report failures indicated by aResult or as a
+  // pending exception on the context.
+
+  if (NS_FAILED(aResult)) {
+    MOZ_ASSERT(!JS_IsExceptionPending(aCx));
+    JS_ReportErrorNumberUC(aCx, js::GetErrorMessage, nullptr,
+                           JSMSG_IMPORT_SCRIPT_NOT_FOUND);
+  }
+
+  JS::Rooted<JS::Value> referencingScript(aCx,
+                                          aRequest->mDynamicReferencingPrivate);
+  JS::Rooted<JSString*> specifier(aCx, aRequest->mDynamicSpecifier);
+  JS::Rooted<JSObject*> promise(aCx, aRequest->mDynamicPromise);
+
+  JS::FinishDynamicModuleImport(aCx, referencingScript, specifier, promise);
+
+  // FinishDynamicModuleImport clears any pending exception.
+  MOZ_ASSERT(!JS_IsExceptionPending(aCx));
+
+  aRequest->ClearDynamicImport();
+}
+
+static void DynamicImportPrefChangedCallback(const char* aPrefName,
+                                             void* aClosure) {
+  bool enabled = Preferences::GetBool(aPrefName);
+  JS::ModuleDynamicImportHook hook =
+      enabled ? HostImportModuleDynamically : nullptr;
+
+  AutoJSAPI jsapi;
+  jsapi.Init();
+  JSRuntime* rt = JS_GetRuntime(jsapi.cx());
+  JS::SetModuleDynamicImportHook(rt, hook);
+}
+
+void ScriptLoader::EnsureModuleHooksInitialized() {
+  AutoJSAPI jsapi;
+  jsapi.Init();
+  JSRuntime* rt = JS_GetRuntime(jsapi.cx());
   if (JS::GetModuleResolveHook(rt)) {
     return;
   }
 
   JS::SetModuleResolveHook(rt, HostResolveImportedModule);
   JS::SetModuleMetadataHook(rt, HostPopulateImportMeta);
+  JS::SetScriptPrivateFinalizeHook(rt, HostFinalizeTopLevelScript);
+
+  Preferences::RegisterCallbackAndCall(DynamicImportPrefChangedCallback,
+                                       "javascript.options.dynamicImport",
+                                       (void*)nullptr);
 }
 
 void ScriptLoader::CheckModuleDependenciesLoaded(ModuleLoadRequest* aRequest) {
   LOG(("ScriptLoadRequest (%p): Check dependencies loaded", aRequest));
 
   RefPtr<ModuleScript> moduleScript = aRequest->mModuleScript;
   if (!moduleScript || moduleScript->HasParseError()) {
     return;
@@ -810,28 +923,44 @@ class ScriptRequestProcessor : public Ru
   RefPtr<ScriptLoader> mLoader;
   RefPtr<ScriptLoadRequest> mRequest;
 
  public:
   ScriptRequestProcessor(ScriptLoader* aLoader, ScriptLoadRequest* aRequest)
       : Runnable("dom::ScriptRequestProcessor"),
         mLoader(aLoader),
         mRequest(aRequest) {}
-  NS_IMETHOD Run() override { return mLoader->ProcessRequest(mRequest); }
+  NS_IMETHOD Run() override {
+    if (mRequest->IsModuleRequest() &&
+        mRequest->AsModuleRequest()->IsDynamicImport()) {
+      mLoader->ProcessDynamicImport(mRequest->AsModuleRequest());
+      return NS_OK;
+    }
+
+    return mLoader->ProcessRequest(mRequest);
+  }
 };
 
+void ScriptLoader::RunScriptWhenSafe(ScriptLoadRequest* aRequest) {
+  auto runnable = new ScriptRequestProcessor(this, aRequest);
+  nsContentUtils::AddScriptRunner(runnable);
+}
+
 void ScriptLoader::ProcessLoadedModuleTree(ModuleLoadRequest* aRequest) {
   MOZ_ASSERT(aRequest->IsReadyToRun());
 
   if (aRequest->IsTopLevel()) {
-    if (aRequest->mIsInline &&
-        aRequest->Element()->GetParserCreated() == NOT_FROM_PARSER) {
+    if (aRequest->IsDynamicImport()) {
+      MOZ_ASSERT(aRequest->isInList());
+      RefPtr<ScriptLoadRequest> req = mDynamicImportRequests.Steal(aRequest);
+      RunScriptWhenSafe(req);
+    } else if (aRequest->mIsInline &&
+               aRequest->Element()->GetParserCreated() == NOT_FROM_PARSER) {
       MOZ_ASSERT(!aRequest->isInList());
-      nsContentUtils::AddScriptRunner(
-          new ScriptRequestProcessor(this, aRequest));
+      RunScriptWhenSafe(aRequest);
     } else {
       MaybeMoveToLoadedList(aRequest);
       ProcessPendingRequests();
     }
   }
 
   if (aRequest->mWasCompiledOMT) {
     mDocument->UnblockOnload(false);
@@ -879,18 +1008,16 @@ bool ScriptLoader::InstantiateModuleTree
   MOZ_ASSERT(moduleScript->ModuleRecord());
 
   nsAutoMicroTask mt;
   AutoJSAPI jsapi;
   if (NS_WARN_IF(!jsapi.Init(moduleScript->ModuleRecord()))) {
     return false;
   }
 
-  EnsureModuleResolveHook(jsapi.cx());
-
   JS::Rooted<JSObject*> module(jsapi.cx(), moduleScript->ModuleRecord());
   bool ok = NS_SUCCEEDED(nsJSUtils::ModuleInstantiate(jsapi.cx(), module));
 
   if (!ok) {
     LOG(("ScriptLoadRequest (%p): Instantiate failed", aRequest));
     MOZ_ASSERT(jsapi.HasException());
     JS::RootedValue exception(jsapi.cx());
     if (!jsapi.StealException(&exception)) {
@@ -1205,17 +1332,18 @@ ScriptLoadRequest* ScriptLoader::CreateL
       aCORSMode, aReferrerPolicy, aElement, aTriggeringPrincipal);
 
   if (aKind == ScriptKind::eClassic) {
     return new ScriptLoadRequest(aKind, aURI, fetchOptions, aIntegrity,
                                  referrer);
   }
 
   MOZ_ASSERT(aKind == ScriptKind::eModule);
-  return new ModuleLoadRequest(aURI, fetchOptions, aIntegrity, referrer, this);
+  return ModuleLoadRequest::CreateTopLevel(aURI, fetchOptions, aIntegrity,
+                                           referrer, this);
 }
 
 bool ScriptLoader::ProcessScriptElement(nsIScriptElement* aElement) {
   // We need a document to evaluate scripts.
   NS_ENSURE_TRUE(mDocument, false);
 
   // Check to see if scripts has been turned off.
   if (!mEnabled || !mDocument->IsScriptEnabled()) {
@@ -1533,17 +1661,17 @@ bool ScriptLoader::ProcessInlineScript(n
                  "Parser-blocking scripts and XSLT scripts in the same doc!");
     mXSLTRequests.AppendElement(request);
     return true;
   }
   if (aElement->GetParserCreated() == NOT_FROM_PARSER) {
     NS_ASSERTION(
         !nsContentUtils::IsSafeToRunScript(),
         "A script-inserted script is inserted without an update batch?");
-    nsContentUtils::AddScriptRunner(new ScriptRequestProcessor(this, request));
+    RunScriptWhenSafe(request);
     return false;
   }
   if (aElement->GetParserCreated() == FROM_PARSER_NETWORK &&
       !ReadyToExecuteParserBlockingScripts()) {
     NS_ASSERTION(!mParserBlockingRequest,
                  "There can be only one parser-blocking script at a time");
     mParserBlockingRequest = request;
     NS_ASSERTION(mXSLTRequests.isEmpty(),
@@ -1910,18 +2038,17 @@ nsresult ScriptLoader::ProcessRequest(Sc
                "Processing requests when running scripts is unsafe.");
   NS_ASSERTION(aRequest->IsReadyToRun(),
                "Processing a request that is not ready to run.");
 
   NS_ENSURE_ARG(aRequest);
 
   if (aRequest->IsModuleRequest()) {
     ModuleLoadRequest* request = aRequest->AsModuleRequest();
-    if (request->mModuleScript &&
-        !request->mModuleScript->HasErrorToRethrow()) {
+    if (request->mModuleScript) {
       if (!InstantiateModuleTree(request)) {
         request->mModuleScript = nullptr;
       }
     }
 
     if (!request->mModuleScript) {
       // There was an error fetching a module script.  Nothing to do here.
       LOG(("ScriptLoadRequest (%p):   Error loading request, firing error",
@@ -2013,16 +2140,35 @@ nsresult ScriptLoader::ProcessRequest(Sc
     // encoding the bytecode once more. We can safely clear the content of this
     // buffer.
     aRequest->mScriptBytecode.clearAndFree();
   }
 
   return rv;
 }
 
+void ScriptLoader::ProcessDynamicImport(ModuleLoadRequest* aRequest) {
+  if (aRequest->mModuleScript) {
+    if (!InstantiateModuleTree(aRequest)) {
+      aRequest->mModuleScript = nullptr;
+    }
+  }
+
+  nsresult rv = NS_ERROR_FAILURE;
+  if (aRequest->mModuleScript) {
+    rv = EvaluateScript(aRequest);
+  }
+
+  if (NS_FAILED(rv)) {
+    FinishDynamicImport(aRequest, rv);
+  }
+
+  return;
+}
+
 void ScriptLoader::FireScriptAvailable(nsresult aResult,
                                        ScriptLoadRequest* aRequest) {
   for (int32_t i = 0; i < mObservers.Count(); i++) {
     nsCOMPtr<nsIScriptLoaderObserver> obs = mObservers[i];
     obs->ScriptAvailable(aResult, aRequest->Element(), aRequest->mIsInline,
                          aRequest->mURI, aRequest->mLineNo);
   }
 
@@ -2057,17 +2203,17 @@ already_AddRefed<nsIScriptGlobalObject> 
   if (NS_FAILED(rv)) {
     return nullptr;
   }
 
   return globalObject.forget();
 }
 
 nsresult ScriptLoader::FillCompileOptionsForRequest(
-    const AutoJSAPI& jsapi, ScriptLoadRequest* aRequest,
+    const mozilla::dom::AutoJSAPI& jsapi, ScriptLoadRequest* aRequest,
     JS::Handle<JSObject*> aScopeChain, JS::CompileOptions* aOptions) {
   // It's very important to use aRequest->mURI, not the final URI of the channel
   // aRequest ended up getting script data from, as the script filename.
   nsresult rv = aRequest->mURI->GetSpec(aRequest->mURL);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return rv;
   }
 
@@ -2198,16 +2344,29 @@ nsresult ScriptLoader::FillCompileOption
       return false;
     }
   }
 
   LOG(("ScriptLoadRequest (%p): Bytecode-cache: Trigger encoding.", aRequest));
   return true;
 }
 
+class MOZ_RAII AutoSetProcessingScriptTag {
+  nsCOMPtr<nsIScriptContext> mContext;
+  bool mOldTag;
+
+ public:
+  explicit AutoSetProcessingScriptTag(nsIScriptContext* aContext)
+      : mContext(aContext), mOldTag(mContext->GetProcessingScriptTag()) {
+    mContext->SetProcessingScriptTag(true);
+  }
+
+  ~AutoSetProcessingScriptTag() { mContext->SetProcessingScriptTag(mOldTag); }
+};
+
 nsresult ScriptLoader::EvaluateScript(ScriptLoadRequest* aRequest) {
   using namespace mozilla::Telemetry;
   MOZ_ASSERT(aRequest->IsReadyToRun());
 
   // We need a document to evaluate scripts.
   if (!mDocument) {
     return NS_ERROR_FAILURE;
   }
@@ -2237,57 +2396,68 @@ nsresult ScriptLoader::EvaluateScript(Sc
 
   // New script entry point required, due to the "Create a script" sub-step of
   // http://www.whatwg.org/specs/web-apps/current-work/#execute-the-script-block
   nsAutoMicroTask mt;
   AutoEntryScript aes(globalObject, "<script> element", true);
   JSContext* cx = aes.cx();
   JS::Rooted<JSObject*> global(cx, globalObject->GetGlobalJSObject());
 
-  bool oldProcessingScriptTag = context->GetProcessingScriptTag();
-  context->SetProcessingScriptTag(true);
+  AutoSetProcessingScriptTag setProcessingScriptTag(context);
+
   nsresult rv;
   {
     if (aRequest->IsModuleRequest()) {
       // When a module is already loaded, it is not feched a second time and the
       // mDataType of the request might remain set to DataType::Unknown.
       MOZ_ASSERT(aRequest->IsTextSource() || aRequest->IsUnknownDataType());
       LOG(("ScriptLoadRequest (%p): Evaluate Module", aRequest));
 
       // currentScript is set to null for modules.
       AutoCurrentScriptUpdater scriptUpdater(this, nullptr);
 
-      EnsureModuleResolveHook(cx);
-
       ModuleLoadRequest* request = aRequest->AsModuleRequest();
       MOZ_ASSERT(request->mModuleScript);
       MOZ_ASSERT(!request->mOffThreadToken);
 
       ModuleScript* moduleScript = request->mModuleScript;
       if (moduleScript->HasErrorToRethrow()) {
         LOG(("ScriptLoadRequest (%p):   module has error to rethrow",
              aRequest));
         JS::Rooted<JS::Value> error(cx, moduleScript->ErrorToRethrow());
         JS_SetPendingException(cx, error);
-        return NS_OK;  // An error is reported by AutoEntryScript.
+
+        // For a dynamic import, the promise is rejected.  Otherwise an error is
+        // either reported by AutoEntryScript.
+        if (request->IsDynamicImport()) {
+          FinishDynamicImport(cx, request, NS_OK);
+        }
+        return NS_OK;
       }
 
       JS::Rooted<JSObject*> module(cx, moduleScript->ModuleRecord());
       MOZ_ASSERT(module);
 
       if (!moduleScript->SourceElementAssociated()) {
         rv = AssociateSourceElementsForModuleTree(cx, request);
         NS_ENSURE_SUCCESS(rv, rv);
       }
 
       rv = nsJSUtils::ModuleEvaluate(cx, module);
       MOZ_ASSERT(NS_FAILED(rv) == aes.HasException());
+
       if (NS_FAILED(rv)) {
         LOG(("ScriptLoadRequest (%p):   evaluation failed", aRequest));
-        rv = NS_OK;  // An error is reported by AutoEntryScript.
+        // For a dynamic import, the promise is rejected.  Otherwise an error is
+        // either reported by AutoEntryScript.
+        rv = NS_OK;
+      }
+
+      if (request->IsDynamicImport()) {
+        FinishDynamicImport(cx, request, rv);
       }
 
       aRequest->mCacheInfo = nullptr;
     } else {
       // Update our current script.
       AutoCurrentScriptUpdater scriptUpdater(this, aRequest->Element());
 
       JS::CompileOptions options(cx);
@@ -2349,18 +2519,17 @@ nsresult ScriptLoader::EvaluateScript(Sc
                   rv = NS_ERROR_OUT_OF_MEMORY;
                 }
               }
             }
           }
 
           // Queue the current script load request to later save the bytecode.
           if (script && encodeBytecode) {
-            aRequest->mScript = script;
-            HoldJSObjects(aRequest);
+            aRequest->SetScript(script);
             TRACE_FOR_TEST(aRequest->Element(), "scriptloader_encode");
             MOZ_ASSERT(aRequest->mBytecodeOffset ==
                        aRequest->mScriptBytecode.length());
             RegisterForBytecodeEncoding(aRequest);
           } else {
             LOG(
                 ("ScriptLoadRequest (%p): Bytecode-cache: disabled (rv = %X, "
                  "script = %p)",
@@ -2374,17 +2543,16 @@ nsresult ScriptLoader::EvaluateScript(Sc
 
     // Even if we are not saving the bytecode of the current script, we have
     // to trigger the encoding of the bytecode, as the current script can
     // call functions of a script for which we are recording the bytecode.
     LOG(("ScriptLoadRequest (%p): ScriptLoader = %p", aRequest, this));
     MaybeTriggerBytecodeEncoding();
   }
 
-  context->SetProcessingScriptTag(oldProcessingScriptTag);
   return rv;
 }
 
 void ScriptLoader::RegisterForBytecodeEncoding(ScriptLoadRequest* aRequest) {
   MOZ_ASSERT(aRequest->mCacheInfo);
   MOZ_ASSERT(aRequest->mScript);
   mBytecodeEncodingQueue.AppendElement(aRequest);
 }
@@ -2562,17 +2730,18 @@ void ScriptLoader::GiveUpBytecodeEncodin
     request->DropBytecodeCacheReferences();
   }
 }
 
 bool ScriptLoader::HasPendingRequests() {
   return mParserBlockingRequest || !mXSLTRequests.isEmpty() ||
          !mLoadedAsyncRequests.isEmpty() ||
          !mNonAsyncExternalScriptInsertedRequests.isEmpty() ||
-         !mDeferRequests.isEmpty() || !mPendingChildLoaders.IsEmpty();
+         !mDeferRequests.isEmpty() || !mDynamicImportRequests.isEmpty() ||
+         !mPendingChildLoaders.IsEmpty();
 }
 
 void ScriptLoader::ProcessPendingRequestsAsync() {
   if (HasPendingRequests()) {
     nsCOMPtr<nsIRunnable> task =
         NewRunnableMethod("dom::ScriptLoader::ProcessPendingRequests", this,
                           &ScriptLoader::ProcessPendingRequests);
     if (mDocument) {
@@ -2975,46 +3144,55 @@ void ScriptLoader::HandleLoadError(Scrip
           mNonAsyncExternalScriptInsertedRequests.Steal(aRequest);
       FireScriptAvailable(aResult, req);
     }
   } else if (aRequest->mIsXSLT) {
     if (aRequest->isInList()) {
       RefPtr<ScriptLoadRequest> req = mXSLTRequests.Steal(aRequest);
       FireScriptAvailable(aResult, req);
     }
-  } else if (aRequest->IsModuleRequest() && !aRequest->IsPreload()) {
+  } else if (aRequest->IsPreload()) {
+    if (aRequest->IsModuleRequest()) {
+      aRequest->Cancel();
+    }
+    if (aRequest->IsTopLevel()) {
+      MOZ_ALWAYS_TRUE(
+          mPreloads.RemoveElement(aRequest, PreloadRequestComparator()));
+    }
+    MOZ_ASSERT(!aRequest->isInList());
+    AccumulateCategorical(LABELS_DOM_SCRIPT_PRELOAD_RESULT::LoadError);
+  } else if (aRequest->IsModuleRequest()) {
     ModuleLoadRequest* modReq = aRequest->AsModuleRequest();
-    MOZ_ASSERT(!modReq->IsTopLevel());
-    MOZ_ASSERT(!modReq->isInList());
-    modReq->Cancel();
-    // A single error is fired for the top level module.
+    if (modReq->IsDynamicImport()) {
+      MOZ_ASSERT(modReq->IsTopLevel());
+      if (aRequest->isInList()) {
+        RefPtr<ScriptLoadRequest> req = mDynamicImportRequests.Steal(aRequest);
+        modReq->Cancel();
+        FinishDynamicImport(modReq, aResult);
+      }
+    } else {
+      MOZ_ASSERT(!modReq->IsTopLevel());
+      MOZ_ASSERT(!modReq->isInList());
+      modReq->Cancel();
+      // The error is handled for the top level module.
+    }
   } else if (mParserBlockingRequest == aRequest) {
     MOZ_ASSERT(!aRequest->isInList());
     mParserBlockingRequest = nullptr;
     UnblockParser(aRequest);
 
     // Ensure that we treat aRequest->Element() as our current parser-inserted
     // script while firing onerror on it.
     MOZ_ASSERT(aRequest->Element()->GetParserCreated());
     nsCOMPtr<nsIScriptElement> oldParserInsertedScript =
         mCurrentParserInsertedScript;
     mCurrentParserInsertedScript = aRequest->Element();
     FireScriptAvailable(aResult, aRequest);
     ContinueParserAsync(aRequest);
     mCurrentParserInsertedScript = oldParserInsertedScript;
-  } else if (aRequest->IsPreload()) {
-    if (aRequest->IsModuleRequest()) {
-      aRequest->Cancel();
-    }
-    if (aRequest->IsTopLevel()) {
-      MOZ_ALWAYS_TRUE(
-          mPreloads.RemoveElement(aRequest, PreloadRequestComparator()));
-    }
-    MOZ_ASSERT(!aRequest->isInList());
-    AccumulateCategorical(LABELS_DOM_SCRIPT_PRELOAD_RESULT::LoadError);
   } else {
     // This happens for blocking requests cancelled by ParsingComplete().
     MOZ_ASSERT(aRequest->IsCanceled());
     MOZ_ASSERT(!aRequest->isInList());
   }
 }
 
 void ScriptLoader::UnblockParser(ScriptLoadRequest* aParserBlockingRequest) {
@@ -3105,21 +3283,22 @@ nsresult ScriptLoader::PrepareLoadedRequ
   // This assertion could fire errorously if we ran out of memory when
   // inserting the request in the array. However it's an unlikely case
   // so if you see this assertion it is likely something else that is
   // wrong, especially if you see it more than once.
   NS_ASSERTION(mDeferRequests.Contains(aRequest) ||
                    mLoadingAsyncRequests.Contains(aRequest) ||
                    mNonAsyncExternalScriptInsertedRequests.Contains(aRequest) ||
                    mXSLTRequests.Contains(aRequest) ||
+                   mDynamicImportRequests.Contains(aRequest) ||
                    (aRequest->IsModuleRequest() &&
                     !aRequest->AsModuleRequest()->IsTopLevel() &&
                     !aRequest->isInList()) ||
                    mPreloads.Contains(aRequest, PreloadRequestComparator()) ||
-                   mParserBlockingRequest,
+                   mParserBlockingRequest == aRequest,
                "aRequest should be pending!");
 
   if (aRequest->IsModuleRequest()) {
     MOZ_ASSERT(aRequest->IsSource());
     ModuleLoadRequest* request = aRequest->AsModuleRequest();
 
     // When loading a module, only responses with a JavaScript MIME type are
     // acceptable.
@@ -3186,16 +3365,23 @@ void ScriptLoader::ParsingComplete(bool 
   }
   mDeferEnabled = false;
   if (aTerminated) {
     mDeferRequests.Clear();
     mLoadingAsyncRequests.Clear();
     mLoadedAsyncRequests.Clear();
     mNonAsyncExternalScriptInsertedRequests.Clear();
     mXSLTRequests.Clear();
+
+    for (ScriptLoadRequest* req = mDynamicImportRequests.getFirst(); req;
+         req = req->getNext()) {
+      req->Cancel();
+      FinishDynamicImport(req->AsModuleRequest(), NS_ERROR_ABORT);
+    }
+
     if (mParserBlockingRequest) {
       mParserBlockingRequest->Cancel();
       mParserBlockingRequest = nullptr;
     }
   }
 
   // Have to call this even if aTerminated so we'll correctly unblock
   // onload and all.
--- a/dom/script/ScriptLoader.h
+++ b/dom/script/ScriptLoader.h
@@ -305,19 +305,26 @@ class ScriptLoader final : public nsISup
 
   /**
    * Destroy and prevent the ScriptLoader or the ScriptLoadRequests from owning
    * any references to the JSScript or to the Request which might be used for
    * caching the encoded bytecode.
    */
   void Destroy() { GiveUpBytecodeEncoding(); }
 
+  void StartDynamicImport(ModuleLoadRequest* aRequest);
+  void FinishDynamicImport(ModuleLoadRequest* aRequest, nsresult aResult);
+  void FinishDynamicImport(JSContext* aCx, ModuleLoadRequest* aRequest,
+                           nsresult aResult);
+
  private:
   virtual ~ScriptLoader();
 
+  void EnsureModuleHooksInitialized();
+
   ScriptLoadRequest* CreateLoadRequest(
       ScriptKind aKind, nsIURI* aURI, nsIScriptElement* aElement,
       nsIPrincipal* aTriggeringPrincipal, mozilla::CORSMode aCORSMode,
       const SRIMetadata& aIntegrity,
       mozilla::net::ReferrerPolicy aReferrerPolicy);
 
   /**
    * Unblocks the creator parser of the parser-blocking scripts.
@@ -414,16 +421,17 @@ class ScriptLoader final : public nsISup
                        SRICheckDataVerifier* aSRIDataVerifier) const;
 
   void ReportErrorToConsole(ScriptLoadRequest* aRequest,
                             nsresult aResult) const;
 
   nsresult AttemptAsyncScriptCompile(ScriptLoadRequest* aRequest,
                                      bool* aCouldCompileOut);
   nsresult ProcessRequest(ScriptLoadRequest* aRequest);
+  void ProcessDynamicImport(ModuleLoadRequest* aRequest);
   nsresult CompileOffThreadOrProcessRequest(ScriptLoadRequest* aRequest);
   void FireScriptAvailable(nsresult aResult, ScriptLoadRequest* aRequest);
   void FireScriptEvaluated(nsresult aResult, ScriptLoadRequest* aRequest);
   nsresult EvaluateScript(ScriptLoadRequest* aRequest);
 
   /**
    * Queue the current script load request to be saved, when the page
    * initialization ends. The page initialization end is defined as being the
@@ -495,25 +503,28 @@ class ScriptLoader final : public nsISup
   void StartFetchingModuleDependencies(ModuleLoadRequest* aRequest);
 
   RefPtr<mozilla::GenericPromise> StartFetchingModuleAndDependencies(
       ModuleLoadRequest* aParent, nsIURI* aURI);
 
   nsresult AssociateSourceElementsForModuleTree(JSContext* aCx,
                                                 ModuleLoadRequest* aRequest);
 
+  void RunScriptWhenSafe(ScriptLoadRequest* aRequest);
+
   nsIDocument* mDocument;  // [WEAK]
   nsCOMArray<nsIScriptLoaderObserver> mObservers;
   ScriptLoadRequestList mNonAsyncExternalScriptInsertedRequests;
   // mLoadingAsyncRequests holds async requests while they're loading; when they
   // have been loaded they are moved to mLoadedAsyncRequests.
   ScriptLoadRequestList mLoadingAsyncRequests;
   ScriptLoadRequestList mLoadedAsyncRequests;
   ScriptLoadRequestList mDeferRequests;
   ScriptLoadRequestList mXSLTRequests;
+  ScriptLoadRequestList mDynamicImportRequests;
   RefPtr<ScriptLoadRequest> mParserBlockingRequest;
 
   // List of script load request that are holding a buffer which has to be saved
   // on the cache.
   ScriptLoadRequestList mBytecodeEncodingQueue;
 
   // In mRequests, the additional information here is stored by the element.
   struct PreloadInfo {
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -1579,16 +1579,19 @@ pref("javascript.options.spectre.jit_to_
 #endif
 
 // Streams API
 pref("javascript.options.streams", true);
 
 // BigInt API
 pref("javascript.options.bigint", false);
 
+// Dynamic module import.
+pref("javascript.options.dynamicImport", false);
+
 // advanced prefs
 pref("advanced.mailftp",                    false);
 pref("image.animation_mode",                "normal");
 
 // Same-origin policy for file URIs, "false" is traditional
 pref("security.fileuri.strict_origin_policy", true);
 
 // If this pref is true, prefs in the logging.config branch will be cleared on
--- a/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/__dir__.ini
+++ b/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/__dir__.ini
@@ -1,1 +1,2 @@
 lsan-allowed: [Init, nsHostResolver::ResolveHost]
+prefs: [javascript.options.dynamicImport:true, security.csp.experimentalEnabled:true]
--- a/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/dynamic-imports.html.ini
+++ b/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/dynamic-imports.html.ini
@@ -1,7 +1,4 @@
 [dynamic-imports.html]
   [Basic dynamic imports]
     expected: FAIL
 
-  [Dynamic imports should resolve module.]
-    expected: FAIL
-
--- a/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/inline-event-handler.html.ini
+++ b/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/inline-event-handler.html.ini
@@ -1,5 +1,5 @@
 [inline-event-handler.html]
-  expected: ERROR
+  expected: TIMEOUT
   [dynamic import should work when triggered from inline event handlers]
     expected: TIMEOUT
 
--- a/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/propagate-nonce-external-module.html.ini
+++ b/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/propagate-nonce-external-module.html.ini
@@ -1,10 +1,7 @@
 [propagate-nonce-external-module.html]
   [Untitled]
     expected: FAIL
 
   [propagate-nonce-external-module]
     expected: FAIL
 
-  [Dynamically imported module should eval when imported from script w/ a valid nonce.]
-    expected: FAIL
-
--- a/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/propagate-nonce-inline-module.html.ini
+++ b/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/propagate-nonce-inline-module.html.ini
@@ -1,10 +1,7 @@
 [propagate-nonce-inline-module.html]
   [Untitled]
     expected: FAIL
 
   [propagate-nonce-inline-module]
     expected: FAIL
 
-  [Dynamically imported module should eval when imported from script w/ a valid nonce.]
-    expected: FAIL
-
--- a/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-external-classic.html.ini
+++ b/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-external-classic.html.ini
@@ -1,17 +1,16 @@
 [string-compilation-base-url-external-classic.html]
-  expected: ERROR
   [setTimeout should successfully import]
-    expected: TIMEOUT
+    expected: FAIL
 
   [eval should successfully import]
-    expected: NOTRUN
+    expected: FAIL
 
   [Function should successfully import]
-    expected: NOTRUN
+    expected: FAIL
 
   [reflected-inline-event-handlers should successfully import]
-    expected: NOTRUN
+    expected: FAIL
 
   [inline-event-handlers-UA-code should successfully import]
-    expected: NOTRUN
+    expected: FAIL
 
--- a/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-external-module.html.ini
+++ b/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-external-module.html.ini
@@ -1,17 +1,10 @@
 [string-compilation-base-url-external-module.html]
-  expected: ERROR
   [setTimeout should successfully import]
-    expected: TIMEOUT
-
-  [eval should successfully import]
-    expected: NOTRUN
-
-  [Function should successfully import]
-    expected: NOTRUN
+    expected: FAIL
 
   [reflected-inline-event-handlers should successfully import]
-    expected: NOTRUN
+    expected: FAIL
 
   [inline-event-handlers-UA-code should successfully import]
-    expected: NOTRUN
+    expected: FAIL
 
--- a/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-inline-classic.html.ini
+++ b/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-inline-classic.html.ini
@@ -1,17 +1,16 @@
 [string-compilation-base-url-inline-classic.html]
-  expected: ERROR
   [setTimeout should successfully import]
-    expected: TIMEOUT
+    expected: FAIL
 
   [eval should successfully import]
-    expected: NOTRUN
+    expected: FAIL
 
   [the Function constructor should successfully import]
-    expected: NOTRUN
+    expected: FAIL
 
   [reflected inline event handlers should successfully import]
-    expected: NOTRUN
+    expected: FAIL
 
   [inline event handlers triggered via UA code should successfully import]
-    expected: NOTRUN
+    expected: FAIL
 
--- a/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-inline-module.html.ini
+++ b/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-base-url-inline-module.html.ini
@@ -1,17 +1,10 @@
 [string-compilation-base-url-inline-module.html]
-  expected: ERROR
   [setTimeout should successfully import]
-    expected: TIMEOUT
-
-  [eval should successfully import]
-    expected: NOTRUN
-
-  [the Function constructor should successfully import]
-    expected: NOTRUN
+    expected: FAIL
 
   [reflected inline event handlers should successfully import]
-    expected: NOTRUN
+    expected: FAIL
 
   [inline event handlers triggered via UA code should successfully import]
-    expected: NOTRUN
+    expected: FAIL
 
--- a/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-classic.html.ini
+++ b/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-classic.html.ini
@@ -1,17 +1,16 @@
 [string-compilation-classic.html]
-  expected: ERROR
   [eval should successfully import]
     expected: FAIL
 
   [setTimeout should successfully import]
-    expected: TIMEOUT
+    expected: FAIL
 
   [the Function constructor should successfully import]
-    expected: NOTRUN
+    expected: FAIL
 
   [reflected inline event handlers should successfully import]
-    expected: NOTRUN
+    expected: FAIL
 
   [inline event handlers triggered via UA code should successfully import]
-    expected: NOTRUN
+    expected: FAIL
 
--- a/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-module.html.ini
+++ b/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-module.html.ini
@@ -1,17 +1,10 @@
 [string-compilation-module.html]
-  expected: ERROR
-  [eval should successfully import]
+  [setTimeout should successfully import]
     expected: FAIL
 
-  [setTimeout should successfully import]
-    expected: TIMEOUT
-
-  [the Function constructor should successfully import]
-    expected: NOTRUN
-
   [reflected inline event handlers should successfully import]
-    expected: NOTRUN
+    expected: FAIL
 
   [inline event handlers triggered via UA code should successfully import]
-    expected: NOTRUN
+    expected: FAIL
 
--- a/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-nonce-classic.html.ini
+++ b/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-nonce-classic.html.ini
@@ -1,20 +1,19 @@
 [string-compilation-nonce-classic.html]
-  expected: ERROR
   [setTimeout must inherit the nonce from the triggering script, thus execute]
-    expected: TIMEOUT
+    expected: FAIL
 
   [direct eval must inherit the nonce from the triggering script, thus execute]
-    expected: NOTRUN
+    expected: FAIL
 
   [indirect eval must inherit the nonce from the triggering script, thus execute]
-    expected: NOTRUN
+    expected: FAIL
 
   [the Function constructor must inherit the nonce from the triggering script, thus execute]
-    expected: NOTRUN
+    expected: FAIL
 
   [reflected inline event handlers must inherit the nonce from the triggering script, thus execute]
-    expected: NOTRUN
+    expected: FAIL
 
   [inline event handlers triggered via UA code must inherit the nonce from the triggering script, thus execute]
-    expected: NOTRUN
+    expected: FAIL
 
--- a/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-nonce-module.html.ini
+++ b/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-nonce-module.html.ini
@@ -1,20 +1,10 @@
 [string-compilation-nonce-module.html]
-  expected: ERROR
   [setTimeout must inherit the nonce from the triggering script, thus execute]
-    expected: TIMEOUT
-
-  [direct eval must inherit the nonce from the triggering script, thus execute]
-    expected: NOTRUN
-
-  [indirect eval must inherit the nonce from the triggering script, thus execute]
-    expected: NOTRUN
-
-  [the Function constructor must inherit the nonce from the triggering script, thus execute]
-    expected: NOTRUN
+    expected: FAIL
 
   [reflected inline event handlers must inherit the nonce from the triggering script, thus execute]
-    expected: NOTRUN
+    expected: FAIL
 
   [inline event handlers triggered via UA code must inherit the nonce from the triggering script, thus execute]
-    expected: NOTRUN
+    expected: FAIL
 
--- a/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-of-promise-result.html.ini
+++ b/testing/web-platform/meta/html/semantics/scripting-1/the-script-element/module/dynamic-import/string-compilation-of-promise-result.html.ini
@@ -1,13 +1,7 @@
 [string-compilation-of-promise-result.html]
   [Evaled the script via eval, successful import]
     expected: FAIL
 
-  [Evaled the script via eval, failed import]
-    expected: FAIL
-
   [Evaled the script via Function, successful import]
     expected: FAIL
 
-  [Evaled the script via Function, failed import]
-    expected: FAIL
-