summaryrefslogtreecommitdiffstats
path: root/js/xpconnect/loader/SyncModuleLoader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/xpconnect/loader/SyncModuleLoader.cpp')
-rw-r--r--js/xpconnect/loader/SyncModuleLoader.cpp259
1 files changed, 259 insertions, 0 deletions
diff --git a/js/xpconnect/loader/SyncModuleLoader.cpp b/js/xpconnect/loader/SyncModuleLoader.cpp
new file mode 100644
index 0000000000..0cff7ae6ac
--- /dev/null
+++ b/js/xpconnect/loader/SyncModuleLoader.cpp
@@ -0,0 +1,259 @@
+/* -*- 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 "SyncModuleLoader.h"
+
+#include "nsISupportsImpl.h"
+
+#include "js/loader/ModuleLoadRequest.h"
+#include "js/RootingAPI.h" // JS::Rooted
+#include "js/PropertyAndElement.h" // JS_SetProperty
+#include "js/Value.h" // JS::Value, JS::NumberValue
+#include "mozJSModuleLoader.h"
+
+using namespace JS::loader;
+
+namespace mozilla {
+namespace loader {
+
+//////////////////////////////////////////////////////////////
+// SyncScriptLoader
+//////////////////////////////////////////////////////////////
+
+NS_IMPL_ISUPPORTS0(SyncScriptLoader)
+
+nsIURI* SyncScriptLoader::GetBaseURI() const { return nullptr; }
+
+void SyncScriptLoader::ReportErrorToConsole(ScriptLoadRequest* aRequest,
+ nsresult aResult) const {}
+
+void SyncScriptLoader::ReportWarningToConsole(
+ ScriptLoadRequest* aRequest, const char* aMessageName,
+ const nsTArray<nsString>& aParams) const {}
+
+nsresult SyncScriptLoader::FillCompileOptionsForRequest(
+ JSContext* cx, ScriptLoadRequest* aRequest, JS::CompileOptions* aOptions,
+ JS::MutableHandle<JSScript*> aIntroductionScript) {
+ return NS_OK;
+}
+
+//////////////////////////////////////////////////////////////
+// SyncModuleLoader
+//////////////////////////////////////////////////////////////
+
+NS_IMPL_ADDREF_INHERITED(SyncModuleLoader, JS::loader::ModuleLoaderBase)
+NS_IMPL_RELEASE_INHERITED(SyncModuleLoader, JS::loader::ModuleLoaderBase)
+
+NS_IMPL_CYCLE_COLLECTION_INHERITED(SyncModuleLoader,
+ JS::loader::ModuleLoaderBase, mLoadRequests)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SyncModuleLoader)
+NS_INTERFACE_MAP_END_INHERITING(JS::loader::ModuleLoaderBase)
+
+SyncModuleLoader::SyncModuleLoader(SyncScriptLoader* aScriptLoader,
+ nsIGlobalObject* aGlobalObject)
+ : ModuleLoaderBase(aScriptLoader, aGlobalObject) {}
+
+SyncModuleLoader::~SyncModuleLoader() { MOZ_ASSERT(mLoadRequests.isEmpty()); }
+
+already_AddRefed<ModuleLoadRequest> SyncModuleLoader::CreateStaticImport(
+ nsIURI* aURI, ModuleLoadRequest* aParent) {
+ RefPtr<SyncLoadContext> context = new SyncLoadContext();
+ RefPtr<ModuleLoadRequest> request = new ModuleLoadRequest(
+ aURI, aParent->ReferrerPolicy(), aParent->mFetchOptions,
+ dom::SRIMetadata(), aParent->mURI, context, false, /* is top level */
+ false, /* is dynamic import */
+ this, aParent->mVisitedSet, aParent->GetRootModule());
+ request->NoCacheEntryFound();
+ return request.forget();
+}
+
+already_AddRefed<ModuleLoadRequest> SyncModuleLoader::CreateDynamicImport(
+ JSContext* aCx, nsIURI* aURI, LoadedScript* aMaybeActiveScript,
+ JS::Handle<JSString*> aSpecifier, JS::Handle<JSObject*> aPromise) {
+ RefPtr<SyncLoadContext> context = new SyncLoadContext();
+ RefPtr<ModuleLoadRequest> request = new ModuleLoadRequest(
+ aURI, aMaybeActiveScript->ReferrerPolicy(),
+ aMaybeActiveScript->GetFetchOptions(), dom::SRIMetadata(),
+ aMaybeActiveScript->BaseURL(), context,
+ /* aIsTopLevel = */ true, /* aIsDynamicImport = */ true, this,
+ ModuleLoadRequest::NewVisitedSetForTopLevelImport(aURI), nullptr);
+
+ request->SetDynamicImport(aMaybeActiveScript, aSpecifier, aPromise);
+ request->NoCacheEntryFound();
+
+ return request.forget();
+}
+
+void SyncModuleLoader::OnDynamicImportStarted(ModuleLoadRequest* aRequest) {
+ MOZ_ASSERT(aRequest->IsDynamicImport());
+ MOZ_ASSERT(!mLoadRequests.Contains(aRequest));
+
+ if (aRequest->IsFetching()) {
+ // This module is newly imported.
+ //
+ // DynamicImportRequests() can contain multiple requests when a dynamic
+ // import is performed while evaluating the top-level script of other
+ // dynamic imports.
+ //
+ // mLoadRequests should be empty given evaluation is performed after
+ // handling all fetching requests.
+ MOZ_ASSERT(DynamicImportRequests().Contains(aRequest));
+ MOZ_ASSERT(mLoadRequests.isEmpty());
+
+ nsresult rv = OnFetchComplete(aRequest, NS_OK);
+ if (NS_FAILED(rv)) {
+ mLoadRequests.CancelRequestsAndClear();
+ CancelDynamicImport(aRequest, rv);
+ return;
+ }
+
+ rv = ProcessRequests();
+ if (NS_FAILED(rv)) {
+ CancelDynamicImport(aRequest, rv);
+ return;
+ }
+ } else {
+ // This module had already been imported.
+ MOZ_ASSERT(DynamicImportRequests().isEmpty());
+ MOZ_ASSERT(mLoadRequests.isEmpty());
+ }
+
+ ProcessDynamicImport(aRequest);
+}
+
+bool SyncModuleLoader::CanStartLoad(ModuleLoadRequest* aRequest,
+ nsresult* aRvOut) {
+ return mozJSModuleLoader::IsTrustedScheme(aRequest->mURI);
+}
+
+nsresult SyncModuleLoader::StartFetch(ModuleLoadRequest* aRequest) {
+ MOZ_ASSERT(aRequest->HasLoadContext());
+
+ aRequest->mBaseURL = aRequest->mURI;
+
+ // Loading script source and compilation are intertwined in
+ // mozJSModuleLoader. Perform both operations here but only report load
+ // failures. Compilation failure is reported in CompileFetchedModule.
+
+ dom::AutoJSAPI jsapi;
+ if (!jsapi.Init(GetGlobalObject())) {
+ return NS_ERROR_FAILURE;
+ }
+
+ JSContext* cx = jsapi.cx();
+ JS::RootedScript script(cx);
+ nsresult rv =
+ mozJSModuleLoader::LoadSingleModuleScript(this, cx, aRequest, &script);
+ MOZ_ASSERT_IF(jsapi.HasException(), NS_FAILED(rv));
+ MOZ_ASSERT(bool(script) == NS_SUCCEEDED(rv));
+
+ // Check for failure to load script source and abort.
+ bool threwException = jsapi.HasException();
+ if (NS_FAILED(rv) && !threwException) {
+ nsAutoCString uri;
+ nsresult rv2 = aRequest->mURI->GetSpec(uri);
+ NS_ENSURE_SUCCESS(rv2, rv2);
+
+ JS_ReportErrorUTF8(cx, "Failed to load %s", PromiseFlatCString(uri).get());
+
+ // Remember the error for MaybeReportLoadError.
+ if (!mLoadException.initialized()) {
+ mLoadException.init(cx);
+ }
+ if (!jsapi.StealException(&mLoadException)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+
+ if (mLoadException.isObject()) {
+ // Expose `nsresult`.
+ JS::Rooted<JS::Value> resultVal(cx, JS::NumberValue(uint32_t(rv)));
+ JS::Rooted<JSObject*> exceptionObj(cx, &mLoadException.toObject());
+ if (!JS_SetProperty(cx, exceptionObj, "result", resultVal)) {
+ // Ignore the error and keep reporting the exception without the result
+ // property.
+ JS_ClearPendingException(cx);
+ }
+ }
+
+ return rv;
+ }
+
+ // Otherwise remember the results in this context so we can report them later.
+ SyncLoadContext* context = aRequest->GetSyncLoadContext();
+ context->mRv = rv;
+ if (threwException) {
+ context->mExceptionValue.init(cx);
+ if (!jsapi.StealException(&context->mExceptionValue)) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ }
+ if (script) {
+ context->mScript.init(cx);
+ context->mScript = script;
+ }
+
+ if (!aRequest->IsDynamicImport()) {
+ // NOTE: Dynamic import is stored into mDynamicImportRequests.
+ mLoadRequests.AppendElement(aRequest);
+ }
+
+ return NS_OK;
+}
+
+nsresult SyncModuleLoader::CompileFetchedModule(
+ JSContext* aCx, JS::Handle<JSObject*> aGlobal, JS::CompileOptions& aOptions,
+ ModuleLoadRequest* aRequest, JS::MutableHandle<JSObject*> aModuleOut) {
+ // Compilation already happened in StartFetch. Report the result here.
+ SyncLoadContext* context = aRequest->GetSyncLoadContext();
+ nsresult rv = context->mRv;
+ if (context->mScript) {
+ aModuleOut.set(JS::GetModuleObject(context->mScript));
+ context->mScript = nullptr;
+ }
+ if (NS_FAILED(rv)) {
+ JS_SetPendingException(aCx, context->mExceptionValue);
+ context->mExceptionValue = JS::UndefinedValue();
+ }
+
+ MOZ_ASSERT(JS_IsExceptionPending(aCx) == NS_FAILED(rv));
+ MOZ_ASSERT(bool(aModuleOut) == NS_SUCCEEDED(rv));
+
+ return rv;
+}
+
+void SyncModuleLoader::MaybeReportLoadError(JSContext* aCx) {
+ if (JS_IsExceptionPending(aCx)) {
+ // Do not override.
+ return;
+ }
+
+ if (mLoadException.isUndefined()) {
+ return;
+ }
+
+ JS_SetPendingException(aCx, mLoadException);
+ mLoadException = JS::UndefinedValue();
+}
+
+void SyncModuleLoader::OnModuleLoadComplete(ModuleLoadRequest* aRequest) {}
+
+nsresult SyncModuleLoader::ProcessRequests() {
+ // Work list to drive module loader since this is all synchronous.
+ while (!mLoadRequests.isEmpty()) {
+ RefPtr<ScriptLoadRequest> request = mLoadRequests.StealFirst();
+ nsresult rv = OnFetchComplete(request->AsModuleRequest(), NS_OK);
+ if (NS_FAILED(rv)) {
+ mLoadRequests.CancelRequestsAndClear();
+ return rv;
+ }
+ }
+
+ return NS_OK;
+}
+
+} // namespace loader
+} // namespace mozilla