WindowContext.cpp (30615B)
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 "mozilla/dom/WindowContext.h" 8 #include "mozilla/dom/WindowGlobalActorsBinding.h" 9 #include "mozilla/dom/WindowGlobalChild.h" 10 #include "mozilla/dom/WindowGlobalParent.h" 11 #include "mozilla/dom/SyncedContextInlines.h" 12 #include "mozilla/dom/BrowsingContext.h" 13 #include "mozilla/dom/CloseWatcherManager.h" 14 #include "mozilla/dom/Document.h" 15 #include "mozilla/dom/DocumentPictureInPicture.h" 16 #include "mozilla/dom/UserActivationIPCUtils.h" 17 #include "mozilla/dom/WorkerCommon.h" 18 #include "mozilla/PermissionDelegateIPCUtils.h" 19 #include "mozilla/StaticPrefs_dom.h" 20 #include "mozilla/StaticPtr.h" 21 #include "mozilla/ClearOnShutdown.h" 22 #include "nsGlobalWindowInner.h" 23 #include "nsIScriptError.h" 24 #include "nsIWebProgressListener.h" 25 #include "nsIXULRuntime.h" 26 #include "nsRFPTargetSetIDL.h" 27 #include "nsRefPtrHashtable.h" 28 #include "nsContentUtils.h" 29 30 namespace mozilla { 31 namespace dom { 32 33 // Explicit specialization of the `Transaction` type. Required by the `extern 34 // template class` declaration in the header. 35 template class syncedcontext::Transaction<WindowContext>; 36 37 static LazyLogModule gWindowContextLog("WindowContext"); 38 static LazyLogModule gWindowContextSyncLog("WindowContextSync"); 39 40 extern mozilla::LazyLogModule gUserInteractionPRLog; 41 42 #define USER_ACTIVATION_LOG(msg, ...) \ 43 MOZ_LOG(gUserInteractionPRLog, LogLevel::Debug, (msg, ##__VA_ARGS__)) 44 45 using WindowContextByIdMap = nsTHashMap<nsUint64HashKey, WindowContext*>; 46 static StaticAutoPtr<WindowContextByIdMap> gWindowContexts; 47 48 /* static */ 49 LogModule* WindowContext::GetLog() { return gWindowContextLog; } 50 51 /* static */ 52 LogModule* WindowContext::GetSyncLog() { return gWindowContextSyncLog; } 53 54 /* static */ 55 already_AddRefed<WindowContext> WindowContext::GetById( 56 uint64_t aInnerWindowId) { 57 if (!gWindowContexts) { 58 return nullptr; 59 } 60 return do_AddRef(gWindowContexts->Get(aInnerWindowId)); 61 } 62 63 BrowsingContextGroup* WindowContext::Group() const { 64 return mBrowsingContext->Group(); 65 } 66 67 WindowGlobalParent* WindowContext::Canonical() { 68 MOZ_RELEASE_ASSERT(XRE_IsParentProcess()); 69 return static_cast<WindowGlobalParent*>(this); 70 } 71 72 bool WindowContext::IsCurrent() const { 73 return mBrowsingContext->mCurrentWindowContext == this; 74 } 75 76 bool WindowContext::IsInBFCache() { 77 if (mozilla::SessionHistoryInParent()) { 78 return mBrowsingContext->IsInBFCache(); 79 } 80 return TopWindowContext()->GetWindowStateSaved(); 81 } 82 83 already_AddRefed<nsIRFPTargetSetIDL> 84 WindowContext::GetOverriddenFingerprintingSettingsWebIDL() const { 85 Maybe<RFPTargetSet> overriddenFingerprintingSettings = 86 GetOverriddenFingerprintingSettings(); 87 if (overriddenFingerprintingSettings.isNothing()) { 88 return nullptr; 89 } 90 91 nsCOMPtr<nsIRFPTargetSetIDL> protections = 92 new nsRFPTargetSetIDL(overriddenFingerprintingSettings.ref()); 93 return protections.forget(); 94 } 95 96 nsGlobalWindowInner* WindowContext::GetInnerWindow() const { 97 return mWindowGlobalChild ? mWindowGlobalChild->GetWindowGlobal() : nullptr; 98 } 99 100 Document* WindowContext::GetDocument() const { 101 nsGlobalWindowInner* innerWindow = GetInnerWindow(); 102 return innerWindow ? innerWindow->GetDocument() : nullptr; 103 } 104 105 Document* WindowContext::GetExtantDoc() const { 106 nsGlobalWindowInner* innerWindow = GetInnerWindow(); 107 return innerWindow ? innerWindow->GetExtantDoc() : nullptr; 108 } 109 110 WindowGlobalChild* WindowContext::GetWindowGlobalChild() const { 111 return mWindowGlobalChild; 112 } 113 114 WindowContext* WindowContext::GetParentWindowContext() { 115 return mBrowsingContext->GetParentWindowContext(); 116 } 117 118 WindowContext* WindowContext::TopWindowContext() { 119 WindowContext* current = this; 120 while (current->GetParentWindowContext()) { 121 current = current->GetParentWindowContext(); 122 } 123 return current; 124 } 125 126 bool WindowContext::IsTop() const { return mBrowsingContext->IsTop(); } 127 128 bool WindowContext::SameOriginWithTop() const { 129 return mBrowsingContext->SameOriginWithTop(); 130 } 131 132 nsIGlobalObject* WindowContext::GetParentObject() const { 133 return xpc::NativeGlobal(xpc::PrivilegedJunkScope()); 134 } 135 136 void WindowContext::AppendChildBrowsingContext( 137 BrowsingContext* aBrowsingContext) { 138 MOZ_DIAGNOSTIC_ASSERT(Group() == aBrowsingContext->Group(), 139 "Mismatched groups?"); 140 MOZ_DIAGNOSTIC_ASSERT(!mChildren.Contains(aBrowsingContext)); 141 142 ClearLightDOMChildren(); 143 144 mChildren.AppendElement(aBrowsingContext); 145 if (!aBrowsingContext->IsEmbedderTypeObjectOrEmbed()) { 146 mNonSyntheticChildren.AppendElement(aBrowsingContext); 147 } 148 149 // If we're the current WindowContext in our BrowsingContext, make sure to 150 // clear any cached `children` value. 151 if (IsCurrent()) { 152 BrowsingContext_Binding::ClearCachedChildrenValue(mBrowsingContext); 153 } 154 } 155 156 void WindowContext::RemoveChildBrowsingContext( 157 BrowsingContext* aBrowsingContext) { 158 MOZ_DIAGNOSTIC_ASSERT(Group() == aBrowsingContext->Group(), 159 "Mismatched groups?"); 160 ClearLightDOMChildren(); 161 162 mChildren.RemoveElement(aBrowsingContext); 163 mNonSyntheticChildren.RemoveElement(aBrowsingContext); 164 165 // If we're the current WindowContext in our BrowsingContext, make sure to 166 // clear any cached `children` value. 167 if (IsCurrent()) { 168 BrowsingContext_Binding::ClearCachedChildrenValue(mBrowsingContext); 169 } 170 } 171 172 void WindowContext::UpdateChildSynthetic(BrowsingContext* aBrowsingContext, 173 bool aIsSynthetic) { 174 if (aIsSynthetic) { 175 mNonSyntheticChildren.RemoveElement(aBrowsingContext); 176 } else { 177 // The same BrowsingContext will be reused for error pages, so it can be in 178 // the list already. 179 if (!mNonSyntheticChildren.Contains(aBrowsingContext)) { 180 mNonSyntheticChildren.AppendElement(aBrowsingContext); 181 } 182 } 183 } 184 185 void WindowContext::ClearLightDOMChildren() { 186 mNonSyntheticLightDOMChildren.reset(); 187 } 188 189 void WindowContext::EnsureLightDOMChildren() { 190 if (mNonSyntheticLightDOMChildren.isSome()) { 191 return; 192 } 193 mNonSyntheticLightDOMChildren.emplace(); 194 195 for (const RefPtr<BrowsingContext>& bc : mNonSyntheticChildren) { 196 if (Element* el = bc->GetEmbedderElement(); el && el->IsInShadowTree()) { 197 continue; 198 } 199 mNonSyntheticLightDOMChildren->AppendElement(bc); 200 } 201 } 202 203 BrowsingContext* WindowContext::NonSyntheticLightDOMChildAt(uint32_t aIndex) { 204 EnsureLightDOMChildren(); 205 return aIndex < mNonSyntheticLightDOMChildren->Length() 206 ? mNonSyntheticLightDOMChildren->ElementAt(aIndex).get() 207 : nullptr; 208 } 209 210 uint32_t WindowContext::NonSyntheticLightDOMChildrenCount() { 211 EnsureLightDOMChildren(); 212 return mNonSyntheticLightDOMChildren->Length(); 213 } 214 215 void WindowContext::SendCommitTransaction(ContentParent* aParent, 216 const BaseTransaction& aTxn, 217 uint64_t aEpoch) { 218 (void)aParent->SendCommitWindowContextTransaction(this, aTxn, aEpoch); 219 } 220 221 void WindowContext::SendCommitTransaction(ContentChild* aChild, 222 const BaseTransaction& aTxn, 223 uint64_t aEpoch) { 224 aChild->SendCommitWindowContextTransaction(this, aTxn, aEpoch); 225 } 226 227 bool WindowContext::CheckOnlyOwningProcessCanSet(ContentParent* aSource) { 228 if (IsInProcess()) { 229 return true; 230 } 231 232 if (XRE_IsParentProcess() && aSource) { 233 return Canonical()->GetContentParent() == aSource; 234 } 235 236 return false; 237 } 238 239 bool WindowContext::CanSet(FieldIndex<IDX_IsSecure>, const bool& aIsSecure, 240 ContentParent* aSource) { 241 return CheckOnlyOwningProcessCanSet(aSource); 242 } 243 244 bool WindowContext::CanSet(FieldIndex<IDX_NeedsBeforeUnload>, 245 const bool& aHasBeforeUnload, 246 ContentParent* aSource) { 247 return CheckOnlyOwningProcessCanSet(aSource); 248 } 249 250 bool WindowContext::CanSet(FieldIndex<IDX_NeedsTraverse>, 251 const bool& aNeedsTraverse, ContentParent* aSource) { 252 return CheckOnlyOwningProcessCanSet(aSource); 253 } 254 255 bool WindowContext::CanSet(FieldIndex<IDX_CookieBehavior>, 256 const Maybe<uint32_t>& aValue, 257 ContentParent* aSource) { 258 return CheckOnlyOwningProcessCanSet(aSource); 259 } 260 261 bool WindowContext::CanSet(FieldIndex<IDX_IsOnContentBlockingAllowList>, 262 const bool& aValue, ContentParent* aSource) { 263 return CheckOnlyOwningProcessCanSet(aSource); 264 } 265 266 bool WindowContext::CanSet(FieldIndex<IDX_IsThirdPartyWindow>, 267 const bool& IsThirdPartyWindow, 268 ContentParent* aSource) { 269 return CheckOnlyOwningProcessCanSet(aSource); 270 } 271 272 bool WindowContext::CanSet(FieldIndex<IDX_IsThirdPartyTrackingResourceWindow>, 273 const bool& aIsThirdPartyTrackingResourceWindow, 274 ContentParent* aSource) { 275 return CheckOnlyOwningProcessCanSet(aSource); 276 } 277 278 bool WindowContext::CanSet(FieldIndex<IDX_UsingStorageAccess>, 279 const bool& aUsingStorageAccess, 280 ContentParent* aSource) { 281 return CheckOnlyOwningProcessCanSet(aSource); 282 } 283 284 bool WindowContext::CanSet(FieldIndex<IDX_ShouldResistFingerprinting>, 285 const bool& aShouldResistFingerprinting, 286 ContentParent* aSource) { 287 return CheckOnlyOwningProcessCanSet(aSource); 288 } 289 290 bool WindowContext::CanSet(FieldIndex<IDX_OverriddenFingerprintingSettings>, 291 const Maybe<RFPTargetSet>& aValue, 292 ContentParent* aSource) { 293 return CheckOnlyOwningProcessCanSet(aSource); 294 } 295 296 bool WindowContext::CanSet(FieldIndex<IDX_IsSecureContext>, 297 const bool& aIsSecureContext, 298 ContentParent* aSource) { 299 return CheckOnlyOwningProcessCanSet(aSource); 300 } 301 302 bool WindowContext::CanSet(FieldIndex<IDX_IsOriginalFrameSource>, 303 const bool& aIsOriginalFrameSource, 304 ContentParent* aSource) { 305 return CheckOnlyOwningProcessCanSet(aSource); 306 } 307 308 bool WindowContext::CanSet(FieldIndex<IDX_DocTreeHadMedia>, const bool& aValue, 309 ContentParent* aSource) { 310 return IsTop(); 311 } 312 313 bool WindowContext::CanSet(FieldIndex<IDX_AutoplayPermission>, 314 const uint32_t& aValue, ContentParent* aSource) { 315 return CheckOnlyOwningProcessCanSet(aSource); 316 } 317 318 bool WindowContext::CanSet(FieldIndex<IDX_ShortcutsPermission>, 319 const uint32_t& aValue, ContentParent* aSource) { 320 return IsTop() && CheckOnlyOwningProcessCanSet(aSource); 321 } 322 323 bool WindowContext::CanSet(FieldIndex<IDX_ActiveMediaSessionContextId>, 324 const Maybe<uint64_t>& aValue, 325 ContentParent* aSource) { 326 return IsTop(); 327 } 328 329 bool WindowContext::CanSet(FieldIndex<IDX_PopupPermission>, const uint32_t&, 330 ContentParent* aSource) { 331 return CheckOnlyOwningProcessCanSet(aSource); 332 } 333 334 bool WindowContext::CanSet( 335 FieldIndex<IDX_DelegatedPermissions>, 336 const PermissionDelegateHandler::DelegatedPermissionList& aValue, 337 ContentParent* aSource) { 338 return CheckOnlyOwningProcessCanSet(aSource); 339 } 340 341 bool WindowContext::CanSet( 342 FieldIndex<IDX_DelegatedExactHostMatchPermissions>, 343 const PermissionDelegateHandler::DelegatedPermissionList& aValue, 344 ContentParent* aSource) { 345 return CheckOnlyOwningProcessCanSet(aSource); 346 } 347 348 bool WindowContext::CanSet(FieldIndex<IDX_IsLocalIP>, const bool& aValue, 349 ContentParent* aSource) { 350 return CheckOnlyOwningProcessCanSet(aSource); 351 } 352 353 bool WindowContext::CanSet(FieldIndex<IDX_AllowJavascript>, bool aValue, 354 ContentParent* aSource) { 355 return (XRE_IsParentProcess() && !aSource) || 356 CheckOnlyOwningProcessCanSet(aSource); 357 } 358 359 void WindowContext::DidSet(FieldIndex<IDX_AllowJavascript>, bool aOldValue) { 360 RecomputeCanExecuteScripts(); 361 } 362 363 bool WindowContext::CanSet(FieldIndex<IDX_HasActivePeerConnections>, bool, 364 ContentParent*) { 365 return XRE_IsParentProcess() && IsTop(); 366 } 367 368 void WindowContext::ProcessCloseRequest() { 369 MOZ_ASSERT(XRE_IsParentProcess(), "Window must be Global Parent"); 370 BrowsingContext* top = mBrowsingContext->Top(); 371 top->PreOrderWalk([&](BrowsingContext* aBrowsingContext) { 372 CanonicalBrowsingContext* canonical = aBrowsingContext->Canonical(); 373 if (WindowGlobalParent* parent = canonical->GetCurrentWindowGlobal()) { 374 (void)parent->SendProcessCloseRequest(aBrowsingContext); 375 } 376 }); 377 } 378 379 void WindowContext::RecomputeCanExecuteScripts(bool aApplyChanges) { 380 const bool old = mCanExecuteScripts; 381 if (!AllowJavascript()) { 382 // Scripting has been explicitly disabled on our WindowContext. 383 mCanExecuteScripts = false; 384 } else { 385 // Otherwise, inherit. 386 mCanExecuteScripts = mBrowsingContext->CanExecuteScripts(); 387 } 388 389 if (aApplyChanges && old != mCanExecuteScripts) { 390 // Inform our active DOM window. 391 if (nsGlobalWindowInner* window = GetInnerWindow()) { 392 // Only update scriptability if the window is current. Windows will have 393 // scriptability disabled when entering the bfcache and updated when 394 // coming out. 395 if (window->IsCurrentInnerWindow()) { 396 auto& scriptability = 397 xpc::Scriptability::Get(window->GetGlobalJSObject()); 398 scriptability.SetWindowAllowsScript(mCanExecuteScripts); 399 } 400 } 401 402 for (const RefPtr<BrowsingContext>& child : Children()) { 403 child->RecomputeCanExecuteScripts(); 404 } 405 } 406 } 407 408 void WindowContext::DidSet(FieldIndex<IDX_SHEntryHasUserInteraction>, 409 bool aOldValue) { 410 MOZ_ASSERT( 411 TopWindowContext() == this, 412 "SHEntryHasUserInteraction can only be set on the top window context"); 413 // This field is set when the child notifies us of new user interaction, so we 414 // also set the currently active shentry in the parent as having interaction. 415 if (XRE_IsParentProcess() && mBrowsingContext) { 416 SessionHistoryEntry* activeEntry = 417 mBrowsingContext->Canonical()->GetActiveSessionHistoryEntry(); 418 if (activeEntry && GetSHEntryHasUserInteraction()) { 419 activeEntry->SetHasUserInteraction(true); 420 } 421 } 422 } 423 424 void WindowContext::DidSet(FieldIndex<IDX_HasActivePeerConnections>, 425 bool aOldValue) { 426 MOZ_ASSERT( 427 TopWindowContext() == this, 428 "IDX_HasActivePeerConnections can only be set on the top window context"); 429 430 BrowsingContext* top = mBrowsingContext->Top(); 431 432 top->PreOrderWalk([&](BrowsingContext* aBrowsingContext) { 433 WindowContext* windowContext = aBrowsingContext->GetCurrentWindowContext(); 434 if (windowContext) { 435 auto* win{windowContext->GetInnerWindow()}; 436 if (win && (aOldValue != win->HasActivePeerConnections())) { 437 dom::UpdateWorkersPeerConnections(*win, 438 win->HasActivePeerConnections()); 439 } 440 } 441 }); 442 } 443 444 void WindowContext::DidSet(FieldIndex<IDX_UserActivationStateAndModifiers>) { 445 MOZ_ASSERT_IF(!IsInProcess(), mLastActivationTimestamp.IsNull()); 446 USER_ACTIVATION_LOG("Set user gesture activation 0x%02" PRIu8 447 " for %s browsing context 0x%08" PRIx64, 448 GetUserActivationStateAndModifiers(), 449 XRE_IsParentProcess() ? "Parent" : "Child", Id()); 450 if (IsInProcess()) { 451 USER_ACTIVATION_LOG( 452 "Set user gesture start time for %s browsing context 0x%08" PRIx64, 453 XRE_IsParentProcess() ? "Parent" : "Child", Id()); 454 if (GetUserActivationState() == UserActivation::State::FullActivated) { 455 mLastActivationTimestamp = TimeStamp::Now(); 456 } else if (GetUserActivationState() == UserActivation::State::None) { 457 mLastActivationTimestamp = TimeStamp(); 458 } 459 } 460 } 461 462 void WindowContext::DidSet(FieldIndex<IDX_HasReportedShadowDOMUsage>, 463 bool aOldValue) { 464 if (!aOldValue && GetHasReportedShadowDOMUsage() && IsInProcess()) { 465 MOZ_ASSERT(TopWindowContext() == this); 466 if (mBrowsingContext) { 467 Document* topLevelDoc = mBrowsingContext->GetDocument(); 468 if (topLevelDoc) { 469 nsAutoString uri; 470 (void)topLevelDoc->GetDocumentURI(uri); 471 if (!uri.IsEmpty()) { 472 nsAutoString msg = u"Shadow DOM used in ["_ns + uri + 473 u"] or in some of its subdocuments."_ns; 474 nsContentUtils::ReportToConsoleNonLocalized( 475 msg, nsIScriptError::infoFlag, "DOM"_ns, topLevelDoc); 476 } 477 } 478 } 479 } 480 } 481 482 bool WindowContext::CanSet(FieldIndex<IDX_WindowStateSaved>, bool aValue, 483 ContentParent* aSource) { 484 return !mozilla::SessionHistoryInParent() && IsTop() && 485 CheckOnlyOwningProcessCanSet(aSource); 486 } 487 488 void WindowContext::CreateFromIPC(IPCInitializer&& aInit) { 489 MOZ_RELEASE_ASSERT(XRE_IsContentProcess(), 490 "Should be a WindowGlobalParent in the parent"); 491 492 RefPtr<BrowsingContext> bc = BrowsingContext::Get(aInit.mBrowsingContextId); 493 MOZ_RELEASE_ASSERT(bc); 494 495 if (bc->IsDiscarded()) { 496 // If we have already closed our browsing context, the 497 // WindowGlobalChild actor is bound to be destroyed soon and it's 498 // safe to ignore creating the WindowContext. 499 return; 500 } 501 502 RefPtr<WindowContext> context = new WindowContext( 503 bc, aInit.mInnerWindowId, aInit.mOuterWindowId, std::move(aInit.mFields)); 504 context->Init(); 505 } 506 507 void WindowContext::Init() { 508 MOZ_LOG(GetLog(), LogLevel::Debug, 509 ("Registering 0x%" PRIx64 " (bc=0x%" PRIx64 ")", mInnerWindowId, 510 mBrowsingContext->Id())); 511 512 // Register the WindowContext in the `WindowContextByIdMap`. 513 if (!gWindowContexts) { 514 gWindowContexts = new WindowContextByIdMap(); 515 ClearOnShutdown(&gWindowContexts); 516 } 517 auto& entry = gWindowContexts->LookupOrInsert(mInnerWindowId); 518 MOZ_RELEASE_ASSERT(!entry, "Duplicate WindowContext for ID!"); 519 entry = this; 520 521 // Register this to the browsing context. 522 mBrowsingContext->RegisterWindowContext(this); 523 Group()->Register(this); 524 } 525 526 void WindowContext::Discard() { 527 MOZ_LOG(GetLog(), LogLevel::Debug, 528 ("Discarding 0x%" PRIx64 " (bc=0x%" PRIx64 ")", mInnerWindowId, 529 mBrowsingContext->Id())); 530 if (mIsDiscarded) { 531 return; 532 } 533 534 mIsDiscarded = true; 535 if (gWindowContexts) { 536 gWindowContexts->Remove(InnerWindowId()); 537 } 538 mBrowsingContext->UnregisterWindowContext(this); 539 Group()->Unregister(this); 540 } 541 542 void WindowContext::AddSecurityState(uint32_t aStateFlags) { 543 MOZ_ASSERT(TopWindowContext() == this); 544 MOZ_ASSERT((aStateFlags & 545 (nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT | 546 nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT | 547 nsIWebProgressListener::STATE_BLOCKED_MIXED_DISPLAY_CONTENT | 548 nsIWebProgressListener::STATE_BLOCKED_MIXED_ACTIVE_CONTENT | 549 nsIWebProgressListener::STATE_HTTPS_ONLY_MODE_UPGRADED | 550 nsIWebProgressListener::STATE_HTTPS_ONLY_MODE_UPGRADE_FAILED | 551 nsIWebProgressListener::STATE_HTTPS_ONLY_MODE_UPGRADED_FIRST)) == 552 aStateFlags, 553 "Invalid flags specified!"); 554 555 if (XRE_IsParentProcess()) { 556 Canonical()->AddSecurityState(aStateFlags); 557 } else { 558 ContentChild* child = ContentChild::GetSingleton(); 559 child->SendAddSecurityState(this, aStateFlags); 560 } 561 } 562 563 void WindowContext::NotifyUserGestureActivation( 564 UserActivation::Modifiers 565 aModifiers /* = UserActivation::Modifiers::None() */) { 566 UserActivation::StateAndModifiers stateAndModifiers; 567 stateAndModifiers.SetState(UserActivation::State::FullActivated); 568 stateAndModifiers.SetModifiers(aModifiers); 569 if (auto* innerWindow = GetInnerWindow()) { 570 innerWindow->EnsureCloseWatcherManager()->NotifyUserInteraction(); 571 } 572 (void)SetUserActivationStateAndModifiers(stateAndModifiers.GetRawData()); 573 } 574 575 void WindowContext::NotifyResetUserGestureActivation() { 576 UserActivation::StateAndModifiers stateAndModifiers; 577 stateAndModifiers.SetState(UserActivation::State::None); 578 (void)SetUserActivationStateAndModifiers(stateAndModifiers.GetRawData()); 579 } 580 581 bool WindowContext::HasBeenUserGestureActivated() { 582 return GetUserActivationState() != UserActivation::State::None; 583 } 584 585 const TimeStamp& WindowContext::GetUserGestureStart() const { 586 MOZ_ASSERT(IsInProcess()); 587 return mLastActivationTimestamp; 588 } 589 590 // https://html.spec.whatwg.org/#transient-activation 591 bool WindowContext::HasValidTransientUserGestureActivation() { 592 MOZ_ASSERT(IsInProcess()); 593 594 if (GetUserActivationState() != UserActivation::State::FullActivated) { 595 // mLastActivationTimestamp should be null if the document hasn't ever been 596 // activated by user gesture 597 MOZ_ASSERT_IF(GetUserActivationState() == UserActivation::State::None, 598 mLastActivationTimestamp.IsNull()); 599 return false; 600 } 601 602 MOZ_ASSERT( 603 !mLastActivationTimestamp.IsNull(), 604 "mLastActivationTimestamp shouldn't be null if the document has ever " 605 "been activated by user gesture"); 606 607 TimeDuration timeout = TimeDuration::FromMilliseconds( 608 StaticPrefs::dom_user_activation_transient_timeout()); 609 610 // "When the current high resolution time given W is greater than or equal to 611 // the last activation timestamp in W, and less than the last activation 612 // timestamp in W plus the transient activation duration, then W is said to 613 // have transient activation." 614 return timeout <= TimeDuration() || 615 (TimeStamp::Now() - mLastActivationTimestamp) <= timeout; 616 } 617 618 template <typename F> 619 static void ConsumeUserGestureActivationBetweenPiP(BrowsingContext* aTop, 620 F&& aCallback) { 621 // https://wicg.github.io/document-picture-in-picture/#user-activation-propagation 622 // Monkey patch to consume user activation 623 if (aTop->GetIsDocumentPiP()) { 624 // 4. If top is a PIP window, then extend navigables with the opener 625 // window's inclusive decendant navigables 626 RefPtr<BrowsingContext> opener = aTop->GetOpener(); 627 if (!opener) { 628 return; 629 } 630 opener->GetBrowsingContext()->PreOrderWalk(aCallback); 631 } else { 632 // 5. Get top-level navigable's last opened PiP window 633 nsPIDOMWindowOuter* outer = aTop->GetDOMWindow(); 634 NS_ENSURE_TRUE_VOID(outer); 635 nsPIDOMWindowInner* inner = outer->GetCurrentInnerWindow(); 636 NS_ENSURE_TRUE_VOID(inner); 637 DocumentPictureInPicture* dpip = inner->GetExtantDocumentPictureInPicture(); 638 if (!dpip) { 639 return; 640 } 641 nsGlobalWindowInner* pip = dpip->GetWindow(); 642 if (!pip) { 643 return; 644 } 645 646 // 6. Extend navigables with the inclusive descendant navigables of the PIP 647 // window. 648 BrowsingContext* pipBC = pip->GetBrowsingContext(); 649 NS_ENSURE_TRUE_VOID(pipBC); 650 WindowContext* pipWC = pipBC->GetCurrentWindowContext(); 651 NS_ENSURE_TRUE_VOID(pipWC); 652 pipBC->PreOrderWalk(aCallback); 653 } 654 } 655 656 // https://html.spec.whatwg.org/#consume-user-activation 657 bool WindowContext::ConsumeTransientUserGestureActivation() { 658 MOZ_ASSERT(IsInProcess()); 659 // 1. If W's navigable is null, then return. 660 if (!IsCurrent()) { 661 return false; 662 } 663 664 if (!HasValidTransientUserGestureActivation()) { 665 return false; 666 } 667 668 // 2. Let top be W's navigable's top-level traversable. 669 BrowsingContext* top = mBrowsingContext->Top(); 670 671 // 3. Let navigables be the inclusive descendant navigables of top's active 672 // document. 673 auto callback = [&](BrowsingContext* aBrowsingContext) { 674 // 4. Let windows be the list of Window objects constructed by taking the 675 // active window of each item in navigables. 676 WindowContext* windowContext = aBrowsingContext->GetCurrentWindowContext(); 677 678 // 5. For each window in windows, if window's last activation timestamp is 679 // not positive infinity, then set window's last activation timestamp to 680 // negative infinity. 681 if (windowContext && windowContext->GetUserActivationState() == 682 UserActivation::State::FullActivated) { 683 auto stateAndModifiers = UserActivation::StateAndModifiers( 684 GetUserActivationStateAndModifiers()); 685 // Setting UserActivationStateAndModifiers will trigger 686 // DidSet(FieldIndex<IDX_UserActivationStateAndModifiers>), 687 // which in turn updates mLastActivationTimestamp. 688 stateAndModifiers.SetState(UserActivation::State::HasBeenActivated); 689 (void)windowContext->SetUserActivationStateAndModifiers( 690 stateAndModifiers.GetRawData()); 691 } 692 }; 693 top->PreOrderWalk(callback); 694 695 ConsumeUserGestureActivationBetweenPiP(top, callback); 696 697 return true; 698 } 699 700 // https://html.spec.whatwg.org/multipage/interaction.html#history-action-activation 701 bool WindowContext::HasValidHistoryActivation() const { 702 MOZ_ASSERT(IsInProcess()); 703 return mHistoryActivation != mLastActivationTimestamp; 704 } 705 706 // https://html.spec.whatwg.org/multipage/interaction.html#consume-history-action-user-activation 707 // Step 1-2 708 void WindowContext::ConsumeHistoryActivation() { 709 MOZ_ASSERT(IsInProcess()); 710 711 // 1. If W's navigable is null, then return. 712 713 // 2. Let top be W's navigable's top-level traversable. 714 RefPtr<BrowsingContext> top = mBrowsingContext->Top(); 715 716 // Consuming a history activation must happen across all child processes, 717 // including for example cross-origin iframes. As such we need to send an 718 // message over the IPC boundary to ensure out of processes contexts also 719 // consume their activations. 720 MOZ_ASSERT(XRE_IsContentProcess()); 721 ContentChild::GetSingleton()->SendConsumeHistoryActivation(top); 722 723 // Update the local process children immediately. 724 top->ConsumeHistoryActivation(); 725 } 726 727 void WindowContext::UpdateLastHistoryActivation() { 728 mHistoryActivation = mLastActivationTimestamp; 729 } 730 731 bool WindowContext::GetTransientUserGestureActivationModifiers( 732 UserActivation::Modifiers* aModifiers) { 733 if (!HasValidTransientUserGestureActivation()) { 734 return false; 735 } 736 737 auto stateAndModifiers = 738 UserActivation::StateAndModifiers(GetUserActivationStateAndModifiers()); 739 *aModifiers = stateAndModifiers.GetModifiers(); 740 return true; 741 } 742 743 bool WindowContext::CanShowPopup() { 744 uint32_t permit = GetPopupPermission(); 745 if (permit == nsIPermissionManager::ALLOW_ACTION) { 746 return true; 747 } 748 if (permit == nsIPermissionManager::DENY_ACTION) { 749 return false; 750 } 751 752 return !StaticPrefs::dom_disable_open_during_load(); 753 } 754 755 void WindowContext::TransientSetHasActivePeerConnections() { 756 if (!IsTop()) { 757 return; 758 } 759 760 mFields.SetWithoutSyncing<IDX_HasActivePeerConnections>(true); 761 } 762 763 WindowContext::IPCInitializer WindowContext::GetIPCInitializer() { 764 IPCInitializer init; 765 init.mInnerWindowId = mInnerWindowId; 766 init.mOuterWindowId = mOuterWindowId; 767 init.mBrowsingContextId = mBrowsingContext->Id(); 768 init.mFields = mFields.RawValues(); 769 return init; 770 } 771 772 WindowContext::WindowContext(BrowsingContext* aBrowsingContext, 773 uint64_t aInnerWindowId, uint64_t aOuterWindowId, 774 FieldValues&& aInit) 775 : mFields(std::move(aInit)), 776 mInnerWindowId(aInnerWindowId), 777 mOuterWindowId(aOuterWindowId), 778 mBrowsingContext(aBrowsingContext) { 779 MOZ_ASSERT(mBrowsingContext); 780 MOZ_ASSERT(mInnerWindowId); 781 MOZ_ASSERT(mOuterWindowId); 782 RecomputeCanExecuteScripts(/* aApplyChanges */ false); 783 } 784 785 WindowContext::~WindowContext() { 786 if (gWindowContexts) { 787 gWindowContexts->Remove(InnerWindowId()); 788 } 789 } 790 791 JSObject* WindowContext::WrapObject(JSContext* cx, 792 JS::Handle<JSObject*> aGivenProto) { 793 return WindowContext_Binding::Wrap(cx, this, aGivenProto); 794 } 795 796 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WindowContext) 797 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 798 NS_INTERFACE_MAP_ENTRY(nsISupports) 799 NS_INTERFACE_MAP_END 800 801 NS_IMPL_CYCLE_COLLECTING_ADDREF(WindowContext) 802 NS_IMPL_CYCLE_COLLECTING_RELEASE(WindowContext) 803 804 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(WindowContext) 805 806 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(WindowContext) 807 if (gWindowContexts) { 808 gWindowContexts->Remove(tmp->InnerWindowId()); 809 } 810 811 NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowsingContext) 812 NS_IMPL_CYCLE_COLLECTION_UNLINK(mChildren) 813 NS_IMPL_CYCLE_COLLECTION_UNLINK(mNonSyntheticChildren) 814 NS_IMPL_CYCLE_COLLECTION_UNLINK(mNonSyntheticLightDOMChildren); 815 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER 816 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 817 818 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(WindowContext) 819 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mBrowsingContext) 820 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChildren) 821 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNonSyntheticChildren) 822 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNonSyntheticLightDOMChildren) 823 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 824 825 } // namespace dom 826 } // namespace mozilla 827 828 namespace IPC { 829 830 using mozilla::dom::MaybeDiscarded; 831 using mozilla::dom::WindowContext; 832 833 void ParamTraits<MaybeDiscarded<WindowContext>>::Write( 834 MessageWriter* aWriter, const MaybeDiscarded<WindowContext>& aParam) { 835 uint64_t id = aParam.ContextId(); 836 WriteParam(aWriter, id); 837 } 838 839 bool ParamTraits<MaybeDiscarded<WindowContext>>::Read( 840 MessageReader* aReader, MaybeDiscarded<WindowContext>* aResult) { 841 uint64_t id = 0; 842 if (!ReadParam(aReader, &id)) { 843 return false; 844 } 845 846 if (id == 0) { 847 *aResult = nullptr; 848 } else if (RefPtr<WindowContext> wc = WindowContext::GetById(id)) { 849 *aResult = std::move(wc); 850 } else { 851 aResult->SetDiscarded(id); 852 } 853 return true; 854 } 855 856 void ParamTraits<WindowContext::IPCInitializer>::Write( 857 MessageWriter* aWriter, const WindowContext::IPCInitializer& aInit) { 858 // Write actor ID parameters. 859 WriteParam(aWriter, aInit.mInnerWindowId); 860 WriteParam(aWriter, aInit.mOuterWindowId); 861 WriteParam(aWriter, aInit.mBrowsingContextId); 862 WriteParam(aWriter, aInit.mFields); 863 } 864 865 bool ParamTraits<WindowContext::IPCInitializer>::Read( 866 MessageReader* aReader, WindowContext::IPCInitializer* aInit) { 867 // Read actor ID parameters. 868 return ReadParam(aReader, &aInit->mInnerWindowId) && 869 ReadParam(aReader, &aInit->mOuterWindowId) && 870 ReadParam(aReader, &aInit->mBrowsingContextId) && 871 ReadParam(aReader, &aInit->mFields); 872 } 873 874 template struct ParamTraits<WindowContext::BaseTransaction>; 875 876 } // namespace IPC