diff options
author | Moonchild <moonchild@palemoon.org> | 2020-07-01 10:15:24 +0000 |
---|---|---|
committer | Moonchild <moonchild@palemoon.org> | 2020-07-01 10:15:24 +0000 |
commit | 9056191e67055c5e96ecc352e592ea9ae94a2236 (patch) | |
tree | 29ef38e1656883879b306f750dfec5a3281f5c94 /dom/script | |
parent | c45b7ee3a985b2b4862fb182cdb34f644f737048 (diff) | |
download | UXP-9056191e67055c5e96ecc352e592ea9ae94a2236.tar UXP-9056191e67055c5e96ecc352e592ea9ae94a2236.tar.gz UXP-9056191e67055c5e96ecc352e592ea9ae94a2236.tar.lz UXP-9056191e67055c5e96ecc352e592ea9ae94a2236.tar.xz UXP-9056191e67055c5e96ecc352e592ea9ae94a2236.zip |
Issue #1603 - Part 2: Split some classes out of ScriptLoader.cpp
This splits ScriptLoader up the same way Mozilla did with the exception of
ScriptRequest due to the fact that ScriptLoader and ScriptRequest are
interdependent and would create a circular dependency if split apart when not
using unified building.
Diffstat (limited to 'dom/script')
-rw-r--r-- | dom/script/ModuleLoadRequest.cpp | 102 | ||||
-rw-r--r-- | dom/script/ModuleLoadRequest.h | 81 | ||||
-rw-r--r-- | dom/script/ModuleScript.cpp | 100 | ||||
-rw-r--r-- | dom/script/ModuleScript.h | 67 | ||||
-rw-r--r-- | dom/script/ScriptLoadHandler.cpp | 216 | ||||
-rw-r--r-- | dom/script/ScriptLoadHandler.h | 76 | ||||
-rw-r--r-- | dom/script/ScriptLoader.cpp | 486 | ||||
-rw-r--r-- | dom/script/ScriptLoader.h | 53 | ||||
-rw-r--r-- | dom/script/moz.build | 3 |
9 files changed, 650 insertions, 534 deletions
diff --git a/dom/script/ModuleLoadRequest.cpp b/dom/script/ModuleLoadRequest.cpp new file mode 100644 index 000000000..e72edca2e --- /dev/null +++ b/dom/script/ModuleLoadRequest.cpp @@ -0,0 +1,102 @@ +/* -*- 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 "ModuleLoadRequest.h" +#include "ModuleScript.h" +#include "ScriptLoader.h" + +namespace mozilla { +namespace dom { + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ModuleLoadRequest) +NS_INTERFACE_MAP_END_INHERITING(ScriptLoadRequest) + +NS_IMPL_CYCLE_COLLECTION_INHERITED(ModuleLoadRequest, ScriptLoadRequest, + mBaseURL, + mLoader, + mParent, + mModuleScript, + mImports) + +NS_IMPL_ADDREF_INHERITED(ModuleLoadRequest, ScriptLoadRequest) +NS_IMPL_RELEASE_INHERITED(ModuleLoadRequest, ScriptLoadRequest) + +ModuleLoadRequest::ModuleLoadRequest(nsIScriptElement* aElement, + uint32_t aVersion, + CORSMode aCORSMode, + const SRIMetadata &aIntegrity, + ScriptLoader* aLoader) + : ScriptLoadRequest(ScriptKind::Module, + aElement, + aVersion, + aCORSMode, + aIntegrity), + mIsTopLevel(true), + mLoader(aLoader) +{} + +void ModuleLoadRequest::Cancel() +{ + ScriptLoadRequest::Cancel(); + mModuleScript = nullptr; + mProgress = ScriptLoadRequest::Progress::Ready; + for (size_t i = 0; i < mImports.Length(); i++) { + mImports[i]->Cancel(); + } + mReady.RejectIfExists(NS_ERROR_FAILURE, __func__); +} + +void +ModuleLoadRequest::SetReady() +{ +#ifdef DEBUG + for (size_t i = 0; i < mImports.Length(); i++) { + MOZ_ASSERT(mImports[i]->IsReadyToRun()); + } +#endif + + ScriptLoadRequest::SetReady(); + mReady.ResolveIfExists(true, __func__); +} + +void +ModuleLoadRequest::ModuleLoaded() +{ + // A module that was found to be marked as fetching in the module map has now + // been loaded. + + mModuleScript = mLoader->GetFetchedModule(mURI); + mLoader->StartFetchingModuleDependencies(this); +} + +void +ModuleLoadRequest::DependenciesLoaded() +{ + // The module and all of its dependencies have been successfully fetched and + // compiled. + + if (!mLoader->InstantiateModuleTree(this)) { + LoadFailed(); + return; + } + + SetReady(); + mLoader->ProcessLoadedModuleTree(this); + mLoader = nullptr; + mParent = nullptr; +} + +void +ModuleLoadRequest::LoadFailed() +{ + Cancel(); + mLoader->ProcessLoadedModuleTree(this); + mLoader = nullptr; + mParent = nullptr; +} + +} // dom namespace +} // mozilla namespace
\ No newline at end of file diff --git a/dom/script/ModuleLoadRequest.h b/dom/script/ModuleLoadRequest.h new file mode 100644 index 000000000..0119fad38 --- /dev/null +++ b/dom/script/ModuleLoadRequest.h @@ -0,0 +1,81 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_ModuleLoadRequest_h +#define mozilla_dom_ModuleLoadRequest_h + +#include "mozilla/dom/ScriptLoader.h" +#include "mozilla/MozPromise.h" + +namespace mozilla { +namespace dom { + +class ModuleScript; +class ScriptLoader; + +// A load request for a module, created for every top level module script and +// every module import. Load request can share a ModuleScript if there are +// multiple imports of the same module. + +class ModuleLoadRequest final : public ScriptLoadRequest +{ + ~ModuleLoadRequest() {} + + ModuleLoadRequest(const ModuleLoadRequest& aOther) = delete; + ModuleLoadRequest(ModuleLoadRequest&& aOther) = delete; + +public: + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ModuleLoadRequest, ScriptLoadRequest) + + ModuleLoadRequest(nsIScriptElement* aElement, + uint32_t aVersion, + CORSMode aCORSMode, + const SRIMetadata& aIntegrity, + ScriptLoader* aLoader); + + bool IsTopLevel() const { + return mIsTopLevel; + } + + void SetReady() override; + void Cancel() override; + + void ModuleLoaded(); + void DependenciesLoaded(); + void LoadFailed(); + + // Is this a request for a top level module script or an import? + bool mIsTopLevel; + + // The base URL used for resolving relative module imports. + nsCOMPtr<nsIURI> mBaseURL; + + // Pointer to the script loader, used to trigger actions when the module load + // finishes. + RefPtr<ScriptLoader> mLoader; + + // The importing module, or nullptr for top level module scripts. Used to + // implement the ancestor list checked when fetching module dependencies. + RefPtr<ModuleLoadRequest> mParent; + + // Set to a module script object after a successful load or nullptr on + // failure. + RefPtr<ModuleScript> mModuleScript; + + // A promise that is completed on successful load of this module and all of + // its dependencies, indicating that the module is ready for instantiation and + // evaluation. + MozPromiseHolder<GenericPromise> mReady; + + // Array of imported modules. + nsTArray<RefPtr<ModuleLoadRequest>> mImports; +}; + +} // dom namespace +} // mozilla namespace + +#endif // mozilla_dom_ModuleLoadRequest_h
\ No newline at end of file diff --git a/dom/script/ModuleScript.cpp b/dom/script/ModuleScript.cpp new file mode 100644 index 000000000..34ef4dec4 --- /dev/null +++ b/dom/script/ModuleScript.cpp @@ -0,0 +1,100 @@ +/* -*- 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/. */ + +/* + * A class that handles loading and evaluation of <script> elements. + */ + +#include "ModuleScript.h" +#include "mozilla/HoldDropJSObjects.h" +#include "ScriptLoader.h" + +namespace mozilla { +namespace dom { + +// A single module script. May be used to satisfy multiple load requests. + +NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ModuleScript) +NS_INTERFACE_MAP_END + +NS_IMPL_CYCLE_COLLECTION_CLASS(ModuleScript) + +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ModuleScript) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mLoader) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mBaseURL) + tmp->UnlinkModuleRecord(); +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ModuleScript) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLoader) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(ModuleScript) + NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mModuleRecord) + NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mException) +NS_IMPL_CYCLE_COLLECTION_TRACE_END + +NS_IMPL_CYCLE_COLLECTING_ADDREF(ModuleScript) +NS_IMPL_CYCLE_COLLECTING_RELEASE(ModuleScript) + +ModuleScript::ModuleScript(ScriptLoader *aLoader, nsIURI* aBaseURL, + JS::Handle<JSObject*> aModuleRecord) + : mLoader(aLoader), + mBaseURL(aBaseURL), + mModuleRecord(aModuleRecord), + mInstantiationState(Uninstantiated) +{ + MOZ_ASSERT(mLoader); + MOZ_ASSERT(mBaseURL); + MOZ_ASSERT(mModuleRecord); + MOZ_ASSERT(mException.isUndefined()); + + // Make module's host defined field point to this module script object. + // This is cleared in the UnlinkModuleRecord(). + JS::SetModuleHostDefinedField(mModuleRecord, JS::PrivateValue(this)); + HoldJSObjects(this); +} + +void +ModuleScript::UnlinkModuleRecord() +{ + // Remove module's back reference to this object request if present. + if (mModuleRecord) { + MOZ_ASSERT(JS::GetModuleHostDefinedField(mModuleRecord).toPrivate() == + this); + JS::SetModuleHostDefinedField(mModuleRecord, JS::UndefinedValue()); + } + mModuleRecord = nullptr; + mException.setUndefined(); +} + +ModuleScript::~ModuleScript() +{ + if (mModuleRecord) { + // The object may be destroyed without being unlinked first. + UnlinkModuleRecord(); + } + DropJSObjects(this); +} + +void +ModuleScript::SetInstantiationResult(JS::Handle<JS::Value> aMaybeException) +{ + MOZ_ASSERT(mInstantiationState == Uninstantiated); + MOZ_ASSERT(mModuleRecord); + MOZ_ASSERT(mException.isUndefined()); + + if (aMaybeException.isUndefined()) { + mInstantiationState = Instantiated; + } else { + mModuleRecord = nullptr; + mException = aMaybeException; + mInstantiationState = Errored; + } +} + +} // dom namespace +} // mozilla namespace diff --git a/dom/script/ModuleScript.h b/dom/script/ModuleScript.h new file mode 100644 index 000000000..dd0d07e84 --- /dev/null +++ b/dom/script/ModuleScript.h @@ -0,0 +1,67 @@ +/* -*- 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/. */ + +#ifndef mozilla_dom_ModuleScript_h +#define mozilla_dom_ModuleScript_h + +#include "nsCOMPtr.h" +#include "nsCycleCollectionParticipant.h" +#include "jsapi.h" + +class nsIURI; + +namespace mozilla { +namespace dom { + +class ScriptLoader; + +class ModuleScript final : public nsISupports +{ + enum InstantiationState { + Uninstantiated, + Instantiated, + Errored + }; + + RefPtr<ScriptLoader> mLoader; + nsCOMPtr<nsIURI> mBaseURL; + JS::Heap<JSObject*> mModuleRecord; + JS::Heap<JS::Value> mException; + InstantiationState mInstantiationState; + + ~ModuleScript(); + +public: + NS_DECL_CYCLE_COLLECTING_ISUPPORTS + NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ModuleScript) + + ModuleScript(ScriptLoader* aLoader, + nsIURI* aBaseURL, + JS::Handle<JSObject*> aModuleRecord); + + ScriptLoader* Loader() const { return mLoader; } + JSObject* ModuleRecord() const { return mModuleRecord; } + JS::Value Exception() const { return mException; } + nsIURI* BaseURL() const { return mBaseURL; } + + void SetInstantiationResult(JS::Handle<JS::Value> aMaybeException); + bool IsUninstantiated() const { + return mInstantiationState == Uninstantiated; + } + bool IsInstantiated() const { + return mInstantiationState == Instantiated; + } + bool InstantiationFailed() const { + return mInstantiationState == Errored; + } + + void UnlinkModuleRecord(); +}; + +} // dom namespace +} // mozilla namespace + +#endif // mozilla_dom_ModuleScript_h
\ No newline at end of file diff --git a/dom/script/ScriptLoadHandler.cpp b/dom/script/ScriptLoadHandler.cpp new file mode 100644 index 000000000..80fb70f6a --- /dev/null +++ b/dom/script/ScriptLoadHandler.cpp @@ -0,0 +1,216 @@ +/* -*- 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/. */ + +/* + * A class that handles loading and evaluation of <script> elements. + */ + +#include "ScriptLoadHandler.h" +#include "ScriptLoader.h" +#include "nsContentUtils.h" + +#include "mozilla/dom/EncodingUtils.h" + +namespace mozilla { +namespace dom { + +ScriptLoadHandler::ScriptLoadHandler(ScriptLoader *aScriptLoader, + ScriptLoadRequest *aRequest, + mozilla::dom::SRICheckDataVerifier *aSRIDataVerifier) + : mScriptLoader(aScriptLoader), + mRequest(aRequest), + mSRIDataVerifier(aSRIDataVerifier), + mSRIStatus(NS_OK), + mDecoder(), + mBuffer() +{} + +ScriptLoadHandler::~ScriptLoadHandler() +{} + +NS_IMPL_ISUPPORTS(ScriptLoadHandler, nsIIncrementalStreamLoaderObserver) + +NS_IMETHODIMP +ScriptLoadHandler::OnIncrementalData(nsIIncrementalStreamLoader* aLoader, + nsISupports* aContext, + uint32_t aDataLength, + const uint8_t* aData, + uint32_t *aConsumedLength) +{ + if (mRequest->IsCanceled()) { + // If request cancelled, ignore any incoming data. + *aConsumedLength = aDataLength; + return NS_OK; + } + + if (!EnsureDecoder(aLoader, aData, aDataLength, + /* aEndOfStream = */ false)) { + return NS_OK; + } + + // Below we will/shall consume entire data chunk. + *aConsumedLength = aDataLength; + + // Decoder has already been initialized. -- trying to decode all loaded bytes. + nsresult rv = TryDecodeRawData(aData, aDataLength, + /* aEndOfStream = */ false); + NS_ENSURE_SUCCESS(rv, rv); + + // If SRI is required for this load, appending new bytes to the hash. + if (mSRIDataVerifier && NS_SUCCEEDED(mSRIStatus)) { + mSRIStatus = mSRIDataVerifier->Update(aDataLength, aData); + } + + return rv; +} + +nsresult +ScriptLoadHandler::TryDecodeRawData(const uint8_t* aData, + uint32_t aDataLength, + bool aEndOfStream) +{ + int32_t srcLen = aDataLength; + const char* src = reinterpret_cast<const char *>(aData); + int32_t dstLen; + nsresult rv = + mDecoder->GetMaxLength(src, srcLen, &dstLen); + + NS_ENSURE_SUCCESS(rv, rv); + + uint32_t haveRead = mBuffer.length(); + + CheckedInt<uint32_t> capacity = haveRead; + capacity += dstLen; + + if (!capacity.isValid() || !mBuffer.reserve(capacity.value())) { + return NS_ERROR_OUT_OF_MEMORY; + } + + rv = mDecoder->Convert(src, + &srcLen, + mBuffer.begin() + haveRead, + &dstLen); + + NS_ENSURE_SUCCESS(rv, rv); + + haveRead += dstLen; + MOZ_ASSERT(haveRead <= capacity.value(), "mDecoder produced more data than expected"); + MOZ_ALWAYS_TRUE(mBuffer.resizeUninitialized(haveRead)); + + return NS_OK; +} + +bool +ScriptLoadHandler::EnsureDecoder(nsIIncrementalStreamLoader *aLoader, + const uint8_t* aData, + uint32_t aDataLength, + bool aEndOfStream) +{ + // Check if decoder has already been created. + if (mDecoder) { + return true; + } + + nsAutoCString charset; + + // JavaScript modules are always UTF-8. + if (mRequest->IsModuleRequest()) { + charset = "UTF-8"; + mDecoder = EncodingUtils::DecoderForEncoding(charset); + return true; + } + + // Determine if BOM check should be done. This occurs either + // if end-of-stream has been reached, or at least 3 bytes have + // been read from input. + if (!aEndOfStream && (aDataLength < 3)) { + return false; + } + + // Do BOM detection. + if (nsContentUtils::CheckForBOM(aData, aDataLength, charset)) { + mDecoder = EncodingUtils::DecoderForEncoding(charset); + return true; + } + + // BOM detection failed, check content stream for charset. + nsCOMPtr<nsIRequest> req; + nsresult rv = aLoader->GetRequest(getter_AddRefs(req)); + NS_ASSERTION(req, "StreamLoader's request went away prematurely"); + NS_ENSURE_SUCCESS(rv, false); + + nsCOMPtr<nsIChannel> channel = do_QueryInterface(req); + + if (channel && + NS_SUCCEEDED(channel->GetContentCharset(charset)) && + EncodingUtils::FindEncodingForLabel(charset, charset)) { + mDecoder = EncodingUtils::DecoderForEncoding(charset); + return true; + } + + // Check the hint charset from the script element or preload + // request. + nsAutoString hintCharset; + if (!mRequest->IsPreload()) { + mRequest->mElement->GetScriptCharset(hintCharset); + } else { + nsTArray<ScriptLoader::PreloadInfo>::index_type i = + mScriptLoader->mPreloads.IndexOf(mRequest, 0, + ScriptLoader::PreloadRequestComparator()); + + NS_ASSERTION(i != mScriptLoader->mPreloads.NoIndex, + "Incorrect preload bookkeeping"); + hintCharset = mScriptLoader->mPreloads[i].mCharset; + } + + if (EncodingUtils::FindEncodingForLabel(hintCharset, charset)) { + mDecoder = EncodingUtils::DecoderForEncoding(charset); + return true; + } + + // Get the charset from the charset of the document. + if (mScriptLoader->mDocument) { + charset = mScriptLoader->mDocument->GetDocumentCharacterSet(); + mDecoder = EncodingUtils::DecoderForEncoding(charset); + return true; + } + + // Curiously, there are various callers that don't pass aDocument. The + // fallback in the old code was ISO-8859-1, which behaved like + // windows-1252. Saying windows-1252 for clarity and for compliance + // with the Encoding Standard. + charset = "windows-1252"; + mDecoder = EncodingUtils::DecoderForEncoding(charset); + return true; +} + +NS_IMETHODIMP +ScriptLoadHandler::OnStreamComplete(nsIIncrementalStreamLoader* aLoader, + nsISupports* aContext, + nsresult aStatus, + uint32_t aDataLength, + const uint8_t* aData) +{ + if (!mRequest->IsCanceled()) { + DebugOnly<bool> encoderSet = + EnsureDecoder(aLoader, aData, aDataLength, /* aEndOfStream = */ true); + MOZ_ASSERT(encoderSet); + DebugOnly<nsresult> rv = TryDecodeRawData(aData, aDataLength, + /* aEndOfStream = */ true); + + // If SRI is required for this load, appending new bytes to the hash. + if (mSRIDataVerifier && NS_SUCCEEDED(mSRIStatus)) { + mSRIStatus = mSRIDataVerifier->Update(aDataLength, aData); + } + } + + // we have to mediate and use mRequest. + return mScriptLoader->OnStreamComplete(aLoader, mRequest, aStatus, mSRIStatus, + mBuffer, mSRIDataVerifier); +} + +} // dom namespace +} // mozilla namespace
\ No newline at end of file diff --git a/dom/script/ScriptLoadHandler.h b/dom/script/ScriptLoadHandler.h new file mode 100644 index 000000000..b70f87397 --- /dev/null +++ b/dom/script/ScriptLoadHandler.h @@ -0,0 +1,76 @@ +/* -*- 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/. */ + +/* + * A class that handles loading and evaluation of <script> elements. + */ + +#ifndef mozilla_dom_ScriptLoadHandler_h +#define mozilla_dom_ScriptLoadHandler_h + +#include "nsIIncrementalStreamLoader.h" +#include "nsIUnicodeDecoder.h" +#include "nsAutoPtr.h" +#include "mozilla/Vector.h" + +namespace mozilla { +namespace dom { + +class ScriptLoadRequest; +class ScriptLoader; +class SRICheckDataVerifier; + +class ScriptLoadHandler final : public nsIIncrementalStreamLoaderObserver +{ +public: + explicit ScriptLoadHandler(ScriptLoader* aScriptLoader, + ScriptLoadRequest* aRequest, + SRICheckDataVerifier* aSRIDataVerifier); + + NS_DECL_ISUPPORTS + NS_DECL_NSIINCREMENTALSTREAMLOADEROBSERVER + +private: + virtual ~ScriptLoadHandler(); + + /* + * Try to decode some raw data. + */ + nsresult TryDecodeRawData(const uint8_t* aData, uint32_t aDataLength, + bool aEndOfStream); + + /* + * Discover the charset by looking at the stream data, the script + * tag, and other indicators. Returns true if charset has been + * discovered. + */ + bool EnsureDecoder(nsIIncrementalStreamLoader *aLoader, + const uint8_t* aData, uint32_t aDataLength, + bool aEndOfStream); + + // ScriptLoader which will handle the parsed script. + RefPtr<ScriptLoader> mScriptLoader; + + // The ScriptLoadRequest for this load. + RefPtr<ScriptLoadRequest> mRequest; + + // SRI data verifier. + nsAutoPtr<SRICheckDataVerifier> mSRIDataVerifier; + + // Status of SRI data operations. + nsresult mSRIStatus; + + // Unicode decoder for charset. + nsCOMPtr<nsIUnicodeDecoder> mDecoder; + + // Accumulated decoded char buffer. + mozilla::Vector<char16_t> mBuffer; +}; + +} // namespace dom +} // namespace mozilla + +#endif //mozilla_dom_ScriptLoader_h diff --git a/dom/script/ScriptLoader.cpp b/dom/script/ScriptLoader.cpp index b5c8053e8..adc046b7c 100644 --- a/dom/script/ScriptLoader.cpp +++ b/dom/script/ScriptLoader.cpp @@ -4,11 +4,10 @@ * 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/. */ -/* - * A class that handles loading and evaluation of <script> elements. - */ - #include "ScriptLoader.h" +#include "ScriptLoadHandler.h" +#include "ModuleLoadRequest.h" +#include "ModuleScript.h" #include "prsystem.h" #include "jsapi.h" @@ -134,96 +133,6 @@ ScriptLoadRequest::MaybeCancelOffThreadScript() mOffThreadToken = nullptr; } -////////////////////////////////////////////////////////////// -// ModuleLoadRequest -////////////////////////////////////////////////////////////// - -// A load request for a module, created for every top level module script and -// every module import. Load request can share a ModuleScript if there are -// multiple imports of the same module. - -class ModuleLoadRequest final : public ScriptLoadRequest -{ - ~ModuleLoadRequest() {} - - ModuleLoadRequest(const ModuleLoadRequest& aOther) = delete; - ModuleLoadRequest(ModuleLoadRequest&& aOther) = delete; - -public: - NS_DECL_ISUPPORTS_INHERITED - NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ModuleLoadRequest, ScriptLoadRequest) - - ModuleLoadRequest(nsIScriptElement* aElement, - uint32_t aVersion, - CORSMode aCORSMode, - const SRIMetadata& aIntegrity, - ScriptLoader* aLoader); - - bool IsTopLevel() const { - return mIsTopLevel; - } - - void SetReady() override; - void Cancel() override; - - void ModuleLoaded(); - void DependenciesLoaded(); - void LoadFailed(); - - // Is this a request for a top level module script or an import? - bool mIsTopLevel; - - // The base URL used for resolving relative module imports. - nsCOMPtr<nsIURI> mBaseURL; - - // Pointer to the script loader, used to trigger actions when the module load - // finishes. - RefPtr<ScriptLoader> mLoader; - - // The importing module, or nullptr for top level module scripts. Used to - // implement the ancestor list checked when fetching module dependencies. - RefPtr<ModuleLoadRequest> mParent; - - // Set to a module script object after a successful load or nullptr on - // failure. - RefPtr<ModuleScript> mModuleScript; - - // A promise that is completed on successful load of this module and all of - // its dependencies, indicating that the module is ready for instantiation and - // evaluation. - MozPromiseHolder<GenericPromise> mReady; - - // Array of imported modules. - nsTArray<RefPtr<ModuleLoadRequest>> mImports; -}; - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ModuleLoadRequest) -NS_INTERFACE_MAP_END_INHERITING(ScriptLoadRequest) - -NS_IMPL_CYCLE_COLLECTION_INHERITED(ModuleLoadRequest, ScriptLoadRequest, - mBaseURL, - mLoader, - mParent, - mModuleScript, - mImports) - -NS_IMPL_ADDREF_INHERITED(ModuleLoadRequest, ScriptLoadRequest) -NS_IMPL_RELEASE_INHERITED(ModuleLoadRequest, ScriptLoadRequest) - -ModuleLoadRequest::ModuleLoadRequest(nsIScriptElement* aElement, - uint32_t aVersion, - CORSMode aCORSMode, - const SRIMetadata &aIntegrity, - ScriptLoader* aLoader) - : ScriptLoadRequest(ScriptKind::Module, - aElement, - aVersion, - aCORSMode, - aIntegrity), - mIsTopLevel(true), - mLoader(aLoader) -{} - inline ModuleLoadRequest* ScriptLoadRequest::AsModuleRequest() { @@ -231,194 +140,6 @@ ScriptLoadRequest::AsModuleRequest() return static_cast<ModuleLoadRequest*>(this); } -void ModuleLoadRequest::Cancel() -{ - ScriptLoadRequest::Cancel(); - mModuleScript = nullptr; - mProgress = ScriptLoadRequest::Progress::Ready; - for (size_t i = 0; i < mImports.Length(); i++) { - mImports[i]->Cancel(); - } - mReady.RejectIfExists(NS_ERROR_FAILURE, __func__); -} - -void -ModuleLoadRequest::SetReady() -{ -#ifdef DEBUG - for (size_t i = 0; i < mImports.Length(); i++) { - MOZ_ASSERT(mImports[i]->IsReadyToRun()); - } -#endif - - ScriptLoadRequest::SetReady(); - mReady.ResolveIfExists(true, __func__); -} - -void -ModuleLoadRequest::ModuleLoaded() -{ - // A module that was found to be marked as fetching in the module map has now - // been loaded. - - mModuleScript = mLoader->GetFetchedModule(mURI); - mLoader->StartFetchingModuleDependencies(this); -} - -void -ModuleLoadRequest::DependenciesLoaded() -{ - // The module and all of its dependencies have been successfully fetched and - // compiled. - - if (!mLoader->InstantiateModuleTree(this)) { - LoadFailed(); - return; - } - - SetReady(); - mLoader->ProcessLoadedModuleTree(this); - mLoader = nullptr; - mParent = nullptr; -} - -void -ModuleLoadRequest::LoadFailed() -{ - Cancel(); - mLoader->ProcessLoadedModuleTree(this); - mLoader = nullptr; - mParent = nullptr; -} - -////////////////////////////////////////////////////////////// -// ModuleScript -////////////////////////////////////////////////////////////// - -// A single module script. May be used to satisfy multiple load requests. - -class ModuleScript final : public nsISupports -{ - enum InstantiationState { - Uninstantiated, - Instantiated, - Errored - }; - - RefPtr<ScriptLoader> mLoader; - nsCOMPtr<nsIURI> mBaseURL; - JS::Heap<JSObject*> mModuleRecord; - JS::Heap<JS::Value> mException; - InstantiationState mInstantiationState; - - ~ModuleScript(); - -public: - NS_DECL_CYCLE_COLLECTING_ISUPPORTS - NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ModuleScript) - - ModuleScript(ScriptLoader* aLoader, - nsIURI* aBaseURL, - JS::Handle<JSObject*> aModuleRecord); - - ScriptLoader* Loader() const { return mLoader; } - JSObject* ModuleRecord() const { return mModuleRecord; } - JS::Value Exception() const { return mException; } - nsIURI* BaseURL() const { return mBaseURL; } - - void SetInstantiationResult(JS::Handle<JS::Value> aMaybeException); - bool IsUninstantiated() const { - return mInstantiationState == Uninstantiated; - } - bool IsInstantiated() const { - return mInstantiationState == Instantiated; - } - bool InstantiationFailed() const { - return mInstantiationState == Errored; - } - - void UnlinkModuleRecord(); -}; - -NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ModuleScript) -NS_INTERFACE_MAP_END - -NS_IMPL_CYCLE_COLLECTION_CLASS(ModuleScript) - -NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ModuleScript) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mLoader) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mBaseURL) - tmp->UnlinkModuleRecord(); -NS_IMPL_CYCLE_COLLECTION_UNLINK_END - -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ModuleScript) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLoader) -NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END - -NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(ModuleScript) - NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mModuleRecord) - NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mException) -NS_IMPL_CYCLE_COLLECTION_TRACE_END - -NS_IMPL_CYCLE_COLLECTING_ADDREF(ModuleScript) -NS_IMPL_CYCLE_COLLECTING_RELEASE(ModuleScript) - -ModuleScript::ModuleScript(ScriptLoader *aLoader, nsIURI* aBaseURL, - JS::Handle<JSObject*> aModuleRecord) - : mLoader(aLoader), - mBaseURL(aBaseURL), - mModuleRecord(aModuleRecord), - mInstantiationState(Uninstantiated) -{ - MOZ_ASSERT(mLoader); - MOZ_ASSERT(mBaseURL); - MOZ_ASSERT(mModuleRecord); - MOZ_ASSERT(mException.isUndefined()); - - // Make module's host defined field point to this module script object. - // This is cleared in the UnlinkModuleRecord(). - JS::SetModuleHostDefinedField(mModuleRecord, JS::PrivateValue(this)); - HoldJSObjects(this); -} - -void -ModuleScript::UnlinkModuleRecord() -{ - // Remove module's back reference to this object request if present. - if (mModuleRecord) { - MOZ_ASSERT(JS::GetModuleHostDefinedField(mModuleRecord).toPrivate() == - this); - JS::SetModuleHostDefinedField(mModuleRecord, JS::UndefinedValue()); - } - mModuleRecord = nullptr; - mException.setUndefined(); -} - -ModuleScript::~ModuleScript() -{ - if (mModuleRecord) { - // The object may be destroyed without being unlinked first. - UnlinkModuleRecord(); - } - DropJSObjects(this); -} - -void -ModuleScript::SetInstantiationResult(JS::Handle<JS::Value> aMaybeException) -{ - MOZ_ASSERT(mInstantiationState == Uninstantiated); - MOZ_ASSERT(mModuleRecord); - MOZ_ASSERT(mException.isUndefined()); - - if (aMaybeException.isUndefined()) { - mInstantiationState = Instantiated; - } else { - mModuleRecord = nullptr; - mException = aMaybeException; - mInstantiationState = Errored; - } -} - ////////////////////////////////////////////////////////////// // ScriptLoadRequestList @@ -2860,204 +2581,5 @@ ScriptLoader::MaybeRemovedDeferRequests() return false; } -////////////////////////////////////////////////////////////// -// ScriptLoadHandler -////////////////////////////////////////////////////////////// - -ScriptLoadHandler::ScriptLoadHandler(ScriptLoader *aScriptLoader, - ScriptLoadRequest *aRequest, - mozilla::dom::SRICheckDataVerifier *aSRIDataVerifier) - : mScriptLoader(aScriptLoader), - mRequest(aRequest), - mSRIDataVerifier(aSRIDataVerifier), - mSRIStatus(NS_OK), - mDecoder(), - mBuffer() -{} - -ScriptLoadHandler::~ScriptLoadHandler() -{} - -NS_IMPL_ISUPPORTS(ScriptLoadHandler, nsIIncrementalStreamLoaderObserver) - -NS_IMETHODIMP -ScriptLoadHandler::OnIncrementalData(nsIIncrementalStreamLoader* aLoader, - nsISupports* aContext, - uint32_t aDataLength, - const uint8_t* aData, - uint32_t *aConsumedLength) -{ - if (mRequest->IsCanceled()) { - // If request cancelled, ignore any incoming data. - *aConsumedLength = aDataLength; - return NS_OK; - } - - if (!EnsureDecoder(aLoader, aData, aDataLength, - /* aEndOfStream = */ false)) { - return NS_OK; - } - - // Below we will/shall consume entire data chunk. - *aConsumedLength = aDataLength; - - // Decoder has already been initialized. -- trying to decode all loaded bytes. - nsresult rv = TryDecodeRawData(aData, aDataLength, - /* aEndOfStream = */ false); - NS_ENSURE_SUCCESS(rv, rv); - - // If SRI is required for this load, appending new bytes to the hash. - if (mSRIDataVerifier && NS_SUCCEEDED(mSRIStatus)) { - mSRIStatus = mSRIDataVerifier->Update(aDataLength, aData); - } - - return rv; -} - -nsresult -ScriptLoadHandler::TryDecodeRawData(const uint8_t* aData, - uint32_t aDataLength, - bool aEndOfStream) -{ - int32_t srcLen = aDataLength; - const char* src = reinterpret_cast<const char *>(aData); - int32_t dstLen; - nsresult rv = - mDecoder->GetMaxLength(src, srcLen, &dstLen); - - NS_ENSURE_SUCCESS(rv, rv); - - uint32_t haveRead = mBuffer.length(); - - CheckedInt<uint32_t> capacity = haveRead; - capacity += dstLen; - - if (!capacity.isValid() || !mBuffer.reserve(capacity.value())) { - return NS_ERROR_OUT_OF_MEMORY; - } - - rv = mDecoder->Convert(src, - &srcLen, - mBuffer.begin() + haveRead, - &dstLen); - - NS_ENSURE_SUCCESS(rv, rv); - - haveRead += dstLen; - MOZ_ASSERT(haveRead <= capacity.value(), "mDecoder produced more data than expected"); - MOZ_ALWAYS_TRUE(mBuffer.resizeUninitialized(haveRead)); - - return NS_OK; -} - -bool -ScriptLoadHandler::EnsureDecoder(nsIIncrementalStreamLoader *aLoader, - const uint8_t* aData, - uint32_t aDataLength, - bool aEndOfStream) -{ - // Check if decoder has already been created. - if (mDecoder) { - return true; - } - - nsAutoCString charset; - - // JavaScript modules are always UTF-8. - if (mRequest->IsModuleRequest()) { - charset = "UTF-8"; - mDecoder = EncodingUtils::DecoderForEncoding(charset); - return true; - } - - // Determine if BOM check should be done. This occurs either - // if end-of-stream has been reached, or at least 3 bytes have - // been read from input. - if (!aEndOfStream && (aDataLength < 3)) { - return false; - } - - // Do BOM detection. - if (nsContentUtils::CheckForBOM(aData, aDataLength, charset)) { - mDecoder = EncodingUtils::DecoderForEncoding(charset); - return true; - } - - // BOM detection failed, check content stream for charset. - nsCOMPtr<nsIRequest> req; - nsresult rv = aLoader->GetRequest(getter_AddRefs(req)); - NS_ASSERTION(req, "StreamLoader's request went away prematurely"); - NS_ENSURE_SUCCESS(rv, false); - - nsCOMPtr<nsIChannel> channel = do_QueryInterface(req); - - if (channel && - NS_SUCCEEDED(channel->GetContentCharset(charset)) && - EncodingUtils::FindEncodingForLabel(charset, charset)) { - mDecoder = EncodingUtils::DecoderForEncoding(charset); - return true; - } - - // Check the hint charset from the script element or preload - // request. - nsAutoString hintCharset; - if (!mRequest->IsPreload()) { - mRequest->mElement->GetScriptCharset(hintCharset); - } else { - nsTArray<ScriptLoader::PreloadInfo>::index_type i = - mScriptLoader->mPreloads.IndexOf(mRequest, 0, - ScriptLoader::PreloadRequestComparator()); - - NS_ASSERTION(i != mScriptLoader->mPreloads.NoIndex, - "Incorrect preload bookkeeping"); - hintCharset = mScriptLoader->mPreloads[i].mCharset; - } - - if (EncodingUtils::FindEncodingForLabel(hintCharset, charset)) { - mDecoder = EncodingUtils::DecoderForEncoding(charset); - return true; - } - - // Get the charset from the charset of the document. - if (mScriptLoader->mDocument) { - charset = mScriptLoader->mDocument->GetDocumentCharacterSet(); - mDecoder = EncodingUtils::DecoderForEncoding(charset); - return true; - } - - // Curiously, there are various callers that don't pass aDocument. The - // fallback in the old code was ISO-8859-1, which behaved like - // windows-1252. Saying windows-1252 for clarity and for compliance - // with the Encoding Standard. - charset = "windows-1252"; - mDecoder = EncodingUtils::DecoderForEncoding(charset); - return true; -} - -NS_IMETHODIMP -ScriptLoadHandler::OnStreamComplete(nsIIncrementalStreamLoader* aLoader, - nsISupports* aContext, - nsresult aStatus, - uint32_t aDataLength, - const uint8_t* aData) -{ - if (!mRequest->IsCanceled()) { - DebugOnly<bool> encoderSet = - EnsureDecoder(aLoader, aData, aDataLength, /* aEndOfStream = */ true); - MOZ_ASSERT(encoderSet); - DebugOnly<nsresult> rv = TryDecodeRawData(aData, aDataLength, - /* aEndOfStream = */ true); - - // If SRI is required for this load, appending new bytes to the hash. - if (mSRIDataVerifier && NS_SUCCEEDED(mSRIStatus)) { - mSRIStatus = mSRIDataVerifier->Update(aDataLength, aData); - } - } - - // we have to mediate and use mRequest. - return mScriptLoader->OnStreamComplete(aLoader, mRequest, aStatus, mSRIStatus, - mBuffer, mSRIDataVerifier); -} - } // dom namespace -} // mozilla namespace
\ No newline at end of file +} // mozilla namespace diff --git a/dom/script/ScriptLoader.h b/dom/script/ScriptLoader.h index 6fe76eca8..3c428deea 100644 --- a/dom/script/ScriptLoader.h +++ b/dom/script/ScriptLoader.h @@ -4,10 +4,6 @@ * 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/. */ -/* - * A class that handles loading and evaluation of <script> elements. - */ - #ifndef mozilla_dom_ScriptLoader_h #define mozilla_dom_ScriptLoader_h @@ -25,7 +21,6 @@ #include "mozilla/CORSMode.h" #include "mozilla/dom/SRIMetadata.h" #include "mozilla/dom/SRICheck.h" -#include "mozilla/LinkedList.h" #include "mozilla/MozPromise.h" #include "mozilla/net/ReferrerPolicy.h" #include "mozilla/Vector.h" @@ -221,6 +216,7 @@ public: NS_RELEASE(aElem); } }; +class ScriptLoadHandler; ////////////////////////////////////////////////////////////// // Script loader implementation @@ -643,53 +639,6 @@ private: nsCOMPtr<nsIConsoleReportCollector> mReporter; }; -class ScriptLoadHandler final : public nsIIncrementalStreamLoaderObserver -{ -public: - explicit ScriptLoadHandler(ScriptLoader* aScriptLoader, - ScriptLoadRequest *aRequest, - mozilla::dom::SRICheckDataVerifier *aSRIDataVerifier); - - NS_DECL_ISUPPORTS - NS_DECL_NSIINCREMENTALSTREAMLOADEROBSERVER - -private: - virtual ~ScriptLoadHandler(); - - /* - * Try to decode some raw data. - */ - nsresult TryDecodeRawData(const uint8_t* aData, uint32_t aDataLength, - bool aEndOfStream); - - /* - * Discover the charset by looking at the stream data, the script - * tag, and other indicators. Returns true if charset has been - * discovered. - */ - bool EnsureDecoder(nsIIncrementalStreamLoader *aLoader, - const uint8_t* aData, uint32_t aDataLength, - bool aEndOfStream); - - // ScriptLoader which will handle the parsed script. - RefPtr<ScriptLoader> mScriptLoader; - - // The ScriptLoadRequest for this load. - RefPtr<ScriptLoadRequest> mRequest; - - // SRI data verifier. - nsAutoPtr<mozilla::dom::SRICheckDataVerifier> mSRIDataVerifier; - - // Status of SRI data operations. - nsresult mSRIStatus; - - // Unicode decoder for charset. - nsCOMPtr<nsIUnicodeDecoder> mDecoder; - - // Accumulated decoded char buffer. - mozilla::Vector<char16_t> mBuffer; -}; - class nsAutoScriptLoaderDisabler { public: diff --git a/dom/script/moz.build b/dom/script/moz.build index 063d15426..280850ed4 100644 --- a/dom/script/moz.build +++ b/dom/script/moz.build @@ -18,8 +18,11 @@ EXPORTS.mozilla.dom += [ ] SOURCES += [ + 'ModuleLoadRequest.cpp', + 'ModuleScript.cpp', 'ScriptElement.cpp', 'ScriptLoader.cpp', + 'ScriptLoadHandler.cpp', 'ScriptSettings.cpp', ] |