Bug 1885602 - Part 4: Implement navigating to the settings from the menu header for...
[gecko.git] / js / loader / LoadedScript.cpp
blobc84ed3ac557eb53a21c6296ef44d4a8afebde54e
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "LoadedScript.h"
9 #include "mozilla/HoldDropJSObjects.h"
10 #include "mozilla/UniquePtr.h" // mozilla::UniquePtr
12 #include "mozilla/dom/ScriptLoadContext.h" // ScriptLoadContext
13 #include "jsfriendapi.h"
14 #include "js/Modules.h" // JS::{Get,Set}ModulePrivate
15 #include "LoadContextBase.h" // LoadContextBase
17 namespace JS::loader {
19 //////////////////////////////////////////////////////////////
20 // LoadedScript
21 //////////////////////////////////////////////////////////////
23 MOZ_DEFINE_MALLOC_SIZE_OF(LoadedScriptMallocSizeOf)
25 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(LoadedScript)
26 NS_INTERFACE_MAP_ENTRY(nsISupports)
27 NS_INTERFACE_MAP_END
29 NS_IMPL_CYCLE_COLLECTION_CLASS(LoadedScript)
31 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(LoadedScript)
32 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFetchOptions)
33 NS_IMPL_CYCLE_COLLECTION_UNLINK(mBaseURL)
34 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
36 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(LoadedScript)
37 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFetchOptions)
38 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
40 NS_IMPL_CYCLE_COLLECTING_ADDREF(LoadedScript)
41 NS_IMPL_CYCLE_COLLECTING_RELEASE(LoadedScript)
43 LoadedScript::LoadedScript(ScriptKind aKind,
44 mozilla::dom::ReferrerPolicy aReferrerPolicy,
45 ScriptFetchOptions* aFetchOptions, nsIURI* aURI)
46 : mKind(aKind),
47 mReferrerPolicy(aReferrerPolicy),
48 mFetchOptions(aFetchOptions),
49 mURI(aURI),
50 mDataType(DataType::eUnknown),
51 mReceivedScriptTextLength(0),
52 mBytecodeOffset(0) {
53 MOZ_ASSERT(mFetchOptions);
54 MOZ_ASSERT(mURI);
57 LoadedScript::~LoadedScript() {
58 mozilla::UnregisterWeakMemoryReporter(this);
59 mozilla::DropJSObjects(this);
62 void LoadedScript::RegisterMemoryReport() {
63 mozilla::RegisterWeakMemoryReporter(this);
66 NS_IMETHODIMP
67 LoadedScript::CollectReports(nsIHandleReportCallback* aHandleReport,
68 nsISupports* aData, bool aAnonymize) {
69 #define COLLECT_REPORT(path, kind) \
70 MOZ_COLLECT_REPORT(path, KIND_HEAP, UNITS_BYTES, \
71 SizeOfIncludingThis(LoadedScriptMallocSizeOf), \
72 "Memory used for LoadedScript to hold on " kind \
73 " across documents")
75 switch (mKind) {
76 case ScriptKind::eClassic:
77 COLLECT_REPORT("explicit/js/script/loaded-script/classic", "scripts");
78 break;
79 case ScriptKind::eImportMap:
80 COLLECT_REPORT("explicit/js/script/loaded-script/import-map",
81 "import-maps");
82 break;
83 case ScriptKind::eModule:
84 COLLECT_REPORT("explicit/js/script/loaded-script/module", "modules");
85 break;
86 case ScriptKind::eEvent:
87 COLLECT_REPORT("explicit/js/script/loaded-script/event", "event scripts");
88 break;
91 #undef COLLECT_REPORT
92 return NS_OK;
95 size_t LoadedScript::SizeOfIncludingThis(
96 mozilla::MallocSizeOf aMallocSizeOf) const {
97 size_t bytes = aMallocSizeOf(this);
99 if (IsTextSource()) {
100 if (IsUTF16Text()) {
101 bytes += ScriptText<char16_t>().sizeOfExcludingThis(aMallocSizeOf);
102 } else {
103 bytes += ScriptText<Utf8Unit>().sizeOfExcludingThis(aMallocSizeOf);
107 bytes += mScriptBytecode.sizeOfExcludingThis(aMallocSizeOf);
108 return bytes;
111 void LoadedScript::AssociateWithScript(JSScript* aScript) {
112 // Verify that the rewritten URL is available when manipulating LoadedScript.
113 MOZ_ASSERT(mBaseURL);
115 // Set a JSScript's private value to point to this object. The JS engine will
116 // increment our reference count by calling HostAddRefTopLevelScript(). This
117 // is decremented by HostReleaseTopLevelScript() below when the JSScript dies.
119 MOZ_ASSERT(JS::GetScriptPrivate(aScript).isUndefined());
120 JS::SetScriptPrivate(aScript, JS::PrivateValue(this));
123 nsresult LoadedScript::GetScriptSource(JSContext* aCx,
124 MaybeSourceText* aMaybeSource,
125 LoadContextBase* aMaybeLoadContext) {
126 // If there's no script text, we try to get it from the element
127 bool isWindowContext =
128 aMaybeLoadContext && aMaybeLoadContext->IsWindowContext();
129 if (isWindowContext && aMaybeLoadContext->AsWindowContext()->mIsInline) {
130 nsAutoString inlineData;
131 auto* scriptLoadContext = aMaybeLoadContext->AsWindowContext();
132 scriptLoadContext->GetScriptElement()->GetScriptText(inlineData);
134 size_t nbytes = inlineData.Length() * sizeof(char16_t);
135 JS::UniqueTwoByteChars chars(
136 static_cast<char16_t*>(JS_malloc(aCx, nbytes)));
137 if (!chars) {
138 return NS_ERROR_OUT_OF_MEMORY;
141 memcpy(chars.get(), inlineData.get(), nbytes);
143 SourceText<char16_t> srcBuf;
144 if (!srcBuf.init(aCx, std::move(chars), inlineData.Length())) {
145 return NS_ERROR_OUT_OF_MEMORY;
148 aMaybeSource->construct<SourceText<char16_t>>(std::move(srcBuf));
149 return NS_OK;
152 size_t length = ScriptTextLength();
153 if (IsUTF16Text()) {
154 JS::UniqueTwoByteChars chars;
155 chars.reset(ScriptText<char16_t>().extractOrCopyRawBuffer());
156 if (!chars) {
157 JS_ReportOutOfMemory(aCx);
158 return NS_ERROR_OUT_OF_MEMORY;
161 SourceText<char16_t> srcBuf;
162 if (!srcBuf.init(aCx, std::move(chars), length)) {
163 return NS_ERROR_OUT_OF_MEMORY;
166 aMaybeSource->construct<SourceText<char16_t>>(std::move(srcBuf));
167 return NS_OK;
170 MOZ_ASSERT(IsUTF8Text());
171 mozilla::UniquePtr<Utf8Unit[], JS::FreePolicy> chars;
172 chars.reset(ScriptText<Utf8Unit>().extractOrCopyRawBuffer());
173 if (!chars) {
174 JS_ReportOutOfMemory(aCx);
175 return NS_ERROR_OUT_OF_MEMORY;
178 SourceText<Utf8Unit> srcBuf;
179 if (!srcBuf.init(aCx, std::move(chars), length)) {
180 return NS_ERROR_OUT_OF_MEMORY;
183 aMaybeSource->construct<SourceText<Utf8Unit>>(std::move(srcBuf));
184 return NS_OK;
187 inline void CheckModuleScriptPrivate(LoadedScript* script,
188 const JS::Value& aPrivate) {
189 #ifdef DEBUG
190 if (script->IsModuleScript()) {
191 JSObject* module = script->AsModuleScript()->mModuleRecord.unbarrieredGet();
192 MOZ_ASSERT_IF(module, JS::GetModulePrivate(module) == aPrivate);
194 #endif
197 void HostAddRefTopLevelScript(const JS::Value& aPrivate) {
198 // Increment the reference count of a LoadedScript object that is now pointed
199 // to by a JSScript. The reference count is decremented by
200 // HostReleaseTopLevelScript() below.
202 auto script = static_cast<LoadedScript*>(aPrivate.toPrivate());
203 CheckModuleScriptPrivate(script, aPrivate);
204 script->AddRef();
207 void HostReleaseTopLevelScript(const JS::Value& aPrivate) {
208 // Decrement the reference count of a LoadedScript object that was pointed to
209 // by a JSScript. The reference count was originally incremented by
210 // HostAddRefTopLevelScript() above.
212 auto script = static_cast<LoadedScript*>(aPrivate.toPrivate());
213 CheckModuleScriptPrivate(script, aPrivate);
214 script->Release();
217 //////////////////////////////////////////////////////////////
218 // EventScript
219 //////////////////////////////////////////////////////////////
221 EventScript::EventScript(mozilla::dom::ReferrerPolicy aReferrerPolicy,
222 ScriptFetchOptions* aFetchOptions, nsIURI* aURI)
223 : LoadedScript(ScriptKind::eEvent, aReferrerPolicy, aFetchOptions, aURI) {
224 // EventScripts are not using ScriptLoadRequest, and mBaseURL and mURI are
225 // the same thing.
226 SetBaseURL(aURI);
229 //////////////////////////////////////////////////////////////
230 // ClassicScript
231 //////////////////////////////////////////////////////////////
233 ClassicScript::ClassicScript(mozilla::dom::ReferrerPolicy aReferrerPolicy,
234 ScriptFetchOptions* aFetchOptions, nsIURI* aURI)
235 : LoadedScript(ScriptKind::eClassic, aReferrerPolicy, aFetchOptions, aURI) {
238 //////////////////////////////////////////////////////////////
239 // ModuleScript
240 //////////////////////////////////////////////////////////////
242 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(ModuleScript, LoadedScript)
244 NS_IMPL_CYCLE_COLLECTION_CLASS(ModuleScript)
246 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ModuleScript, LoadedScript)
247 tmp->UnlinkModuleRecord();
248 tmp->mParseError.setUndefined();
249 tmp->mErrorToRethrow.setUndefined();
250 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
252 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ModuleScript, LoadedScript)
253 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
255 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(ModuleScript, LoadedScript)
256 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mModuleRecord)
257 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mParseError)
258 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mErrorToRethrow)
259 NS_IMPL_CYCLE_COLLECTION_TRACE_END
261 ModuleScript::ModuleScript(mozilla::dom::ReferrerPolicy aReferrerPolicy,
262 ScriptFetchOptions* aFetchOptions, nsIURI* aURI)
263 : LoadedScript(ScriptKind::eModule, aReferrerPolicy, aFetchOptions, aURI),
264 mHadImportMap(false),
265 mDebuggerDataInitialized(false) {
266 MOZ_ASSERT(!ModuleRecord());
267 MOZ_ASSERT(!HasParseError());
268 MOZ_ASSERT(!HasErrorToRethrow());
271 void ModuleScript::Shutdown() {
272 if (mModuleRecord) {
273 JS::ClearModuleEnvironment(mModuleRecord);
276 UnlinkModuleRecord();
279 void ModuleScript::UnlinkModuleRecord() {
280 // Remove the module record's pointer to this object if present and decrement
281 // our reference count. The reference is added by SetModuleRecord() below.
283 // This takes care not to trigger gray unmarking because this takes a lot of
284 // time when we're tearing down the entire page. This is safe because we are
285 // only writing undefined into the module private, so it won't create any
286 // black-gray edges.
287 if (mModuleRecord) {
288 JSObject* module = mModuleRecord.unbarrieredGet();
289 MOZ_ASSERT(JS::GetModulePrivate(module).toPrivate() == this);
290 JS::ClearModulePrivate(module);
291 mModuleRecord = nullptr;
295 ModuleScript::~ModuleScript() {
296 // The object may be destroyed without being unlinked first.
297 UnlinkModuleRecord();
300 void ModuleScript::SetModuleRecord(JS::Handle<JSObject*> aModuleRecord) {
301 MOZ_ASSERT(!mModuleRecord);
302 MOZ_ASSERT_IF(IsModuleScript(), !AsModuleScript()->HasParseError());
303 MOZ_ASSERT_IF(IsModuleScript(), !AsModuleScript()->HasErrorToRethrow());
305 mModuleRecord = aModuleRecord;
307 // Make module's host defined field point to this object. The JS engine will
308 // increment our reference count by calling HostAddRefTopLevelScript(). This
309 // is decremented when the field is cleared in UnlinkModuleRecord() above or
310 // when the module record dies.
311 MOZ_ASSERT(JS::GetModulePrivate(mModuleRecord).isUndefined());
312 JS::SetModulePrivate(mModuleRecord, JS::PrivateValue(this));
314 mozilla::HoldJSObjects(this);
317 void ModuleScript::SetParseError(const JS::Value& aError) {
318 MOZ_ASSERT(!aError.isUndefined());
319 MOZ_ASSERT(!HasParseError());
320 MOZ_ASSERT(!HasErrorToRethrow());
322 UnlinkModuleRecord();
323 mParseError = aError;
324 mozilla::HoldJSObjects(this);
327 void ModuleScript::SetErrorToRethrow(const JS::Value& aError) {
328 MOZ_ASSERT(!aError.isUndefined());
330 // This is only called after SetModuleRecord() or SetParseError() so we don't
331 // need to call HoldJSObjects() here.
332 MOZ_ASSERT(ModuleRecord() || HasParseError());
334 mErrorToRethrow = aError;
337 void ModuleScript::SetForPreload(bool aValue) { mForPreload = aValue; }
338 void ModuleScript::SetHadImportMap(bool aValue) { mHadImportMap = aValue; }
340 void ModuleScript::SetDebuggerDataInitialized() {
341 MOZ_ASSERT(ModuleRecord());
342 MOZ_ASSERT(!mDebuggerDataInitialized);
344 mDebuggerDataInitialized = true;
347 } // namespace JS::loader