1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "SyncModuleLoader.h"
9 #include "nsISupportsImpl.h"
11 #include "js/loader/ModuleLoadRequest.h"
12 #include "js/RootingAPI.h" // JS::Rooted
13 #include "js/PropertyAndElement.h" // JS_SetProperty
14 #include "js/Value.h" // JS::Value, JS::NumberValue
15 #include "mozJSModuleLoader.h"
17 using namespace JS::loader
;
22 //////////////////////////////////////////////////////////////
24 //////////////////////////////////////////////////////////////
26 NS_IMPL_ISUPPORTS0(SyncScriptLoader
)
28 nsIURI
* SyncScriptLoader::GetBaseURI() const { return nullptr; }
30 void SyncScriptLoader::ReportErrorToConsole(ScriptLoadRequest
* aRequest
,
31 nsresult aResult
) const {}
33 void SyncScriptLoader::ReportWarningToConsole(
34 ScriptLoadRequest
* aRequest
, const char* aMessageName
,
35 const nsTArray
<nsString
>& aParams
) const {}
37 nsresult
SyncScriptLoader::FillCompileOptionsForRequest(
38 JSContext
* cx
, ScriptLoadRequest
* aRequest
, JS::CompileOptions
* aOptions
,
39 JS::MutableHandle
<JSScript
*> aIntroductionScript
) {
43 //////////////////////////////////////////////////////////////
45 //////////////////////////////////////////////////////////////
47 NS_IMPL_ADDREF_INHERITED(SyncModuleLoader
, JS::loader::ModuleLoaderBase
)
48 NS_IMPL_RELEASE_INHERITED(SyncModuleLoader
, JS::loader::ModuleLoaderBase
)
50 NS_IMPL_CYCLE_COLLECTION_INHERITED(SyncModuleLoader
,
51 JS::loader::ModuleLoaderBase
, mLoadRequests
)
53 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SyncModuleLoader
)
54 NS_INTERFACE_MAP_END_INHERITING(JS::loader::ModuleLoaderBase
)
56 SyncModuleLoader::SyncModuleLoader(SyncScriptLoader
* aScriptLoader
,
57 nsIGlobalObject
* aGlobalObject
)
58 : ModuleLoaderBase(aScriptLoader
, aGlobalObject
) {}
60 SyncModuleLoader::~SyncModuleLoader() { MOZ_ASSERT(mLoadRequests
.isEmpty()); }
62 already_AddRefed
<ModuleLoadRequest
> SyncModuleLoader::CreateStaticImport(
63 nsIURI
* aURI
, JS::ModuleType aModuleType
, ModuleLoadRequest
* aParent
) {
64 RefPtr
<SyncLoadContext
> context
= new SyncLoadContext();
65 RefPtr
<ModuleLoadRequest
> request
= new ModuleLoadRequest(
66 aURI
, aModuleType
, aParent
->ReferrerPolicy(), aParent
->mFetchOptions
,
67 dom::SRIMetadata(), aParent
->mURI
, context
, false, /* is top level */
68 false, /* is dynamic import */
69 this, aParent
->mVisitedSet
, aParent
->GetRootModule());
70 request
->NoCacheEntryFound();
71 return request
.forget();
74 already_AddRefed
<ModuleLoadRequest
> SyncModuleLoader::CreateDynamicImport(
75 JSContext
* aCx
, nsIURI
* aURI
, JS::ModuleType aModuleType
,
76 LoadedScript
* aMaybeActiveScript
, JS::Handle
<JSString
*> aSpecifier
,
77 JS::Handle
<JSObject
*> aPromise
) {
78 RefPtr
<SyncLoadContext
> context
= new SyncLoadContext();
79 RefPtr
<VisitedURLSet
> visitedSet
=
80 ModuleLoadRequest::NewVisitedSetForTopLevelImport(aURI
, aModuleType
);
81 RefPtr
<ModuleLoadRequest
> request
= new ModuleLoadRequest(
82 aURI
, aModuleType
, aMaybeActiveScript
->ReferrerPolicy(),
83 aMaybeActiveScript
->GetFetchOptions(), dom::SRIMetadata(),
84 aMaybeActiveScript
->BaseURL(), context
,
85 /* aIsTopLevel = */ true, /* aIsDynamicImport = */ true, this,
88 request
->SetDynamicImport(aMaybeActiveScript
, aSpecifier
, aPromise
);
89 request
->NoCacheEntryFound();
91 return request
.forget();
94 void SyncModuleLoader::OnDynamicImportStarted(ModuleLoadRequest
* aRequest
) {
95 MOZ_ASSERT(aRequest
->IsDynamicImport());
96 MOZ_ASSERT(!mLoadRequests
.Contains(aRequest
));
98 if (aRequest
->IsFetching()) {
99 // This module is newly imported.
101 // DynamicImportRequests() can contain multiple requests when a dynamic
102 // import is performed while evaluating the top-level script of other
105 // mLoadRequests should be empty given evaluation is performed after
106 // handling all fetching requests.
107 MOZ_ASSERT(DynamicImportRequests().Contains(aRequest
));
108 MOZ_ASSERT(mLoadRequests
.isEmpty());
110 nsresult rv
= OnFetchComplete(aRequest
, NS_OK
);
112 mLoadRequests
.CancelRequestsAndClear();
113 CancelDynamicImport(aRequest
, rv
);
117 rv
= ProcessRequests();
119 CancelDynamicImport(aRequest
, rv
);
123 // This module had already been imported.
124 MOZ_ASSERT(DynamicImportRequests().isEmpty());
125 MOZ_ASSERT(mLoadRequests
.isEmpty());
128 ProcessDynamicImport(aRequest
);
131 bool SyncModuleLoader::CanStartLoad(ModuleLoadRequest
* aRequest
,
133 return mozJSModuleLoader::IsTrustedScheme(aRequest
->mURI
);
136 nsresult
SyncModuleLoader::StartFetch(ModuleLoadRequest
* aRequest
) {
137 MOZ_ASSERT(aRequest
->HasLoadContext());
139 aRequest
->mBaseURL
= aRequest
->mURI
;
141 // Loading script source and compilation are intertwined in
142 // mozJSModuleLoader. Perform both operations here but only report load
143 // failures. Compilation failure is reported in CompileFetchedModule.
145 dom::AutoJSAPI jsapi
;
146 if (!jsapi
.Init(GetGlobalObject())) {
147 return NS_ERROR_FAILURE
;
150 JSContext
* cx
= jsapi
.cx();
151 JS::RootedScript
script(cx
);
153 mozJSModuleLoader::LoadSingleModuleScript(this, cx
, aRequest
, &script
);
154 MOZ_ASSERT_IF(jsapi
.HasException(), NS_FAILED(rv
));
155 MOZ_ASSERT(bool(script
) == NS_SUCCEEDED(rv
));
157 // Check for failure to load script source and abort.
158 bool threwException
= jsapi
.HasException();
159 if (NS_FAILED(rv
) && !threwException
) {
161 nsresult rv2
= aRequest
->mURI
->GetSpec(uri
);
162 NS_ENSURE_SUCCESS(rv2
, rv2
);
164 JS_ReportErrorUTF8(cx
, "Failed to load %s", PromiseFlatCString(uri
).get());
166 // Remember the error for MaybeReportLoadError.
167 if (!mLoadException
.initialized()) {
168 mLoadException
.init(cx
);
170 if (!jsapi
.StealException(&mLoadException
)) {
171 return NS_ERROR_OUT_OF_MEMORY
;
174 if (mLoadException
.isObject()) {
175 // Expose `nsresult`.
176 JS::Rooted
<JS::Value
> resultVal(cx
, JS::NumberValue(uint32_t(rv
)));
177 JS::Rooted
<JSObject
*> exceptionObj(cx
, &mLoadException
.toObject());
178 if (!JS_SetProperty(cx
, exceptionObj
, "result", resultVal
)) {
179 // Ignore the error and keep reporting the exception without the result
181 JS_ClearPendingException(cx
);
188 // Otherwise remember the results in this context so we can report them later.
189 SyncLoadContext
* context
= aRequest
->GetSyncLoadContext();
191 if (threwException
) {
192 context
->mExceptionValue
.init(cx
);
193 if (!jsapi
.StealException(&context
->mExceptionValue
)) {
194 return NS_ERROR_OUT_OF_MEMORY
;
198 context
->mScript
.init(cx
);
199 context
->mScript
= script
;
202 if (!aRequest
->IsDynamicImport()) {
203 // NOTE: Dynamic import is stored into mDynamicImportRequests.
204 mLoadRequests
.AppendElement(aRequest
);
210 nsresult
SyncModuleLoader::CompileFetchedModule(
211 JSContext
* aCx
, JS::Handle
<JSObject
*> aGlobal
, JS::CompileOptions
& aOptions
,
212 ModuleLoadRequest
* aRequest
, JS::MutableHandle
<JSObject
*> aModuleOut
) {
213 // Compilation already happened in StartFetch. Report the result here.
214 SyncLoadContext
* context
= aRequest
->GetSyncLoadContext();
215 nsresult rv
= context
->mRv
;
216 if (context
->mScript
) {
217 aModuleOut
.set(JS::GetModuleObject(context
->mScript
));
218 context
->mScript
= nullptr;
221 JS_SetPendingException(aCx
, context
->mExceptionValue
);
222 context
->mExceptionValue
= JS::UndefinedValue();
225 MOZ_ASSERT(JS_IsExceptionPending(aCx
) == NS_FAILED(rv
));
226 MOZ_ASSERT(bool(aModuleOut
) == NS_SUCCEEDED(rv
));
231 void SyncModuleLoader::MaybeReportLoadError(JSContext
* aCx
) {
232 if (JS_IsExceptionPending(aCx
)) {
237 if (mLoadException
.isUndefined()) {
241 JS_SetPendingException(aCx
, mLoadException
);
242 mLoadException
= JS::UndefinedValue();
245 void SyncModuleLoader::OnModuleLoadComplete(ModuleLoadRequest
* aRequest
) {}
247 nsresult
SyncModuleLoader::ProcessRequests() {
248 // Work list to drive module loader since this is all synchronous.
249 while (!mLoadRequests
.isEmpty()) {
250 RefPtr
<ScriptLoadRequest
> request
= mLoadRequests
.StealFirst();
251 nsresult rv
= OnFetchComplete(request
->AsModuleRequest(), NS_OK
);
253 mLoadRequests
.CancelRequestsAndClear();
261 } // namespace loader
262 } // namespace mozilla