/src/mozilla-central/dom/serviceworkers/ServiceWorkerEvents.cpp
Line | Count | Source (jump to first uncovered line) |
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 "ServiceWorkerEvents.h" |
8 | | |
9 | | #include "nsAutoPtr.h" |
10 | | #include "nsContentUtils.h" |
11 | | #include "nsIConsoleReportCollector.h" |
12 | | #include "nsIHttpChannelInternal.h" |
13 | | #include "nsINetworkInterceptController.h" |
14 | | #include "nsIOutputStream.h" |
15 | | #include "nsIScriptError.h" |
16 | | #include "nsITimedChannel.h" |
17 | | #include "mozilla/Encoding.h" |
18 | | #include "nsContentPolicyUtils.h" |
19 | | #include "nsContentUtils.h" |
20 | | #include "nsComponentManagerUtils.h" |
21 | | #include "nsServiceManagerUtils.h" |
22 | | #include "nsStreamUtils.h" |
23 | | #include "nsNetCID.h" |
24 | | #include "nsNetUtil.h" |
25 | | #include "nsSerializationHelper.h" |
26 | | #include "nsQueryObject.h" |
27 | | #include "ServiceWorker.h" |
28 | | #include "ServiceWorkerManager.h" |
29 | | |
30 | | #include "mozilla/ErrorResult.h" |
31 | | #include "mozilla/LoadInfo.h" |
32 | | #include "mozilla/Move.h" |
33 | | #include "mozilla/Preferences.h" |
34 | | #include "mozilla/dom/BodyUtil.h" |
35 | | #include "mozilla/dom/Client.h" |
36 | | #include "mozilla/dom/EventBinding.h" |
37 | | #include "mozilla/dom/FetchEventBinding.h" |
38 | | #include "mozilla/dom/MessagePort.h" |
39 | | #include "mozilla/dom/PromiseNativeHandler.h" |
40 | | #include "mozilla/dom/PushEventBinding.h" |
41 | | #include "mozilla/dom/PushMessageDataBinding.h" |
42 | | #include "mozilla/dom/PushUtil.h" |
43 | | #include "mozilla/dom/Request.h" |
44 | | #include "mozilla/dom/TypedArray.h" |
45 | | #include "mozilla/dom/Response.h" |
46 | | #include "mozilla/dom/WorkerScope.h" |
47 | | #include "mozilla/dom/WorkerPrivate.h" |
48 | | |
49 | | #include "js/Conversions.h" |
50 | | #include "js/TypeDecls.h" |
51 | | #include "xpcpublic.h" |
52 | | |
53 | | using namespace mozilla; |
54 | | using namespace mozilla::dom; |
55 | | |
56 | | namespace { |
57 | | |
58 | | void |
59 | | AsyncLog(nsIInterceptedChannel *aInterceptedChannel, |
60 | | const nsACString& aRespondWithScriptSpec, |
61 | | uint32_t aRespondWithLineNumber, uint32_t aRespondWithColumnNumber, |
62 | | const nsACString& aMessageName, const nsTArray<nsString>& aParams) |
63 | 0 | { |
64 | 0 | MOZ_ASSERT(aInterceptedChannel); |
65 | 0 | nsCOMPtr<nsIConsoleReportCollector> reporter = |
66 | 0 | aInterceptedChannel->GetConsoleReportCollector(); |
67 | 0 | if (reporter) { |
68 | 0 | reporter->AddConsoleReport(nsIScriptError::errorFlag, |
69 | 0 | NS_LITERAL_CSTRING("Service Worker Interception"), |
70 | 0 | nsContentUtils::eDOM_PROPERTIES, |
71 | 0 | aRespondWithScriptSpec, |
72 | 0 | aRespondWithLineNumber, |
73 | 0 | aRespondWithColumnNumber, |
74 | 0 | aMessageName, aParams); |
75 | 0 | } |
76 | 0 | } |
77 | | |
78 | | template<typename... Params> |
79 | | void |
80 | | AsyncLog(nsIInterceptedChannel* aInterceptedChannel, |
81 | | const nsACString& aRespondWithScriptSpec, |
82 | | uint32_t aRespondWithLineNumber, uint32_t aRespondWithColumnNumber, |
83 | | // We have to list one explicit string so that calls with an |
84 | | // nsTArray of params won't end up in here. |
85 | | const nsACString& aMessageName, const nsAString& aFirstParam, |
86 | | Params&&... aParams) |
87 | 0 | { |
88 | 0 | nsTArray<nsString> paramsList(sizeof...(Params) + 1); |
89 | 0 | StringArrayAppender::Append(paramsList, sizeof...(Params) + 1, |
90 | 0 | aFirstParam, std::forward<Params>(aParams)...); |
91 | 0 | AsyncLog(aInterceptedChannel, aRespondWithScriptSpec, aRespondWithLineNumber, |
92 | 0 | aRespondWithColumnNumber, aMessageName, paramsList); |
93 | 0 | } Unexecuted instantiation: Unified_cpp_dom_serviceworkers0.cpp:void (anonymous namespace)::AsyncLog<nsTString<char16_t>&>(nsIInterceptedChannel*, nsTSubstring<char> const&, unsigned int, unsigned int, nsTSubstring<char> const&, nsTSubstring<char16_t> const&, nsTString<char16_t>&) Unexecuted instantiation: Unified_cpp_dom_serviceworkers0.cpp:void (anonymous namespace)::AsyncLog<>(nsIInterceptedChannel*, nsTSubstring<char> const&, unsigned int, unsigned int, nsTSubstring<char> const&, nsTSubstring<char16_t> const&) |
94 | | |
95 | | } // anonymous namespace |
96 | | |
97 | | namespace mozilla { |
98 | | namespace dom { |
99 | | |
100 | | CancelChannelRunnable::CancelChannelRunnable( |
101 | | nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel, |
102 | | nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo>& aRegistration, |
103 | | nsresult aStatus) |
104 | | : Runnable("dom::CancelChannelRunnable") |
105 | | , mChannel(aChannel) |
106 | | , mRegistration(aRegistration) |
107 | | , mStatus(aStatus) |
108 | 0 | { |
109 | 0 | } |
110 | | |
111 | | NS_IMETHODIMP |
112 | | CancelChannelRunnable::Run() |
113 | 0 | { |
114 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
115 | 0 |
|
116 | 0 | // TODO: When bug 1204254 is implemented, this time marker should be moved to |
117 | 0 | // the point where the body of the network request is complete. |
118 | 0 | mChannel->SetHandleFetchEventEnd(TimeStamp::Now()); |
119 | 0 | mChannel->SaveTimeStamps(); |
120 | 0 |
|
121 | 0 | mChannel->CancelInterception(mStatus); |
122 | 0 | mRegistration->MaybeScheduleUpdate(); |
123 | 0 | return NS_OK; |
124 | 0 | } |
125 | | |
126 | | FetchEvent::FetchEvent(EventTarget* aOwner) |
127 | | : ExtendableEvent(aOwner) |
128 | | , mPreventDefaultLineNumber(0) |
129 | | , mPreventDefaultColumnNumber(0) |
130 | | , mIsReload(false) |
131 | | , mWaitToRespond(false) |
132 | 0 | { |
133 | 0 | } |
134 | | |
135 | | FetchEvent::~FetchEvent() |
136 | 0 | { |
137 | 0 | } |
138 | | |
139 | | void |
140 | | FetchEvent::PostInit(nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel, |
141 | | nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo>& aRegistration, |
142 | | const nsACString& aScriptSpec) |
143 | 0 | { |
144 | 0 | mChannel = aChannel; |
145 | 0 | mRegistration = aRegistration; |
146 | 0 | mScriptSpec.Assign(aScriptSpec); |
147 | 0 | } |
148 | | |
149 | | /*static*/ already_AddRefed<FetchEvent> |
150 | | FetchEvent::Constructor(const GlobalObject& aGlobal, |
151 | | const nsAString& aType, |
152 | | const FetchEventInit& aOptions, |
153 | | ErrorResult& aRv) |
154 | 0 | { |
155 | 0 | RefPtr<EventTarget> owner = do_QueryObject(aGlobal.GetAsSupports()); |
156 | 0 | MOZ_ASSERT(owner); |
157 | 0 | RefPtr<FetchEvent> e = new FetchEvent(owner); |
158 | 0 | bool trusted = e->Init(owner); |
159 | 0 | e->InitEvent(aType, aOptions.mBubbles, aOptions.mCancelable); |
160 | 0 | e->SetTrusted(trusted); |
161 | 0 | e->SetComposed(aOptions.mComposed); |
162 | 0 | e->mRequest = aOptions.mRequest; |
163 | 0 | e->mClientId = aOptions.mClientId; |
164 | 0 | e->mIsReload = aOptions.mIsReload; |
165 | 0 | return e.forget(); |
166 | 0 | } |
167 | | |
168 | | namespace { |
169 | | |
170 | | struct RespondWithClosure |
171 | | { |
172 | | nsMainThreadPtrHandle<nsIInterceptedChannel> mInterceptedChannel; |
173 | | nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> mRegistration; |
174 | | const nsString mRequestURL; |
175 | | const nsCString mRespondWithScriptSpec; |
176 | | const uint32_t mRespondWithLineNumber; |
177 | | const uint32_t mRespondWithColumnNumber; |
178 | | |
179 | | RespondWithClosure(nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel, |
180 | | nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo>& aRegistration, |
181 | | const nsAString& aRequestURL, |
182 | | const nsACString& aRespondWithScriptSpec, |
183 | | uint32_t aRespondWithLineNumber, |
184 | | uint32_t aRespondWithColumnNumber) |
185 | | : mInterceptedChannel(aChannel) |
186 | | , mRegistration(aRegistration) |
187 | | , mRequestURL(aRequestURL) |
188 | | , mRespondWithScriptSpec(aRespondWithScriptSpec) |
189 | | , mRespondWithLineNumber(aRespondWithLineNumber) |
190 | | , mRespondWithColumnNumber(aRespondWithColumnNumber) |
191 | 0 | { |
192 | 0 | } |
193 | | }; |
194 | | |
195 | | class FinishResponse final : public Runnable |
196 | | { |
197 | | nsMainThreadPtrHandle<nsIInterceptedChannel> mChannel; |
198 | | |
199 | | public: |
200 | | explicit FinishResponse(nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel) |
201 | | : Runnable("dom::FinishResponse") |
202 | | , mChannel(aChannel) |
203 | 0 | { |
204 | 0 | } |
205 | | |
206 | | NS_IMETHOD |
207 | | Run() override |
208 | 0 | { |
209 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
210 | 0 |
|
211 | 0 | nsresult rv = mChannel->FinishSynthesizedResponse(); |
212 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
213 | 0 | mChannel->CancelInterception(NS_ERROR_INTERCEPTION_FAILED); |
214 | 0 | return NS_OK; |
215 | 0 | } |
216 | 0 | |
217 | 0 | TimeStamp timeStamp = TimeStamp::Now(); |
218 | 0 | mChannel->SetHandleFetchEventEnd(timeStamp); |
219 | 0 | mChannel->SetFinishSynthesizedResponseEnd(timeStamp); |
220 | 0 | mChannel->SaveTimeStamps(); |
221 | 0 |
|
222 | 0 | return rv; |
223 | 0 | } |
224 | | }; |
225 | | |
226 | | class BodyCopyHandle final : public nsIInterceptedBodyCallback |
227 | | { |
228 | | UniquePtr<RespondWithClosure> mClosure; |
229 | | |
230 | | ~BodyCopyHandle() |
231 | 0 | { |
232 | 0 | } |
233 | | |
234 | | public: |
235 | | NS_DECL_THREADSAFE_ISUPPORTS |
236 | | |
237 | | explicit BodyCopyHandle(UniquePtr<RespondWithClosure>&& aClosure) |
238 | | : mClosure(std::move(aClosure)) |
239 | 0 | { |
240 | 0 | } |
241 | | |
242 | | NS_IMETHOD |
243 | | BodyComplete(nsresult aRv) override |
244 | 0 | { |
245 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
246 | 0 |
|
247 | 0 | nsCOMPtr<nsIRunnable> event; |
248 | 0 | if (NS_WARN_IF(NS_FAILED(aRv))) { |
249 | 0 | AsyncLog(mClosure->mInterceptedChannel, mClosure->mRespondWithScriptSpec, |
250 | 0 | mClosure->mRespondWithLineNumber, |
251 | 0 | mClosure->mRespondWithColumnNumber, |
252 | 0 | NS_LITERAL_CSTRING("InterceptionFailedWithURL"), |
253 | 0 | mClosure->mRequestURL); |
254 | 0 | event = new CancelChannelRunnable(mClosure->mInterceptedChannel, |
255 | 0 | mClosure->mRegistration, |
256 | 0 | NS_ERROR_INTERCEPTION_FAILED); |
257 | 0 | } else { |
258 | 0 | event = new FinishResponse(mClosure->mInterceptedChannel); |
259 | 0 | } |
260 | 0 |
|
261 | 0 | mClosure.reset(); |
262 | 0 |
|
263 | 0 | event->Run(); |
264 | 0 |
|
265 | 0 | return NS_OK; |
266 | 0 | } |
267 | | }; |
268 | | |
269 | | NS_IMPL_ISUPPORTS(BodyCopyHandle, nsIInterceptedBodyCallback) |
270 | | |
271 | | class StartResponse final : public Runnable |
272 | | { |
273 | | nsMainThreadPtrHandle<nsIInterceptedChannel> mChannel; |
274 | | RefPtr<InternalResponse> mInternalResponse; |
275 | | ChannelInfo mWorkerChannelInfo; |
276 | | const nsCString mScriptSpec; |
277 | | const nsCString mResponseURLSpec; |
278 | | UniquePtr<RespondWithClosure> mClosure; |
279 | | |
280 | | public: |
281 | | StartResponse(nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel, |
282 | | InternalResponse* aInternalResponse, |
283 | | const ChannelInfo& aWorkerChannelInfo, |
284 | | const nsACString& aScriptSpec, |
285 | | const nsACString& aResponseURLSpec, |
286 | | UniquePtr<RespondWithClosure>&& aClosure) |
287 | | : Runnable("dom::StartResponse") |
288 | | , mChannel(aChannel) |
289 | | , mInternalResponse(aInternalResponse) |
290 | | , mWorkerChannelInfo(aWorkerChannelInfo) |
291 | | , mScriptSpec(aScriptSpec) |
292 | | , mResponseURLSpec(aResponseURLSpec) |
293 | | , mClosure(std::move(aClosure)) |
294 | 0 | { |
295 | 0 | } |
296 | | |
297 | | NS_IMETHOD |
298 | | Run() override |
299 | 0 | { |
300 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
301 | 0 |
|
302 | 0 | nsCOMPtr<nsIChannel> underlyingChannel; |
303 | 0 | nsresult rv = mChannel->GetChannel(getter_AddRefs(underlyingChannel)); |
304 | 0 | NS_ENSURE_SUCCESS(rv, rv); |
305 | 0 | NS_ENSURE_TRUE(underlyingChannel, NS_ERROR_UNEXPECTED); |
306 | 0 | nsCOMPtr<nsILoadInfo> loadInfo = underlyingChannel->GetLoadInfo(); |
307 | 0 |
|
308 | 0 | if (!loadInfo || !CSPPermitsResponse(loadInfo)) { |
309 | 0 | mChannel->CancelInterception(NS_ERROR_CONTENT_BLOCKED); |
310 | 0 | return NS_OK; |
311 | 0 | } |
312 | 0 | |
313 | 0 | ChannelInfo channelInfo; |
314 | 0 | if (mInternalResponse->GetChannelInfo().IsInitialized()) { |
315 | 0 | channelInfo = mInternalResponse->GetChannelInfo(); |
316 | 0 | } else { |
317 | 0 | // We are dealing with a synthesized response here, so fall back to the |
318 | 0 | // channel info for the worker script. |
319 | 0 | channelInfo = mWorkerChannelInfo; |
320 | 0 | } |
321 | 0 | rv = mChannel->SetChannelInfo(&channelInfo); |
322 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
323 | 0 | mChannel->CancelInterception(NS_ERROR_INTERCEPTION_FAILED); |
324 | 0 | return NS_OK; |
325 | 0 | } |
326 | 0 | |
327 | 0 | rv = mChannel->SynthesizeStatus(mInternalResponse->GetUnfilteredStatus(), |
328 | 0 | mInternalResponse->GetUnfilteredStatusText()); |
329 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
330 | 0 | mChannel->CancelInterception(NS_ERROR_INTERCEPTION_FAILED); |
331 | 0 | return NS_OK; |
332 | 0 | } |
333 | 0 | |
334 | 0 | AutoTArray<InternalHeaders::Entry, 5> entries; |
335 | 0 | mInternalResponse->UnfilteredHeaders()->GetEntries(entries); |
336 | 0 | for (uint32_t i = 0; i < entries.Length(); ++i) { |
337 | 0 | mChannel->SynthesizeHeader(entries[i].mName, entries[i].mValue); |
338 | 0 | } |
339 | 0 |
|
340 | 0 | auto castLoadInfo = static_cast<mozilla::net::LoadInfo*>(loadInfo.get()); |
341 | 0 | castLoadInfo->SynthesizeServiceWorkerTainting(mInternalResponse->GetTainting()); |
342 | 0 |
|
343 | 0 | // Get the preferred alternative data type of outter channel |
344 | 0 | nsAutoCString preferredAltDataType(EmptyCString()); |
345 | 0 | nsCOMPtr<nsICacheInfoChannel> outerChannel = do_QueryInterface(underlyingChannel); |
346 | 0 | if (outerChannel) { |
347 | 0 | outerChannel->GetPreferredAlternativeDataType(preferredAltDataType); |
348 | 0 | } |
349 | 0 |
|
350 | 0 | // Get the alternative data type saved in the InternalResponse |
351 | 0 | nsAutoCString altDataType; |
352 | 0 | nsCOMPtr<nsICacheInfoChannel> cacheInfoChannel = |
353 | 0 | mInternalResponse->TakeCacheInfoChannel().get(); |
354 | 0 | if (cacheInfoChannel) { |
355 | 0 | cacheInfoChannel->GetAlternativeDataType(altDataType); |
356 | 0 | } |
357 | 0 |
|
358 | 0 | nsCOMPtr<nsIInputStream> body; |
359 | 0 | if (preferredAltDataType.Equals(altDataType)) { |
360 | 0 | body = mInternalResponse->TakeAlternativeBody(); |
361 | 0 | } |
362 | 0 | if (!body) { |
363 | 0 | mInternalResponse->GetUnfilteredBody(getter_AddRefs(body)); |
364 | 0 | } else { |
365 | 0 | Telemetry::ScalarAdd(Telemetry::ScalarID::SW_ALTERNATIVE_BODY_USED_COUNT, 1); |
366 | 0 | } |
367 | 0 |
|
368 | 0 | RefPtr<BodyCopyHandle> copyHandle; |
369 | 0 | copyHandle = new BodyCopyHandle(std::move(mClosure)); |
370 | 0 |
|
371 | 0 | rv = mChannel->StartSynthesizedResponse(body, copyHandle, cacheInfoChannel, |
372 | 0 | mResponseURLSpec, |
373 | 0 | mInternalResponse->IsRedirected()); |
374 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
375 | 0 | mChannel->CancelInterception(NS_ERROR_INTERCEPTION_FAILED); |
376 | 0 | return NS_OK; |
377 | 0 | } |
378 | 0 | |
379 | 0 | nsCOMPtr<nsIObserverService> obsService = services::GetObserverService(); |
380 | 0 | if (obsService) { |
381 | 0 | obsService->NotifyObservers(underlyingChannel, "service-worker-synthesized-response", nullptr); |
382 | 0 | } |
383 | 0 |
|
384 | 0 | return rv; |
385 | 0 | } |
386 | | |
387 | | bool CSPPermitsResponse(nsILoadInfo* aLoadInfo) |
388 | 0 | { |
389 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
390 | 0 | MOZ_ASSERT(aLoadInfo); |
391 | 0 | nsresult rv; |
392 | 0 | nsCOMPtr<nsIURI> uri; |
393 | 0 | nsCString url = mInternalResponse->GetUnfilteredURL(); |
394 | 0 | if (url.IsEmpty()) { |
395 | 0 | // Synthetic response. The buck stops at the worker script. |
396 | 0 | url = mScriptSpec; |
397 | 0 | } |
398 | 0 | rv = NS_NewURI(getter_AddRefs(uri), url, nullptr, nullptr); |
399 | 0 | NS_ENSURE_SUCCESS(rv, false); |
400 | 0 | int16_t decision = nsIContentPolicy::ACCEPT; |
401 | 0 | rv = NS_CheckContentLoadPolicy(uri, aLoadInfo, EmptyCString(), &decision); |
402 | 0 | NS_ENSURE_SUCCESS(rv, false); |
403 | 0 | return decision == nsIContentPolicy::ACCEPT; |
404 | 0 | } |
405 | | }; |
406 | | |
407 | | class RespondWithHandler final : public PromiseNativeHandler |
408 | | { |
409 | | nsMainThreadPtrHandle<nsIInterceptedChannel> mInterceptedChannel; |
410 | | nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> mRegistration; |
411 | | const RequestMode mRequestMode; |
412 | | const RequestRedirect mRequestRedirectMode; |
413 | | #ifdef DEBUG |
414 | | const bool mIsClientRequest; |
415 | | #endif |
416 | | const nsCString mScriptSpec; |
417 | | const nsString mRequestURL; |
418 | | const nsCString mRequestFragment; |
419 | | const nsCString mRespondWithScriptSpec; |
420 | | const uint32_t mRespondWithLineNumber; |
421 | | const uint32_t mRespondWithColumnNumber; |
422 | | bool mRequestWasHandled; |
423 | | public: |
424 | | NS_DECL_ISUPPORTS |
425 | | |
426 | | RespondWithHandler(nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel, |
427 | | nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo>& aRegistration, |
428 | | RequestMode aRequestMode, bool aIsClientRequest, |
429 | | RequestRedirect aRedirectMode, |
430 | | const nsACString& aScriptSpec, |
431 | | const nsAString& aRequestURL, |
432 | | const nsACString& aRequestFragment, |
433 | | const nsACString& aRespondWithScriptSpec, |
434 | | uint32_t aRespondWithLineNumber, |
435 | | uint32_t aRespondWithColumnNumber) |
436 | | : mInterceptedChannel(aChannel) |
437 | | , mRegistration(aRegistration) |
438 | | , mRequestMode(aRequestMode) |
439 | | , mRequestRedirectMode(aRedirectMode) |
440 | | #ifdef DEBUG |
441 | | , mIsClientRequest(aIsClientRequest) |
442 | | #endif |
443 | | , mScriptSpec(aScriptSpec) |
444 | | , mRequestURL(aRequestURL) |
445 | | , mRequestFragment(aRequestFragment) |
446 | | , mRespondWithScriptSpec(aRespondWithScriptSpec) |
447 | | , mRespondWithLineNumber(aRespondWithLineNumber) |
448 | | , mRespondWithColumnNumber(aRespondWithColumnNumber) |
449 | | , mRequestWasHandled(false) |
450 | 0 | { |
451 | 0 | } |
452 | | |
453 | | void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override; |
454 | | |
455 | | void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override; |
456 | | |
457 | | void CancelRequest(nsresult aStatus); |
458 | | |
459 | | void AsyncLog(const nsACString& aMessageName, const nsTArray<nsString>& aParams) |
460 | 0 | { |
461 | 0 | ::AsyncLog(mInterceptedChannel, mRespondWithScriptSpec, mRespondWithLineNumber, |
462 | 0 | mRespondWithColumnNumber, aMessageName, aParams); |
463 | 0 | } |
464 | | |
465 | | void AsyncLog(const nsACString& aSourceSpec, uint32_t aLine, uint32_t aColumn, |
466 | | const nsACString& aMessageName, const nsTArray<nsString>& aParams) |
467 | 0 | { |
468 | 0 | ::AsyncLog(mInterceptedChannel, aSourceSpec, aLine, aColumn, aMessageName, |
469 | 0 | aParams); |
470 | 0 | } |
471 | | |
472 | | private: |
473 | | ~RespondWithHandler() |
474 | 0 | { |
475 | 0 | if (!mRequestWasHandled) { |
476 | 0 | ::AsyncLog(mInterceptedChannel, mRespondWithScriptSpec, |
477 | 0 | mRespondWithLineNumber, mRespondWithColumnNumber, |
478 | 0 | NS_LITERAL_CSTRING("InterceptionFailedWithURL"), mRequestURL); |
479 | 0 | CancelRequest(NS_ERROR_INTERCEPTION_FAILED); |
480 | 0 | } |
481 | 0 | } |
482 | | }; |
483 | | |
484 | | class MOZ_STACK_CLASS AutoCancel |
485 | | { |
486 | | RefPtr<RespondWithHandler> mOwner; |
487 | | nsCString mSourceSpec; |
488 | | uint32_t mLine; |
489 | | uint32_t mColumn; |
490 | | nsCString mMessageName; |
491 | | nsTArray<nsString> mParams; |
492 | | |
493 | | public: |
494 | | AutoCancel(RespondWithHandler* aOwner, const nsString& aRequestURL) |
495 | | : mOwner(aOwner) |
496 | | , mLine(0) |
497 | | , mColumn(0) |
498 | | , mMessageName(NS_LITERAL_CSTRING("InterceptionFailedWithURL")) |
499 | 0 | { |
500 | 0 | mParams.AppendElement(aRequestURL); |
501 | 0 | } |
502 | | |
503 | | ~AutoCancel() |
504 | 0 | { |
505 | 0 | if (mOwner) { |
506 | 0 | if (mSourceSpec.IsEmpty()) { |
507 | 0 | mOwner->AsyncLog(mMessageName, mParams); |
508 | 0 | } else { |
509 | 0 | mOwner->AsyncLog(mSourceSpec, mLine, mColumn, mMessageName, mParams); |
510 | 0 | } |
511 | 0 | mOwner->CancelRequest(NS_ERROR_INTERCEPTION_FAILED); |
512 | 0 | } |
513 | 0 | } |
514 | | |
515 | | // This function steals the error message from a ErrorResult. |
516 | | void |
517 | | SetCancelErrorResult(JSContext* aCx, ErrorResult& aRv) |
518 | 0 | { |
519 | 0 | MOZ_DIAGNOSTIC_ASSERT(aRv.Failed()); |
520 | 0 | MOZ_DIAGNOSTIC_ASSERT(!JS_IsExceptionPending(aCx)); |
521 | 0 |
|
522 | 0 | // Storing the error as exception in the JSContext. |
523 | 0 | if (!aRv.MaybeSetPendingException(aCx)) { |
524 | 0 | return; |
525 | 0 | } |
526 | 0 | |
527 | 0 | MOZ_ASSERT(!aRv.Failed()); |
528 | 0 |
|
529 | 0 | // Let's take the pending exception. |
530 | 0 | JS::Rooted<JS::Value> exn(aCx); |
531 | 0 | if (!JS_GetPendingException(aCx, &exn)) { |
532 | 0 | return; |
533 | 0 | } |
534 | 0 | |
535 | 0 | JS_ClearPendingException(aCx); |
536 | 0 |
|
537 | 0 | // Converting the exception in a js::ErrorReport. |
538 | 0 | js::ErrorReport report(aCx); |
539 | 0 | if (!report.init(aCx, exn, js::ErrorReport::WithSideEffects)) { |
540 | 0 | JS_ClearPendingException(aCx); |
541 | 0 | return; |
542 | 0 | } |
543 | 0 | |
544 | 0 | MOZ_ASSERT(mOwner); |
545 | 0 | MOZ_ASSERT(mMessageName.EqualsLiteral("InterceptionFailedWithURL")); |
546 | 0 | MOZ_ASSERT(mParams.Length() == 1); |
547 | 0 |
|
548 | 0 | // Let's store the error message here. |
549 | 0 | mMessageName.Assign(report.toStringResult().c_str()); |
550 | 0 | mParams.Clear(); |
551 | 0 | } |
552 | | |
553 | | template<typename... Params> |
554 | | void SetCancelMessage(const nsACString& aMessageName, Params&&... aParams) |
555 | 0 | { |
556 | 0 | MOZ_ASSERT(mOwner); |
557 | 0 | MOZ_ASSERT(mMessageName.EqualsLiteral("InterceptionFailedWithURL")); |
558 | 0 | MOZ_ASSERT(mParams.Length() == 1); |
559 | 0 | mMessageName = aMessageName; |
560 | 0 | mParams.Clear(); |
561 | 0 | StringArrayAppender::Append(mParams, sizeof...(Params), |
562 | 0 | std::forward<Params>(aParams)...); |
563 | 0 | } Unexecuted instantiation: Unified_cpp_dom_serviceworkers0.cpp:void mozilla::dom::(anonymous namespace)::AutoCancel::SetCancelMessage<nsTString<char16_t> const&>(nsTSubstring<char> const&, nsTString<char16_t> const&) Unexecuted instantiation: Unified_cpp_dom_serviceworkers0.cpp:void mozilla::dom::(anonymous namespace)::AutoCancel::SetCancelMessage<nsTString<char16_t> const&, NS_ConvertASCIItoUTF16&>(nsTSubstring<char> const&, nsTString<char16_t> const&, NS_ConvertASCIItoUTF16&) Unexecuted instantiation: Unified_cpp_dom_serviceworkers0.cpp:void mozilla::dom::(anonymous namespace)::AutoCancel::SetCancelMessage<nsTString<char16_t> const&, NS_ConvertUTF8toUTF16&>(nsTSubstring<char> const&, nsTString<char16_t> const&, NS_ConvertUTF8toUTF16&) |
564 | | |
565 | | template<typename... Params> |
566 | | void SetCancelMessageAndLocation(const nsACString& aSourceSpec, |
567 | | uint32_t aLine, uint32_t aColumn, |
568 | | const nsACString& aMessageName, |
569 | | Params&&... aParams) |
570 | 0 | { |
571 | 0 | MOZ_ASSERT(mOwner); |
572 | 0 | MOZ_ASSERT(mMessageName.EqualsLiteral("InterceptionFailedWithURL")); |
573 | 0 | MOZ_ASSERT(mParams.Length() == 1); |
574 | 0 |
|
575 | 0 | mSourceSpec = aSourceSpec; |
576 | 0 | mLine = aLine; |
577 | 0 | mColumn = aColumn; |
578 | 0 |
|
579 | 0 | mMessageName = aMessageName; |
580 | 0 | mParams.Clear(); |
581 | 0 | StringArrayAppender::Append(mParams, sizeof...(Params), |
582 | 0 | std::forward<Params>(aParams)...); |
583 | 0 | } |
584 | | |
585 | | void Reset() |
586 | 0 | { |
587 | 0 | mOwner = nullptr; |
588 | 0 | } |
589 | | }; |
590 | | |
591 | | NS_IMPL_ISUPPORTS0(RespondWithHandler) |
592 | | |
593 | | void |
594 | | RespondWithHandler::ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) |
595 | 0 | { |
596 | 0 | AutoCancel autoCancel(this, mRequestURL); |
597 | 0 | mInterceptedChannel->SetFinishResponseStart(TimeStamp::Now()); |
598 | 0 |
|
599 | 0 | if (!aValue.isObject()) { |
600 | 0 | NS_WARNING("FetchEvent::RespondWith was passed a promise resolved to a non-Object value"); |
601 | 0 |
|
602 | 0 | nsCString sourceSpec; |
603 | 0 | uint32_t line = 0; |
604 | 0 | uint32_t column = 0; |
605 | 0 | nsString valueString; |
606 | 0 | nsContentUtils::ExtractErrorValues(aCx, aValue, sourceSpec, &line, &column, |
607 | 0 | valueString); |
608 | 0 |
|
609 | 0 | autoCancel.SetCancelMessageAndLocation(sourceSpec, line, column, |
610 | 0 | NS_LITERAL_CSTRING("InterceptedNonResponseWithURL"), |
611 | 0 | mRequestURL, valueString); |
612 | 0 | return; |
613 | 0 | } |
614 | 0 |
|
615 | 0 | RefPtr<Response> response; |
616 | 0 | nsresult rv = UNWRAP_OBJECT(Response, &aValue.toObject(), response); |
617 | 0 | if (NS_FAILED(rv)) { |
618 | 0 | nsCString sourceSpec; |
619 | 0 | uint32_t line = 0; |
620 | 0 | uint32_t column = 0; |
621 | 0 | nsString valueString; |
622 | 0 | nsContentUtils::ExtractErrorValues(aCx, aValue, sourceSpec, &line, &column, |
623 | 0 | valueString); |
624 | 0 |
|
625 | 0 | autoCancel.SetCancelMessageAndLocation(sourceSpec, line, column, |
626 | 0 | NS_LITERAL_CSTRING("InterceptedNonResponseWithURL"), |
627 | 0 | mRequestURL, valueString); |
628 | 0 | return; |
629 | 0 | } |
630 | 0 |
|
631 | 0 | WorkerPrivate* worker = GetCurrentThreadWorkerPrivate(); |
632 | 0 | MOZ_ASSERT(worker); |
633 | 0 | worker->AssertIsOnWorkerThread(); |
634 | 0 |
|
635 | 0 | // Section "HTTP Fetch", step 3.3: |
636 | 0 | // If one of the following conditions is true, return a network error: |
637 | 0 | // * response's type is "error". |
638 | 0 | // * request's mode is not "no-cors" and response's type is "opaque". |
639 | 0 | // * request's redirect mode is not "manual" and response's type is |
640 | 0 | // "opaqueredirect". |
641 | 0 | // * request's redirect mode is not "follow" and response's url list |
642 | 0 | // has more than one item. |
643 | 0 |
|
644 | 0 | if (response->Type() == ResponseType::Error) { |
645 | 0 | autoCancel.SetCancelMessage( |
646 | 0 | NS_LITERAL_CSTRING("InterceptedErrorResponseWithURL"), mRequestURL); |
647 | 0 | return; |
648 | 0 | } |
649 | 0 |
|
650 | 0 | MOZ_ASSERT_IF(mIsClientRequest, mRequestMode == RequestMode::Same_origin || |
651 | 0 | mRequestMode == RequestMode::Navigate); |
652 | 0 |
|
653 | 0 | if (response->Type() == ResponseType::Opaque && mRequestMode != RequestMode::No_cors) { |
654 | 0 | uint32_t mode = static_cast<uint32_t>(mRequestMode); |
655 | 0 | NS_ConvertASCIItoUTF16 modeString(RequestModeValues::strings[mode].value, |
656 | 0 | RequestModeValues::strings[mode].length); |
657 | 0 |
|
658 | 0 | autoCancel.SetCancelMessage( |
659 | 0 | NS_LITERAL_CSTRING("BadOpaqueInterceptionRequestModeWithURL"), |
660 | 0 | mRequestURL, modeString); |
661 | 0 | return; |
662 | 0 | } |
663 | 0 |
|
664 | 0 | if (mRequestRedirectMode != RequestRedirect::Manual && |
665 | 0 | response->Type() == ResponseType::Opaqueredirect) { |
666 | 0 | autoCancel.SetCancelMessage( |
667 | 0 | NS_LITERAL_CSTRING("BadOpaqueRedirectInterceptionWithURL"), mRequestURL); |
668 | 0 | return; |
669 | 0 | } |
670 | 0 |
|
671 | 0 | if (mRequestRedirectMode != RequestRedirect::Follow && response->Redirected()) { |
672 | 0 | autoCancel.SetCancelMessage( |
673 | 0 | NS_LITERAL_CSTRING("BadRedirectModeInterceptionWithURL"), mRequestURL); |
674 | 0 | return; |
675 | 0 | } |
676 | 0 |
|
677 | 0 | if (NS_WARN_IF(response->BodyUsed())) { |
678 | 0 | autoCancel.SetCancelMessage( |
679 | 0 | NS_LITERAL_CSTRING("InterceptedUsedResponseWithURL"), mRequestURL); |
680 | 0 | return; |
681 | 0 | } |
682 | 0 |
|
683 | 0 | RefPtr<InternalResponse> ir = response->GetInternalResponse(); |
684 | 0 | if (NS_WARN_IF(!ir)) { |
685 | 0 | return; |
686 | 0 | } |
687 | 0 | |
688 | 0 | // An extra safety check to make sure our invariant that opaque and cors |
689 | 0 | // responses always have a URL does not break. |
690 | 0 | if (NS_WARN_IF((response->Type() == ResponseType::Opaque || |
691 | 0 | response->Type() == ResponseType::Cors) && |
692 | 0 | ir->GetUnfilteredURL().IsEmpty())) { |
693 | 0 | MOZ_DIAGNOSTIC_ASSERT(false, "Cors or opaque Response without a URL"); |
694 | 0 | return; |
695 | 0 | } |
696 | 0 | |
697 | 0 | Telemetry::ScalarAdd(Telemetry::ScalarID::SW_SYNTHESIZED_RES_COUNT, 1); |
698 | 0 |
|
699 | 0 | if (mRequestMode == RequestMode::Same_origin && |
700 | 0 | response->Type() == ResponseType::Cors) { |
701 | 0 | Telemetry::ScalarAdd(Telemetry::ScalarID::SW_CORS_RES_FOR_SO_REQ_COUNT, 1); |
702 | 0 |
|
703 | 0 | // XXXtt: Will have a pref to enable the quirk response in bug 1419684. |
704 | 0 | // The variadic template provided by StringArrayAppender requires exactly |
705 | 0 | // an nsString. |
706 | 0 | NS_ConvertUTF8toUTF16 responseURL(ir->GetUnfilteredURL()); |
707 | 0 | autoCancel.SetCancelMessage( |
708 | 0 | NS_LITERAL_CSTRING("CorsResponseForSameOriginRequest"), mRequestURL, |
709 | 0 | responseURL); |
710 | 0 | return; |
711 | 0 | } |
712 | 0 |
|
713 | 0 | // Propagate the URL to the content if the request mode is not "navigate". |
714 | 0 | // Note that, we only reflect the final URL if the response.redirected is |
715 | 0 | // false. We propagate all the URLs if the response.redirected is true. |
716 | 0 | nsCString responseURL; |
717 | 0 | if (mRequestMode != RequestMode::Navigate) { |
718 | 0 | responseURL = ir->GetUnfilteredURL(); |
719 | 0 |
|
720 | 0 | // Similar to how we apply the request fragment to redirects automatically |
721 | 0 | // we also want to apply it automatically when propagating the response |
722 | 0 | // URL from a service worker interception. Currently response.url strips |
723 | 0 | // the fragment, so this will never conflict with an existing fragment |
724 | 0 | // on the response. In the future we will have to check for a response |
725 | 0 | // fragment and avoid overriding in that case. |
726 | 0 | if (!mRequestFragment.IsEmpty() && !responseURL.IsEmpty()) { |
727 | 0 | MOZ_ASSERT(!responseURL.Contains('#')); |
728 | 0 | responseURL.Append(NS_LITERAL_CSTRING("#")); |
729 | 0 | responseURL.Append(mRequestFragment); |
730 | 0 | } |
731 | 0 | } |
732 | 0 |
|
733 | 0 | UniquePtr<RespondWithClosure> closure(new RespondWithClosure(mInterceptedChannel, |
734 | 0 | mRegistration, |
735 | 0 | mRequestURL, |
736 | 0 | mRespondWithScriptSpec, |
737 | 0 | mRespondWithLineNumber, |
738 | 0 | mRespondWithColumnNumber)); |
739 | 0 |
|
740 | 0 | nsCOMPtr<nsIRunnable> startRunnable = new StartResponse(mInterceptedChannel, |
741 | 0 | ir, |
742 | 0 | worker->GetChannelInfo(), |
743 | 0 | mScriptSpec, |
744 | 0 | responseURL, |
745 | 0 | std::move(closure)); |
746 | 0 |
|
747 | 0 | nsCOMPtr<nsIInputStream> body; |
748 | 0 | ir->GetUnfilteredBody(getter_AddRefs(body)); |
749 | 0 | // Errors and redirects may not have a body. |
750 | 0 | if (body) { |
751 | 0 | ErrorResult error; |
752 | 0 | response->SetBodyUsed(aCx, error); |
753 | 0 | if (NS_WARN_IF(error.Failed())) { |
754 | 0 | autoCancel.SetCancelErrorResult(aCx, error); |
755 | 0 | return; |
756 | 0 | } |
757 | 0 | } |
758 | 0 | |
759 | 0 | MOZ_ALWAYS_SUCCEEDS(worker->DispatchToMainThread(startRunnable.forget())); |
760 | 0 |
|
761 | 0 | MOZ_ASSERT(!closure); |
762 | 0 | autoCancel.Reset(); |
763 | 0 | mRequestWasHandled = true; |
764 | 0 | } |
765 | | |
766 | | void |
767 | | RespondWithHandler::RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) |
768 | 0 | { |
769 | 0 | nsCString sourceSpec = mRespondWithScriptSpec; |
770 | 0 | uint32_t line = mRespondWithLineNumber; |
771 | 0 | uint32_t column = mRespondWithColumnNumber; |
772 | 0 | nsString valueString; |
773 | 0 |
|
774 | 0 | mInterceptedChannel->SetFinishResponseStart(TimeStamp::Now()); |
775 | 0 |
|
776 | 0 | nsContentUtils::ExtractErrorValues(aCx, aValue, sourceSpec, &line, &column, |
777 | 0 | valueString); |
778 | 0 |
|
779 | 0 | ::AsyncLog(mInterceptedChannel, sourceSpec, line, column, |
780 | 0 | NS_LITERAL_CSTRING("InterceptionRejectedResponseWithURL"), |
781 | 0 | mRequestURL, valueString); |
782 | 0 |
|
783 | 0 | CancelRequest(NS_ERROR_INTERCEPTION_FAILED); |
784 | 0 | } |
785 | | |
786 | | void |
787 | | RespondWithHandler::CancelRequest(nsresult aStatus) |
788 | 0 | { |
789 | 0 | nsCOMPtr<nsIRunnable> runnable = |
790 | 0 | new CancelChannelRunnable(mInterceptedChannel, mRegistration, aStatus); |
791 | 0 | // Note, this may run off the worker thread during worker termination. |
792 | 0 | WorkerPrivate* worker = GetCurrentThreadWorkerPrivate(); |
793 | 0 | if (worker) { |
794 | 0 | MOZ_ALWAYS_SUCCEEDS(worker->DispatchToMainThread(runnable.forget())); |
795 | 0 | } else { |
796 | 0 | MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable.forget())); |
797 | 0 | } |
798 | 0 | mRequestWasHandled = true; |
799 | 0 | } |
800 | | |
801 | | } // namespace |
802 | | |
803 | | void |
804 | | FetchEvent::RespondWith(JSContext* aCx, Promise& aArg, ErrorResult& aRv) |
805 | 0 | { |
806 | 0 | if (EventPhase() == Event_Binding::NONE || mWaitToRespond) { |
807 | 0 | aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); |
808 | 0 | return; |
809 | 0 | } |
810 | 0 | |
811 | 0 | |
812 | 0 | // Record where respondWith() was called in the script so we can include the |
813 | 0 | // information in any error reporting. We should be guaranteed not to get |
814 | 0 | // a file:// string here because service workers require http/https. |
815 | 0 | nsCString spec; |
816 | 0 | uint32_t line = 0; |
817 | 0 | uint32_t column = 0; |
818 | 0 | nsJSUtils::GetCallingLocation(aCx, spec, &line, &column); |
819 | 0 |
|
820 | 0 | RefPtr<InternalRequest> ir = mRequest->GetInternalRequest(); |
821 | 0 |
|
822 | 0 | nsAutoCString requestURL; |
823 | 0 | ir->GetURL(requestURL); |
824 | 0 |
|
825 | 0 | StopImmediatePropagation(); |
826 | 0 | mWaitToRespond = true; |
827 | 0 | RefPtr<RespondWithHandler> handler = |
828 | 0 | new RespondWithHandler(mChannel, mRegistration, mRequest->Mode(), |
829 | 0 | ir->IsClientRequest(), mRequest->Redirect(), |
830 | 0 | mScriptSpec, NS_ConvertUTF8toUTF16(requestURL), |
831 | 0 | ir->GetFragment(), spec, line, column); |
832 | 0 | aArg.AppendNativeHandler(handler); |
833 | 0 |
|
834 | 0 | if (!WaitOnPromise(aArg)) { |
835 | 0 | aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); |
836 | 0 | } |
837 | 0 | } |
838 | | |
839 | | void |
840 | | FetchEvent::PreventDefault(JSContext* aCx, CallerType aCallerType) |
841 | 0 | { |
842 | 0 | MOZ_ASSERT(aCx); |
843 | 0 | MOZ_ASSERT(aCallerType != CallerType::System, |
844 | 0 | "Since when do we support system-principal service workers?"); |
845 | 0 |
|
846 | 0 | if (mPreventDefaultScriptSpec.IsEmpty()) { |
847 | 0 | // Note when the FetchEvent might have been canceled by script, but don't |
848 | 0 | // actually log the location until we are sure it matters. This is |
849 | 0 | // determined in ServiceWorkerPrivate.cpp. We only remember the first |
850 | 0 | // call to preventDefault() as its the most likely to have actually canceled |
851 | 0 | // the event. |
852 | 0 | nsJSUtils::GetCallingLocation(aCx, mPreventDefaultScriptSpec, |
853 | 0 | &mPreventDefaultLineNumber, |
854 | 0 | &mPreventDefaultColumnNumber); |
855 | 0 | } |
856 | 0 |
|
857 | 0 | Event::PreventDefault(aCx, aCallerType); |
858 | 0 | } |
859 | | |
860 | | void |
861 | | FetchEvent::ReportCanceled() |
862 | 0 | { |
863 | 0 | MOZ_ASSERT(!mPreventDefaultScriptSpec.IsEmpty()); |
864 | 0 |
|
865 | 0 | RefPtr<InternalRequest> ir = mRequest->GetInternalRequest(); |
866 | 0 | nsAutoCString url; |
867 | 0 | ir->GetURL(url); |
868 | 0 |
|
869 | 0 | // The variadic template provided by StringArrayAppender requires exactly |
870 | 0 | // an nsString. |
871 | 0 | NS_ConvertUTF8toUTF16 requestURL(url); |
872 | 0 | //nsString requestURL; |
873 | 0 | //CopyUTF8toUTF16(url, requestURL); |
874 | 0 |
|
875 | 0 | ::AsyncLog(mChannel.get(), mPreventDefaultScriptSpec, |
876 | 0 | mPreventDefaultLineNumber, mPreventDefaultColumnNumber, |
877 | 0 | NS_LITERAL_CSTRING("InterceptionCanceledWithURL"), requestURL); |
878 | 0 | } |
879 | | |
880 | | namespace { |
881 | | |
882 | | class WaitUntilHandler final : public PromiseNativeHandler |
883 | | { |
884 | | WorkerPrivate* mWorkerPrivate; |
885 | | const nsCString mScope; |
886 | | nsCString mSourceSpec; |
887 | | uint32_t mLine; |
888 | | uint32_t mColumn; |
889 | | nsString mRejectValue; |
890 | | |
891 | | ~WaitUntilHandler() |
892 | 0 | { |
893 | 0 | } |
894 | | |
895 | | public: |
896 | | NS_DECL_THREADSAFE_ISUPPORTS |
897 | | |
898 | | WaitUntilHandler(WorkerPrivate* aWorkerPrivate, JSContext* aCx) |
899 | | : mWorkerPrivate(aWorkerPrivate) |
900 | | , mScope(mWorkerPrivate->ServiceWorkerScope()) |
901 | | , mLine(0) |
902 | | , mColumn(0) |
903 | 0 | { |
904 | 0 | mWorkerPrivate->AssertIsOnWorkerThread(); |
905 | 0 |
|
906 | 0 | // Save the location of the waitUntil() call itself as a fallback |
907 | 0 | // in case the rejection value does not contain any location info. |
908 | 0 | nsJSUtils::GetCallingLocation(aCx, mSourceSpec, &mLine, &mColumn); |
909 | 0 | } |
910 | | |
911 | | void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override |
912 | 0 | { |
913 | 0 | // do nothing, we are only here to report errors |
914 | 0 | } |
915 | | |
916 | | void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override |
917 | 0 | { |
918 | 0 | mWorkerPrivate->AssertIsOnWorkerThread(); |
919 | 0 |
|
920 | 0 | nsCString spec; |
921 | 0 | uint32_t line = 0; |
922 | 0 | uint32_t column = 0; |
923 | 0 | nsContentUtils::ExtractErrorValues(aCx, aValue, spec, &line, &column, |
924 | 0 | mRejectValue); |
925 | 0 |
|
926 | 0 | // only use the extracted location if we found one |
927 | 0 | if (!spec.IsEmpty()) { |
928 | 0 | mSourceSpec = spec; |
929 | 0 | mLine = line; |
930 | 0 | mColumn = column; |
931 | 0 | } |
932 | 0 |
|
933 | 0 | MOZ_ALWAYS_SUCCEEDS(mWorkerPrivate->DispatchToMainThread( |
934 | 0 | NewRunnableMethod("WaitUntilHandler::ReportOnMainThread", |
935 | 0 | this, &WaitUntilHandler::ReportOnMainThread))); |
936 | 0 | } |
937 | | |
938 | | void |
939 | | ReportOnMainThread() |
940 | 0 | { |
941 | 0 | MOZ_ASSERT(NS_IsMainThread()); |
942 | 0 | RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); |
943 | 0 | if (!swm) { |
944 | 0 | // browser shutdown |
945 | 0 | return; |
946 | 0 | } |
947 | 0 | |
948 | 0 | // TODO: Make the error message a localized string. (bug 1222720) |
949 | 0 | nsString message; |
950 | 0 | message.AppendLiteral("Service worker event waitUntil() was passed a " |
951 | 0 | "promise that rejected with '"); |
952 | 0 | message.Append(mRejectValue); |
953 | 0 | message.AppendLiteral("'."); |
954 | 0 |
|
955 | 0 | // Note, there is a corner case where this won't report to the window |
956 | 0 | // that triggered the error. Consider a navigation fetch event that |
957 | 0 | // rejects waitUntil() without holding respondWith() open. In this case |
958 | 0 | // there is no controlling document yet, the window did call .register() |
959 | 0 | // because there is no documeny yet, and the navigation is no longer |
960 | 0 | // being intercepted. |
961 | 0 |
|
962 | 0 | swm->ReportToAllClients(mScope, message, NS_ConvertUTF8toUTF16(mSourceSpec), |
963 | 0 | EmptyString(), mLine, mColumn, |
964 | 0 | nsIScriptError::errorFlag); |
965 | 0 | } |
966 | | }; |
967 | | |
968 | | NS_IMPL_ISUPPORTS0(WaitUntilHandler) |
969 | | |
970 | | } // anonymous namespace |
971 | | |
972 | | NS_IMPL_ADDREF_INHERITED(FetchEvent, ExtendableEvent) |
973 | | NS_IMPL_RELEASE_INHERITED(FetchEvent, ExtendableEvent) |
974 | | |
975 | 0 | NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FetchEvent) |
976 | 0 | NS_INTERFACE_MAP_END_INHERITING(ExtendableEvent) |
977 | | |
978 | | NS_IMPL_CYCLE_COLLECTION_INHERITED(FetchEvent, ExtendableEvent, mRequest) |
979 | | |
980 | | ExtendableEvent::ExtendableEvent(EventTarget* aOwner) |
981 | | : Event(aOwner, nullptr, nullptr) |
982 | 0 | { |
983 | 0 | } |
984 | | |
985 | | bool |
986 | | ExtendableEvent::WaitOnPromise(Promise& aPromise) |
987 | 0 | { |
988 | 0 | if (!mExtensionsHandler) { |
989 | 0 | return false; |
990 | 0 | } |
991 | 0 | return mExtensionsHandler->WaitOnPromise(aPromise); |
992 | 0 | } |
993 | | |
994 | | void |
995 | | ExtendableEvent::SetKeepAliveHandler(ExtensionsHandler* aExtensionsHandler) |
996 | 0 | { |
997 | 0 | MOZ_ASSERT(!mExtensionsHandler); |
998 | 0 | WorkerPrivate* worker = GetCurrentThreadWorkerPrivate(); |
999 | 0 | MOZ_ASSERT(worker); |
1000 | 0 | worker->AssertIsOnWorkerThread(); |
1001 | 0 | mExtensionsHandler = aExtensionsHandler; |
1002 | 0 | } |
1003 | | |
1004 | | void |
1005 | | ExtendableEvent::WaitUntil(JSContext* aCx, Promise& aPromise, ErrorResult& aRv) |
1006 | 0 | { |
1007 | 0 | MOZ_ASSERT(!NS_IsMainThread()); |
1008 | 0 |
|
1009 | 0 | if (!WaitOnPromise(aPromise)) { |
1010 | 0 | aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR); |
1011 | 0 | return; |
1012 | 0 | } |
1013 | 0 | |
1014 | 0 | // Append our handler to each waitUntil promise separately so we |
1015 | 0 | // can record the location in script where waitUntil was called. |
1016 | 0 | RefPtr<WaitUntilHandler> handler = |
1017 | 0 | new WaitUntilHandler(GetCurrentThreadWorkerPrivate(), aCx); |
1018 | 0 | aPromise.AppendNativeHandler(handler); |
1019 | 0 | } |
1020 | | |
1021 | | NS_IMPL_ADDREF_INHERITED(ExtendableEvent, Event) |
1022 | | NS_IMPL_RELEASE_INHERITED(ExtendableEvent, Event) |
1023 | | |
1024 | 0 | NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ExtendableEvent) |
1025 | 0 | NS_INTERFACE_MAP_END_INHERITING(Event) |
1026 | | |
1027 | | namespace { |
1028 | | nsresult |
1029 | | ExtractBytesFromUSVString(const nsAString& aStr, nsTArray<uint8_t>& aBytes) |
1030 | 0 | { |
1031 | 0 | MOZ_ASSERT(aBytes.IsEmpty()); |
1032 | 0 | auto encoder = UTF_8_ENCODING->NewEncoder(); |
1033 | 0 | CheckedInt<size_t> needed = |
1034 | 0 | encoder->MaxBufferLengthFromUTF16WithoutReplacement(aStr.Length()); |
1035 | 0 | if (NS_WARN_IF(!needed.isValid() || |
1036 | 0 | !aBytes.SetLength(needed.value(), fallible))) { |
1037 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
1038 | 0 | } |
1039 | 0 | uint32_t result; |
1040 | 0 | size_t read; |
1041 | 0 | size_t written; |
1042 | 0 | Tie(result, read, written) = |
1043 | 0 | encoder->EncodeFromUTF16WithoutReplacement(aStr, aBytes, true); |
1044 | 0 | MOZ_ASSERT(result == kInputEmpty); |
1045 | 0 | MOZ_ASSERT(read == aStr.Length()); |
1046 | 0 | aBytes.TruncateLength(written); |
1047 | 0 | return NS_OK; |
1048 | 0 | } |
1049 | | |
1050 | | nsresult |
1051 | | ExtractBytesFromData(const OwningArrayBufferViewOrArrayBufferOrUSVString& aDataInit, nsTArray<uint8_t>& aBytes) |
1052 | 0 | { |
1053 | 0 | if (aDataInit.IsArrayBufferView()) { |
1054 | 0 | const ArrayBufferView& view = aDataInit.GetAsArrayBufferView(); |
1055 | 0 | if (NS_WARN_IF(!PushUtil::CopyArrayBufferViewToArray(view, aBytes))) { |
1056 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
1057 | 0 | } |
1058 | 0 | return NS_OK; |
1059 | 0 | } |
1060 | 0 | if (aDataInit.IsArrayBuffer()) { |
1061 | 0 | const ArrayBuffer& buffer = aDataInit.GetAsArrayBuffer(); |
1062 | 0 | if (NS_WARN_IF(!PushUtil::CopyArrayBufferToArray(buffer, aBytes))) { |
1063 | 0 | return NS_ERROR_OUT_OF_MEMORY; |
1064 | 0 | } |
1065 | 0 | return NS_OK; |
1066 | 0 | } |
1067 | 0 | if (aDataInit.IsUSVString()) { |
1068 | 0 | return ExtractBytesFromUSVString(aDataInit.GetAsUSVString(), aBytes); |
1069 | 0 | } |
1070 | 0 | MOZ_ASSERT_UNREACHABLE("Unexpected push message data"); |
1071 | 0 | return NS_ERROR_FAILURE; |
1072 | 0 | } |
1073 | | } |
1074 | | |
1075 | | PushMessageData::PushMessageData(nsISupports* aOwner, |
1076 | | nsTArray<uint8_t>&& aBytes) |
1077 | 0 | : mOwner(aOwner), mBytes(std::move(aBytes)) {} |
1078 | | |
1079 | | PushMessageData::~PushMessageData() |
1080 | 0 | { |
1081 | 0 | } |
1082 | | |
1083 | | NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PushMessageData, mOwner) |
1084 | | |
1085 | | NS_IMPL_CYCLE_COLLECTING_ADDREF(PushMessageData) |
1086 | | NS_IMPL_CYCLE_COLLECTING_RELEASE(PushMessageData) |
1087 | | |
1088 | 0 | NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PushMessageData) |
1089 | 0 | NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY |
1090 | 0 | NS_INTERFACE_MAP_ENTRY(nsISupports) |
1091 | 0 | NS_INTERFACE_MAP_END |
1092 | | |
1093 | | JSObject* |
1094 | | PushMessageData::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) |
1095 | 0 | { |
1096 | 0 | return mozilla::dom::PushMessageData_Binding::Wrap(aCx, this, aGivenProto); |
1097 | 0 | } |
1098 | | |
1099 | | void |
1100 | | PushMessageData::Json(JSContext* cx, JS::MutableHandle<JS::Value> aRetval, |
1101 | | ErrorResult& aRv) |
1102 | 0 | { |
1103 | 0 | if (NS_FAILED(EnsureDecodedText())) { |
1104 | 0 | aRv.Throw(NS_ERROR_DOM_UNKNOWN_ERR); |
1105 | 0 | return; |
1106 | 0 | } |
1107 | 0 | BodyUtil::ConsumeJson(cx, aRetval, mDecodedText, aRv); |
1108 | 0 | } |
1109 | | |
1110 | | void |
1111 | | PushMessageData::Text(nsAString& aData) |
1112 | 0 | { |
1113 | 0 | if (NS_SUCCEEDED(EnsureDecodedText())) { |
1114 | 0 | aData = mDecodedText; |
1115 | 0 | } |
1116 | 0 | } |
1117 | | |
1118 | | void |
1119 | | PushMessageData::ArrayBuffer(JSContext* cx, |
1120 | | JS::MutableHandle<JSObject*> aRetval, |
1121 | | ErrorResult& aRv) |
1122 | 0 | { |
1123 | 0 | uint8_t* data = GetContentsCopy(); |
1124 | 0 | if (data) { |
1125 | 0 | BodyUtil::ConsumeArrayBuffer(cx, aRetval, mBytes.Length(), data, aRv); |
1126 | 0 | } |
1127 | 0 | } |
1128 | | |
1129 | | already_AddRefed<mozilla::dom::Blob> |
1130 | | PushMessageData::Blob(ErrorResult& aRv) |
1131 | 0 | { |
1132 | 0 | uint8_t* data = GetContentsCopy(); |
1133 | 0 | if (data) { |
1134 | 0 | RefPtr<mozilla::dom::Blob> blob = BodyUtil::ConsumeBlob( |
1135 | 0 | mOwner, EmptyString(), mBytes.Length(), data, aRv); |
1136 | 0 | if (blob) { |
1137 | 0 | return blob.forget(); |
1138 | 0 | } |
1139 | 0 | } |
1140 | 0 | return nullptr; |
1141 | 0 | } |
1142 | | |
1143 | | nsresult |
1144 | | PushMessageData::EnsureDecodedText() |
1145 | 0 | { |
1146 | 0 | if (mBytes.IsEmpty() || !mDecodedText.IsEmpty()) { |
1147 | 0 | return NS_OK; |
1148 | 0 | } |
1149 | 0 | nsresult rv = BodyUtil::ConsumeText( |
1150 | 0 | mBytes.Length(), |
1151 | 0 | reinterpret_cast<uint8_t*>(mBytes.Elements()), |
1152 | 0 | mDecodedText |
1153 | 0 | ); |
1154 | 0 | if (NS_WARN_IF(NS_FAILED(rv))) { |
1155 | 0 | mDecodedText.Truncate(); |
1156 | 0 | return rv; |
1157 | 0 | } |
1158 | 0 | return NS_OK; |
1159 | 0 | } |
1160 | | |
1161 | | uint8_t* |
1162 | | PushMessageData::GetContentsCopy() |
1163 | 0 | { |
1164 | 0 | uint32_t length = mBytes.Length(); |
1165 | 0 | void* data = malloc(length); |
1166 | 0 | if (!data) { |
1167 | 0 | return nullptr; |
1168 | 0 | } |
1169 | 0 | memcpy(data, mBytes.Elements(), length); |
1170 | 0 | return reinterpret_cast<uint8_t*>(data); |
1171 | 0 | } |
1172 | | |
1173 | | PushEvent::PushEvent(EventTarget* aOwner) |
1174 | | : ExtendableEvent(aOwner) |
1175 | 0 | { |
1176 | 0 | } |
1177 | | |
1178 | | already_AddRefed<PushEvent> |
1179 | | PushEvent::Constructor(mozilla::dom::EventTarget* aOwner, |
1180 | | const nsAString& aType, |
1181 | | const PushEventInit& aOptions, |
1182 | | ErrorResult& aRv) |
1183 | 0 | { |
1184 | 0 | RefPtr<PushEvent> e = new PushEvent(aOwner); |
1185 | 0 | bool trusted = e->Init(aOwner); |
1186 | 0 | e->InitEvent(aType, aOptions.mBubbles, aOptions.mCancelable); |
1187 | 0 | e->SetTrusted(trusted); |
1188 | 0 | e->SetComposed(aOptions.mComposed); |
1189 | 0 | if(aOptions.mData.WasPassed()){ |
1190 | 0 | nsTArray<uint8_t> bytes; |
1191 | 0 | nsresult rv = ExtractBytesFromData(aOptions.mData.Value(), bytes); |
1192 | 0 | if (NS_FAILED(rv)) { |
1193 | 0 | aRv.Throw(rv); |
1194 | 0 | return nullptr; |
1195 | 0 | } |
1196 | 0 | e->mData = new PushMessageData(aOwner, std::move(bytes)); |
1197 | 0 | } |
1198 | 0 | return e.forget(); |
1199 | 0 | } |
1200 | | |
1201 | | NS_IMPL_ADDREF_INHERITED(PushEvent, ExtendableEvent) |
1202 | | NS_IMPL_RELEASE_INHERITED(PushEvent, ExtendableEvent) |
1203 | | |
1204 | 0 | NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PushEvent) |
1205 | 0 | NS_INTERFACE_MAP_END_INHERITING(ExtendableEvent) |
1206 | | |
1207 | | NS_IMPL_CYCLE_COLLECTION_INHERITED(PushEvent, ExtendableEvent, mData) |
1208 | | |
1209 | | JSObject* |
1210 | | PushEvent::WrapObjectInternal(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) |
1211 | 0 | { |
1212 | 0 | return mozilla::dom::PushEvent_Binding::Wrap(aCx, this, aGivenProto); |
1213 | 0 | } |
1214 | | |
1215 | | ExtendableMessageEvent::ExtendableMessageEvent(EventTarget* aOwner) |
1216 | | : ExtendableEvent(aOwner) |
1217 | | , mData(JS::UndefinedValue()) |
1218 | 0 | { |
1219 | 0 | mozilla::HoldJSObjects(this); |
1220 | 0 | } |
1221 | | |
1222 | | ExtendableMessageEvent::~ExtendableMessageEvent() |
1223 | 0 | { |
1224 | 0 | mData.setUndefined(); |
1225 | 0 | DropJSObjects(this); |
1226 | 0 | } |
1227 | | |
1228 | | void |
1229 | | ExtendableMessageEvent::GetData(JSContext* aCx, |
1230 | | JS::MutableHandle<JS::Value> aData, |
1231 | | ErrorResult& aRv) |
1232 | 0 | { |
1233 | 0 | aData.set(mData); |
1234 | 0 | if (!JS_WrapValue(aCx, aData)) { |
1235 | 0 | aRv.Throw(NS_ERROR_FAILURE); |
1236 | 0 | } |
1237 | 0 | } |
1238 | | |
1239 | | void |
1240 | | ExtendableMessageEvent::GetSource(Nullable<OwningClientOrServiceWorkerOrMessagePort>& aValue) const |
1241 | 0 | { |
1242 | 0 | if (mClient) { |
1243 | 0 | aValue.SetValue().SetAsClient() = mClient; |
1244 | 0 | } else if (mServiceWorker) { |
1245 | 0 | aValue.SetValue().SetAsServiceWorker() = mServiceWorker; |
1246 | 0 | } else if (mMessagePort) { |
1247 | 0 | aValue.SetValue().SetAsMessagePort() = mMessagePort; |
1248 | 0 | } else { |
1249 | 0 | // nullptr source is possible for manually constructed event |
1250 | 0 | aValue.SetNull(); |
1251 | 0 | } |
1252 | 0 | } |
1253 | | |
1254 | | /* static */ already_AddRefed<ExtendableMessageEvent> |
1255 | | ExtendableMessageEvent::Constructor(const GlobalObject& aGlobal, |
1256 | | const nsAString& aType, |
1257 | | const ExtendableMessageEventInit& aOptions, |
1258 | | ErrorResult& aRv) |
1259 | 0 | { |
1260 | 0 | nsCOMPtr<EventTarget> t = do_QueryInterface(aGlobal.GetAsSupports()); |
1261 | 0 | return Constructor(t, aType, aOptions, aRv); |
1262 | 0 | } |
1263 | | |
1264 | | /* static */ already_AddRefed<ExtendableMessageEvent> |
1265 | | ExtendableMessageEvent::Constructor(mozilla::dom::EventTarget* aEventTarget, |
1266 | | const nsAString& aType, |
1267 | | const ExtendableMessageEventInit& aOptions, |
1268 | | ErrorResult& aRv) |
1269 | 0 | { |
1270 | 0 | RefPtr<ExtendableMessageEvent> event = new ExtendableMessageEvent(aEventTarget); |
1271 | 0 |
|
1272 | 0 | event->InitEvent(aType, aOptions.mBubbles, aOptions.mCancelable); |
1273 | 0 | bool trusted = event->Init(aEventTarget); |
1274 | 0 | event->SetTrusted(trusted); |
1275 | 0 |
|
1276 | 0 | event->mData = aOptions.mData; |
1277 | 0 | event->mOrigin = aOptions.mOrigin; |
1278 | 0 | event->mLastEventId = aOptions.mLastEventId; |
1279 | 0 |
|
1280 | 0 | if (!aOptions.mSource.IsNull()) { |
1281 | 0 | if (aOptions.mSource.Value().IsClient()) { |
1282 | 0 | event->mClient = aOptions.mSource.Value().GetAsClient(); |
1283 | 0 | } else if (aOptions.mSource.Value().IsServiceWorker()){ |
1284 | 0 | event->mServiceWorker = aOptions.mSource.Value().GetAsServiceWorker(); |
1285 | 0 | } else if (aOptions.mSource.Value().IsMessagePort()){ |
1286 | 0 | event->mMessagePort = aOptions.mSource.Value().GetAsMessagePort(); |
1287 | 0 | } |
1288 | 0 | } |
1289 | 0 |
|
1290 | 0 | event->mPorts.AppendElements(aOptions.mPorts); |
1291 | 0 | return event.forget(); |
1292 | 0 | } |
1293 | | |
1294 | | void |
1295 | | ExtendableMessageEvent::GetPorts(nsTArray<RefPtr<MessagePort>>& aPorts) |
1296 | 0 | { |
1297 | 0 | aPorts = mPorts; |
1298 | 0 | } |
1299 | | |
1300 | | NS_IMPL_CYCLE_COLLECTION_CLASS(ExtendableMessageEvent) |
1301 | | |
1302 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(ExtendableMessageEvent, Event) |
1303 | 0 | tmp->mData.setUndefined(); |
1304 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mClient) |
1305 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mServiceWorker) |
1306 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessagePort) |
1307 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK(mPorts) |
1308 | 0 | NS_IMPL_CYCLE_COLLECTION_UNLINK_END |
1309 | | |
1310 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(ExtendableMessageEvent, Event) |
1311 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mClient) |
1312 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mServiceWorker) |
1313 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessagePort) |
1314 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPorts) |
1315 | 0 | NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END |
1316 | | |
1317 | 0 | NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(ExtendableMessageEvent, Event) |
1318 | 0 | NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mData) |
1319 | 0 | NS_IMPL_CYCLE_COLLECTION_TRACE_END |
1320 | | |
1321 | 0 | NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ExtendableMessageEvent) |
1322 | 0 | NS_INTERFACE_MAP_END_INHERITING(Event) |
1323 | | |
1324 | | NS_IMPL_ADDREF_INHERITED(ExtendableMessageEvent, Event) |
1325 | | NS_IMPL_RELEASE_INHERITED(ExtendableMessageEvent, Event) |
1326 | | |
1327 | | } // namespace dom |
1328 | | } // namespace mozilla |