summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--dom/base/nsPlainTextSerializer.h1
-rw-r--r--dom/script/ModuleLoadRequest.cpp102
-rw-r--r--dom/script/ModuleLoadRequest.h81
-rw-r--r--dom/script/ModuleScript.cpp100
-rw-r--r--dom/script/ModuleScript.h67
-rw-r--r--dom/script/ScriptLoadHandler.cpp216
-rw-r--r--dom/script/ScriptLoadHandler.h76
-rw-r--r--dom/script/ScriptLoader.cpp486
-rw-r--r--dom/script/ScriptLoader.h53
-rw-r--r--dom/script/moz.build3
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',
]