/* -*- 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 "ModuleLoadRequest.h" #include "ModuleScript.h" #include "prsystem.h" #include "jsapi.h" #include "jsfriendapi.h" #include "xpcpublic.h" #include "nsCycleCollectionParticipant.h" #include "nsIContent.h" #include "nsJSUtils.h" #include "mozilla/dom/ScriptSettings.h" #include "mozilla/dom/Element.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 "nsITimedChannel.h" #include "nsIScriptElement.h" #include "nsIDOMHTMLScriptElement.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 "ImportManager.h" #include "mozilla/dom/EncodingUtils.h" #include "mozilla/ConsoleReportCollector.h" #include "mozilla/Attributes.h" #include "mozilla/Unused.h" #include "nsIScriptError.h" using JS::SourceBufferHolder; namespace mozilla { namespace dom { static LazyLogModule gCspPRLog("CSP"); void ImplCycleCollectionUnlink(ScriptLoadRequestList& aField); void ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback, ScriptLoadRequestList& aField, const char* aName, uint32_t aFlags = 0); ////////////////////////////////////////////////////////////// // nsScriptLoadRequest ////////////////////////////////////////////////////////////// NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ScriptLoadRequest) NS_INTERFACE_MAP_END NS_IMPL_CYCLE_COLLECTING_ADDREF(ScriptLoadRequest) NS_IMPL_CYCLE_COLLECTING_RELEASE(ScriptLoadRequest) NS_IMPL_CYCLE_COLLECTION_CLASS(ScriptLoadRequest) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ScriptLoadRequest) NS_IMPL_CYCLE_COLLECTION_UNLINK(mElement) NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ScriptLoadRequest) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mElement) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END ScriptLoadRequest::~ScriptLoadRequest() { js_free(mScriptTextBuf); // We should always clean up any off-thread script parsing resources. MOZ_ASSERT(!mOffThreadToken); // But play it safe in release builds and try to clean them up here // as a fail safe. MaybeCancelOffThreadScript(); } void ScriptLoadRequest::SetReady() { MOZ_ASSERT(mProgress != Progress::Ready); mProgress = Progress::Ready; } void ScriptLoadRequest::Cancel() { MaybeCancelOffThreadScript(); mIsCanceled = true; } void ScriptLoadRequest::MaybeCancelOffThreadScript() { MOZ_ASSERT(NS_IsMainThread()); if (!mOffThreadToken) { return; } JSContext* cx = danger::GetJSContext(); JS::CancelOffThreadScript(cx, mOffThreadToken); mOffThreadToken = nullptr; } inline ModuleLoadRequest* ScriptLoadRequest::AsModuleRequest() { MOZ_ASSERT(IsModuleRequest()); return static_cast(this); } void ScriptLoadRequest::SetScriptMode(bool aDeferAttr, bool aAsyncAttr) { if (aAsyncAttr) { mScriptMode = ScriptMode::eAsync; } else if (aDeferAttr || IsModuleRequest()) { mScriptMode = ScriptMode::eDeferred; } else { mScriptMode = ScriptMode::eBlocking; } } ////////////////////////////////////////////////////////////// // ScriptLoadRequestList ////////////////////////////////////////////////////////////// ScriptLoadRequestList::~ScriptLoadRequestList() { Clear(); } void ScriptLoadRequestList::Clear() { while (!isEmpty()) { RefPtr first = StealFirst(); first->Cancel(); // And just let it go out of scope and die. } } #ifdef DEBUG bool ScriptLoadRequestList::Contains(ScriptLoadRequest* aElem) const { for (const ScriptLoadRequest* req = getFirst(); req; req = req->getNext()) { if (req == aElem) { return true; } } return false; } #endif // DEBUG inline void ImplCycleCollectionUnlink(ScriptLoadRequestList& aField) { while (!aField.isEmpty()) { RefPtr first = aField.StealFirst(); } } inline void ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback, ScriptLoadRequestList& aField, const char* aName, uint32_t aFlags) { for (ScriptLoadRequest* request = aField.getFirst(); request; request = request->getNext()) { CycleCollectionNoteChild(aCallback, request, aName, aFlags); } } ////////////////////////////////////////////////////////////// // 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, 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), mReporter(new ConsoleReportCollector()) { } ScriptLoader::~ScriptLoader() { 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(); } } // 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 //