diff options
Diffstat (limited to 'dom/script/ScriptLoadRequest.cpp')
-rw-r--r-- | dom/script/ScriptLoadRequest.cpp | 319 |
1 files changed, 319 insertions, 0 deletions
diff --git a/dom/script/ScriptLoadRequest.cpp b/dom/script/ScriptLoadRequest.cpp new file mode 100644 index 0000000000..08c72a118c --- /dev/null +++ b/dom/script/ScriptLoadRequest.cpp @@ -0,0 +1,319 @@ +/* -*- 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 "ScriptLoadRequest.h" + +#include "mozilla/dom/Document.h" +#include "mozilla/HoldDropJSObjects.h" +#include "mozilla/StaticPrefs_dom.h" +#include "mozilla/Unused.h" +#include "mozilla/Utf8.h" // mozilla::Utf8Unit + +#include "ModuleLoadRequest.h" +#include "nsContentUtils.h" +#include "nsICacheInfoChannel.h" +#include "nsIClassOfService.h" +#include "nsISupportsPriority.h" +#include "ScriptLoadRequest.h" +#include "ScriptSettings.h" + +namespace mozilla { +namespace dom { + +////////////////////////////////////////////////////////////// +// ScriptFetchOptions +////////////////////////////////////////////////////////////// + +NS_IMPL_CYCLE_COLLECTION(ScriptFetchOptions, mElement, mTriggeringPrincipal) + +NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(ScriptFetchOptions, AddRef) +NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(ScriptFetchOptions, Release) + +ScriptFetchOptions::ScriptFetchOptions(mozilla::CORSMode aCORSMode, + ReferrerPolicy aReferrerPolicy, + Element* aElement, + nsIPrincipal* aTriggeringPrincipal) + : mCORSMode(aCORSMode), + mReferrerPolicy(aReferrerPolicy), + mIsPreload(false), + mElement(aElement), + mTriggeringPrincipal(aTriggeringPrincipal) { + MOZ_ASSERT(mTriggeringPrincipal); +} + +ScriptFetchOptions::~ScriptFetchOptions() = default; + +////////////////////////////////////////////////////////////// +// ScriptLoadRequest +////////////////////////////////////////////////////////////// + +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(mFetchOptions, mCacheInfo) + tmp->mScript = nullptr; + if (Runnable* runnable = tmp->mRunnable.exchange(nullptr)) { + runnable->Release(); + } + tmp->DropBytecodeCacheReferences(); + tmp->MaybeUnblockOnload(); +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ScriptLoadRequest) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFetchOptions, mCacheInfo, + mLoadBlockedDocument) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(ScriptLoadRequest) + NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mScript) +NS_IMPL_CYCLE_COLLECTION_TRACE_END + +ScriptLoadRequest::ScriptLoadRequest(ScriptKind aKind, nsIURI* aURI, + ScriptFetchOptions* aFetchOptions, + const SRIMetadata& aIntegrity, + nsIURI* aReferrer) + : mKind(aKind), + mScriptMode(ScriptMode::eBlocking), + mProgress(Progress::eLoading), + mDataType(DataType::eUnknown), + mScriptFromHead(false), + mIsInline(true), + mInDeferList(false), + mInAsyncList(false), + mIsNonAsyncScriptInserted(false), + mIsXSLT(false), + mIsCanceled(false), + mWasCompiledOMT(false), + mIsTracking(false), + mFetchOptions(aFetchOptions), + mOffThreadToken(nullptr), + mRunnable(nullptr), + mScriptTextLength(0), + mScriptBytecode(), + mBytecodeOffset(0), + mURI(aURI), + mLineNo(1), + mIntegrity(aIntegrity), + mReferrer(aReferrer), + mUnreportedPreloadError(NS_OK) { + MOZ_ASSERT(mFetchOptions); +} + +ScriptLoadRequest::~ScriptLoadRequest() { + // When speculative parsing is enabled, it is possible to off-main-thread + // compile scripts that are never executed. These should be cleaned up here + // if they exist. + MOZ_ASSERT_IF( + !StaticPrefs:: + dom_script_loader_external_scripts_speculative_omt_parse_enabled(), + !mOffThreadToken); + + MaybeCancelOffThreadScript(); + + if (mScript) { + DropBytecodeCacheReferences(); + } + + MaybeUnblockOnload(); + DropJSObjects(this); +} + +void ScriptLoadRequest::BlockOnload(Document* aDocument) { + MOZ_ASSERT(!mLoadBlockedDocument); + aDocument->BlockOnload(); + mLoadBlockedDocument = aDocument; +} + +void ScriptLoadRequest::MaybeUnblockOnload() { + if (mLoadBlockedDocument) { + mLoadBlockedDocument->UnblockOnload(false); + mLoadBlockedDocument = nullptr; + } +} + +void ScriptLoadRequest::SetReady() { + MOZ_ASSERT(mProgress != Progress::eReady); + mProgress = Progress::eReady; +} + +void ScriptLoadRequest::Cancel() { + MaybeCancelOffThreadScript(); + mIsCanceled = true; +} + +void ScriptLoadRequest::MaybeCancelOffThreadScript() { + MOZ_ASSERT(NS_IsMainThread()); + + if (!mOffThreadToken) { + return; + } + + JSContext* cx = danger::GetJSContext(); + // Follow the same conditions as ScriptLoader::AttemptAsyncScriptCompile + if (IsModuleRequest()) { + JS::CancelOffThreadModule(cx, mOffThreadToken); + } else if (IsSource()) { + JS::CancelOffThreadScript(cx, mOffThreadToken); + } else { + MOZ_ASSERT(IsBytecode()); + JS::CancelOffThreadScriptDecoder(cx, mOffThreadToken); + } + + // Cancellation request above should guarantee removal of the parse task, so + // releasing the runnable should be safe to do here. + if (Runnable* runnable = mRunnable.exchange(nullptr)) { + runnable->Release(); + } + + MaybeUnblockOnload(); + mOffThreadToken = nullptr; +} + +void ScriptLoadRequest::DropBytecodeCacheReferences() { + mCacheInfo = nullptr; + mScript = nullptr; + DropJSObjects(this); +} + +inline ModuleLoadRequest* ScriptLoadRequest::AsModuleRequest() { + MOZ_ASSERT(IsModuleRequest()); + return static_cast<ModuleLoadRequest*>(this); +} + +void ScriptLoadRequest::SetScriptMode(bool aDeferAttr, bool aAsyncAttr, + bool aLinkPreload) { + if (aLinkPreload) { + mScriptMode = ScriptMode::eLinkPreload; + } else if (aAsyncAttr) { + mScriptMode = ScriptMode::eAsync; + } else if (aDeferAttr || IsModuleRequest()) { + mScriptMode = ScriptMode::eDeferred; + } else { + mScriptMode = ScriptMode::eBlocking; + } +} + +void ScriptLoadRequest::SetUnknownDataType() { + mDataType = DataType::eUnknown; + mScriptData.reset(); +} + +void ScriptLoadRequest::SetTextSource() { + MOZ_ASSERT(IsUnknownDataType()); + mDataType = DataType::eTextSource; + if (StaticPrefs::dom_script_loader_external_scripts_utf8_parsing_enabled()) { + mScriptData.emplace(VariantType<ScriptTextBuffer<Utf8Unit>>()); + } else { + mScriptData.emplace(VariantType<ScriptTextBuffer<char16_t>>()); + } +} + +void ScriptLoadRequest::SetBinASTSource() { MOZ_CRASH("BinAST not supported"); } + +void ScriptLoadRequest::SetBytecode() { + MOZ_ASSERT(IsUnknownDataType()); + mDataType = DataType::eBytecode; +} + +bool ScriptLoadRequest::ShouldAcceptBinASTEncoding() const { + MOZ_CRASH("BinAST not supported"); +} + +void ScriptLoadRequest::ClearScriptSource() { + if (IsTextSource()) { + ClearScriptText(); + } else if (IsBinASTSource()) { + ScriptBinASTData().clearAndFree(); + } +} + +void ScriptLoadRequest::SetScript(JSScript* aScript) { + MOZ_ASSERT(!mScript); + mScript = aScript; + HoldJSObjects(this); +} + +// static +void ScriptLoadRequest::PrioritizeAsPreload(nsIChannel* aChannel) { + if (nsCOMPtr<nsIClassOfService> cos = do_QueryInterface(aChannel)) { + cos->AddClassFlags(nsIClassOfService::Unblocked); + } + if (nsCOMPtr<nsISupportsPriority> sp = do_QueryInterface(aChannel)) { + sp->AdjustPriority(nsISupportsPriority::PRIORITY_HIGHEST); + } +} + +void ScriptLoadRequest::PrioritizeAsPreload() { + if (!IsLinkPreloadScript()) { + // Do the prioritization only if this request has not already been created + // as a preload. + PrioritizeAsPreload(Channel()); + } +} + +nsIScriptElement* ScriptLoadRequest::GetScriptElement() const { + nsCOMPtr<nsIScriptElement> scriptElement = + do_QueryInterface(mFetchOptions->mElement); + return scriptElement; +} + +void ScriptLoadRequest::SetIsLoadRequest(nsIScriptElement* aElement) { + MOZ_ASSERT(aElement); + MOZ_ASSERT(!GetScriptElement()); + MOZ_ASSERT(IsPreload()); + mFetchOptions->mElement = do_QueryInterface(aElement); + mFetchOptions->mIsPreload = false; +} + +////////////////////////////////////////////////////////////// +// ScriptLoadRequestList +////////////////////////////////////////////////////////////// + +ScriptLoadRequestList::~ScriptLoadRequestList() { Clear(); } + +void ScriptLoadRequestList::Clear() { + while (!isEmpty()) { + RefPtr<ScriptLoadRequest> 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<ScriptLoadRequest> 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); + } +} + +} // namespace dom +} // namespace mozilla |