SyncModuleLoader.cpp (8622B)
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/. */ 6 7 #include "SyncModuleLoader.h" 8 9 #include "nsISupportsImpl.h" 10 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" 16 #include "nsContentSecurityUtils.h" 17 18 using namespace JS::loader; 19 20 namespace mozilla { 21 namespace loader { 22 23 ////////////////////////////////////////////////////////////// 24 // SyncScriptLoader 25 ////////////////////////////////////////////////////////////// 26 27 NS_IMPL_ISUPPORTS0(SyncScriptLoader) 28 29 nsIURI* SyncScriptLoader::GetBaseURI() const { return nullptr; } 30 31 void SyncScriptLoader::ReportErrorToConsole(ScriptLoadRequest* aRequest, 32 nsresult aResult) const {} 33 34 void SyncScriptLoader::ReportWarningToConsole( 35 ScriptLoadRequest* aRequest, const char* aMessageName, 36 const nsTArray<nsString>& aParams) const {} 37 38 nsresult SyncScriptLoader::FillCompileOptionsForRequest( 39 JSContext* cx, ScriptLoadRequest* aRequest, JS::CompileOptions* aOptions, 40 JS::MutableHandle<JSScript*> aIntroductionScript) { 41 return NS_OK; 42 } 43 44 ////////////////////////////////////////////////////////////// 45 // SyncModuleLoader 46 ////////////////////////////////////////////////////////////// 47 48 NS_IMPL_ADDREF_INHERITED(SyncModuleLoader, JS::loader::ModuleLoaderBase) 49 NS_IMPL_RELEASE_INHERITED(SyncModuleLoader, JS::loader::ModuleLoaderBase) 50 51 NS_IMPL_CYCLE_COLLECTION_INHERITED(SyncModuleLoader, 52 JS::loader::ModuleLoaderBase, mLoadRequests) 53 54 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SyncModuleLoader) 55 NS_INTERFACE_MAP_END_INHERITING(JS::loader::ModuleLoaderBase) 56 57 SyncModuleLoader::SyncModuleLoader(SyncScriptLoader* aScriptLoader, 58 nsIGlobalObject* aGlobalObject) 59 : ModuleLoaderBase(aScriptLoader, aGlobalObject) {} 60 61 SyncModuleLoader::~SyncModuleLoader() { MOZ_ASSERT(mLoadRequests.isEmpty()); } 62 63 already_AddRefed<ModuleLoadRequest> SyncModuleLoader::CreateRequest( 64 JSContext* aCx, nsIURI* aURI, JS::Handle<JSObject*> aModuleRequest, 65 JS::Handle<JS::Value> aHostDefined, JS::Handle<JS::Value> aPayload, 66 bool aIsDynamicImport, ScriptFetchOptions* aOptions, 67 dom::ReferrerPolicy aReferrerPolicy, nsIURI* aBaseURL, 68 const dom::SRIMetadata& aSriMetadata) { 69 RefPtr<SyncLoadContext> context = new SyncLoadContext(); 70 JS::ModuleType moduleType = GetModuleRequestType(aCx, aModuleRequest); 71 72 ModuleLoadRequest::Kind kind; 73 ModuleLoadRequest* root = nullptr; 74 if (aIsDynamicImport) { 75 kind = ModuleLoadRequest::Kind::DynamicImport; 76 } else { 77 MOZ_ASSERT(!aHostDefined.isUndefined()); 78 root = static_cast<ModuleLoadRequest*>(aHostDefined.toPrivate()); 79 MOZ_ASSERT(root); 80 kind = ModuleLoadRequest::Kind::StaticImport; 81 } 82 83 RefPtr<ModuleLoadRequest> request = new ModuleLoadRequest( 84 moduleType, dom::SRIMetadata(), aBaseURL, context, kind, this, root); 85 request->NoCacheEntryFound(aReferrerPolicy, aOptions, aURI); 86 return request.forget(); 87 } 88 89 void SyncModuleLoader::OnDynamicImportStarted(ModuleLoadRequest* aRequest) { 90 MOZ_ASSERT(aRequest->IsDynamicImport()); 91 MOZ_ASSERT(!mLoadRequests.Contains(aRequest)); 92 93 if (aRequest->IsFetching()) { 94 // This module is newly imported. 95 // 96 // DynamicImportRequests() can contain multiple requests when a dynamic 97 // import is performed while evaluating the top-level script of other 98 // dynamic imports. 99 // 100 // mLoadRequests should be empty given evaluation is performed after 101 // handling all fetching requests. 102 MOZ_ASSERT(DynamicImportRequests().Contains(aRequest)); 103 MOZ_ASSERT(mLoadRequests.isEmpty()); 104 105 nsresult rv = OnFetchComplete(aRequest, NS_OK); 106 if (NS_FAILED(rv)) { 107 mLoadRequests.CancelRequestsAndClear(); 108 CancelDynamicImport(aRequest, rv); 109 return; 110 } 111 112 rv = ProcessRequests(); 113 if (NS_FAILED(rv)) { 114 CancelDynamicImport(aRequest, rv); 115 return; 116 } 117 } else { 118 // This module had already been imported. 119 MOZ_ASSERT(DynamicImportRequests().isEmpty()); 120 MOZ_ASSERT(mLoadRequests.isEmpty()); 121 } 122 123 ProcessDynamicImport(aRequest); 124 } 125 126 bool SyncModuleLoader::CanStartLoad(ModuleLoadRequest* aRequest, 127 nsresult* aRvOut) { 128 return nsContentSecurityUtils::IsTrustedScheme(aRequest->URI()); 129 } 130 131 nsresult SyncModuleLoader::StartFetch(ModuleLoadRequest* aRequest) { 132 MOZ_ASSERT(aRequest->HasLoadContext()); 133 134 aRequest->SetBaseURL(aRequest->URI()); 135 136 // Loading script source and compilation are intertwined in 137 // mozJSModuleLoader. Perform both operations here but only report load 138 // failures. Compilation failure is reported in CompileFetchedModule. 139 140 dom::AutoJSAPI jsapi; 141 if (!jsapi.Init(GetGlobalObject())) { 142 return NS_ERROR_FAILURE; 143 } 144 145 JSContext* cx = jsapi.cx(); 146 JS::RootedScript script(cx); 147 nsresult rv = 148 mozJSModuleLoader::LoadSingleModuleScript(this, cx, aRequest, &script); 149 MOZ_ASSERT_IF(jsapi.HasException(), NS_FAILED(rv)); 150 MOZ_ASSERT(bool(script) == NS_SUCCEEDED(rv)); 151 152 // Check for failure to load script source and abort. 153 bool threwException = jsapi.HasException(); 154 if (NS_FAILED(rv) && !threwException) { 155 nsAutoCString uri; 156 nsresult rv2 = aRequest->URI()->GetSpec(uri); 157 NS_ENSURE_SUCCESS(rv2, rv2); 158 159 JS_ReportErrorUTF8(cx, "Failed to load %s", PromiseFlatCString(uri).get()); 160 161 // Remember the error for MaybeReportLoadError. 162 if (!mLoadException.initialized()) { 163 mLoadException.init(cx); 164 } 165 if (!jsapi.StealException(&mLoadException)) { 166 return NS_ERROR_OUT_OF_MEMORY; 167 } 168 169 if (mLoadException.isObject()) { 170 // Expose `nsresult`. 171 JS::Rooted<JS::Value> resultVal(cx, JS::NumberValue(uint32_t(rv))); 172 JS::Rooted<JSObject*> exceptionObj(cx, &mLoadException.toObject()); 173 if (!JS_SetProperty(cx, exceptionObj, "result", resultVal)) { 174 // Ignore the error and keep reporting the exception without the result 175 // property. 176 JS_ClearPendingException(cx); 177 } 178 } 179 180 return rv; 181 } 182 183 // Otherwise remember the results in this context so we can report them later. 184 SyncLoadContext* context = aRequest->GetSyncLoadContext(); 185 context->mRv = rv; 186 if (threwException) { 187 context->mExceptionValue.init(cx); 188 if (!jsapi.StealException(&context->mExceptionValue)) { 189 return NS_ERROR_OUT_OF_MEMORY; 190 } 191 } 192 if (script) { 193 context->mScript.init(cx); 194 context->mScript = script; 195 } 196 197 if (!aRequest->IsDynamicImport()) { 198 // NOTE: Dynamic import is stored into mDynamicImportRequests. 199 mLoadRequests.AppendElement(aRequest); 200 } 201 202 return NS_OK; 203 } 204 205 nsresult SyncModuleLoader::CompileFetchedModule( 206 JSContext* aCx, JS::Handle<JSObject*> aGlobal, JS::CompileOptions& aOptions, 207 ModuleLoadRequest* aRequest, JS::MutableHandle<JSObject*> aModuleOut) { 208 // Compilation already happened in StartFetch. Report the result here. 209 SyncLoadContext* context = aRequest->GetSyncLoadContext(); 210 nsresult rv = context->mRv; 211 if (context->mScript) { 212 aModuleOut.set(JS::GetModuleObject(context->mScript)); 213 context->mScript = nullptr; 214 } 215 if (NS_FAILED(rv)) { 216 JS_SetPendingException(aCx, context->mExceptionValue); 217 context->mExceptionValue = JS::UndefinedValue(); 218 } 219 220 MOZ_ASSERT(JS_IsExceptionPending(aCx) == NS_FAILED(rv)); 221 MOZ_ASSERT(bool(aModuleOut) == NS_SUCCEEDED(rv)); 222 223 return rv; 224 } 225 226 void SyncModuleLoader::MaybeReportLoadError(JSContext* aCx) { 227 if (JS_IsExceptionPending(aCx)) { 228 // Do not override. 229 return; 230 } 231 232 if (mLoadException.isUndefined()) { 233 return; 234 } 235 236 JS_SetPendingException(aCx, mLoadException); 237 mLoadException = JS::UndefinedValue(); 238 } 239 240 void SyncModuleLoader::OnModuleLoadComplete(ModuleLoadRequest* aRequest) {} 241 242 nsresult SyncModuleLoader::ProcessRequests() { 243 // Work list to drive module loader since this is all synchronous. 244 while (!mLoadRequests.isEmpty()) { 245 RefPtr<ScriptLoadRequest> request = mLoadRequests.StealFirst(); 246 nsresult rv = OnFetchComplete(request->AsModuleRequest(), NS_OK); 247 if (NS_FAILED(rv)) { 248 mLoadRequests.CancelRequestsAndClear(); 249 return rv; 250 } 251 } 252 253 return NS_OK; 254 } 255 256 } // namespace loader 257 } // namespace mozilla