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 //////////////////////////////////////////////////////////////
21 //////////////////////////////////////////////////////////////
23 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(LoadedScript
)
24 NS_INTERFACE_MAP_ENTRY(nsISupports
)
27 NS_IMPL_CYCLE_COLLECTION_CLASS(LoadedScript
)
29 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(LoadedScript
)
30 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFetchOptions
)
31 NS_IMPL_CYCLE_COLLECTION_UNLINK(mBaseURL
)
32 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
34 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(LoadedScript
)
35 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFetchOptions
)
36 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
38 NS_IMPL_CYCLE_COLLECTING_ADDREF(LoadedScript
)
39 NS_IMPL_CYCLE_COLLECTING_RELEASE(LoadedScript
)
41 LoadedScript::LoadedScript(ScriptKind aKind
,
42 mozilla::dom::ReferrerPolicy aReferrerPolicy
,
43 ScriptFetchOptions
* aFetchOptions
, nsIURI
* aURI
)
45 mReferrerPolicy(aReferrerPolicy
),
46 mFetchOptions(aFetchOptions
),
48 mDataType(DataType::eUnknown
),
49 mReceivedScriptTextLength(0),
51 MOZ_ASSERT(mFetchOptions
);
55 LoadedScript::~LoadedScript() { mozilla::DropJSObjects(this); }
57 void LoadedScript::AssociateWithScript(JSScript
* aScript
) {
58 // Verify that the rewritten URL is available when manipulating LoadedScript.
61 // Set a JSScript's private value to point to this object. The JS engine will
62 // increment our reference count by calling HostAddRefTopLevelScript(). This
63 // is decremented by HostReleaseTopLevelScript() below when the JSScript dies.
65 MOZ_ASSERT(JS::GetScriptPrivate(aScript
).isUndefined());
66 JS::SetScriptPrivate(aScript
, JS::PrivateValue(this));
69 nsresult
LoadedScript::GetScriptSource(JSContext
* aCx
,
70 MaybeSourceText
* aMaybeSource
,
71 LoadContextBase
* aMaybeLoadContext
) {
72 // If there's no script text, we try to get it from the element
73 bool isWindowContext
=
74 aMaybeLoadContext
&& aMaybeLoadContext
->IsWindowContext();
75 if (isWindowContext
&& aMaybeLoadContext
->AsWindowContext()->mIsInline
) {
76 nsAutoString inlineData
;
77 auto* scriptLoadContext
= aMaybeLoadContext
->AsWindowContext();
78 scriptLoadContext
->GetScriptElement()->GetScriptText(inlineData
);
80 size_t nbytes
= inlineData
.Length() * sizeof(char16_t
);
81 JS::UniqueTwoByteChars
chars(
82 static_cast<char16_t
*>(JS_malloc(aCx
, nbytes
)));
84 return NS_ERROR_OUT_OF_MEMORY
;
87 memcpy(chars
.get(), inlineData
.get(), nbytes
);
89 SourceText
<char16_t
> srcBuf
;
90 if (!srcBuf
.init(aCx
, std::move(chars
), inlineData
.Length())) {
91 return NS_ERROR_OUT_OF_MEMORY
;
94 aMaybeSource
->construct
<SourceText
<char16_t
>>(std::move(srcBuf
));
98 size_t length
= ScriptTextLength();
100 JS::UniqueTwoByteChars chars
;
101 chars
.reset(ScriptText
<char16_t
>().extractOrCopyRawBuffer());
103 JS_ReportOutOfMemory(aCx
);
104 return NS_ERROR_OUT_OF_MEMORY
;
107 SourceText
<char16_t
> srcBuf
;
108 if (!srcBuf
.init(aCx
, std::move(chars
), length
)) {
109 return NS_ERROR_OUT_OF_MEMORY
;
112 aMaybeSource
->construct
<SourceText
<char16_t
>>(std::move(srcBuf
));
116 MOZ_ASSERT(IsUTF8Text());
117 mozilla::UniquePtr
<Utf8Unit
[], JS::FreePolicy
> chars
;
118 chars
.reset(ScriptText
<Utf8Unit
>().extractOrCopyRawBuffer());
120 JS_ReportOutOfMemory(aCx
);
121 return NS_ERROR_OUT_OF_MEMORY
;
124 SourceText
<Utf8Unit
> srcBuf
;
125 if (!srcBuf
.init(aCx
, std::move(chars
), length
)) {
126 return NS_ERROR_OUT_OF_MEMORY
;
129 aMaybeSource
->construct
<SourceText
<Utf8Unit
>>(std::move(srcBuf
));
133 inline void CheckModuleScriptPrivate(LoadedScript
* script
,
134 const JS::Value
& aPrivate
) {
136 if (script
->IsModuleScript()) {
137 JSObject
* module
= script
->AsModuleScript()->mModuleRecord
.unbarrieredGet();
138 MOZ_ASSERT_IF(module
, JS::GetModulePrivate(module
) == aPrivate
);
143 void HostAddRefTopLevelScript(const JS::Value
& aPrivate
) {
144 // Increment the reference count of a LoadedScript object that is now pointed
145 // to by a JSScript. The reference count is decremented by
146 // HostReleaseTopLevelScript() below.
148 auto script
= static_cast<LoadedScript
*>(aPrivate
.toPrivate());
149 CheckModuleScriptPrivate(script
, aPrivate
);
153 void HostReleaseTopLevelScript(const JS::Value
& aPrivate
) {
154 // Decrement the reference count of a LoadedScript object that was pointed to
155 // by a JSScript. The reference count was originally incremented by
156 // HostAddRefTopLevelScript() above.
158 auto script
= static_cast<LoadedScript
*>(aPrivate
.toPrivate());
159 CheckModuleScriptPrivate(script
, aPrivate
);
163 //////////////////////////////////////////////////////////////
165 //////////////////////////////////////////////////////////////
167 EventScript::EventScript(mozilla::dom::ReferrerPolicy aReferrerPolicy
,
168 ScriptFetchOptions
* aFetchOptions
, nsIURI
* aURI
)
169 : LoadedScript(ScriptKind::eEvent
, aReferrerPolicy
, aFetchOptions
, aURI
) {
170 // EventScripts are not using ScriptLoadRequest, and mBaseURL and mURI are
175 //////////////////////////////////////////////////////////////
177 //////////////////////////////////////////////////////////////
179 ClassicScript::ClassicScript(mozilla::dom::ReferrerPolicy aReferrerPolicy
,
180 ScriptFetchOptions
* aFetchOptions
, nsIURI
* aURI
)
181 : LoadedScript(ScriptKind::eClassic
, aReferrerPolicy
, aFetchOptions
, aURI
) {
184 //////////////////////////////////////////////////////////////
186 //////////////////////////////////////////////////////////////
188 NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(ModuleScript
, LoadedScript
)
190 NS_IMPL_CYCLE_COLLECTION_CLASS(ModuleScript
)
192 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ModuleScript
, LoadedScript
)
193 tmp
->UnlinkModuleRecord();
194 tmp
->mParseError
.setUndefined();
195 tmp
->mErrorToRethrow
.setUndefined();
196 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
198 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ModuleScript
, LoadedScript
)
199 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
201 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(ModuleScript
, LoadedScript
)
202 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mModuleRecord
)
203 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mParseError
)
204 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mErrorToRethrow
)
205 NS_IMPL_CYCLE_COLLECTION_TRACE_END
207 ModuleScript::ModuleScript(mozilla::dom::ReferrerPolicy aReferrerPolicy
,
208 ScriptFetchOptions
* aFetchOptions
, nsIURI
* aURI
)
209 : LoadedScript(ScriptKind::eModule
, aReferrerPolicy
, aFetchOptions
, aURI
),
210 mDebuggerDataInitialized(false) {
211 MOZ_ASSERT(!ModuleRecord());
212 MOZ_ASSERT(!HasParseError());
213 MOZ_ASSERT(!HasErrorToRethrow());
216 void ModuleScript::Shutdown() {
218 JS::ClearModuleEnvironment(mModuleRecord
);
221 UnlinkModuleRecord();
224 void ModuleScript::UnlinkModuleRecord() {
225 // Remove the module record's pointer to this object if present and decrement
226 // our reference count. The reference is added by SetModuleRecord() below.
228 // This takes care not to trigger gray unmarking because this takes a lot of
229 // time when we're tearing down the entire page. This is safe because we are
230 // only writing undefined into the module private, so it won't create any
233 JSObject
* module
= mModuleRecord
.unbarrieredGet();
234 MOZ_ASSERT(JS::GetModulePrivate(module
).toPrivate() == this);
235 JS::ClearModulePrivate(module
);
236 mModuleRecord
= nullptr;
240 ModuleScript::~ModuleScript() {
241 // The object may be destroyed without being unlinked first.
242 UnlinkModuleRecord();
245 void ModuleScript::SetModuleRecord(JS::Handle
<JSObject
*> aModuleRecord
) {
246 MOZ_ASSERT(!mModuleRecord
);
247 MOZ_ASSERT_IF(IsModuleScript(), !AsModuleScript()->HasParseError());
248 MOZ_ASSERT_IF(IsModuleScript(), !AsModuleScript()->HasErrorToRethrow());
250 mModuleRecord
= aModuleRecord
;
252 // Make module's host defined field point to this object. The JS engine will
253 // increment our reference count by calling HostAddRefTopLevelScript(). This
254 // is decremented when the field is cleared in UnlinkModuleRecord() above or
255 // when the module record dies.
256 MOZ_ASSERT(JS::GetModulePrivate(mModuleRecord
).isUndefined());
257 JS::SetModulePrivate(mModuleRecord
, JS::PrivateValue(this));
259 mozilla::HoldJSObjects(this);
262 void ModuleScript::SetParseError(const JS::Value
& aError
) {
263 MOZ_ASSERT(!aError
.isUndefined());
264 MOZ_ASSERT(!HasParseError());
265 MOZ_ASSERT(!HasErrorToRethrow());
267 UnlinkModuleRecord();
268 mParseError
= aError
;
269 mozilla::HoldJSObjects(this);
272 void ModuleScript::SetErrorToRethrow(const JS::Value
& aError
) {
273 MOZ_ASSERT(!aError
.isUndefined());
275 // This is only called after SetModuleRecord() or SetParseError() so we don't
276 // need to call HoldJSObjects() here.
277 MOZ_ASSERT(ModuleRecord() || HasParseError());
279 mErrorToRethrow
= aError
;
282 void ModuleScript::SetDebuggerDataInitialized() {
283 MOZ_ASSERT(ModuleRecord());
284 MOZ_ASSERT(!mDebuggerDataInitialized
);
286 mDebuggerDataInitialized
= true;
289 } // namespace JS::loader