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