/* -*- 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 "ModuleLoadRequest.h" #include "ModuleScript.h" #include "prsystem.h" #include "jsapi.h" #include "jsfriendapi.h" #include "js/Utility.h" #include "xpcpublic.h" #include "nsCycleCollectionParticipant.h" #include "nsIContent.h" #include "nsJSUtils.h" #include "mozilla/dom/DocGroup.h" #include "mozilla/dom/Element.h" #include "mozilla/dom/ScriptSettings.h" #include "mozilla/dom/SRILogHelper.h" #include "nsGkAtoms.h" #include "nsNetUtil.h" #include "nsIScriptGlobalObject.h" #include "nsIScriptContext.h" #include "nsIScriptSecurityManager.h" #include "nsIPrincipal.h" #include "nsJSPrincipals.h" #include "nsContentPolicyUtils.h" #include "nsIHttpChannel.h" #include "nsIHttpChannelInternal.h" #include "nsIClassOfService.h" #include "nsICacheInfoChannel.h" #include "nsITimedChannel.h" #include "nsIScriptElement.h" #include "nsIDocShell.h" #include "nsContentUtils.h" #include "nsUnicharUtils.h" #include "nsAutoPtr.h" #include "nsIXPConnect.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 "mozilla/ConsoleReportCollector.h" #include "mozilla/AsyncEventDispatcher.h" #include "mozilla/Attributes.h" #include "mozilla/Telemetry.h" #include "mozilla/TimeStamp.h" #include "mozilla/Unused.h" #include "nsIScriptError.h" #include "nsIOutputStream.h" using JS::SourceBufferHolder; 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 NS_NAMED_LITERAL_CSTRING(kNullMimeType, "javascript/null"); ////////////////////////////////////////////////////////////// // 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, mParserBlockingRequest, mBytecodeEncodingQueue, mPreloads, mPendingChildLoaders, mFetchedModules) NS_IMPL_CYCLE_COLLECTING_ADDREF(ScriptLoader) NS_IMPL_CYCLE_COLLECTING_RELEASE(ScriptLoader) ScriptLoader::ScriptLoader(nsIDocument *aDocument) : mDocument(aDocument), mParserBlockingBlockerCount(0), mBlockerCount(0), mNumberOfProcessors(0), mEnabled(true), mDeferEnabled(false), mDocumentParsingDone(false), mBlockingDOMContentLoaded(false), mLoadEventFired(false), mGiveUpEncoding(false), mReporter(new ConsoleReportCollector()) { LOG(("ScriptLoader::ScriptLoader %p", this)); } 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 = 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); } } // 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(nsIIncrementalStreamLoader* aLoader, 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, as well as the size of the source. if (aRequest->IsLoadingSource()) { if (aRequest->mIsInline) { AccumulateCategorical(LABELS_DOM_SCRIPT_LOADING_SOURCE::Inline); nsAutoString inlineData; aRequest->mElement->GetScriptText(inlineData); Accumulate(DOM_SCRIPT_INLINE_SIZE, inlineData.Length()); } else { AccumulateCategorical(LABELS_DOM_SCRIPT_LOADING_SOURCE::SourceFallback); Accumulate(DOM_SCRIPT_SOURCE_SIZE, aRequest->mScriptText.length()); } } else { MOZ_ASSERT(aRequest->IsLoading()); if (aRequest->IsSource()) { AccumulateCategorical(LABELS_DOM_SCRIPT_LOADING_SOURCE::Source); Accumulate(DOM_SCRIPT_SOURCE_SIZE, aRequest->mScriptText.length()); } else { MOZ_ASSERT(aRequest->IsBytecode()); AccumulateCategorical(LABELS_DOM_SCRIPT_LOADING_SOURCE::AltData); Accumulate(DOM_SCRIPT_BYTECODE_SIZE, aRequest->mScriptBytecode.length()); } } // Skip if we do not have any cache information for the given script. if (!aLoader) { return; } nsCOMPtr channel; aLoader->GetRequest(getter_AddRefs(channel)); nsCOMPtr cic(do_QueryInterface(channel)); if (!cic) { return; } int32_t fetchCount = 0; if (NS_SUCCEEDED(cic->GetCacheTokenFetchCount(&fetchCount))) { Accumulate(DOM_SCRIPT_FETCH_COUNT, fetchCount); } } // 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 //