262 lines
8.8 KiB
C++
262 lines
8.8 KiB
C++
/* -*- 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, JS::ModuleType aModuleType, ModuleLoadRequest* aParent,
|
|
const mozilla::dom::SRIMetadata& aSriMetadata) {
|
|
RefPtr<SyncLoadContext> context = new SyncLoadContext();
|
|
RefPtr<ModuleLoadRequest> request = new ModuleLoadRequest(
|
|
aURI, aModuleType, aParent->ReferrerPolicy(), aParent->mFetchOptions,
|
|
dom::SRIMetadata(), aParent->mURI, context,
|
|
ModuleLoadRequest::Kind::StaticImport, this, aParent->mVisitedSet,
|
|
aParent->GetRootModule());
|
|
request->NoCacheEntryFound();
|
|
return request.forget();
|
|
}
|
|
|
|
already_AddRefed<ModuleLoadRequest> SyncModuleLoader::CreateDynamicImport(
|
|
JSContext* aCx, nsIURI* aURI, JS::ModuleType aModuleType,
|
|
LoadedScript* aMaybeActiveScript, JS::Handle<JSString*> aSpecifier,
|
|
JS::Handle<JSObject*> aPromise) {
|
|
RefPtr<SyncLoadContext> context = new SyncLoadContext();
|
|
RefPtr<VisitedURLSet> visitedSet =
|
|
ModuleLoadRequest::NewVisitedSetForTopLevelImport(aURI, aModuleType);
|
|
RefPtr<ModuleLoadRequest> request = new ModuleLoadRequest(
|
|
aURI, aModuleType, aMaybeActiveScript->ReferrerPolicy(),
|
|
aMaybeActiveScript->GetFetchOptions(), dom::SRIMetadata(),
|
|
aMaybeActiveScript->BaseURL(), context,
|
|
ModuleLoadRequest::Kind::DynamicImport, this, visitedSet, 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
|