/* -*- 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 "nsIChildChannel.h" #include "zlib.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/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/OffThreadScriptCompilation.h" #include "js/PropertyAndElement.h" // JS_DefineProperty #include "js/Realm.h" #include "js/SourceText.h" #include "js/Transcoding.h" #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/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_javascript.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 namespace JS::loader; using mozilla::Telemetry::LABELS_DOM_SCRIPT_PRELOAD_RESULT; 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(ScriptLoader, mNonAsyncExternalScriptInsertedRequests, mLoadingAsyncRequests, mLoadedAsyncRequests, mOffThreadCompilingRequests, mDeferRequests, mXSLTRequests, mParserBlockingRequest, mBytecodeEncodingQueue, mPreloads, mPendingChildLoaders, mModuleLoader, mWebExtModuleLoaders, mShadowRealmModuleLoaders) 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(); 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(); } for (size_t i = 0; i < mPreloads.Length(); i++) { AccumulateCategorical(LABELS_DOM_SCRIPT_PRELOAD_RESULT::NotUsed); } if (mShutdownObserver) { mShutdownObserver->Unregister(); mShutdownObserver = nullptr; } mModuleLoader = nullptr; } 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::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 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->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 //