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/. */
6
7 #include "ScriptLoadRequest.h"
8
9 #include "mozilla/dom/Document.h"
10 #include "mozilla/HoldDropJSObjects.h"
11 #include "mozilla/StaticPrefs_dom.h"
12 #include "mozilla/Unused.h"
13 #include "mozilla/Utf8.h" // mozilla::Utf8Unit
14
15 #include "ModuleLoadRequest.h"
16 #include "nsContentUtils.h"
17 #include "nsICacheInfoChannel.h"
18 #include "nsIClassOfService.h"
19 #include "nsISupportsPriority.h"
20 #include "ScriptLoadRequest.h"
21 #include "ScriptSettings.h"
22
23 namespace mozilla {
24 namespace dom {
25
26 //////////////////////////////////////////////////////////////
27 // ScriptFetchOptions
28 //////////////////////////////////////////////////////////////
29
NS_IMPL_CYCLE_COLLECTION(ScriptFetchOptions,mElement,mTriggeringPrincipal,mWebExtGlobal)30 NS_IMPL_CYCLE_COLLECTION(ScriptFetchOptions, mElement, mTriggeringPrincipal,
31 mWebExtGlobal)
32
33 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(ScriptFetchOptions, AddRef)
34 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(ScriptFetchOptions, Release)
35
36 ScriptFetchOptions::ScriptFetchOptions(mozilla::CORSMode aCORSMode,
37 ReferrerPolicy aReferrerPolicy,
38 Element* aElement,
39 nsIPrincipal* aTriggeringPrincipal,
40 nsIGlobalObject* aWebExtGlobal)
41 : mCORSMode(aCORSMode),
42 mReferrerPolicy(aReferrerPolicy),
43 mIsPreload(false),
44 mElement(aElement),
45 mTriggeringPrincipal(aTriggeringPrincipal),
46 mWebExtGlobal(aWebExtGlobal) {
47 MOZ_ASSERT(mTriggeringPrincipal);
48 }
49
50 ScriptFetchOptions::~ScriptFetchOptions() = default;
51
52 //////////////////////////////////////////////////////////////
53 // ScriptLoadRequest
54 //////////////////////////////////////////////////////////////
55
56 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ScriptLoadRequest)
57 NS_INTERFACE_MAP_END
58
59 NS_IMPL_CYCLE_COLLECTING_ADDREF(ScriptLoadRequest)
60 NS_IMPL_CYCLE_COLLECTING_RELEASE(ScriptLoadRequest)
61
62 NS_IMPL_CYCLE_COLLECTION_CLASS(ScriptLoadRequest)
63
64 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ScriptLoadRequest)
65 // XXX missing mLoadBlockedDocument ?
66 NS_IMPL_CYCLE_COLLECTION_UNLINK(mFetchOptions, mCacheInfo)
67 tmp->mScript = nullptr;
68 if (Runnable* runnable = tmp->mRunnable.exchange(nullptr)) {
69 runnable->Release();
70 }
71 tmp->DropBytecodeCacheReferences();
72 tmp->MaybeUnblockOnload();
73 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
74
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ScriptLoadRequest)75 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(ScriptLoadRequest)
76 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFetchOptions, mCacheInfo,
77 mLoadBlockedDocument)
78 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
79
80 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(ScriptLoadRequest)
81 NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mScript)
82 NS_IMPL_CYCLE_COLLECTION_TRACE_END
83
84 ScriptLoadRequest::ScriptLoadRequest(ScriptKind aKind, nsIURI* aURI,
85 ScriptFetchOptions* aFetchOptions,
86 const SRIMetadata& aIntegrity,
87 nsIURI* aReferrer)
88 : mKind(aKind),
89 mScriptMode(ScriptMode::eBlocking),
90 mProgress(Progress::eLoading),
91 mDataType(DataType::eUnknown),
92 mScriptFromHead(false),
93 mIsInline(true),
94 mInDeferList(false),
95 mInAsyncList(false),
96 mIsNonAsyncScriptInserted(false),
97 mIsXSLT(false),
98 mIsCanceled(false),
99 mWasCompiledOMT(false),
100 mIsTracking(false),
101 mFetchOptions(aFetchOptions),
102 mOffThreadToken(nullptr),
103 mRunnable(nullptr),
104 mScriptTextLength(0),
105 mScriptBytecode(),
106 mBytecodeOffset(0),
107 mURI(aURI),
108 mLineNo(1),
109 mIntegrity(aIntegrity),
110 mReferrer(aReferrer),
111 mUnreportedPreloadError(NS_OK) {
112 MOZ_ASSERT(mFetchOptions);
113 }
114
~ScriptLoadRequest()115 ScriptLoadRequest::~ScriptLoadRequest() {
116 // When speculative parsing is enabled, it is possible to off-main-thread
117 // compile scripts that are never executed. These should be cleaned up here
118 // if they exist.
119 MOZ_ASSERT_IF(
120 !StaticPrefs::
121 dom_script_loader_external_scripts_speculative_omt_parse_enabled(),
122 !mOffThreadToken);
123
124 MaybeCancelOffThreadScript();
125
126 if (mScript) {
127 DropBytecodeCacheReferences();
128 }
129
130 MaybeUnblockOnload();
131 DropJSObjects(this);
132 }
133
BlockOnload(Document * aDocument)134 void ScriptLoadRequest::BlockOnload(Document* aDocument) {
135 MOZ_ASSERT(!mLoadBlockedDocument);
136 aDocument->BlockOnload();
137 mLoadBlockedDocument = aDocument;
138 }
139
MaybeUnblockOnload()140 void ScriptLoadRequest::MaybeUnblockOnload() {
141 if (mLoadBlockedDocument) {
142 mLoadBlockedDocument->UnblockOnload(false);
143 mLoadBlockedDocument = nullptr;
144 }
145 }
146
SetReady()147 void ScriptLoadRequest::SetReady() {
148 MOZ_ASSERT(mProgress != Progress::eReady);
149 mProgress = Progress::eReady;
150 }
151
Cancel()152 void ScriptLoadRequest::Cancel() {
153 MaybeCancelOffThreadScript();
154 mIsCanceled = true;
155 }
156
MaybeCancelOffThreadScript()157 void ScriptLoadRequest::MaybeCancelOffThreadScript() {
158 MOZ_ASSERT(NS_IsMainThread());
159
160 if (!mOffThreadToken) {
161 return;
162 }
163
164 JSContext* cx = danger::GetJSContext();
165 // Follow the same conditions as ScriptLoader::AttemptAsyncScriptCompile
166 if (IsModuleRequest()) {
167 JS::CancelOffThreadModule(cx, mOffThreadToken);
168 } else if (IsSource()) {
169 JS::CancelOffThreadScript(cx, mOffThreadToken);
170 } else {
171 MOZ_ASSERT(IsBytecode());
172 JS::CancelOffThreadScriptDecoder(cx, mOffThreadToken);
173 }
174
175 // Cancellation request above should guarantee removal of the parse task, so
176 // releasing the runnable should be safe to do here.
177 if (Runnable* runnable = mRunnable.exchange(nullptr)) {
178 runnable->Release();
179 }
180
181 MaybeUnblockOnload();
182 mOffThreadToken = nullptr;
183 }
184
DropBytecodeCacheReferences()185 void ScriptLoadRequest::DropBytecodeCacheReferences() {
186 mCacheInfo = nullptr;
187 DropJSObjects(this);
188 }
189
AsModuleRequest()190 inline ModuleLoadRequest* ScriptLoadRequest::AsModuleRequest() {
191 MOZ_ASSERT(IsModuleRequest());
192 return static_cast<ModuleLoadRequest*>(this);
193 }
194
SetScriptMode(bool aDeferAttr,bool aAsyncAttr,bool aLinkPreload)195 void ScriptLoadRequest::SetScriptMode(bool aDeferAttr, bool aAsyncAttr,
196 bool aLinkPreload) {
197 if (aLinkPreload) {
198 mScriptMode = ScriptMode::eLinkPreload;
199 } else if (aAsyncAttr) {
200 mScriptMode = ScriptMode::eAsync;
201 } else if (aDeferAttr || IsModuleRequest()) {
202 mScriptMode = ScriptMode::eDeferred;
203 } else {
204 mScriptMode = ScriptMode::eBlocking;
205 }
206 }
207
SetUnknownDataType()208 void ScriptLoadRequest::SetUnknownDataType() {
209 mDataType = DataType::eUnknown;
210 mScriptData.reset();
211 }
212
SetTextSource()213 void ScriptLoadRequest::SetTextSource() {
214 MOZ_ASSERT(IsUnknownDataType());
215 mDataType = DataType::eTextSource;
216 if (StaticPrefs::dom_script_loader_external_scripts_utf8_parsing_enabled()) {
217 mScriptData.emplace(VariantType<ScriptTextBuffer<Utf8Unit>>());
218 } else {
219 mScriptData.emplace(VariantType<ScriptTextBuffer<char16_t>>());
220 }
221 }
222
SetBytecode()223 void ScriptLoadRequest::SetBytecode() {
224 MOZ_ASSERT(IsUnknownDataType());
225 mDataType = DataType::eBytecode;
226 }
227
ClearScriptSource()228 void ScriptLoadRequest::ClearScriptSource() {
229 if (IsTextSource()) {
230 ClearScriptText();
231 }
232 }
233
SetScript(JSScript * aScript)234 void ScriptLoadRequest::SetScript(JSScript* aScript) {
235 MOZ_ASSERT(!mScript);
236 mScript = aScript;
237 HoldJSObjects(this);
238 }
239
240 // static
PrioritizeAsPreload(nsIChannel * aChannel)241 void ScriptLoadRequest::PrioritizeAsPreload(nsIChannel* aChannel) {
242 if (nsCOMPtr<nsIClassOfService> cos = do_QueryInterface(aChannel)) {
243 cos->AddClassFlags(nsIClassOfService::Unblocked);
244 }
245 if (nsCOMPtr<nsISupportsPriority> sp = do_QueryInterface(aChannel)) {
246 sp->AdjustPriority(nsISupportsPriority::PRIORITY_HIGHEST);
247 }
248 }
249
PrioritizeAsPreload()250 void ScriptLoadRequest::PrioritizeAsPreload() {
251 if (!IsLinkPreloadScript()) {
252 // Do the prioritization only if this request has not already been created
253 // as a preload.
254 PrioritizeAsPreload(Channel());
255 }
256 }
257
GetScriptElement() const258 nsIScriptElement* ScriptLoadRequest::GetScriptElement() const {
259 nsCOMPtr<nsIScriptElement> scriptElement =
260 do_QueryInterface(mFetchOptions->mElement);
261 return scriptElement;
262 }
263
SetIsLoadRequest(nsIScriptElement * aElement)264 void ScriptLoadRequest::SetIsLoadRequest(nsIScriptElement* aElement) {
265 MOZ_ASSERT(aElement);
266 MOZ_ASSERT(!GetScriptElement());
267 MOZ_ASSERT(IsPreload());
268 mFetchOptions->mElement = do_QueryInterface(aElement);
269 mFetchOptions->mIsPreload = false;
270 }
271
272 //////////////////////////////////////////////////////////////
273 // ScriptLoadRequestList
274 //////////////////////////////////////////////////////////////
275
~ScriptLoadRequestList()276 ScriptLoadRequestList::~ScriptLoadRequestList() { Clear(); }
277
Clear()278 void ScriptLoadRequestList::Clear() {
279 while (!isEmpty()) {
280 RefPtr<ScriptLoadRequest> first = StealFirst();
281 first->Cancel();
282 // And just let it go out of scope and die.
283 }
284 }
285
286 #ifdef DEBUG
Contains(ScriptLoadRequest * aElem) const287 bool ScriptLoadRequestList::Contains(ScriptLoadRequest* aElem) const {
288 for (const ScriptLoadRequest* req = getFirst(); req; req = req->getNext()) {
289 if (req == aElem) {
290 return true;
291 }
292 }
293
294 return false;
295 }
296 #endif // DEBUG
297
ImplCycleCollectionUnlink(ScriptLoadRequestList & aField)298 inline void ImplCycleCollectionUnlink(ScriptLoadRequestList& aField) {
299 while (!aField.isEmpty()) {
300 RefPtr<ScriptLoadRequest> first = aField.StealFirst();
301 }
302 }
303
ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback & aCallback,ScriptLoadRequestList & aField,const char * aName,uint32_t aFlags)304 inline void ImplCycleCollectionTraverse(
305 nsCycleCollectionTraversalCallback& aCallback,
306 ScriptLoadRequestList& aField, const char* aName, uint32_t aFlags) {
307 for (ScriptLoadRequest* request = aField.getFirst(); request;
308 request = request->getNext()) {
309 CycleCollectionNoteChild(aCallback, request, aName, aFlags);
310 }
311 }
312
313 } // namespace dom
314 } // namespace mozilla
315