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