tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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