forked from mirrors/gecko-dev
		
	 23b8f88057
			
		
	
	
		23b8f88057
		
	
	
	
	
		
			
			The behavior is longer tightly associated with "component". Differential Revision: https://phabricator.services.mozilla.com/D199453
		
			
				
	
	
		
			259 lines
		
	
	
	
		
			8.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			259 lines
		
	
	
	
		
			8.6 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, 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
 |