Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  ScriptLoader.cpp   Sprache: 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 "ScriptLoader.h"
#include "ScriptLoadHandler.h"
#include "ScriptTrace.h"
#include "ModuleLoader.h"
#include "nsGenericHTMLElement.h"
#include "SharedScriptCache.h"

#include "mozilla/Assertions.h"
#include "mozilla/dom/FetchPriority.h"
#include "mozilla/glean/DomMetrics.h"
#include "mozilla/dom/RequestBinding.h"

#include "prsystem.h"
#include "js/ColumnNumber.h"  // JS::ColumnNumberOneOrigin
#include "js/CompilationAndEvaluation.h"
#include "js/CompileOptions.h"  // JS::CompileOptions, JS::OwningCompileOptions, JS::DecodeOptions, JS::OwningDecodeOptions, JS::DelazificationOption
#include "js/ContextOptions.h"  // JS::ContextOptionsRef
#include "js/experimental/JSStencil.h"  // JS::Stencil, JS::InstantiationStorage, JS::StartCollectingDelazifications, JS::FinishCollectingDelazifications, JS::AbortCollectingDelazifications
#include "js/experimental/CompileScript.h"  // JS::FrontendContext, JS::NewFrontendContext, JS::DestroyFrontendContext, JS::SetNativeStackQuota, JS::ThreadStackQuotaForSize, JS::CompilationStorage, JS::CompileGlobalScriptToStencil, JS::CompileModuleScriptToStencil, JS::DecodeStencil, JS::PrepareForInstantiate
#include "js/loader/ScriptLoadRequest.h"
#include "ScriptCompression.h"
#include "js/loader/LoadedScript.h"
#include "js/loader/ModuleLoadRequest.h"
#include "js/MemoryFunctions.h"
#include "js/Modules.h"
#include "js/PropertyAndElement.h"  // JS_DefineProperty
#include "js/Transcoding.h"  // JS::TranscodeRange, JS::TranscodeResult, JS::IsTranscodeFailureResult
#include "js/Utility.h"
#include "xpcpublic.h"
#include "GeckoProfiler.h"
#include "nsContentSecurityManager.h"
#include "nsCycleCollectionParticipant.h"
#include "nsIContent.h"
#include "nsJSUtils.h"
#include "mozilla/dom/AutoEntryScript.h"
#include "mozilla/dom/DocGroup.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/JSExecutionUtils.h"  // mozilla::dom::Compile, mozilla::dom::InstantiateStencil, mozilla::dom::EvaluationExceptionToNSResult
#include "mozilla/dom/ScriptDecoding.h"    // mozilla::dom::ScriptDecoding
#include "mozilla/dom/ScriptSettings.h"
#include "mozilla/dom/SRILogHelper.h"
#include "mozilla/dom/WindowContext.h"
#include "mozilla/Mutex.h"  // mozilla::Mutex
#include "mozilla/net/HttpBaseChannel.h"
#include "mozilla/net/UrlClassifierFeatureFactory.h"
#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/StaticPrefs_javascript.h"
#include "mozilla/StaticPrefs_network.h"
#include "nsAboutProtocolUtils.h"
#include "nsGkAtoms.h"
#include "nsNetUtil.h"
#include "nsIScriptGlobalObject.h"
#include "nsIScriptContext.h"
#include "nsIPrincipal.h"
#include "nsJSPrincipals.h"
#include "nsContentPolicyUtils.h"
#include "nsContentSecurityUtils.h"
#include "nsIClassifiedChannel.h"
#include "nsIHttpChannel.h"
#include "nsIHttpChannelInternal.h"
#include "nsIClassOfService.h"
#include "nsICacheInfoChannel.h"
#include "nsITimedChannel.h"
#include "nsITimer.h"
#include "nsIScriptElement.h"
#include "nsISupportsPriority.h"
#include "nsIDocShell.h"
#include "nsContentUtils.h"
#include "nsUnicharUtils.h"
#include "nsError.h"
#include "nsThreadUtils.h"
#include "nsIContentSecurityPolicy.h"
#include "mozilla/Logging.h"
#include "nsCRT.h"
#include "nsContentCreatorFunctions.h"
#include "nsProxyRelease.h"
#include "nsQueryObject.h"
#include "nsINetworkPredictor.h"
#include "mozilla/ConsoleReportCollector.h"
#include "mozilla/CycleCollectedJSContext.h"
#include "mozilla/EventQueue.h"
#include "mozilla/LoadInfo.h"
#include "ReferrerInfo.h"

#include "mozilla/AsyncEventDispatcher.h"
#include "mozilla/Attributes.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/TaskController.h"
#include "mozilla/Telemetry.h"
#include "mozilla/TimeStamp.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/Utf8.h"  // mozilla::Utf8Unit
#include "nsIScriptError.h"
#include "nsIAsyncOutputStream.h"
#include "js/loader/ModuleLoaderBase.h"
#include "mozilla/Maybe.h"

using namespace JS::loader;

namespace mozilla::dom {

LazyLogModule ScriptLoader::gCspPRLog("CSP");
LazyLogModule ScriptLoader::gScriptLoaderLog("ScriptLoader");

#undef LOG
#define LOG(args) \
  MOZ_LOG(ScriptLoader::gScriptLoaderLog, mozilla::LogLevel::Debug, args)

#define LOG_ENABLED() \
  MOZ_LOG_TEST(ScriptLoader::gScriptLoaderLog, mozilla::LogLevel::Debug)

// Alternate Data MIME type used by the ScriptLoader to register that we want to
// store bytecode without reading it.
static constexpr auto kNullMimeType = "javascript/null"_ns;

/////////////////////////////////////////////////////////////
// AsyncCompileShutdownObserver
/////////////////////////////////////////////////////////////

NS_IMPL_ISUPPORTS(AsyncCompileShutdownObserver, nsIObserver)

void AsyncCompileShutdownObserver::OnShutdown() {
  if (mScriptLoader) {
    mScriptLoader->Destroy();
    MOZ_ASSERT(!mScriptLoader);
  }
}

void AsyncCompileShutdownObserver::Unregister() {
  if (mScriptLoader) {
    mScriptLoader = nullptr;
    nsContentUtils::UnregisterShutdownObserver(this);
  }
}

NS_IMETHODIMP
AsyncCompileShutdownObserver::Observe(nsISupports* aSubject, const char* aTopic,
                                      const char16_t* aData) {
  OnShutdown();
  return NS_OK;
}

//////////////////////////////////////////////////////////////
// ScriptLoader::PreloadInfo
//////////////////////////////////////////////////////////////

inline void ImplCycleCollectionUnlink(ScriptLoader::PreloadInfo& aField) {
  ImplCycleCollectionUnlink(aField.mRequest);
}

inline void ImplCycleCollectionTraverse(
    nsCycleCollectionTraversalCallback& aCallback,
    ScriptLoader::PreloadInfo& aField, const char* aName, uint32_t aFlags = 0) {
  ImplCycleCollectionTraverse(aCallback, aField.mRequest, aName, aFlags);
}

//////////////////////////////////////////////////////////////
// ScriptLoader
//////////////////////////////////////////////////////////////

NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ScriptLoader)
NS_INTERFACE_MAP_END

NS_IMPL_CYCLE_COLLECTION_CLASS(ScriptLoader)

NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ScriptLoader)
  if (tmp->mDocument) {
    tmp->DropDocumentReference();
  }
  NS_IMPL_CYCLE_COLLECTION_UNLINK(
      mNonAsyncExternalScriptInsertedRequests, mLoadingAsyncRequests,
      mLoadedAsyncRequests, mOffThreadCompilingRequests, mDeferRequests,
      mXSLTRequests, mParserBlockingRequest, mBytecodeEncodingQueue, mPreloads,
      mPendingChildLoaders, mModuleLoader, mWebExtModuleLoaders,
      mShadowRealmModuleLoaders)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END

NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ScriptLoader)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(
      mNonAsyncExternalScriptInsertedRequests, mLoadingAsyncRequests,
      mLoadedAsyncRequests, mOffThreadCompilingRequests, mDeferRequests,
      mXSLTRequests, mParserBlockingRequest, mBytecodeEncodingQueue, mPreloads,
      mPendingChildLoaders, mModuleLoader, mWebExtModuleLoaders,
      mShadowRealmModuleLoaders)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END

NS_IMPL_CYCLE_COLLECTING_ADDREF(ScriptLoader)
NS_IMPL_CYCLE_COLLECTING_RELEASE(ScriptLoader)

ScriptLoader::ScriptLoader(Document* aDocument)
    : mDocument(aDocument),
      mParserBlockingBlockerCount(0),
      mBlockerCount(0),
      mNumberOfProcessors(0),
      mTotalFullParseSize(0),
      mPhysicalSizeOfMemory(-1),
      mEnabled(true),
      mDeferEnabled(false),
      mSpeculativeOMTParsingEnabled(false),
      mDeferCheckpointReached(false),
      mBlockingDOMContentLoaded(false),
      mLoadEventFired(false),
      mGiveUpEncoding(false),
      mReporter(new ConsoleReportCollector()) {
  LOG(("ScriptLoader::ScriptLoader %p"this));

  mSpeculativeOMTParsingEnabled = StaticPrefs::
      dom_script_loader_external_scripts_speculative_omt_parse_enabled();

#ifdef NIGHTLY_BUILD
  if (StaticPrefs::dom_script_loader_navigation_cache()) {
    mCache = SharedScriptCache::Get();
    RegisterToCache();
  }
#endif

  mShutdownObserver = new AsyncCompileShutdownObserver(this);
  nsContentUtils::RegisterShutdownObserver(mShutdownObserver);
}

ScriptLoader::~ScriptLoader() {
  LOG(("ScriptLoader::~ScriptLoader %p"this));

  mObservers.Clear();

  if (mParserBlockingRequest) {
    FireScriptAvailable(NS_ERROR_ABORT, mParserBlockingRequest);
  }

  for (ScriptLoadRequest* req = mXSLTRequests.getFirst(); req;
       req = req->getNext()) {
    FireScriptAvailable(NS_ERROR_ABORT, req);
  }

  for (ScriptLoadRequest* req = mDeferRequests.getFirst(); req;
       req = req->getNext()) {
    FireScriptAvailable(NS_ERROR_ABORT, req);
  }

  for (ScriptLoadRequest* req = mLoadingAsyncRequests.getFirst(); req;
       req = req->getNext()) {
    FireScriptAvailable(NS_ERROR_ABORT, req);
  }

  for (ScriptLoadRequest* req = mLoadedAsyncRequests.getFirst(); req;
       req = req->getNext()) {
    FireScriptAvailable(NS_ERROR_ABORT, req);
  }

  for (ScriptLoadRequest* req =
           mNonAsyncExternalScriptInsertedRequests.getFirst();
       req; req = req->getNext()) {
    FireScriptAvailable(NS_ERROR_ABORT, req);
  }

  // Unblock the kids, in case any of them moved to a different document
  // subtree in the meantime and therefore aren't actually going away.
  for (uint32_t j = 0; j < mPendingChildLoaders.Length(); ++j) {
    mPendingChildLoaders[j]->RemoveParserBlockingScriptExecutionBlocker();
  }

  if (mShutdownObserver) {
    mShutdownObserver->Unregister();
    mShutdownObserver = nullptr;
  }

  mModuleLoader = nullptr;

  if (mProcessPendingRequestsAsyncBypassParserBlocking) {
    mProcessPendingRequestsAsyncBypassParserBlocking->Cancel();
  }
}

void ScriptLoader::SetGlobalObject(nsIGlobalObject* aGlobalObject) {
  if (!aGlobalObject) {
    // The document is being detached.
    CancelAndClearScriptLoadRequests();
    return;
  }

  MOZ_ASSERT(!HasPendingRequests());

  if (!mModuleLoader) {
    // The module loader is associated with a global object, so don't create it
    // until we have a global set.
    mModuleLoader = new ModuleLoader(this, aGlobalObject, ModuleLoader::Normal);
  }

  MOZ_ASSERT(mModuleLoader->GetGlobalObject() == aGlobalObject);
  MOZ_ASSERT(aGlobalObject->GetModuleLoader(dom::danger::GetJSContext()) ==
             mModuleLoader);
}

void ScriptLoader::DropDocumentReference() {
  if (mDocument && mCache) {
    DeregisterFromCache();
  }

  mDocument = nullptr;
}

void ScriptLoader::RegisterToCache() {
  if (mCache) {
    MOZ_ASSERT(mDocument);
    mCache->RegisterLoader(*this);
  }
}

void ScriptLoader::DeregisterFromCache() {
  if (mCache) {
    MOZ_ASSERT(mDocument);
    mCache->CancelLoadsForLoader(*this);
    mCache->UnregisterLoader(*this);
  }
}

nsIPrincipal* ScriptLoader::LoaderPrincipal() const {
  return mDocument->NodePrincipal();
}

nsIPrincipal* ScriptLoader::PartitionedPrincipal() const {
  if (mDocument && StaticPrefs::privacy_partition_network_state()) {
    return mDocument->PartitionedPrincipal();
  }
  return LoaderPrincipal();
}

bool ScriptLoader::ShouldBypassCache() const {
  return mDocument && nsContentUtils::ShouldBypassSubResourceCache(mDocument);
}

void ScriptLoader::RegisterContentScriptModuleLoader(ModuleLoader* aLoader) {
  MOZ_ASSERT(aLoader);
  MOZ_ASSERT(aLoader->GetScriptLoader() == this);

  mWebExtModuleLoaders.AppendElement(aLoader);
}

void ScriptLoader::RegisterShadowRealmModuleLoader(ModuleLoader* aLoader) {
  MOZ_ASSERT(aLoader);
  MOZ_ASSERT(aLoader->GetScriptLoader() == this);

  mShadowRealmModuleLoaders.AppendElement(aLoader);
}

// Collect telemtry data about the cache information, and the kind of source
// which are being loaded, and where it is being loaded from.
static void CollectScriptTelemetry(ScriptLoadRequest* aRequest) {
  using namespace mozilla::Telemetry;

  MOZ_ASSERT(aRequest->IsFetching());

  // Skip this function if we are not running telemetry.
  if (!CanRecordExtended()) {
    return;
  }

  // Report the type of source. This is used to monitor the status of the
  // JavaScript Start-up Bytecode Cache, with the expectation of an almost zero
  // source-fallback and alternate-data being roughtly equal to source loads.
  if (aRequest->mFetchSourceOnly) {
    if (aRequest->GetScriptLoadContext()->mIsInline) {
      AccumulateCategorical(LABELS_DOM_SCRIPT_LOADING_SOURCE::Inline);
    } else if (aRequest->IsTextSource()) {
      AccumulateCategorical(LABELS_DOM_SCRIPT_LOADING_SOURCE::SourceFallback);
    }
  } else {
    if (aRequest->IsTextSource()) {
      AccumulateCategorical(LABELS_DOM_SCRIPT_LOADING_SOURCE::Source);
    } else if (aRequest->IsBytecode()) {
      AccumulateCategorical(LABELS_DOM_SCRIPT_LOADING_SOURCE::AltData);
    }
  }
}

// Helper method for checking if the script element is an event-handler
// This means that it has both a for-attribute and a event-attribute.
// Also, if the for-attribute has a value that matches "\s*window\s*",
// and the event-attribute matches "\s*onload([ \(].*)?" then it isn't an
// eventhandler. (both matches are case insensitive).
// This is how IE seems to filter out a window's onload handler from a
// <script for=... event=...> element.

static bool IsScriptEventHandler(ScriptKind kind, nsIContent* aScriptElement) {
  if (kind != ScriptKind::eClassic) {
    return false;
  }

  if (!aScriptElement->IsHTMLElement()) {
    return false;
  }

  nsAutoString forAttr, eventAttr;
  if (!aScriptElement->AsElement()->GetAttr(nsGkAtoms::_for, forAttr) ||
      !aScriptElement->AsElement()->GetAttr(nsGkAtoms::event, eventAttr)) {
    return false;
  }

  const nsAString& for_str =
      nsContentUtils::TrimWhitespace<nsCRT::IsAsciiSpace>(forAttr);
  if (!for_str.LowerCaseEqualsLiteral("window")) {
    return true;
  }

  // We found for="window", now check for event="onload".
  const nsAString& event_str =
      nsContentUtils::TrimWhitespace<nsCRT::IsAsciiSpace>(eventAttr, false);
  if (!StringBeginsWith(event_str, u"onload"_ns,
                        nsCaseInsensitiveStringComparator)) {
    // It ain't "onload.*".

    return true;
  }

  nsAutoString::const_iterator start, end;
  event_str.BeginReading(start);
  event_str.EndReading(end);

  start.advance(6);  // advance past "onload"

  if (start != end && *start != '(' && *start != ' ') {
    // We got onload followed by something other than space or
    // '('. Not good enough.

    return true;
  }

  return false;
}

nsContentPolicyType ScriptLoadRequestToContentPolicyType(
    ScriptLoadRequest* aRequest) {
  if (aRequest->GetScriptLoadContext()->IsPreload()) {
    if (aRequest->IsModuleRequest()) {
      return aRequest->AsModuleRequest()->mModuleType ==
                     JS::ModuleType::JavaScript
                 ? nsIContentPolicy::TYPE_INTERNAL_MODULE_PRELOAD
                 : nsIContentPolicy::TYPE_INTERNAL_JSON_PRELOAD;
    }

    return nsIContentPolicy::TYPE_INTERNAL_SCRIPT_PRELOAD;
  }

  if (aRequest->IsModuleRequest()) {
    return aRequest->AsModuleRequest()->mModuleType ==
                   JS::ModuleType::JavaScript
               ? nsIContentPolicy::TYPE_INTERNAL_MODULE
               : nsIContentPolicy::TYPE_JSON;
  }

  return nsIContentPolicy::TYPE_INTERNAL_SCRIPT;
}

nsresult ScriptLoader::CheckContentPolicy(Document* aDocument,
                                          nsIScriptElement* aElement,
                                          const nsAString& aNonce,
                                          ScriptLoadRequest* aRequest) {
  nsContentPolicyType contentPolicyType =
      ScriptLoadRequestToContentPolicyType(aRequest);

  nsCOMPtr<nsINode> requestingNode = do_QueryInterface(aElement);
  nsCOMPtr<nsILoadInfo> secCheckLoadInfo = new net::LoadInfo(
      aDocument->NodePrincipal(),  // loading principal
      aDocument->NodePrincipal(),  // triggering principal
      requestingNode, nsILoadInfo::SEC_ONLY_FOR_EXPLICIT_CONTENTSEC_CHECK,
      contentPolicyType);
  secCheckLoadInfo->SetParserCreatedScript(aElement->GetParserCreated() !=
                                           mozilla::dom::NOT_FROM_PARSER);
  // Use nonce of the current element, instead of the preload, because those
  // are allowed to differ.
  secCheckLoadInfo->SetCspNonce(aNonce);
  if (aRequest->mIntegrity.IsValid()) {
    MOZ_ASSERT(!aRequest->mIntegrity.IsEmpty());
    secCheckLoadInfo->SetIntegrityMetadata(
        aRequest->mIntegrity.GetIntegrityString());
  }

  int16_t shouldLoad = nsIContentPolicy::ACCEPT;
  nsresult rv =
      NS_CheckContentLoadPolicy(aRequest->mURI, secCheckLoadInfo, &shouldLoad,
                                nsContentUtils::GetContentPolicy());
  if (NS_FAILED(rv) || NS_CP_REJECTED(shouldLoad)) {
    if (NS_FAILED(rv) || shouldLoad != nsIContentPolicy::REJECT_TYPE) {
      return NS_ERROR_CONTENT_BLOCKED;
    }
    return NS_ERROR_CONTENT_BLOCKED_SHOW_ALT;
  }

  return NS_OK;
}

/* static */
bool ScriptLoader::IsAboutPageLoadingChromeURI(ScriptLoadRequest* aRequest,
                                               Document* aDocument) {
  // if the uri to be loaded is not of scheme chrome:, there is nothing to do.
  if (!aRequest->mURI->SchemeIs("chrome")) {
    return false;
  }

  // we can either get here with a regular contentPrincipal or with a
  // NullPrincipal in case we are showing an error page in a sandboxed iframe.
  // In either case if the about: page is linkable from content, there is
  // nothing to do.
  uint32_t aboutModuleFlags = 0;
  nsresult rv = NS_OK;

  nsCOMPtr<nsIPrincipal> triggeringPrincipal = aRequest->TriggeringPrincipal();
  if (triggeringPrincipal->GetIsContentPrincipal()) {
    if (!triggeringPrincipal->SchemeIs("about")) {
      return false;
    }
    rv = triggeringPrincipal->GetAboutModuleFlags(&aboutModuleFlags);
    NS_ENSURE_SUCCESS(rv, false);
  } else if (triggeringPrincipal->GetIsNullPrincipal()) {
    nsCOMPtr<nsIURI> docURI = aDocument->GetDocumentURI();
    if (!docURI->SchemeIs("about")) {
      return false;
    }

    nsCOMPtr<nsIAboutModule> aboutModule;
    rv = NS_GetAboutModule(docURI, getter_AddRefs(aboutModule));
    if (NS_FAILED(rv) || !aboutModule) {
      return false;
    }
    rv = aboutModule->GetURIFlags(docURI, &aboutModuleFlags);
    NS_ENSURE_SUCCESS(rv, false);
  } else {
    return false;
  }

  if (aboutModuleFlags & nsIAboutModule::MAKE_LINKABLE) {
    return false;
  }

  // seems like an about page wants to load a chrome URI.
  return true;
}

nsIURI* ScriptLoader::GetBaseURI() const {
  MOZ_ASSERT(mDocument);
  return mDocument->GetDocBaseURI();
}

class ScriptRequestProcessor : public Runnable {
 private:
  RefPtr<ScriptLoader> mLoader;
  RefPtr<ScriptLoadRequest> mRequest;

 public:
  ScriptRequestProcessor(ScriptLoader* aLoader, ScriptLoadRequest* aRequest)
      : Runnable("dom::ScriptRequestProcessor"),
        mLoader(aLoader),
        mRequest(aRequest) {}
  NS_IMETHOD Run() override { return mLoader->ProcessRequest(mRequest); }
};

void ScriptLoader::RunScriptWhenSafe(ScriptLoadRequest* aRequest) {
  auto* runnable = new ScriptRequestProcessor(this, aRequest);
  nsContentUtils::AddScriptRunner(runnable);
}

nsresult ScriptLoader::RestartLoad(ScriptLoadRequest* aRequest) {
  aRequest->DropBytecode();
  TRACE_FOR_TEST(aRequest, "scriptloader_fallback");

  // Notify preload restart so that we can register this preload request again.
  aRequest->GetScriptLoadContext()->NotifyRestart(mDocument);

  // Start a new channel from which we explicitly request to stream the source
  // instead of the bytecode.
  aRequest->mFetchSourceOnly = true;
  nsresult rv;
  if (aRequest->IsModuleRequest()) {
    rv = aRequest->AsModuleRequest()->RestartModuleLoad();
  } else {
    rv = StartLoad(aRequest, Nothing());
  }
  if (NS_FAILED(rv)) {
    return rv;
  }

  // Close the current channel and this ScriptLoadHandler as we created a new
  // one for the same request.
  return NS_BINDING_RETARGETED;
}

nsresult ScriptLoader::StartLoad(
    ScriptLoadRequest* aRequest,
    const Maybe<nsAutoString>& aCharsetForPreload) {
  if (aRequest->IsModuleRequest()) {
    return aRequest->AsModuleRequest()->StartModuleLoad();
  }

  return StartClassicLoad(aRequest, aCharsetForPreload);
}

static nsSecurityFlags CORSModeToSecurityFlags(CORSMode aCORSMode) {
  nsSecurityFlags securityFlags =
      nsContentSecurityManager::ComputeSecurityFlags(
          aCORSMode, nsContentSecurityManager::CORSSecurityMapping::
                         CORS_NONE_MAPS_TO_DISABLED_CORS_CHECKS);

  securityFlags |= nsILoadInfo::SEC_ALLOW_CHROME;

  return securityFlags;
}

nsresult ScriptLoader::StartClassicLoad(
    ScriptLoadRequest* aRequest,
    const Maybe<nsAutoString>& aCharsetForPreload) {
  MOZ_ASSERT(aRequest->IsFetching());
  NS_ENSURE_TRUE(mDocument, NS_ERROR_NULL_POINTER);
  aRequest->SetUnknownDataType();

  // If this document is sandboxed without 'allow-scripts', abort.
  if (mDocument->HasScriptsBlockedBySandbox()) {
    return NS_OK;
  }

  if (LOG_ENABLED()) {
    nsAutoCString url;
    aRequest->mURI->GetAsciiSpec(url);
    LOG(("ScriptLoadRequest (%p): Start Classic Load (url = %s)", aRequest,
         url.get()));
  }

  nsSecurityFlags securityFlags = CORSModeToSecurityFlags(aRequest->CORSMode());

  nsresult rv = StartLoadInternal(aRequest, securityFlags, aCharsetForPreload);

  NS_ENSURE_SUCCESS(rv, rv);

  return NS_OK;
}

static bool IsWebExtensionRequest(ScriptLoadRequest* aRequest) {
  if (!aRequest->IsModuleRequest()) {
    return false;
  }

  ModuleLoader* loader =
      ModuleLoader::From(aRequest->AsModuleRequest()->mLoader);
  return loader->GetKind() == ModuleLoader::WebExtension;
}

static nsresult CreateChannelForScriptLoading(
    nsIChannel** aOutChannel, Document* aDocument, nsIURI* aURI,
    nsINode* aContext, nsIPrincipal* aTriggeringPrincipal,
    nsSecurityFlags aSecurityFlags, nsContentPolicyType aContentPolicyType) {
  nsCOMPtr<nsILoadGroup> loadGroup = aDocument->GetDocumentLoadGroup();
  nsCOMPtr<nsPIDOMWindowOuter> window = aDocument->GetWindow();
  NS_ENSURE_TRUE(window, NS_ERROR_NULL_POINTER);
  nsIDocShell* docshell = window->GetDocShell();
  nsCOMPtr<nsIInterfaceRequestor> prompter(do_QueryInterface(docshell));

  return NS_NewChannelWithTriggeringPrincipal(
      aOutChannel, aURI, aContext, aTriggeringPrincipal, aSecurityFlags,
      aContentPolicyType,
      /* aPerformanceStorage = */ nullptr, loadGroup, prompter);
}

static nsresult CreateChannelForScriptLoading(nsIChannel** aOutChannel,
                                              Document* aDocument,
                                              ScriptLoadRequest* aRequest,
                                              nsSecurityFlags aSecurityFlags) {
  nsContentPolicyType contentPolicyType =
      ScriptLoadRequestToContentPolicyType(aRequest);
  nsCOMPtr<nsINode> context;
  if (aRequest->GetScriptLoadContext()->HasScriptElement()) {
    context = do_QueryInterface(
        aRequest->GetScriptLoadContext()->GetScriptElementForLoadingNode());
  } else {
    context = aDocument;
  }

  return CreateChannelForScriptLoading(aOutChannel, aDocument, aRequest->mURI,
                                       context, aRequest->TriggeringPrincipal(),
                                       aSecurityFlags, contentPolicyType);
}

static void PrepareLoadInfoForScriptLoading(nsIChannel* aChannel,
                                            const ScriptLoadRequest* aRequest) {
  nsCOMPtr<nsILoadInfo> loadInfo = aChannel->LoadInfo();
  loadInfo->SetParserCreatedScript(aRequest->ParserMetadata() ==
                                   ParserMetadata::ParserInserted);
  loadInfo->SetCspNonce(aRequest->Nonce());
  if (aRequest->mIntegrity.IsValid()) {
    MOZ_ASSERT(!aRequest->mIntegrity.IsEmpty());
    loadInfo->SetIntegrityMetadata(aRequest->mIntegrity.GetIntegrityString());
  }
}

// static
void ScriptLoader::PrepareCacheInfoChannel(nsIChannel* aChannel,
                                           ScriptLoadRequest* aRequest) {
  // To avoid decoding issues, the build-id is part of the bytecode MIME type
  // constant.
  aRequest->mCacheInfo = nullptr;
  nsCOMPtr<nsICacheInfoChannel> cic(do_QueryInterface(aChannel));
  if (cic && StaticPrefs::dom_script_loader_bytecode_cache_enabled()) {
    MOZ_ASSERT(!IsWebExtensionRequest(aRequest),
               "Can not bytecode cache WebExt code");
    if (!aRequest->mFetchSourceOnly) {
      // Inform the HTTP cache that we prefer to have information coming from
      // the bytecode cache instead of the sources, if such entry is already
      // registered.
      LOG(("ScriptLoadRequest (%p): Maybe request bytecode", aRequest));
      cic->PreferAlternativeDataType(
          ScriptLoader::BytecodeMimeTypeFor(aRequest), ""_ns,
          nsICacheInfoChannel::PreferredAlternativeDataDeliveryType::ASYNC);
    } else {
      // If we are explicitly loading from the sources, such as after a
      // restarted request, we might still want to save the bytecode after.
      //
      // The following tell the cache to look for an alternative data type which
      // does not exist, such that we can later save the bytecode with a
      // different alternative data type.
      LOG(("ScriptLoadRequest (%p): Request saving bytecode later", aRequest));
      cic->PreferAlternativeDataType(
          kNullMimeType, ""_ns,
          nsICacheInfoChannel::PreferredAlternativeDataDeliveryType::ASYNC);
    }
  }
}

static void AdjustPriorityAndClassOfServiceForLinkPreloadScripts(
    nsIChannel* aChannel, ScriptLoadRequest* aRequest) {
  MOZ_ASSERT(aRequest->GetScriptLoadContext()->IsLinkPreloadScript());

  // Put it to the group that is not blocked by leaders and doesn't block
  // follower at the same time.
  // Giving it a much higher priority will make this request be processed
  // ahead of other Unblocked requests, but with the same weight as
  // Leaders. This will make us behave similar way for both http2 and http1.
  ScriptLoadContext::PrioritizeAsPreload(aChannel);

  if (!StaticPrefs::network_fetchpriority_enabled()) {
    return;
  }

  const auto fetchPriority = ToFetchPriority(aRequest->FetchPriority());
  if (nsCOMPtr<nsISupportsPriority> supportsPriority =
          do_QueryInterface(aChannel)) {
    LOG(("Is ));

    // The spec defines the priority to be set in an implementation defined
    // manner (<https://fetch.spec.whatwg.org/#concept-fetch>, step 15 and
    // <https://html.spec.whatwg.org/#concept-script-fetch-options-fetch-priority>).
    // See corresponding preferences in StaticPrefList.yaml for more context.
    const int32_t supportsPriorityDelta =
        FETCH_PRIORITY_ADJUSTMENT_FOR(link_preload_script, fetchPriority);
    supportsPriority->AdjustPriority(supportsPriorityDelta);
#ifdef DEBUG
    int32_t adjustedPriority;
    supportsPriority->GetPriority(&adjustedPriority);
    LogPriorityMapping(ScriptLoader::gScriptLoaderLog, fetchPriority,
                       adjustedPriority);
#endif
  }

  if (nsCOMPtr<nsIClassOfService> cos = do_QueryInterface(aChannel)) {
    cos->SetFetchPriorityDOM(fetchPriority);
  }
}

void AdjustPriorityForNonLinkPreloadScripts(nsIChannel* aChannel,
                                            ScriptLoadRequest* aRequest) {
  MOZ_ASSERT(!aRequest->GetScriptLoadContext()->IsLinkPreloadScript());

  if (!StaticPrefs::network_fetchpriority_enabled()) {
    return;
  }

  const auto fetchPriority = ToFetchPriority(aRequest->FetchPriority());
  if (nsCOMPtr<nsISupportsPriority> supportsPriority =
          do_QueryInterface(aChannel)) {
    LOG(("Is not ));

    // The spec defines the priority to be set in an implementation defined
    // manner (<https://fetch.spec.whatwg.org/#concept-fetch>, step 15 and
    // <https://html.spec.whatwg.org/#concept-script-fetch-options-fetch-priority>).
    // See corresponding preferences in StaticPrefList.yaml for more context.
    const int32_t supportsPriorityDelta = [&]() {
      const ScriptLoadContext* scriptLoadContext =
          aRequest->GetScriptLoadContext();
      if (aRequest->IsModuleRequest()) {
        return FETCH_PRIORITY_ADJUSTMENT_FOR(module_script, fetchPriority);
      }

      if (scriptLoadContext->IsAsyncScript() ||
          scriptLoadContext->IsDeferredScript()) {
        return FETCH_PRIORITY_ADJUSTMENT_FOR(async_or_defer_script,
                                             fetchPriority);
      }

      if (scriptLoadContext->mScriptFromHead) {
        return FETCH_PRIORITY_ADJUSTMENT_FOR(script_in_head, fetchPriority);
      }

      return FETCH_PRIORITY_ADJUSTMENT_FOR(other_script, fetchPriority);
    }();

    if (supportsPriorityDelta) {
      supportsPriority->AdjustPriority(supportsPriorityDelta);
#ifdef DEBUG
      int32_t adjustedPriority;
      supportsPriority->GetPriority(&adjustedPriority);
      LogPriorityMapping(ScriptLoader::gScriptLoaderLog, fetchPriority,
                         adjustedPriority);
#endif
    }
  }
  if (nsCOMPtr<nsIClassOfService> cos = do_QueryInterface(aChannel)) {
    cos->SetFetchPriorityDOM(fetchPriority);
  }
}

// static
void ScriptLoader::PrepareRequestPriorityAndRequestDependencies(
    nsIChannel* aChannel, ScriptLoadRequest* aRequest) {
  if (aRequest->GetScriptLoadContext()->IsLinkPreloadScript()) {
    // This is <link rel="preload" as="script"> or <link rel="modulepreload">
    // initiated speculative load
    // (https://developer.mozilla.org/en-US/docs/Web/Performance/Speculative_loading).
    AdjustPriorityAndClassOfServiceForLinkPreloadScripts(aChannel, aRequest);
    ScriptLoadContext::AddLoadBackgroundFlag(aChannel);
  } else if (nsCOMPtr<nsIClassOfService> cos = do_QueryInterface(aChannel)) {
    AdjustPriorityForNonLinkPreloadScripts(aChannel, aRequest);

    if (aRequest->GetScriptLoadContext()->mScriptFromHead &&
        aRequest->GetScriptLoadContext()->IsBlockingScript()) {
      // synchronous head scripts block loading of most other non js/css
      // content such as images, Leader implicitely disallows tailing
      cos->AddClassFlags(nsIClassOfService::Leader);
    } else if (aRequest->GetScriptLoadContext()->IsDeferredScript() &&
               !StaticPrefs::network_http_tailing_enabled()) {
      // Bug 1395525 and the !StaticPrefs::network_http_tailing_enabled() bit:
      // We want to make sure that turing tailing off by the pref makes the
      // browser behave exactly the same way as before landing the tailing
      // patch.

      // head/body deferred scripts are blocked by leaders but are not
      // allowed tailing because they block DOMContentLoaded
      cos->AddClassFlags(nsIClassOfService::TailForbidden);
    } else {
      // other scripts (=body sync or head/body async) are neither blocked
      // nor prioritized
      cos->AddClassFlags(nsIClassOfService::Unblocked);

      if (aRequest->GetScriptLoadContext()->IsAsyncScript()) {
        // async scripts are allowed tailing, since those and only those
        // don't block DOMContentLoaded; this flag doesn't enforce tailing,
        // just overweights the Unblocked flag when the channel is found
        // to be a thrird-party tracker and thus set the Tail flag to engage
        // tailing.
        cos->AddClassFlags(nsIClassOfService::TailAllowed);
      }
    }
  }
}

inline nsLiteralString GetInitiatorType(ScriptLoadRequest* aRequest) {
  if (aRequest->mEarlyHintPreloaderId) {
    return u"early-hints"_ns;
  }

  if (aRequest->GetScriptLoadContext()->IsLinkPreloadScript()) {
    return u"link"_ns;
  }

  return u"script"_ns;
}

// static
nsresult ScriptLoader::PrepareHttpRequestAndInitiatorType(
    nsIChannel* aChannel, ScriptLoadRequest* aRequest,
    const Maybe<nsAutoString>& aCharsetForPreload) {
  nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aChannel));
  nsresult rv = NS_OK;

  if (httpChannel) {
    // HTTP content negotation has little value in this context.
    nsAutoCString acceptTypes("*/*");
    rv = httpChannel->SetRequestHeader("Accept"_ns, acceptTypes, false);
    MOZ_ASSERT(NS_SUCCEEDED(rv));

    nsCOMPtr<nsIReferrerInfo> referrerInfo =
        new ReferrerInfo(aRequest->mReferrer, aRequest->ReferrerPolicy());
    rv = httpChannel->SetReferrerInfoWithoutClone(referrerInfo);
    MOZ_ASSERT(NS_SUCCEEDED(rv));

    nsCOMPtr<nsIHttpChannelInternal> internalChannel(
        do_QueryInterface(httpChannel));
    if (internalChannel) {
      rv = internalChannel->SetIntegrityMetadata(
          aRequest->mIntegrity.GetIntegrityString());
      MOZ_ASSERT(NS_SUCCEEDED(rv));
    }

    nsAutoString hintCharset;
    if (!aRequest->GetScriptLoadContext()->IsPreload() &&
        aRequest->GetScriptLoadContext()->HasScriptElement()) {
      aRequest->GetScriptLoadContext()->GetHintCharset(hintCharset);
    } else if (aCharsetForPreload.isSome()) {
      hintCharset = aCharsetForPreload.ref();
    }

    rv = httpChannel->SetClassicScriptHintCharset(hintCharset);
    NS_ENSURE_SUCCESS(rv, rv);
  }

  // Set the initiator type
  nsCOMPtr<nsITimedChannel> timedChannel(do_QueryInterface(httpChannel));
  if (timedChannel) {
    timedChannel->SetInitiatorType(GetInitiatorType(aRequest));
  }

  return rv;
}

nsresult ScriptLoader::PrepareIncrementalStreamLoader(
    nsIIncrementalStreamLoader** aOutLoader, nsIChannel* aChannel,
    ScriptLoadRequest* aRequest) {
  UniquePtr<mozilla::dom::SRICheckDataVerifier> sriDataVerifier;
  if (!aRequest->mIntegrity.IsEmpty()) {
    nsAutoCString sourceUri;
    if (mDocument->GetDocumentURI()) {
      mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
    }
    sriDataVerifier = MakeUnique<SRICheckDataVerifier>(aRequest->mIntegrity,
                                                       sourceUri, mReporter);
  }

  RefPtr<ScriptLoadHandler> handler =
      new ScriptLoadHandler(this, aRequest, std::move(sriDataVerifier));

  aChannel->SetNotificationCallbacks(handler);

  nsresult rv = NS_NewIncrementalStreamLoader(aOutLoader, handler);
  NS_ENSURE_SUCCESS(rv, rv);
  return rv;
}

nsresult ScriptLoader::StartLoadInternal(
    ScriptLoadRequest* aRequest, nsSecurityFlags securityFlags,
    const Maybe<nsAutoString>& aCharsetForPreload) {
  nsCOMPtr<nsIChannel> channel;
  nsresult rv = CreateChannelForScriptLoading(
      getter_AddRefs(channel), mDocument, aRequest, securityFlags);

  NS_ENSURE_SUCCESS(rv, rv);

  if (aRequest->mEarlyHintPreloaderId) {
    nsCOMPtr<nsIHttpChannelInternal> channelInternal =
        do_QueryInterface(channel);
    NS_ENSURE_TRUE(channelInternal != nullptr, NS_ERROR_FAILURE);

    rv = channelInternal->SetEarlyHintPreloaderId(
        aRequest->mEarlyHintPreloaderId);
    NS_ENSURE_SUCCESS(rv, rv);
  }

  PrepareLoadInfoForScriptLoading(channel, aRequest);

  nsCOMPtr<nsIScriptGlobalObject> scriptGlobal = GetScriptGlobalObject();
  if (!scriptGlobal) {
    return NS_ERROR_FAILURE;
  }

  ScriptLoader::PrepareCacheInfoChannel(channel, aRequest);

  LOG(("ScriptLoadRequest (%p): mode=%u tracking=%d", aRequest,
       unsigned(aRequest->GetScriptLoadContext()->mScriptMode),
       aRequest->GetScriptLoadContext()->IsTracking()));

  PrepareRequestPriorityAndRequestDependencies(channel, aRequest);

  rv =
      PrepareHttpRequestAndInitiatorType(channel, aRequest, aCharsetForPreload);
  NS_ENSURE_SUCCESS(rv, rv);

  mozilla::net::PredictorLearn(
      aRequest->mURI, mDocument->GetDocumentURI(),
      nsINetworkPredictor::LEARN_LOAD_SUBRESOURCE,
      mDocument->NodePrincipal()->OriginAttributesRef());

  nsCOMPtr<nsIIncrementalStreamLoader> loader;
  rv =
      PrepareIncrementalStreamLoader(getter_AddRefs(loader), channel, aRequest);
  NS_ENSURE_SUCCESS(rv, rv);

  auto key = PreloadHashKey::CreateAsScript(
      aRequest->mURI, aRequest->CORSMode(), aRequest->mKind);
  aRequest->GetScriptLoadContext()->NotifyOpen(
      key, channel, mDocument,
      aRequest->GetScriptLoadContext()->IsLinkPreloadScript(),
      aRequest->IsModuleRequest());

  rv = channel->AsyncOpen(loader);

  if (NS_FAILED(rv)) {
    // Make sure to inform any <link preload> tags about failure to load the
    // resource.
    aRequest->GetScriptLoadContext()->NotifyStart(channel);
    aRequest->GetScriptLoadContext()->NotifyStop(rv);
  }

  NS_ENSURE_SUCCESS(rv, rv);

  return NS_OK;
}

bool ScriptLoader::PreloadURIComparator::Equals(const PreloadInfo& aPi,
                                                nsIURI* const& aURI) const {
  bool same;
  return NS_SUCCEEDED(aPi.mRequest->mURI->Equals(aURI, &same)) && same;
}

static bool CSPAllowsInlineScript(nsIScriptElement* aElement,
                                  const nsAString& aNonce,
                                  Document* aDocument) {
  nsCOMPtr<nsIContentSecurityPolicy> csp = aDocument->GetCsp();
  if (!csp) {
    // no CSP --> allow
    return true;
  }

  bool parserCreated =
      aElement->GetParserCreated() != mozilla::dom::NOT_FROM_PARSER;
  nsCOMPtr<Element> element = do_QueryInterface(aElement);

  bool allowInlineScript = false;
  nsresult rv = csp->GetAllowsInline(
      nsIContentSecurityPolicy::SCRIPT_SRC_ELEM_DIRECTIVE,
      false /* aHasUnsafeHash */, aNonce, parserCreated, element,
      nullptr /* nsICSPEventListener */, u""_ns,
      aElement->GetScriptLineNumber(),
      aElement->GetScriptColumnNumber().oneOriginValue(), &allowInlineScript);
  return NS_SUCCEEDED(rv) && allowInlineScript;
}

namespace {
RequestPriority FetchPriorityToRequestPriority(
    const FetchPriority aFetchPriority) {
  switch (aFetchPriority) {
    case FetchPriority::High:
      return RequestPriority::High;
    case FetchPriority::Low:
      return RequestPriority::Low;
    case FetchPriority::Auto:
      return RequestPriority::Auto;
  }

  MOZ_ASSERT_UNREACHABLE();
  return RequestPriority::Auto;
}
}  // namespace

void ScriptLoader::NotifyObserversForCachedScript(
    nsIURI* aURI, nsINode* aContext, nsIPrincipal* aTriggeringPrincipal,
    nsSecurityFlags aSecurityFlags, nsContentPolicyType aContentPolicyType,
    SubResourceNetworkMetadataHolder* aNetworkMetadata) {
  nsCOMPtr<nsIObserverService> obsService = services::GetObserverService();

  if (!obsService->HasObservers("http-on-resource-cache-response")) {
    return;
  }

  nsCOMPtr<nsIChannel> channel;
  nsresult rv = CreateChannelForScriptLoading(
      getter_AddRefs(channel), mDocument, aURI, aContext, aTriggeringPrincipal,
      aSecurityFlags, aContentPolicyType);
  if (NS_FAILED(rv)) {
    return;
  }

  RefPtr<net::HttpBaseChannel> httpBaseChannel = do_QueryObject(channel);
  if (httpBaseChannel) {
    const net::nsHttpResponseHead* responseHead = nullptr;
    if (aNetworkMetadata) {
      responseHead = aNetworkMetadata->GetResponseHead();
    }
    httpBaseChannel->SetDummyChannelForCachedResource(responseHead);
  }

  // TODO: Populate fields.

  // TODO: Move the handling into SharedSubResourceCache once the notification
  //       is merged between CSS and JS (bug 1919218)

  obsService->NotifyObservers(channel, "http-on-resource-cache-response",
                              nullptr);
}

already_AddRefed<ScriptLoadRequest> ScriptLoader::CreateLoadRequest(
    ScriptKind aKind, nsIURI* aURI, nsIScriptElement* aElement,
    nsIPrincipal* aTriggeringPrincipal, CORSMode aCORSMode,
    const nsAString& aNonce, RequestPriority aRequestPriority,
    const SRIMetadata& aIntegrity, ReferrerPolicy aReferrerPolicy,
    ParserMetadata aParserMetadata, RequestType requestType) {
  nsIURI* referrer = mDocument->GetDocumentURIAsReferrer();
  RefPtr<ScriptFetchOptions> fetchOptions =
      new ScriptFetchOptions(aCORSMode, aNonce, aRequestPriority,
                             aParserMetadata, aTriggeringPrincipal);
  RefPtr<ScriptLoadContext> context = new ScriptLoadContext(aElement);

  if (aKind == ScriptKind::eClassic || aKind == ScriptKind::eImportMap) {
    RefPtr<ScriptLoadRequest> aRequest =
        new ScriptLoadRequest(aKind, aURI, aReferrerPolicy, fetchOptions,
                              aIntegrity, referrer, context);
    if (requestType == RequestType::External && mCache) {
      ScriptHashKey key(this, aRequest);
      auto cacheResult = mCache->Lookup(*this, key,
                                        /* aSyncLoad = */ true);
      if (cacheResult.mState == CachedSubResourceState::Complete) {
        if (NS_FAILED(
                CheckContentPolicy(mDocument, aElement, aNonce, aRequest))) {
          aRequest->NoCacheEntryFound();
          return aRequest.forget();
        }

        nsCOMPtr<nsINode> context;
        if (aElement) {
          context = do_QueryInterface(aElement);
        } else {
          context = mDocument;
        }

        NotifyObserversForCachedScript(aURI, context, aTriggeringPrincipal,
                                       CORSModeToSecurityFlags(aCORSMode),
                                       nsIContentPolicy::TYPE_INTERNAL_SCRIPT,
                                       cacheResult.mNetworkMetadata);

        {
          nsAutoCString name;
          nsString entryName;
          aURI->GetSpec(name);
          CopyUTF8toUTF16(name, entryName);

          auto now = TimeStamp::Now();

          SharedSubResourceCacheUtils::AddPerformanceEntryForCache(
              entryName, GetInitiatorType(aRequest),
              cacheResult.mNetworkMetadata, now, now, mDocument);
        }

        aRequest->CacheEntryFound(cacheResult.mCompleteValue);
        return aRequest.forget();
      }

      aRequest->NoCacheEntryFound();
      return aRequest.forget();
    }

    aRequest->NoCacheEntryFound();
    return aRequest.forget();
  }

  MOZ_ASSERT(aKind == ScriptKind::eModule);
  RefPtr<ModuleLoadRequest> aRequest = ModuleLoader::CreateTopLevel(
      aURI, aReferrerPolicy, fetchOptions, aIntegrity, referrer, this, context);
  return aRequest.forget();
}

bool ScriptLoader::ProcessScriptElement(nsIScriptElement* aElement) {
  // We need a document to evaluate scripts.
  NS_ENSURE_TRUE(mDocument, false);

  // Check to see if scripts has been turned off.
  if (!mEnabled || !mDocument->IsScriptEnabled()) {
    return false;
  }

  NS_ASSERTION(!aElement->IsMalformed(), "Executing malformed script");

  nsCOMPtr<nsIContent> scriptContent = do_QueryInterface(aElement);

  ScriptKind scriptKind;
  if (aElement->GetScriptIsModule()) {
    scriptKind = ScriptKind::eModule;
  } else if (aElement->GetScriptIsImportMap()) {
    scriptKind = ScriptKind::eImportMap;
  } else {
    scriptKind = ScriptKind::eClassic;
  }

  // Step 13. Check that the script is not an eventhandler
  if (IsScriptEventHandler(scriptKind, scriptContent)) {
    return false;
  }

  // "In modern user agents that support module scripts, the script element with
  // the nomodule attribute will be ignored".
  // "The nomodule attribute must not be specified on module scripts (and will
  // be ignored if it is)."
  if (scriptKind == ScriptKind::eClassic && scriptContent->IsHTMLElement() &&
      scriptContent->AsElement()->HasAttr(nsGkAtoms::nomodule)) {
    return false;
  }

  // Step 15. and later in the HTML5 spec
  if (aElement->GetScriptExternal()) {
    return ProcessExternalScript(aElement, scriptKind, scriptContent);
  }

  return ProcessInlineScript(aElement, scriptKind);
}

static ParserMetadata GetParserMetadata(nsIScriptElement* aElement) {
  return aElement->GetParserCreated() == mozilla::dom::NOT_FROM_PARSER
             ? ParserMetadata::NotParserInserted
             : ParserMetadata::ParserInserted;
}

bool ScriptLoader::ProcessExternalScript(nsIScriptElement* aElement,
                                         ScriptKind aScriptKind,
                                         nsIContent* aScriptContent) {
  LOG(("ScriptLoader (%p): Process external script for element %p"this,
       aElement));

  // https://html.spec.whatwg.org/multipage/scripting.html#prepare-the-script-element
  // Step 30.1. If el's type is "importmap", then queue an element task on the
  // DOM manipulation task source given el to fire an event named error at el,
  // and return.
  if (aScriptKind == ScriptKind::eImportMap) {
    NS_DispatchToCurrentThread(
        NewRunnableMethod("nsIScriptElement::FireErrorEvent", aElement,
                          &nsIScriptElement::FireErrorEvent));
    nsContentUtils::ReportToConsole(
        nsIScriptError::warningFlag, "Script Loader"_ns, mDocument,
        nsContentUtils::eDOM_PROPERTIES, "ImportMapExternalNotSupported");
    return false;
  }

  nsCOMPtr<nsIURI> scriptURI = aElement->GetScriptURI();
  if (!scriptURI) {
    // Asynchronously report the failure to create a URI object
    NS_DispatchToCurrentThread(
        NewRunnableMethod("nsIScriptElement::FireErrorEvent", aElement,
                          &nsIScriptElement::FireErrorEvent));
    return false;
  }

  nsString nonce = nsContentSecurityUtils::GetIsElementNonceableNonce(
      *aScriptContent->AsElement());
  SRIMetadata sriMetadata;
  {
    nsAutoString integrity;
    aScriptContent->AsElement()->GetAttr(nsGkAtoms::integrity, integrity);
    GetSRIMetadata(integrity, &sriMetadata);
  }

  RefPtr<ScriptLoadRequest> request =
      LookupPreloadRequest(aElement, aScriptKind, sriMetadata);
  if (request) {
    if (NS_FAILED(CheckContentPolicy(mDocument, aElement, nonce, request))) {
      LOG(("ScriptLoader (%p): content policy check failed for preload"this));

      // Probably plans have changed; even though the preload was allowed seems
      // like the actual load is not; let's cancel the preload request.
      request->Cancel();
      return false;
    }

    // Use the preload request.

    LOG(("ScriptLoadRequest (%p): Using preload request", request.get()));

    // https://html.spec.whatwg.org/multipage/webappapis.html#fetch-a-module-script-tree
    // Step 1. Disallow further import maps given settings object.
    if (request->IsModuleRequest()) {
      LOG(("ScriptLoadRequest (%p): Disallow further import maps.",
           request.get()));
      mModuleLoader->DisallowImportMaps();
    }

    // It's possible these attributes changed since we started the preload so
    // update them here.
    request->GetScriptLoadContext()->SetScriptMode(
        aElement->GetScriptDeferred(), aElement->GetScriptAsync(), false);

    // The request will be added to another list or set as
    // mParserBlockingRequest below.
    if (request->GetScriptLoadContext()->mInCompilingList) {
      mOffThreadCompilingRequests.Remove(request);
      request->GetScriptLoadContext()->mInCompilingList = false;
    }
  } else {
    // No usable preload found.

    nsCOMPtr<nsIPrincipal> principal =
        aElement->GetScriptURITriggeringPrincipal();
    if (!principal) {
      principal = aScriptContent->NodePrincipal();
    }

    CORSMode ourCORSMode = aElement->GetCORSMode();
    const FetchPriority fetchPriority = aElement->GetFetchPriority();
    ReferrerPolicy referrerPolicy = GetReferrerPolicy(aElement);
    ParserMetadata parserMetadata = GetParserMetadata(aElement);

    request = CreateLoadRequest(
        aScriptKind, scriptURI, aElement, principal, ourCORSMode, nonce,
        FetchPriorityToRequestPriority(fetchPriority), sriMetadata,
        referrerPolicy, parserMetadata, RequestType::External);
    request->GetScriptLoadContext()->mIsInline = false;
    request->GetScriptLoadContext()->SetScriptMode(
        aElement->GetScriptDeferred(), aElement->GetScriptAsync(), false);
    // keep request->GetScriptLoadContext()->mScriptFromHead to false so we
    // don't treat non preloaded scripts as blockers for full page load. See bug
    // 792438.

    LOG(("ScriptLoadRequest (%p): Created request for external script",
         request.get()));

    if (!request->IsStencil()) {
      nsresult rv = StartLoad(request, Nothing());
      if (NS_FAILED(rv)) {
        ReportErrorToConsole(request, rv);

        // Asynchronously report the load failure
        nsCOMPtr<nsIRunnable> runnable =
            NewRunnableMethod("nsIScriptElement::FireErrorEvent", aElement,
                              &nsIScriptElement::FireErrorEvent);
        if (mDocument) {
          mDocument->Dispatch(runnable.forget());
        } else {
          NS_DispatchToCurrentThread(runnable.forget());
        }
        return false;
      }
    }
  }

  // We should still be in loading stage of script unless we're loading a
  // module or speculatively off-main-thread parsing a script.
  NS_ASSERTION(SpeculativeOMTParsingEnabled() ||
                   !request->GetScriptLoadContext()->CompileStarted() ||
                   request->IsModuleRequest(),
               "Request should not yet be in compiling stage.");

  if (request->GetScriptLoadContext()->IsAsyncScript()) {
    AddAsyncRequest(request);
    if (request->IsFinished()) {
      // The script is available already. Run it ASAP when the event
      // loop gets a chance to spin.

      // KVKV TODO: Instead of processing immediately, try off-thread-parsing
      // it and only schedule a pending ProcessRequest if that fails.
      ProcessPendingRequestsAsync();
    }
    return false;
  }
  if (!aElement->GetParserCreated()) {
    // Violate the HTML5 spec in order to make LABjs and the "order" plug-in
    // for RequireJS work with their Gecko-sniffed code path. See
    // http://lists.w3.org/Archives/Public/public-html/2010Oct/0088.html
    request->GetScriptLoadContext()->mIsNonAsyncScriptInserted = true;
    mNonAsyncExternalScriptInsertedRequests.AppendElement(request);
    if (request->IsFinished()) {
      // The script is available already. Run it ASAP when the event
      // loop gets a chance to spin.
      ProcessPendingRequestsAsync();
    }
    return false;
  }
  // we now have a parser-inserted request that may or may not be still
  // loading
  if (request->GetScriptLoadContext()->IsDeferredScript()) {
    // We don't want to run this yet.
    // If we come here, the script is a parser-created script and it has
    // the defer attribute but not the async attribute OR it is a module
    // script without the async attribute. Since a
    // a parser-inserted script is being run, we came here by the parser
    // running the script, which means the parser is still alive and the
    // parse is ongoing.
    NS_ASSERTION(mDocument->GetCurrentContentSink() ||
                     aElement->GetParserCreated() == FROM_PARSER_XSLT,
                 "Non-XSLT Defer script on a document without an active "
                 "parser; bug 592366.");
    AddDeferRequest(request);
    return false;
  }

  if (aElement->GetParserCreated() == FROM_PARSER_XSLT) {
    // Need to maintain order for XSLT-inserted scripts
    NS_ASSERTION(!mParserBlockingRequest,
                 "Parser-blocking scripts and XSLT scripts in the same doc!");
    request->GetScriptLoadContext()->mIsXSLT = true;
    mXSLTRequests.AppendElement(request);
    if (request->IsFinished()) {
      // The script is available already. Run it ASAP when the event
      // loop gets a chance to spin.
      ProcessPendingRequestsAsync();
    }
    return true;
  }

  if (request->IsFinished() && ReadyToExecuteParserBlockingScripts()) {
    // The request has already been loaded and there are no pending style
    // sheets. If the script comes from the network stream, cheat for
    // performance reasons and avoid a trip through the event loop.
    if (aElement->GetParserCreated() == FROM_PARSER_NETWORK) {
      return ProcessRequest(request) == NS_ERROR_HTMLPARSER_BLOCK;
    }
    // Otherwise, we've got a document.written script, make a trip through
    // the event loop to hide the preload effects from the scripts on the
    // Web page.
    NS_ASSERTION(!mParserBlockingRequest,
                 "There can be only one parser-blocking script at a time");
    NS_ASSERTION(mXSLTRequests.isEmpty(),
                 "Parser-blocking scripts and XSLT scripts in the same doc!");
    mParserBlockingRequest = request;
    ProcessPendingRequestsAsync();
    return true;
  }

  // The script hasn't loaded yet or there's a style sheet blocking it.
  // The script will be run when it loads or the style sheet loads.
  NS_ASSERTION(!mParserBlockingRequest,
               "There can be only one parser-blocking script at a time");
  NS_ASSERTION(mXSLTRequests.isEmpty(),
               "Parser-blocking scripts and XSLT scripts in the same doc!");
  mParserBlockingRequest = request;
  return true;
}

bool ScriptLoader::ProcessInlineScript(nsIScriptElement* aElement,
                                       ScriptKind aScriptKind) {
  // Is this document sandboxed without 'allow-scripts'?
  if (mDocument->HasScriptsBlockedBySandbox()) {
    return false;
  }

  nsCOMPtr<Element> element = do_QueryInterface(aElement);
  nsString nonce = nsContentSecurityUtils::GetIsElementNonceableNonce(*element);

  // Does CSP allow this inline script to run?
  if (!CSPAllowsInlineScript(aElement, nonce, mDocument)) {
    return false;
  }

  // Check if adding an import map script is allowed. If not, we bail out
  // early to prevent creating a load request.
  if (aScriptKind == ScriptKind::eImportMap) {
    // https://html.spec.whatwg.org/multipage/scripting.html#prepare-the-script-element
    // Step 31.2 type is "importmap":
    //   Step 1. If el's relevant global object's import maps allowed is false,
    //   then queue an element task on the DOM manipulation task source given el
    //   to fire an event named error at el, and return.
    if (!mModuleLoader->IsImportMapAllowed()) {
      NS_WARNING("ScriptLoader: import maps allowed is false.");
      const char* msg = mModuleLoader->HasImportMapRegistered()
                            ? "ImportMapNotAllowedMultiple"
                            : "ImportMapNotAllowedAfterModuleLoad";
      nsContentUtils::ReportToConsole(nsIScriptError::warningFlag,
                                      "Script Loader"_ns, mDocument,
                                      nsContentUtils::eDOM_PROPERTIES, msg);
      NS_DispatchToCurrentThread(
          NewRunnableMethod("nsIScriptElement::FireErrorEvent", aElement,
                            &nsIScriptElement::FireErrorEvent));
      return false;
    }
  }

  // Inline classic scripts ignore their CORS mode and are always CORS_NONE.
  CORSMode corsMode = CORS_NONE;
  if (aScriptKind == ScriptKind::eModule) {
    corsMode = aElement->GetCORSMode();
  }
  // <https://html.spec.whatwg.org/multipage/scripting.html#prepare-the-script-element>
  // step 29 specifies to use the fetch priority. Presumably it has no effect
  // for inline scripts.
  const auto fetchPriority = aElement->GetFetchPriority();

  ReferrerPolicy referrerPolicy = GetReferrerPolicy(aElement);
  ParserMetadata parserMetadata = GetParserMetadata(aElement);

  // NOTE: The `nonce` as specified here is significant, because it's inherited
  // by other scripts (e.g. modules created via dynamic imports).
  RefPtr<ScriptLoadRequest> request =
      CreateLoadRequest(aScriptKind, mDocument->GetDocumentURI(), aElement,
                        mDocument->NodePrincipal(), corsMode, nonce,
                        FetchPriorityToRequestPriority(fetchPriority),
                        SRIMetadata(),  // SRI doesn't apply
                        referrerPolicy, parserMetadata, RequestType::Inline);
  request->GetScriptLoadContext()->mIsInline = true;
  request->GetScriptLoadContext()->mLineNo = aElement->GetScriptLineNumber();
  request->GetScriptLoadContext()->mColumnNo =
      aElement->GetScriptColumnNumber();
  request->mFetchSourceOnly = true;
  request->SetTextSource(request->mLoadContext.get());
  TRACE_FOR_TEST_BOOL(request, "scriptloader_load_source");
  CollectScriptTelemetry(request);

  // Only the 'async' attribute is heeded on an inline module script and
  // inline classic scripts ignore both these attributes.
  MOZ_ASSERT(!aElement->GetScriptDeferred());
  MOZ_ASSERT_IF(!request->IsModuleRequest(), !aElement->GetScriptAsync());
  request->GetScriptLoadContext()->SetScriptMode(
      false, aElement->GetScriptAsync(), false);

  LOG(("ScriptLoadRequest (%p): Created request for inline script",
       request.get()));

  request->mBaseURL = mDocument->GetDocBaseURI();

  if (request->IsModuleRequest()) {
    // https://html.spec.whatwg.org/multipage/webappapis.html#fetch-an-inline-module-script-graph
    // Step 1. Disallow further import maps given settings object.
    mModuleLoader->DisallowImportMaps();

    ModuleLoadRequest* modReq = request->AsModuleRequest();
    if (aElement->GetParserCreated() != NOT_FROM_PARSER) {
      if (aElement->GetScriptAsync()) {
        AddAsyncRequest(modReq);
      } else {
        AddDeferRequest(modReq);
      }
    }

    // This calls OnFetchComplete directly since there's no need to start
    // fetching an inline script.
    nsresult rv = modReq->OnFetchComplete(NS_OK);
    if (NS_FAILED(rv)) {
      ReportErrorToConsole(modReq, rv);
      HandleLoadError(modReq, rv);
    }

    return false;
  }

  if (request->IsImportMapRequest()) {
    // https://html.spec.whatwg.org/multipage/scripting.html#prepare-the-script-element
    // Step 31.2 type is "importmap":
    //   Impl note: Step 1 is done above before creating a ScriptLoadRequest.
    MOZ_ASSERT(mModuleLoader->IsImportMapAllowed());

    //   Step 2. Set el's relevant global object's import maps allowed to false.
    mModuleLoader->DisallowImportMaps();

    //   Step 3. Let result be the result of creating an import map parse result
    //   given source text and base URL.
    UniquePtr<ImportMap> importMap = mModuleLoader->ParseImportMap(request);
    if (!importMap) {
      // If parsing import maps fails, the exception will be reported in
      // ModuleLoaderBase::ParseImportMap, and the registration of the import
      // map will bail out early.
      return false;
    }

    // Remove any module preloads. Module specifier resolution is invalidated by
    // adding an import map, and incorrect dependencies may have been loaded.
    mPreloads.RemoveElementsBy([](const PreloadInfo& info) {
      if (info.mRequest->IsModuleRequest()) {
        info.mRequest->Cancel();
        return true;
      }
      return false;
    });

    // TODO: Bug 1781758: Move RegisterImportMap into EvaluateScriptElement.
    //
    // https://html.spec.whatwg.org/multipage/scripting.html#execute-the-script-element
    // The spec defines 'register an import map' should be done in
    // 'execute the script element', because inside 'execute the script element'
    // it will perform a 'preparation-time document check'.
    // However, as import maps could be only inline scripts by now, the
    // 'preparation-time document check' will never fail for import maps.
    // So we simply call 'register an import map' here.
    mModuleLoader->RegisterImportMap(std::move(importMap));
    return false;
  }

  request->mState = ScriptLoadRequest::State::Ready;
  if (aElement->GetParserCreated() == FROM_PARSER_XSLT &&
      (!ReadyToExecuteParserBlockingScripts() || !mXSLTRequests.isEmpty())) {
    // Need to maintain order for XSLT-inserted scripts
    NS_ASSERTION(!mParserBlockingRequest,
                 "Parser-blocking scripts and XSLT scripts in the same doc!");
    mXSLTRequests.AppendElement(request);
    return true;
  }
  if (aElement->GetParserCreated() == NOT_FROM_PARSER) {
    NS_ASSERTION(
        !nsContentUtils::IsSafeToRunScript(),
        "A script-inserted script is inserted without an update batch?");
    RunScriptWhenSafe(request);
    return false;
  }
  if (aElement->GetParserCreated() == FROM_PARSER_NETWORK &&
      !ReadyToExecuteParserBlockingScripts()) {
    NS_ASSERTION(!mParserBlockingRequest,
                 "There can be only one parser-blocking script at a time");
    mParserBlockingRequest = request;
    NS_ASSERTION(mXSLTRequests.isEmpty(),
                 "Parser-blocking scripts and XSLT scripts in the same doc!");
    return true;
  }
  // We now have a document.written inline script or we have an inline script
  // from the network but there is no style sheet that is blocking scripts.
  // Don't check for style sheets blocking scripts in the document.write
  // case to avoid style sheet network activity affecting when
  // document.write returns. It's not really necessary to do this if
  // there's no document.write currently on the call stack. However,
  // this way matches IE more closely than checking if document.write
  // is on the call stack.
  NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
               "Not safe to run a parser-inserted script?");
  return ProcessRequest(request) == NS_ERROR_HTMLPARSER_BLOCK;
}

ScriptLoadRequest* ScriptLoader::LookupPreloadRequest(
    nsIScriptElement* aElement, ScriptKind aScriptKind,
    const SRIMetadata& aSRIMetadata) {
  MOZ_ASSERT(aElement);

  nsTArray<PreloadInfo>::index_type i =
      mPreloads.IndexOf(aElement->GetScriptURI(), 0, PreloadURIComparator());
  if (i == nsTArray<PreloadInfo>::NoIndex) {
    return nullptr;
  }
  RefPtr<ScriptLoadRequest> request = mPreloads[i].mRequest;
  if (aScriptKind != request->mKind) {
    return nullptr;
  }

  // Found preloaded request. Note that a script-inserted script can steal a
  // preload!
  request->GetScriptLoadContext()->SetIsLoadRequest(aElement);

  if (request->GetScriptLoadContext()->mWasCompiledOMT &&
      !request->IsModuleRequest()) {
    request->SetReady();
  }

  nsString preloadCharset(mPreloads[i].mCharset);
  mPreloads.RemoveElementAt(i);

  // Double-check that the charset the preload used is the same as the charset
  // we have now.
  nsAutoString elementCharset;
  aElement->GetScriptCharset(elementCharset);

  // Bug 1832361: charset and crossorigin attributes shouldn't affect matching
  // of module scripts and modulepreload
  if (!request->IsModuleRequest() &&
      (!elementCharset.Equals(preloadCharset) ||
       aElement->GetCORSMode() != request->CORSMode())) {
    // Drop the preload.
    request->Cancel();
    return nullptr;
  }

  if (!aSRIMetadata.CanTrustBeDelegatedTo(request->mIntegrity)) {
    // Don't cancel link preload requests, we want to deliver onload according
    // the result of the load, cancellation would unexpectedly lead to error
    // notification.
    if (!request->GetScriptLoadContext()->IsLinkPreloadScript()) {
      request->Cancel();
    }
    return nullptr;
  }

  // Report any errors that we skipped while preloading.
  ReportPreloadErrorsToConsole(request);

  // This makes sure the pending preload (if exists) for this resource is
  // properly marked as used and thus not notified in the console as unused.
  request->GetScriptLoadContext()->NotifyUsage(mDocument);
  // A used preload must no longer be found in the Document's hash table.  Any
  // <link preload> tag after the <script> tag will start a new request, that
  // can be satisfied from a different cache, but not from the preload cache.
  request->GetScriptLoadContext()->RemoveSelf(mDocument);

  return request;
}

void ScriptLoader::GetSRIMetadata(const nsAString& aIntegrityAttr,
                                  SRIMetadata* aMetadataOut) {
  MOZ_ASSERT(aMetadataOut->IsEmpty());

  if (aIntegrityAttr.IsEmpty()) {
    return;
  }

  MOZ_LOG(SRILogHelper::GetSriLog(), mozilla::LogLevel::Debug,
          ("ScriptLoader::GetSRIMetadata, integrity=%s",
           NS_ConvertUTF16toUTF8(aIntegrityAttr).get()));

  nsAutoCString sourceUri;
  if (mDocument->GetDocumentURI()) {
    mDocument->GetDocumentURI()->GetAsciiSpec(sourceUri);
  }
  SRICheck::IntegrityMetadata(aIntegrityAttr, sourceUri, mReporter,
                              aMetadataOut);
}

ReferrerPolicy ScriptLoader::GetReferrerPolicy(nsIScriptElement* aElement) {
  ReferrerPolicy scriptReferrerPolicy = aElement->GetReferrerPolicy();
  if (scriptReferrerPolicy != ReferrerPolicy::_empty) {
    return scriptReferrerPolicy;
  }
  return mDocument->GetReferrerPolicy();
}

void ScriptLoader::CancelAndClearScriptLoadRequests() {
  // Cancel all requests that have not been executed and remove them.

  if (mParserBlockingRequest) {
    mParserBlockingRequest->Cancel();
    mParserBlockingRequest = nullptr;
  }

  mDeferRequests.CancelRequestsAndClear();
  mLoadingAsyncRequests.CancelRequestsAndClear();
  mLoadedAsyncRequests.CancelRequestsAndClear();
  mNonAsyncExternalScriptInsertedRequests.CancelRequestsAndClear();
  mXSLTRequests.CancelRequestsAndClear();
  mOffThreadCompilingRequests.CancelRequestsAndClear();

  if (mModuleLoader) {
    mModuleLoader->CancelAndClearDynamicImports();
  }

  for (ModuleLoader* loader : mWebExtModuleLoaders) {
    loader->CancelAndClearDynamicImports();
  }

  for (ModuleLoader* loader : mShadowRealmModuleLoaders) {
    loader->CancelAndClearDynamicImports();
  }

  for (size_t i = 0; i < mPreloads.Length(); i++) {
    mPreloads[i].mRequest->Cancel();
  }
  mPreloads.Clear();
}

nsresult ScriptLoader::CompileOffThreadOrProcessRequest(
    ScriptLoadRequest* aRequest) {
  NS_ASSERTION(nsContentUtils::IsSafeToRunScript(),
               "Processing requests when running scripts is unsafe.");

  if (!aRequest->IsStencil() &&
      !aRequest->GetScriptLoadContext()->mCompileOrDecodeTask &&
      !aRequest->GetScriptLoadContext()->CompileStarted()) {
    bool couldCompile = false;
    nsresult rv = AttemptOffThreadScriptCompile(aRequest, &couldCompile);
    if (NS_FAILED(rv)) {
--> --------------------

--> maximum size reached

--> --------------------

96%


¤ Dauer der Verarbeitung: 0.36 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung ist noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge