/* -*- 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 "ScriptLoadRequest.h" #include "ScriptTrace.h" #include "LoadedScript.h" #include "ModuleLoadRequest.h" #include "prsystem.h" #include "jsapi.h" #include "jsfriendapi.h" #include "js/Array.h" // JS::GetArrayLength #include "js/CompilationAndEvaluation.h" #include "js/ContextOptions.h" // JS::ContextOptionsRef #include "js/friend/ErrorMessages.h" // js::GetErrorMessage, JSMSG_* #include "js/MemoryFunctions.h" #include "js/Modules.h" // JS::FinishDynamicModuleImport, JS::{G,S}etModuleResolveHook, JS::Get{ModulePrivate,ModuleScript,RequestedModule{s,Specifier,SourcePos}}, JS::SetModule{DynamicImport,Metadata}Hook #include "js/OffThreadScriptCompilation.h" #include "js/Realm.h" #include "js/SourceText.h" #include "js/Utility.h" #include "xpcpublic.h" #include "GeckoProfiler.h" #include "nsCycleCollectionParticipant.h" #include "nsIContent.h" #include "nsJSUtils.h" #include "mozilla/dom/DocGroup.h" #include "mozilla/dom/Element.h" #include "mozilla/dom/JSExecutionContext.h" #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/net/UrlClassifierFeatureFactory.h" #include "mozilla/Preferences.h" #include "mozilla/StaticPrefs_dom.h" #include "mozilla/StaticPrefs_network.h" #include "nsAboutProtocolUtils.h" #include "nsGkAtoms.h" #include "nsNetUtil.h" #include "nsGlobalWindowInner.h" #include "nsIScriptGlobalObject.h" #include "nsIScriptContext.h" #include "nsIPrincipal.h" #include "nsJSPrincipals.h" #include "nsContentPolicyUtils.h" #include "nsIClassifiedChannel.h" #include "nsIHttpChannel.h" #include "nsIHttpChannelInternal.h" #include "nsIClassOfService.h" #include "nsICacheInfoChannel.h" #include "nsITimedChannel.h" #include "nsIScriptElement.h" #include "nsISupportsPriority.h" #include "nsIDocShell.h" #include "nsContentUtils.h" #include "nsUnicharUtils.h" #include "nsError.h" #include "nsThreadUtils.h" #include "nsDocShellCID.h" #include "nsIContentSecurityPolicy.h" #include "mozilla/Logging.h" #include "nsCRT.h" #include "nsContentCreatorFunctions.h" #include "nsProxyRelease.h" #include "nsSandboxFlags.h" #include "nsContentTypeParser.h" #include "nsINetworkPredictor.h" #include "nsMimeTypes.h" #include "mozilla/ConsoleReportCollector.h" #include "mozilla/CycleCollectedJSContext.h" #include "mozilla/LoadInfo.h" #include "ReferrerInfo.h" #include "mozilla/AsyncEventDispatcher.h" #include "mozilla/Attributes.h" #include "mozilla/ScopeExit.h" #include "mozilla/Telemetry.h" #include "mozilla/TimeStamp.h" #include "mozilla/UniquePtr.h" #include "mozilla/Unused.h" #include "mozilla/Utf8.h" // mozilla::Utf8Unit #include "nsIScriptError.h" #include "nsIAsyncOutputStream.h" using JS::SourceText; using mozilla::Telemetry::LABELS_DOM_SCRIPT_PRELOAD_RESULT; namespace mozilla { namespace 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->Shutdown(); Unregister(); } } 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(ScriptLoader, mNonAsyncExternalScriptInsertedRequests, mLoadingAsyncRequests, mLoadedAsyncRequests, mDeferRequests, mXSLTRequests, mDynamicImportRequests, mParserBlockingRequest, mBytecodeEncodingQueue, mPreloads, mPendingChildLoaders, mFetchedModules) NS_IMPL_CYCLE_COLLECTING_ADDREF(ScriptLoader) NS_IMPL_CYCLE_COLLECTING_RELEASE(ScriptLoader) ScriptLoader::ScriptLoader(Document* aDocument) : mDocument(aDocument), mParserBlockingBlockerCount(0), mBlockerCount(0), mNumberOfProcessors(0), mEnabled(true), mDeferEnabled(false), mSpeculativeOMTParsingEnabled(false), mDeferCheckpointReached(false), mBlockingDOMContentLoaded(false), mLoadEventFired(false), mGiveUpEncoding(false), mReporter(new ConsoleReportCollector()) { LOG(("ScriptLoader::ScriptLoader %p", this)); EnsureModuleHooksInitialized(); mSpeculativeOMTParsingEnabled = StaticPrefs:: dom_script_loader_external_scripts_speculative_omt_parse_enabled(); mShutdownObserver = new AsyncCompileShutdownObserver(this); nsContentUtils::RegisterShutdownObserver(mShutdownObserver); } ScriptLoader::~ScriptLoader() { LOG(("ScriptLoader::~ScriptLoader %p", this)); mObservers.Clear(); if (mParserBlockingRequest) { mParserBlockingRequest->FireScriptAvailable(NS_ERROR_ABORT); } for (ScriptLoadRequest* req = mXSLTRequests.getFirst(); req; req = req->getNext()) { req->FireScriptAvailable(NS_ERROR_ABORT); } for (ScriptLoadRequest* req = mDeferRequests.getFirst(); req; req = req->getNext()) { req->FireScriptAvailable(NS_ERROR_ABORT); } for (ScriptLoadRequest* req = mLoadingAsyncRequests.getFirst(); req; req = req->getNext()) { req->FireScriptAvailable(NS_ERROR_ABORT); } for (ScriptLoadRequest* req = mLoadedAsyncRequests.getFirst(); req; req = req->getNext()) { req->FireScriptAvailable(NS_ERROR_ABORT); } for (ScriptLoadRequest* req = mDynamicImportRequests.getFirst(); req; req = req->getNext()) { FinishDynamicImportAndReject(req->AsModuleRequest(), NS_ERROR_ABORT); } for (ScriptLoadRequest* req = mNonAsyncExternalScriptInsertedRequests.getFirst(); req; req = req->getNext()) { req->FireScriptAvailable(NS_ERROR_ABORT); } // 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(); } for (size_t i = 0; i < mPreloads.Length(); i++) { AccumulateCategorical(LABELS_DOM_SCRIPT_PRELOAD_RESULT::NotUsed); } if (mShutdownObserver) { mShutdownObserver->Unregister(); mShutdownObserver = nullptr; } } // 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; // Skip this function if we are not running telemetry. if (!CanRecordExtended()) { return; } // Report the script kind. if (aRequest->IsModuleRequest()) { AccumulateCategorical(LABELS_DOM_SCRIPT_KIND::ModuleScript); } else { AccumulateCategorical(LABELS_DOM_SCRIPT_KIND::ClassicScript); } // 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->IsLoadingSource()) { if (aRequest->mIsInline) { AccumulateCategorical(LABELS_DOM_SCRIPT_LOADING_SOURCE::Inline); } else if (aRequest->IsTextSource()) { AccumulateCategorical(LABELS_DOM_SCRIPT_LOADING_SOURCE::SourceFallback); } // TODO: Add telemetry for BinAST encoded source. } else { MOZ_ASSERT(aRequest->IsLoading()); if (aRequest->IsTextSource()) { AccumulateCategorical(LABELS_DOM_SCRIPT_LOADING_SOURCE::Source); } else if (aRequest->IsBytecode()) { AccumulateCategorical(LABELS_DOM_SCRIPT_LOADING_SOURCE::AltData); } // TODO: Add telemetry for BinAST encoded source. } } // 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 //