diff options
-rw-r--r-- | dom/base/nsPlainTextSerializer.h | 1 | ||||
-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 |
10 files changed, 651 insertions, 534 deletions
diff --git a/dom/base/nsPlainTextSerializer.h b/dom/base/nsPlainTextSerializer.h index 650a8e3e7..58aeb4207 100644 --- a/dom/base/nsPlainTextSerializer.h +++ b/dom/base/nsPlainTextSerializer.h @@ -16,6 +16,7 @@ #include "mozilla/Attributes.h" #include "nsCOMPtr.h" #include "nsIAtom.h" +#include "nsCycleCollectionParticipant.h" #include "nsIContentSerializer.h" #include "nsIDocumentEncoder.h" #include "nsILineBreaker.h" 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', ] |