BrowsingContext.cpp (161972B)
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/BrowsingContext.h" 8 9 #include "ipc/IPCMessageUtils.h" 10 11 #ifdef ACCESSIBILITY 12 # include "mozilla/a11y/DocAccessibleParent.h" 13 # include "mozilla/a11y/Platform.h" 14 # include "nsAccessibilityService.h" 15 # if defined(XP_WIN) 16 # include "mozilla/a11y/AccessibleWrap.h" 17 # include "mozilla/a11y/Compatibility.h" 18 # include "mozilla/a11y/nsWinUtils.h" 19 # endif 20 #endif 21 #include "js/LocaleSensitive.h" 22 #include "mozilla/AppShutdown.h" 23 #include "mozilla/dom/CanonicalBrowsingContext.h" 24 #include "mozilla/dom/BindingIPCUtils.h" 25 #include "mozilla/dom/BrowserHost.h" 26 #include "mozilla/dom/BrowserChild.h" 27 #include "mozilla/dom/BrowserParent.h" 28 #include "mozilla/dom/BrowsingContextGroup.h" 29 #include "mozilla/dom/BrowsingContextBinding.h" 30 #include "mozilla/dom/ContentChild.h" 31 #include "mozilla/dom/ContentParent.h" 32 #include "mozilla/dom/Document.h" 33 #include "mozilla/dom/DocumentPictureInPicture.h" 34 #include "mozilla/dom/Element.h" 35 #include "mozilla/dom/Geolocation.h" 36 #include "mozilla/dom/HTMLEmbedElement.h" 37 #include "mozilla/dom/HTMLIFrameElement.h" 38 #include "mozilla/dom/Location.h" 39 #include "mozilla/dom/LocationBinding.h" 40 #include "mozilla/dom/MediaDevices.h" 41 #include "mozilla/dom/Navigation.h" 42 #include "mozilla/dom/Navigator.h" 43 #include "mozilla/dom/PopupBlocker.h" 44 #include "mozilla/dom/ReferrerInfo.h" 45 #include "mozilla/dom/ScriptSettings.h" 46 #include "mozilla/dom/SessionStoreChild.h" 47 #include "mozilla/dom/SessionStorageManager.h" 48 #include "mozilla/dom/StructuredCloneTags.h" 49 #include "mozilla/dom/UserActivationIPCUtils.h" 50 #include "mozilla/dom/WindowBinding.h" 51 #include "mozilla/dom/WindowContext.h" 52 #include "mozilla/dom/WindowGlobalChild.h" 53 #include "mozilla/dom/WindowGlobalParent.h" 54 #include "mozilla/dom/WindowProxyHolder.h" 55 #include "mozilla/dom/SyncedContextInlines.h" 56 #include "mozilla/dom/XULFrameElement.h" 57 #include "mozilla/ipc/ProtocolUtils.h" 58 #include "mozilla/net/DocumentLoadListener.h" 59 #include "mozilla/net/RequestContextService.h" 60 #include "mozilla/Assertions.h" 61 #include "mozilla/AsyncEventDispatcher.h" 62 #include "mozilla/ClearOnShutdown.h" 63 #include "mozilla/Components.h" 64 #include "mozilla/Logging.h" 65 #include "mozilla/MediaFeatureChange.h" 66 #include "mozilla/Services.h" 67 #include "mozilla/StaticPrefs_fission.h" 68 #include "mozilla/StaticPrefs_media.h" 69 #include "mozilla/StaticPrefs_page_load.h" 70 #include "mozilla/StaticPtr.h" 71 #include "mozilla/URLQueryStringStripper.h" 72 #include "mozilla/EventStateManager.h" 73 #include "mozilla/glean/DomMetrics.h" 74 #include "mozilla/StartupTimeline.h" 75 #include "GeckoProfiler.h" 76 #include "mozilla/ProfilerMarkers.h" 77 #include "nsIURIFixup.h" 78 #include "nsIXULRuntime.h" 79 80 #include "nsDocShell.h" 81 #include "nsDocShellLoadState.h" 82 #include "nsFocusManager.h" 83 #include "nsGlobalWindowInner.h" 84 #include "nsGlobalWindowOuter.h" 85 #include "PresShell.h" 86 #include "nsIObserverService.h" 87 #include "nsISHistory.h" 88 #include "nsJSUtils.h" 89 #include "nsContentUtils.h" 90 #include "nsQueryObject.h" 91 #include "nsSandboxFlags.h" 92 #include "nsScreen.h" 93 #include "nsScriptError.h" 94 #include "nsThreadUtils.h" 95 #include "xpcprivate.h" 96 97 #include "AutoplayPolicy.h" 98 #include "GVAutoplayRequestStatusIPC.h" 99 100 extern mozilla::LazyLogModule gAutoplayPermissionLog; 101 extern mozilla::LazyLogModule gNavigationAPILog; 102 extern mozilla::LazyLogModule gTimeoutDeferralLog; 103 104 #define AUTOPLAY_LOG(msg, ...) \ 105 MOZ_LOG(gAutoplayPermissionLog, LogLevel::Debug, (msg, ##__VA_ARGS__)) 106 107 namespace IPC { 108 // Allow serialization and deserialization of OrientationType over IPC 109 template <> 110 struct ParamTraits<mozilla::dom::OrientationType> 111 : public mozilla::dom::WebIDLEnumSerializer<mozilla::dom::OrientationType> { 112 }; 113 114 template <> 115 struct ParamTraits<mozilla::dom::DisplayMode> 116 : public mozilla::dom::WebIDLEnumSerializer<mozilla::dom::DisplayMode> {}; 117 118 template <> 119 struct ParamTraits<mozilla::dom::PrefersColorSchemeOverride> 120 : public mozilla::dom::WebIDLEnumSerializer< 121 mozilla::dom::PrefersColorSchemeOverride> {}; 122 123 template <> 124 struct ParamTraits<mozilla::dom::ForcedColorsOverride> 125 : public mozilla::dom::WebIDLEnumSerializer< 126 mozilla::dom::ForcedColorsOverride> {}; 127 128 template <> 129 struct ParamTraits<mozilla::dom::ExplicitActiveStatus> 130 : public ContiguousEnumSerializer< 131 mozilla::dom::ExplicitActiveStatus, 132 mozilla::dom::ExplicitActiveStatus::None, 133 mozilla::dom::ExplicitActiveStatus::EndGuard_> {}; 134 135 // Allow serialization and deserialization of TouchEventsOverride over IPC 136 template <> 137 struct ParamTraits<mozilla::dom::TouchEventsOverride> 138 : public mozilla::dom::WebIDLEnumSerializer< 139 mozilla::dom::TouchEventsOverride> {}; 140 141 template <> 142 struct ParamTraits<mozilla::dom::EmbedderColorSchemes> { 143 using paramType = mozilla::dom::EmbedderColorSchemes; 144 145 static void Write(MessageWriter* aWriter, const paramType& aParam) { 146 WriteParam(aWriter, aParam.mUsed); 147 WriteParam(aWriter, aParam.mPreferred); 148 } 149 150 static bool Read(MessageReader* aReader, paramType* aResult) { 151 return ReadParam(aReader, &aResult->mUsed) && 152 ReadParam(aReader, &aResult->mPreferred); 153 } 154 }; 155 156 } // namespace IPC 157 158 namespace mozilla { 159 namespace dom { 160 161 // Explicit specialization of the `Transaction` type. Required by the `extern 162 // template class` declaration in the header. 163 template class syncedcontext::Transaction<BrowsingContext>; 164 165 extern mozilla::LazyLogModule gUserInteractionPRLog; 166 167 #define USER_ACTIVATION_LOG(msg, ...) \ 168 MOZ_LOG(gUserInteractionPRLog, LogLevel::Debug, (msg, ##__VA_ARGS__)) 169 170 static LazyLogModule gBrowsingContextLog("BrowsingContext"); 171 static LazyLogModule gBrowsingContextSyncLog("BrowsingContextSync"); 172 173 typedef nsTHashMap<nsUint64HashKey, BrowsingContext*> BrowsingContextMap; 174 175 // All BrowsingContexts indexed by Id 176 static StaticAutoPtr<BrowsingContextMap> sBrowsingContexts; 177 // Top-level Content BrowsingContexts only, indexed by BrowserId instead of Id 178 static StaticAutoPtr<BrowsingContextMap> sCurrentTopByBrowserId; 179 180 static bool gIPCEnabledAnnotation = false; 181 static bool gFissionEnabledAnnotation = false; 182 183 static void UnregisterBrowserId(BrowsingContext* aBrowsingContext) { 184 if (!aBrowsingContext->IsTopContent() || !sCurrentTopByBrowserId) { 185 return; 186 } 187 188 // Avoids an extra lookup 189 auto browserIdEntry = 190 sCurrentTopByBrowserId->Lookup(aBrowsingContext->BrowserId()); 191 if (browserIdEntry && browserIdEntry.Data() == aBrowsingContext) { 192 browserIdEntry.Remove(); 193 } 194 } 195 196 static void Register(BrowsingContext* aBrowsingContext) { 197 sBrowsingContexts->InsertOrUpdate(aBrowsingContext->Id(), aBrowsingContext); 198 if (aBrowsingContext->IsTopContent()) { 199 sCurrentTopByBrowserId->InsertOrUpdate(aBrowsingContext->BrowserId(), 200 aBrowsingContext); 201 } 202 203 aBrowsingContext->Group()->Register(aBrowsingContext); 204 } 205 206 // static 207 void BrowsingContext::UpdateCurrentTopByBrowserId( 208 BrowsingContext* aNewBrowsingContext) { 209 if (aNewBrowsingContext->IsTopContent()) { 210 sCurrentTopByBrowserId->InsertOrUpdate(aNewBrowsingContext->BrowserId(), 211 aNewBrowsingContext); 212 } 213 } 214 215 BrowsingContext* BrowsingContext::GetParent() const { 216 return mParentWindow ? mParentWindow->GetBrowsingContext() : nullptr; 217 } 218 219 bool BrowsingContext::IsInSubtreeOf(BrowsingContext* aContext) { 220 BrowsingContext* bc = this; 221 do { 222 if (bc == aContext) { 223 return true; 224 } 225 } while ((bc = bc->GetParent())); 226 return false; 227 } 228 229 BrowsingContext* BrowsingContext::Top() { 230 BrowsingContext* bc = this; 231 while (bc->mParentWindow) { 232 bc = bc->GetParent(); 233 } 234 return bc; 235 } 236 237 const BrowsingContext* BrowsingContext::Top() const { 238 const BrowsingContext* bc = this; 239 while (bc->mParentWindow) { 240 bc = bc->GetParent(); 241 } 242 return bc; 243 } 244 245 int32_t BrowsingContext::IndexOf(BrowsingContext* aChild) { 246 int32_t index = -1; 247 for (BrowsingContext* child : Children()) { 248 ++index; 249 if (child == aChild) { 250 break; 251 } 252 } 253 return index; 254 } 255 256 WindowContext* BrowsingContext::GetTopWindowContext() const { 257 if (mParentWindow) { 258 return mParentWindow->TopWindowContext(); 259 } 260 return mCurrentWindowContext; 261 } 262 263 /* static */ 264 void BrowsingContext::Init() { 265 if (!sBrowsingContexts) { 266 sBrowsingContexts = new BrowsingContextMap(); 267 sCurrentTopByBrowserId = new BrowsingContextMap(); 268 ClearOnShutdown(&sBrowsingContexts); 269 ClearOnShutdown(&sCurrentTopByBrowserId); 270 CrashReporter::RegisterAnnotationBool( 271 CrashReporter::Annotation::DOMIPCEnabled, &gIPCEnabledAnnotation); 272 CrashReporter::RegisterAnnotationBool( 273 CrashReporter::Annotation::DOMFissionEnabled, 274 &gFissionEnabledAnnotation); 275 } 276 } 277 278 /* static */ 279 LogModule* BrowsingContext::GetLog() { return gBrowsingContextLog; } 280 281 /* static */ 282 LogModule* BrowsingContext::GetSyncLog() { return gBrowsingContextSyncLog; } 283 284 /* static */ 285 already_AddRefed<BrowsingContext> BrowsingContext::Get(uint64_t aId) { 286 if (sBrowsingContexts) { 287 return do_AddRef(sBrowsingContexts->Get(aId)); 288 } 289 290 return nullptr; 291 } 292 293 /* static */ 294 already_AddRefed<BrowsingContext> BrowsingContext::GetCurrentTopByBrowserId( 295 uint64_t aBrowserId) { 296 return do_AddRef(sCurrentTopByBrowserId->Get(aBrowserId)); 297 } 298 299 /* static */ 300 already_AddRefed<BrowsingContext> BrowsingContext::GetFromWindow( 301 WindowProxyHolder& aProxy) { 302 return do_AddRef(aProxy.get()); 303 } 304 305 CanonicalBrowsingContext* BrowsingContext::Canonical() { 306 return CanonicalBrowsingContext::Cast(this); 307 } 308 309 bool BrowsingContext::IsOwnedByProcess() const { 310 return mIsInProcess && mDocShell && 311 !nsDocShell::Cast(mDocShell)->WillChangeProcess(); 312 } 313 314 bool BrowsingContext::SameOriginWithTop() { 315 MOZ_ASSERT(IsInProcess()); 316 // If the top BrowsingContext is not same-process to us, it is cross-origin 317 if (!Top()->IsInProcess()) { 318 return false; 319 } 320 321 nsIDocShell* docShell = GetDocShell(); 322 if (!docShell) { 323 return false; 324 } 325 Document* doc = docShell->GetDocument(); 326 if (!doc) { 327 return false; 328 } 329 nsIPrincipal* principal = doc->NodePrincipal(); 330 331 nsIDocShell* topDocShell = Top()->GetDocShell(); 332 if (!topDocShell) { 333 return false; 334 } 335 Document* topDoc = topDocShell->GetDocument(); 336 if (!topDoc) { 337 return false; 338 } 339 nsIPrincipal* topPrincipal = topDoc->NodePrincipal(); 340 341 return principal->Equals(topPrincipal); 342 } 343 344 /* static */ 345 already_AddRefed<BrowsingContext> BrowsingContext::CreateDetached( 346 nsGlobalWindowInner* aParent, BrowsingContext* aOpener, 347 BrowsingContextGroup* aSpecificGroup, const nsAString& aName, Type aType, 348 CreateDetachedOptions aOptions) { 349 if (aParent) { 350 MOZ_DIAGNOSTIC_ASSERT(aParent->GetWindowContext()); 351 MOZ_DIAGNOSTIC_ASSERT(aParent->GetBrowsingContext()->mType == aType); 352 MOZ_DIAGNOSTIC_ASSERT(aParent->GetBrowsingContext()->GetBrowserId() != 0); 353 } 354 355 MOZ_DIAGNOSTIC_ASSERT(aType != Type::Chrome || XRE_IsParentProcess()); 356 357 uint64_t id = nsContentUtils::GenerateBrowsingContextId(); 358 359 MOZ_LOG(GetLog(), LogLevel::Debug, 360 ("Creating 0x%08" PRIx64 " in %s", id, 361 XRE_IsParentProcess() ? "Parent" : "Child")); 362 363 RefPtr<BrowsingContext> parentBC = 364 aParent ? aParent->GetBrowsingContext() : nullptr; 365 RefPtr<WindowContext> parentWC = 366 aParent ? aParent->GetWindowContext() : nullptr; 367 BrowsingContext* inherit = parentBC ? parentBC.get() : aOpener; 368 369 // Determine which BrowsingContextGroup this context should be created in. 370 RefPtr<BrowsingContextGroup> group = aSpecificGroup; 371 if (aType == Type::Chrome) { 372 MOZ_DIAGNOSTIC_ASSERT(!group); 373 group = BrowsingContextGroup::GetChromeGroup(); 374 } else if (!group) { 375 group = BrowsingContextGroup::Select(parentWC, aOpener); 376 } 377 378 // Configure initial values for synced fields. 379 FieldValues fields; 380 fields.Get<IDX_Name>() = aName; 381 382 if (aOpener) { 383 MOZ_DIAGNOSTIC_ASSERT(!aParent, 384 "new BC with both initial opener and parent"); 385 MOZ_DIAGNOSTIC_ASSERT(aOpener->Group() == group); 386 MOZ_DIAGNOSTIC_ASSERT(aOpener->mType == aType); 387 fields.Get<IDX_OpenerId>() = aOpener->Id(); 388 fields.Get<IDX_HadOriginalOpener>() = true; 389 fields.Get<IDX_MessageManagerGroup>() = 390 aOpener->Top()->GetMessageManagerGroup(); 391 392 if (aType == Type::Chrome && !aParent) { 393 // See SetOpener for why we do this inheritance. 394 fields.Get<IDX_PrefersColorSchemeOverride>() = 395 aOpener->Top()->GetPrefersColorSchemeOverride(); 396 } 397 } 398 399 if (aParent) { 400 MOZ_DIAGNOSTIC_ASSERT(parentBC->Group() == group); 401 MOZ_DIAGNOSTIC_ASSERT(parentBC->mType == aType); 402 fields.Get<IDX_EmbedderInnerWindowId>() = aParent->WindowID(); 403 // Non-toplevel content documents are always embededed within content. 404 fields.Get<IDX_EmbeddedInContentDocument>() = 405 parentBC->mType == Type::Content; 406 407 // XXX(farre): Can/Should we check aParent->IsLoading() here? (Bug 408 // 1608448) Check if the parent was itself loading already 409 auto readystate = aParent->GetDocument()->GetReadyStateEnum(); 410 fields.Get<IDX_AncestorLoading>() = 411 parentBC->GetAncestorLoading() || 412 readystate == Document::ReadyState::READYSTATE_LOADING || 413 readystate == Document::ReadyState::READYSTATE_INTERACTIVE; 414 } 415 416 fields.Get<IDX_BrowserId>() = 417 parentBC ? parentBC->GetBrowserId() : nsContentUtils::GenerateBrowserId(); 418 419 fields.Get<IDX_OpenerPolicy>() = nsILoadInfo::OPENER_POLICY_UNSAFE_NONE; 420 if (aOpener && aOpener->SameOriginWithTop()) { 421 // We inherit the opener policy if there is a creator and if the creator's 422 // origin is same origin with the creator's top-level origin. 423 // If it is cross origin we should not inherit the CrossOriginOpenerPolicy 424 fields.Get<IDX_OpenerPolicy>() = aOpener->Top()->GetOpenerPolicy(); 425 426 // If we inherit a policy which is potentially cross-origin isolated, we 427 // must be in a potentially cross-origin isolated BCG. 428 bool isPotentiallyCrossOriginIsolated = 429 fields.Get<IDX_OpenerPolicy>() == 430 nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP; 431 MOZ_RELEASE_ASSERT(isPotentiallyCrossOriginIsolated == 432 group->IsPotentiallyCrossOriginIsolated()); 433 } else if (aOpener) { 434 // They are not same origin 435 auto topPolicy = aOpener->Top()->GetOpenerPolicy(); 436 MOZ_RELEASE_ASSERT( 437 topPolicy == nsILoadInfo::OPENER_POLICY_UNSAFE_NONE || 438 topPolicy == nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_ALLOW_POPUPS || 439 aOptions.isForPrinting); 440 if (aOptions.isForPrinting) { 441 // Ensure our opener policy is consistent for printing for our top. 442 fields.Get<IDX_OpenerPolicy>() = topPolicy; 443 } 444 } else if (!aParent && group->IsPotentiallyCrossOriginIsolated()) { 445 // If we're creating a brand-new toplevel BC in a potentially cross-origin 446 // isolated group, it should start out with a strict opener policy. 447 fields.Get<IDX_OpenerPolicy>() = 448 nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP; 449 } 450 451 fields.Get<IDX_HistoryID>() = nsID::GenerateUUID(); 452 fields.Get<IDX_ExplicitActive>() = [&] { 453 if (parentBC || aType == Type::Content) { 454 // Non-root browsing-contexts inherit their status from its parent. 455 // Top-content either gets managed by the top chrome, or gets manually 456 // managed by the front-end (see ManuallyManagesActiveness). In any case 457 // we want to start off as inactive. 458 return ExplicitActiveStatus::None; 459 } 460 // Chrome starts as active. 461 return ExplicitActiveStatus::Active; 462 }(); 463 464 fields.Get<IDX_FullZoom>() = parentBC ? parentBC->FullZoom() : 1.0f; 465 fields.Get<IDX_TextZoom>() = parentBC ? parentBC->TextZoom() : 1.0f; 466 467 bool allowContentRetargeting = 468 inherit ? inherit->GetAllowContentRetargetingOnChildren() : true; 469 fields.Get<IDX_AllowContentRetargeting>() = allowContentRetargeting; 470 fields.Get<IDX_AllowContentRetargetingOnChildren>() = allowContentRetargeting; 471 472 // Assume top allows fullscreen for its children unless otherwise stated. 473 // Subframes start with it false unless otherwise noted in SetEmbedderElement. 474 fields.Get<IDX_FullscreenAllowedByOwner>() = !aParent; 475 476 fields.Get<IDX_DefaultLoadFlags>() = 477 inherit ? inherit->GetDefaultLoadFlags() : nsIRequest::LOAD_NORMAL; 478 479 fields.Get<IDX_OrientationLock>() = mozilla::hal::ScreenOrientation::None; 480 481 fields.Get<IDX_UseGlobalHistory>() = 482 inherit ? inherit->GetUseGlobalHistory() : false; 483 484 fields.Get<IDX_UseErrorPages>() = true; 485 486 fields.Get<IDX_TouchEventsOverrideInternal>() = TouchEventsOverride::None; 487 488 fields.Get<IDX_AllowJavascript>() = 489 inherit ? inherit->GetAllowJavascript() : true; 490 491 fields.Get<IDX_IPAddressSpace>() = inherit 492 ? inherit->GetIPAddressSpace() 493 : nsILoadInfo::IPAddressSpace::Unknown; 494 495 bool parentalControlsEnabled; 496 if (inherit) { 497 parentalControlsEnabled = inherit->GetParentalControlsEnabled(); 498 } else if (XRE_IsParentProcess()) { 499 parentalControlsEnabled = 500 CanonicalBrowsingContext::ShouldEnforceParentalControls(); 501 } else { 502 parentalControlsEnabled = false; 503 } 504 505 fields.Get<IDX_ParentalControlsEnabled>() = parentalControlsEnabled; 506 507 fields.Get<IDX_IsPopupRequested>() = aOptions.isPopupRequested; 508 509 fields.Get<IDX_TopLevelCreatedByWebContent>() = 510 aOptions.topLevelCreatedByWebContent; 511 512 if (!parentBC) { 513 fields.Get<IDX_ShouldDelayMediaFromStart>() = 514 StaticPrefs::media_block_autoplay_until_in_foreground(); 515 } 516 517 RefPtr<BrowsingContext> context; 518 if (XRE_IsParentProcess()) { 519 context = new CanonicalBrowsingContext(parentWC, group, id, 520 /* aOwnerProcessId */ 0, 521 /* aEmbedderProcessId */ 0, aType, 522 std::move(fields)); 523 } else { 524 context = 525 new BrowsingContext(parentWC, group, id, aType, std::move(fields)); 526 } 527 528 context->mWindowless = aOptions.windowless; 529 context->mEmbeddedByThisProcess = XRE_IsParentProcess() || aParent; 530 context->mCreatedDynamically = aOptions.createdDynamically; 531 if (inherit) { 532 context->mPrivateBrowsingId = inherit->mPrivateBrowsingId; 533 context->mUseRemoteTabs = inherit->mUseRemoteTabs; 534 context->mUseRemoteSubframes = inherit->mUseRemoteSubframes; 535 context->mOriginAttributes = inherit->mOriginAttributes; 536 } 537 538 nsCOMPtr<nsIRequestContextService> rcsvc = 539 net::RequestContextService::GetOrCreate(); 540 if (rcsvc) { 541 nsCOMPtr<nsIRequestContext> requestContext; 542 nsresult rv = rcsvc->NewRequestContext(getter_AddRefs(requestContext)); 543 if (NS_SUCCEEDED(rv) && requestContext) { 544 context->mRequestContextId = requestContext->GetID(); 545 } 546 } 547 548 return context.forget(); 549 } 550 551 already_AddRefed<BrowsingContext> BrowsingContext::CreateIndependent( 552 Type aType, bool aWindowless) { 553 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess(), 554 "BCs created in the content process must be related to " 555 "some BrowserChild"); 556 RefPtr<BrowsingContext> bc( 557 CreateDetached(nullptr, nullptr, nullptr, u""_ns, aType, {})); 558 bc->mWindowless = aWindowless; 559 bc->mEmbeddedByThisProcess = true; 560 bc->EnsureAttached(); 561 return bc.forget(); 562 } 563 564 void BrowsingContext::EnsureAttached() { 565 if (!mEverAttached) { 566 Register(this); 567 568 // Attach the browsing context to the tree. 569 Attach(/* aFromIPC */ false, /* aOriginProcess */ nullptr); 570 } 571 } 572 573 /* static */ 574 mozilla::ipc::IPCResult BrowsingContext::CreateFromIPC( 575 BrowsingContext::IPCInitializer&& aInit, BrowsingContextGroup* aGroup, 576 ContentParent* aOriginProcess) { 577 MOZ_DIAGNOSTIC_ASSERT(aOriginProcess || XRE_IsContentProcess()); 578 MOZ_DIAGNOSTIC_ASSERT(aGroup); 579 580 uint64_t originId = 0; 581 if (aOriginProcess) { 582 originId = aOriginProcess->ChildID(); 583 aGroup->EnsureHostProcess(aOriginProcess); 584 } 585 586 MOZ_LOG(GetLog(), LogLevel::Debug, 587 ("Creating 0x%08" PRIx64 " from IPC (origin=0x%08" PRIx64 ")", 588 aInit.mId, originId)); 589 590 RefPtr<WindowContext> parent = aInit.GetParent(); 591 592 RefPtr<BrowsingContext> context; 593 if (XRE_IsParentProcess()) { 594 // If the new BrowsingContext has a parent, it is a sub-frame embedded in 595 // whatever process sent the message. If it doesn't, and is not windowless, 596 // it is a new window or tab, and will be embedded in the parent process. 597 uint64_t embedderProcessId = (aInit.mWindowless || parent) ? originId : 0; 598 context = new CanonicalBrowsingContext(parent, aGroup, aInit.mId, originId, 599 embedderProcessId, Type::Content, 600 std::move(aInit.mFields)); 601 } else { 602 context = new BrowsingContext(parent, aGroup, aInit.mId, Type::Content, 603 std::move(aInit.mFields)); 604 } 605 606 context->mWindowless = aInit.mWindowless; 607 context->mCreatedDynamically = aInit.mCreatedDynamically; 608 context->mChildOffset = aInit.mChildOffset; 609 if (context->GetHasSessionHistory()) { 610 context->CreateChildSHistory(); 611 if (mozilla::SessionHistoryInParent()) { 612 context->GetChildSessionHistory()->SetIndexAndLength( 613 aInit.mSessionHistoryIndex, aInit.mSessionHistoryCount, nsID()); 614 } 615 } 616 617 // NOTE: Call through the `Set` methods for these values to ensure that any 618 // relevant process-local state is also updated. 619 context->SetOriginAttributes(aInit.mOriginAttributes); 620 context->SetRemoteTabs(aInit.mUseRemoteTabs); 621 context->SetRemoteSubframes(aInit.mUseRemoteSubframes); 622 context->mRequestContextId = aInit.mRequestContextId; 623 // NOTE: Private browsing ID is set by `SetOriginAttributes`. 624 625 if (const char* failure = 626 context->BrowsingContextCoherencyChecks(aOriginProcess)) { 627 mozilla::ipc::IProtocol* actor = aOriginProcess; 628 if (!actor) { 629 actor = ContentChild::GetSingleton(); 630 } 631 return IPC_FAIL_UNSAFE_PRINTF(actor, "Incoherent BrowsingContext: %s", 632 failure); 633 } 634 635 Register(context); 636 637 context->Attach(/* aFromIPC */ true, aOriginProcess); 638 return IPC_OK(); 639 } 640 641 BrowsingContext::BrowsingContext(WindowContext* aParentWindow, 642 BrowsingContextGroup* aGroup, 643 uint64_t aBrowsingContextId, Type aType, 644 FieldValues&& aInit) 645 : mFields(std::move(aInit)), 646 mType(aType), 647 mBrowsingContextId(aBrowsingContextId), 648 mGroup(aGroup), 649 mParentWindow(aParentWindow), 650 mPrivateBrowsingId(0), 651 mEverAttached(false), 652 mIsInProcess(false), 653 mIsDiscarded(false), 654 mWindowless(false), 655 mDanglingRemoteOuterProxies(false), 656 mEmbeddedByThisProcess(false), 657 mUseRemoteTabs(false), 658 mUseRemoteSubframes(false), 659 mCreatedDynamically(false), 660 mIsInBFCache(false), 661 mCanExecuteScripts(true), 662 mChildOffset(0) { 663 MOZ_RELEASE_ASSERT(!mParentWindow || mParentWindow->Group() == mGroup); 664 MOZ_RELEASE_ASSERT(mBrowsingContextId != 0); 665 MOZ_RELEASE_ASSERT(mGroup); 666 } 667 668 void BrowsingContext::SetDocShell(nsIDocShell* aDocShell) { 669 // XXX(nika): We should communicate that we are now an active BrowsingContext 670 // process to the parent & do other validation here. 671 MOZ_DIAGNOSTIC_ASSERT(mEverAttached); 672 MOZ_RELEASE_ASSERT(aDocShell->GetBrowsingContext() == this); 673 mDocShell = aDocShell; 674 mDanglingRemoteOuterProxies = !mIsInProcess; 675 mIsInProcess = true; 676 if (mChildSessionHistory) { 677 mChildSessionHistory->SetIsInProcess(true); 678 } 679 680 RecomputeCanExecuteScripts(); 681 ClearCachedValuesOfLocations(); 682 } 683 684 // This class implements a callback that will return the remote window proxy for 685 // mBrowsingContext in that compartment, if it has one. It also removes the 686 // proxy from the map, because the object will be transplanted into another kind 687 // of object. 688 class MOZ_STACK_CLASS CompartmentRemoteProxyTransplantCallback 689 : public js::CompartmentTransplantCallback { 690 public: 691 explicit CompartmentRemoteProxyTransplantCallback( 692 BrowsingContext* aBrowsingContext) 693 : mBrowsingContext(aBrowsingContext) {} 694 695 virtual JSObject* getObjectToTransplant( 696 JS::Compartment* compartment) override { 697 auto* priv = xpc::CompartmentPrivate::Get(compartment); 698 if (!priv) { 699 return nullptr; 700 } 701 702 auto& map = priv->GetRemoteProxyMap(); 703 auto result = map.lookup(mBrowsingContext); 704 if (!result) { 705 return nullptr; 706 } 707 JSObject* resultObject = result->value(); 708 map.remove(result); 709 710 return resultObject; 711 } 712 713 private: 714 BrowsingContext* mBrowsingContext; 715 }; 716 717 void BrowsingContext::CleanUpDanglingRemoteOuterWindowProxies( 718 JSContext* aCx, JS::MutableHandle<JSObject*> aOuter) { 719 if (!mDanglingRemoteOuterProxies) { 720 return; 721 } 722 mDanglingRemoteOuterProxies = false; 723 724 CompartmentRemoteProxyTransplantCallback cb(this); 725 js::RemapRemoteWindowProxies(aCx, &cb, aOuter); 726 } 727 728 bool BrowsingContext::IsActive() const { 729 const BrowsingContext* current = this; 730 do { 731 auto explicit_ = current->GetExplicitActive(); 732 if (explicit_ != ExplicitActiveStatus::None) { 733 return explicit_ == ExplicitActiveStatus::Active; 734 } 735 if (mParentWindow && !mParentWindow->IsCurrent()) { 736 return false; 737 } 738 } while ((current = current->GetParent())); 739 740 return false; 741 } 742 743 bool BrowsingContext::GetIsActiveBrowserWindow() { 744 if (!XRE_IsParentProcess()) { 745 return Top()->GetIsActiveBrowserWindowInternal(); 746 } 747 748 // chrome:// urls loaded in the parent won't receive 749 // their own activation so we defer to the top chrome 750 // Browsing Context when in the parent process. 751 return Canonical() 752 ->TopCrossChromeBoundary() 753 ->GetIsActiveBrowserWindowInternal(); 754 } 755 756 void BrowsingContext::SetIsActiveBrowserWindow(bool aActive) { 757 (void)SetIsActiveBrowserWindowInternal(aActive); 758 } 759 760 bool BrowsingContext::FullscreenAllowed() const { 761 for (auto* current = this; current; current = current->GetParent()) { 762 if (!current->GetFullscreenAllowedByOwner()) { 763 return false; 764 } 765 } 766 return true; 767 } 768 769 static bool OwnerAllowsFullscreen(const Element& aEmbedder) { 770 if (aEmbedder.IsXULElement()) { 771 return !aEmbedder.HasAttr(nsGkAtoms::disablefullscreen); 772 } 773 if (aEmbedder.IsHTMLElement(nsGkAtoms::iframe)) { 774 // This is controlled by feature policy. 775 return true; 776 } 777 if (const auto* embed = HTMLEmbedElement::FromNode(aEmbedder)) { 778 return embed->AllowFullscreen(); 779 } 780 return false; 781 } 782 783 void BrowsingContext::SetEmbedderElement(Element* aEmbedder) { 784 mEmbeddedByThisProcess = true; 785 786 if (RefPtr<WindowContext> parent = GetParentWindowContext()) { 787 parent->ClearLightDOMChildren(); 788 } 789 790 // Update embedder-element-specific fields in a shared transaction. 791 // Don't do this when clearing our embedder, as we're being destroyed either 792 // way. 793 if (aEmbedder) { 794 Transaction txn; 795 txn.SetEmbedderElementType(Some(aEmbedder->LocalName())); 796 txn.SetEmbeddedInContentDocument( 797 aEmbedder->OwnerDoc()->IsContentDocument()); 798 if (nsCOMPtr<nsPIDOMWindowInner> inner = 799 do_QueryInterface(aEmbedder->GetOwnerGlobal())) { 800 txn.SetEmbedderInnerWindowId(inner->WindowID()); 801 } 802 txn.SetFullscreenAllowedByOwner(OwnerAllowsFullscreen(*aEmbedder)); 803 if (XRE_IsParentProcess() && aEmbedder->IsXULElement() && IsTopContent()) { 804 nsAutoString messageManagerGroup; 805 aEmbedder->GetAttr(nsGkAtoms::messagemanagergroup, messageManagerGroup); 806 txn.SetMessageManagerGroup(messageManagerGroup); 807 txn.SetUseGlobalHistory( 808 !aEmbedder->HasAttr(nsGkAtoms::disableglobalhistory)); 809 if (!aEmbedder->HasAttr(nsGkAtoms::manualactiveness)) { 810 // We're active iff the parent cross-chrome-boundary is active. Note we 811 // can't just use this->Canonical()->GetParentCrossChromeBoundary here, 812 // since mEmbedderElement is still null at this point. 813 RefPtr bc = aEmbedder->OwnerDoc()->GetBrowsingContext(); 814 const bool isActive = bc && bc->IsActive(); 815 txn.SetExplicitActive(isActive ? ExplicitActiveStatus::Active 816 : ExplicitActiveStatus::Inactive); 817 if (auto* bp = Canonical()->GetBrowserParent()) { 818 bp->SetRenderLayers(isActive); 819 } 820 } 821 } 822 823 MOZ_ALWAYS_SUCCEEDS(txn.Commit(this)); 824 } 825 826 if (XRE_IsParentProcess() && IsTopContent()) { 827 Canonical()->MaybeSetPermanentKey(aEmbedder); 828 } 829 830 mEmbedderElement = aEmbedder; 831 832 if (mEmbedderElement) { 833 if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) { 834 obs->NotifyWhenScriptSafe(ToSupports(this), 835 "browsing-context-did-set-embedder", nullptr); 836 } 837 838 if (IsEmbedderTypeObjectOrEmbed()) { 839 (void)SetIsSyntheticDocumentContainer(true); 840 } 841 } 842 } 843 844 bool BrowsingContext::IsEmbedderTypeObjectOrEmbed() { 845 if (const Maybe<nsString>& type = GetEmbedderElementType()) { 846 return nsGkAtoms::object->Equals(*type) || nsGkAtoms::embed->Equals(*type); 847 } 848 return false; 849 } 850 851 void BrowsingContext::Embed() { 852 if (auto* frame = HTMLIFrameElement::FromNode(mEmbedderElement)) { 853 frame->BindToBrowsingContext(this); 854 } 855 } 856 857 const char* BrowsingContext::BrowsingContextCoherencyChecks( 858 ContentParent* aOriginProcess) { 859 #define COHERENCY_ASSERT(condition) \ 860 if (!(condition)) return "Assertion " #condition " failed"; 861 862 if (mGroup->IsPotentiallyCrossOriginIsolated() != 863 (Top()->GetOpenerPolicy() == 864 nsILoadInfo::OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP)) { 865 return "Invalid CrossOriginIsolated state"; 866 } 867 868 if (aOriginProcess && !IsContent()) { 869 return "Content cannot create chrome BCs"; 870 } 871 872 // LoadContext should generally match our opener or parent. 873 if (IsContent()) { 874 if (RefPtr<BrowsingContext> opener = GetOpener()) { 875 COHERENCY_ASSERT(opener->mType == mType); 876 COHERENCY_ASSERT(opener->mGroup == mGroup); 877 COHERENCY_ASSERT(opener->mUseRemoteTabs == mUseRemoteTabs); 878 COHERENCY_ASSERT(opener->mUseRemoteSubframes == mUseRemoteSubframes); 879 COHERENCY_ASSERT(opener->mPrivateBrowsingId == mPrivateBrowsingId); 880 COHERENCY_ASSERT( 881 opener->mOriginAttributes.EqualsIgnoringFPD(mOriginAttributes)); 882 } 883 } 884 if (RefPtr<BrowsingContext> parent = GetParent()) { 885 COHERENCY_ASSERT(parent->mType == mType); 886 COHERENCY_ASSERT(parent->mGroup == mGroup); 887 COHERENCY_ASSERT(parent->mUseRemoteTabs == mUseRemoteTabs); 888 COHERENCY_ASSERT(parent->mUseRemoteSubframes == mUseRemoteSubframes); 889 COHERENCY_ASSERT(parent->mPrivateBrowsingId == mPrivateBrowsingId); 890 COHERENCY_ASSERT( 891 parent->mOriginAttributes.EqualsIgnoringFPD(mOriginAttributes)); 892 } 893 894 // UseRemoteSubframes and UseRemoteTabs must match. 895 if (mUseRemoteSubframes && !mUseRemoteTabs) { 896 return "Cannot set useRemoteSubframes without also setting useRemoteTabs"; 897 } 898 899 // Double-check OriginAttributes/Private Browsing 900 // Chrome browsing contexts must not have a private browsing OriginAttribute 901 // Content browsing contexts must maintain the equality: 902 // mOriginAttributes.mPrivateBrowsingId == mPrivateBrowsingId 903 if (IsChrome()) { 904 COHERENCY_ASSERT(mOriginAttributes.mPrivateBrowsingId == 0); 905 } else { 906 COHERENCY_ASSERT(mOriginAttributes.mPrivateBrowsingId == 907 mPrivateBrowsingId); 908 } 909 #undef COHERENCY_ASSERT 910 911 return nullptr; 912 } 913 914 void BrowsingContext::Attach(bool aFromIPC, ContentParent* aOriginProcess) { 915 MOZ_DIAGNOSTIC_ASSERT(!mEverAttached); 916 MOZ_DIAGNOSTIC_ASSERT_IF(aFromIPC, aOriginProcess || XRE_IsContentProcess()); 917 mEverAttached = true; 918 919 if (MOZ_LOG_TEST(GetLog(), LogLevel::Debug)) { 920 nsAutoCString suffix; 921 mOriginAttributes.CreateSuffix(suffix); 922 MOZ_LOG(GetLog(), LogLevel::Debug, 923 ("%s: Connecting 0x%08" PRIx64 " to 0x%08" PRIx64 924 " (private=%d, remote=%d, fission=%d, oa=%s)", 925 XRE_IsParentProcess() ? "Parent" : "Child", Id(), 926 GetParent() ? GetParent()->Id() : 0, (int)mPrivateBrowsingId, 927 (int)mUseRemoteTabs, (int)mUseRemoteSubframes, suffix.get())); 928 } 929 930 MOZ_DIAGNOSTIC_ASSERT(mGroup); 931 MOZ_DIAGNOSTIC_ASSERT(!mIsDiscarded); 932 933 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED 934 // We'll already have checked this if `aFromIPC` is set before calling this 935 // function. 936 if (!aFromIPC) { 937 if (const char* failure = BrowsingContextCoherencyChecks(aOriginProcess)) { 938 MOZ_CRASH_UNSAFE_PRINTF("Incoherent BrowsingContext: %s", failure); 939 } 940 } 941 #endif 942 943 // Add ourselves either to our parent or BrowsingContextGroup's child list. 944 // Important: We shouldn't return IPC_FAIL after this point, since the 945 // BrowsingContext will have already been added to the tree. 946 if (mParentWindow) { 947 if (!aFromIPC) { 948 MOZ_DIAGNOSTIC_ASSERT(!mParentWindow->IsDiscarded(), 949 "local attach in discarded window"); 950 MOZ_DIAGNOSTIC_ASSERT(!GetParent()->IsDiscarded(), 951 "local attach call in discarded bc"); 952 MOZ_DIAGNOSTIC_ASSERT(mParentWindow->GetWindowGlobalChild(), 953 "local attach call with oop parent window"); 954 MOZ_DIAGNOSTIC_ASSERT(mParentWindow->GetWindowGlobalChild()->CanSend(), 955 "local attach call with dead parent window"); 956 } 957 mChildOffset = 958 mCreatedDynamically ? -1 : mParentWindow->Children().Length(); 959 mParentWindow->AppendChildBrowsingContext(this); 960 RecomputeCanExecuteScripts(); 961 } else { 962 mGroup->Toplevels().AppendElement(this); 963 } 964 965 if (GetIsPopupSpam()) { 966 PopupBlocker::RegisterOpenPopupSpam(); 967 } 968 969 if (IsTop() && GetHasSessionHistory() && !mChildSessionHistory) { 970 CreateChildSHistory(); 971 } 972 973 // Why the context is being attached. This will always be "attach" in the 974 // content process, but may be "replace" if it's known the context being 975 // replaced in the parent process. 976 const char16_t* why = u"attach"; 977 978 if (XRE_IsContentProcess() && !aFromIPC) { 979 // Send attach to our parent if we need to. 980 ContentChild::GetSingleton()->SendCreateBrowsingContext( 981 mGroup->Id(), GetIPCInitializer()); 982 } else if (XRE_IsParentProcess()) { 983 // If this window was created as a subframe by a content process, it must be 984 // being hosted within the same BrowserParent as its mParentWindow. 985 // Toplevel BrowsingContexts created by content have their BrowserParent 986 // configured during `RecvConstructPopupBrowser`. 987 if (mParentWindow && aOriginProcess) { 988 MOZ_DIAGNOSTIC_ASSERT( 989 mParentWindow->Canonical()->GetContentParent() == aOriginProcess, 990 "Creator process isn't the same as our embedder?"); 991 Canonical()->SetCurrentBrowserParent( 992 mParentWindow->Canonical()->GetBrowserParent()); 993 } 994 995 mGroup->EachOtherParent(aOriginProcess, [&](ContentParent* aParent) { 996 MOZ_DIAGNOSTIC_ASSERT(IsContent(), 997 "chrome BCG cannot be synced to content process"); 998 if (!Canonical()->IsEmbeddedInProcess(aParent->ChildID())) { 999 (void)aParent->SendCreateBrowsingContext(mGroup->Id(), 1000 GetIPCInitializer()); 1001 } 1002 }); 1003 1004 if (IsTop() && IsContent() && Canonical()->GetWebProgress()) { 1005 why = u"replace"; 1006 } 1007 1008 // We want to create a BrowsingContextWebProgress for all content 1009 // BrowsingContexts. 1010 if (IsContent() && !Canonical()->mWebProgress) { 1011 Canonical()->mWebProgress = new BrowsingContextWebProgress(Canonical()); 1012 } 1013 } 1014 1015 if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) { 1016 obs->NotifyWhenScriptSafe(ToSupports(this), "browsing-context-attached", 1017 why); 1018 } 1019 1020 if (XRE_IsParentProcess()) { 1021 Canonical()->CanonicalAttach(); 1022 } 1023 } 1024 1025 void BrowsingContext::Detach(bool aFromIPC) { 1026 MOZ_LOG(GetLog(), LogLevel::Debug, 1027 ("%s: Detaching 0x%08" PRIx64 " from 0x%08" PRIx64, 1028 XRE_IsParentProcess() ? "Parent" : "Child", Id(), 1029 GetParent() ? GetParent()->Id() : 0)); 1030 1031 MOZ_DIAGNOSTIC_ASSERT(mEverAttached); 1032 MOZ_DIAGNOSTIC_ASSERT(!mIsDiscarded); 1033 1034 if (XRE_IsParentProcess()) { 1035 Canonical()->AddPendingDiscard(); 1036 Canonical()->mActiveEntryList = nullptr; 1037 } 1038 auto callListeners = 1039 MakeScopeExit([&, listeners = std::move(mDiscardListeners), id = Id()] { 1040 for (const auto& listener : listeners) { 1041 listener(id); 1042 } 1043 if (XRE_IsParentProcess()) { 1044 Canonical()->RemovePendingDiscard(); 1045 } 1046 }); 1047 1048 nsCOMPtr<nsIRequestContextService> rcsvc = 1049 net::RequestContextService::GetOrCreate(); 1050 if (rcsvc) { 1051 rcsvc->RemoveRequestContext(GetRequestContextId()); 1052 } 1053 1054 // This will only ever be null if the cycle-collector has unlinked us. Don't 1055 // try to detach ourselves in that case. 1056 if (NS_WARN_IF(!mGroup)) { 1057 MOZ_ASSERT_UNREACHABLE(); 1058 return; 1059 } 1060 1061 if (mParentWindow) { 1062 mParentWindow->RemoveChildBrowsingContext(this); 1063 } else { 1064 mGroup->Toplevels().RemoveElement(this); 1065 } 1066 1067 if (XRE_IsParentProcess()) { 1068 RefPtr<CanonicalBrowsingContext> self{Canonical()}; 1069 Group()->EachParent([&](ContentParent* aParent) { 1070 // Only the embedder process is allowed to initiate a BrowsingContext 1071 // detach, so if we've gotten here, the host process already knows we've 1072 // been detached, and there's no need to tell it again. 1073 // 1074 // If the owner process is not the same as the embedder process, its 1075 // BrowsingContext will be detached when its nsWebBrowser instance is 1076 // destroyed. 1077 bool doDiscard = !Canonical()->IsEmbeddedInProcess(aParent->ChildID()) && 1078 !Canonical()->IsOwnedByProcess(aParent->ChildID()); 1079 1080 // Hold a strong reference to ourself, and keep our BrowsingContextGroup 1081 // alive, until the responses comes back to ensure we don't die while 1082 // messages relating to this context are in-flight. 1083 // 1084 // When the callback is called, the keepalive on our group will be 1085 // destroyed, and the reference to the BrowsingContext will be dropped, 1086 // which may cause it to be fully destroyed. 1087 mGroup->AddKeepAlive(); 1088 self->AddPendingDiscard(); 1089 auto callback = [self](auto) { 1090 self->mGroup->RemoveKeepAlive(); 1091 self->RemovePendingDiscard(); 1092 }; 1093 1094 aParent->SendDiscardBrowsingContext(this, doDiscard, callback, callback); 1095 }); 1096 } else { 1097 // Hold a strong reference to ourself until the responses come back to 1098 // ensure the BrowsingContext isn't cleaned up before the parent process 1099 // acknowledges the discard request. 1100 auto callback = [self = RefPtr{this}](auto) {}; 1101 ContentChild::GetSingleton()->SendDiscardBrowsingContext( 1102 this, !aFromIPC, callback, callback); 1103 } 1104 1105 mGroup->Unregister(this); 1106 UnregisterBrowserId(this); 1107 mIsDiscarded = true; 1108 1109 if (XRE_IsParentProcess()) { 1110 nsFocusManager* fm = nsFocusManager::GetFocusManager(); 1111 if (fm) { 1112 fm->BrowsingContextDetached(this); 1113 } 1114 } 1115 1116 if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) { 1117 // Why the context is being discarded. This will always be "discard" in the 1118 // content process, but may be "replace" if it's known the context being 1119 // replaced in the parent process. 1120 const char16_t* why = u"discard"; 1121 if (XRE_IsParentProcess() && Canonical()->IsReplaced()) { 1122 why = u"replace"; 1123 } 1124 obs->NotifyObservers(ToSupports(this), "browsing-context-discarded", why); 1125 } 1126 1127 // NOTE: Doesn't use SetClosed, as it will be set in all processes 1128 // automatically by calls to Detach() 1129 mFields.SetWithoutSyncing<IDX_Closed>(true); 1130 1131 if (GetIsPopupSpam()) { 1132 PopupBlocker::UnregisterOpenPopupSpam(); 1133 // NOTE: Doesn't use SetIsPopupSpam, as it will be set all processes 1134 // automatically. 1135 mFields.SetWithoutSyncing<IDX_IsPopupSpam>(false); 1136 } 1137 1138 AssertOriginAttributesMatchPrivateBrowsing(); 1139 1140 if (XRE_IsParentProcess()) { 1141 Canonical()->CanonicalDiscard(); 1142 } 1143 } 1144 1145 void BrowsingContext::AddDiscardListener( 1146 std::function<void(uint64_t)>&& aListener) { 1147 if (mIsDiscarded) { 1148 aListener(Id()); 1149 return; 1150 } 1151 mDiscardListeners.AppendElement(std::move(aListener)); 1152 } 1153 1154 void BrowsingContext::PrepareForProcessChange() { 1155 MOZ_LOG(GetLog(), LogLevel::Debug, 1156 ("%s: Preparing 0x%08" PRIx64 " for a process change", 1157 XRE_IsParentProcess() ? "Parent" : "Child", Id())); 1158 1159 MOZ_ASSERT(mIsInProcess, "Must currently be an in-process frame"); 1160 MOZ_ASSERT(!mIsDiscarded, "We're already closed?"); 1161 1162 mIsInProcess = false; 1163 mUserGestureStart = TimeStamp(); 1164 1165 ClearCachedValuesOfLocations(); 1166 1167 // NOTE: For now, clear our nsDocShell reference, as we're primarily in a 1168 // different process now. This may need to change in the future with 1169 // Cross-Process BFCache. 1170 mDocShell = nullptr; 1171 if (mChildSessionHistory) { 1172 // This can be removed once session history is stored exclusively in the 1173 // parent process. 1174 mChildSessionHistory->SetIsInProcess(false); 1175 } 1176 1177 if (!mWindowProxy) { 1178 return; 1179 } 1180 1181 // We have to go through mWindowProxy rather than calling GetDOMWindow() on 1182 // mDocShell because the mDocshell reference gets cleared immediately after 1183 // the window is closed. 1184 nsGlobalWindowOuter::PrepareForProcessChange(mWindowProxy); 1185 MOZ_ASSERT(!mWindowProxy); 1186 } 1187 1188 bool BrowsingContext::IsTargetable() const { 1189 return !GetClosed() && AncestorsAreCurrent(); 1190 } 1191 1192 void BrowsingContext::SetOpener(BrowsingContext* aOpener) { 1193 MOZ_DIAGNOSTIC_ASSERT(!aOpener || aOpener->Group() == Group()); 1194 MOZ_DIAGNOSTIC_ASSERT(!aOpener || aOpener->mType == mType); 1195 1196 MOZ_ALWAYS_SUCCEEDS(SetOpenerId(aOpener ? aOpener->Id() : 0)); 1197 1198 if (IsChrome() && IsTop() && aOpener) { 1199 // Inherit color scheme overrides from parent window. This is to inherit the 1200 // color scheme of dark themed PBM windows in dialogs opened by such 1201 // windows. 1202 auto openerOverride = aOpener->Top()->PrefersColorSchemeOverride(); 1203 if (openerOverride != PrefersColorSchemeOverride()) { 1204 MOZ_ALWAYS_SUCCEEDS(SetPrefersColorSchemeOverride(openerOverride)); 1205 } 1206 } 1207 } 1208 1209 bool BrowsingContext::HasOpener() const { 1210 if (sBrowsingContexts) { 1211 return sBrowsingContexts->Contains(GetOpenerId()); 1212 } 1213 1214 return false; 1215 } 1216 1217 bool BrowsingContext::AncestorsAreCurrent() const { 1218 const BrowsingContext* bc = this; 1219 while (true) { 1220 if (bc->IsDiscarded()) { 1221 return false; 1222 } 1223 1224 if (WindowContext* wc = bc->GetParentWindowContext()) { 1225 if (!wc->IsCurrent() || wc->IsDiscarded()) { 1226 return false; 1227 } 1228 1229 bc = wc->GetBrowsingContext(); 1230 } else { 1231 return true; 1232 } 1233 } 1234 } 1235 1236 bool BrowsingContext::IsInBFCache() const { 1237 if (mozilla::SessionHistoryInParent()) { 1238 return mIsInBFCache; 1239 } 1240 return mParentWindow && 1241 mParentWindow->TopWindowContext()->GetWindowStateSaved(); 1242 } 1243 1244 Span<RefPtr<BrowsingContext>> BrowsingContext::Children() const { 1245 if (WindowContext* current = mCurrentWindowContext) { 1246 return current->Children(); 1247 } 1248 return Span<RefPtr<BrowsingContext>>(); 1249 } 1250 1251 void BrowsingContext::GetChildren( 1252 nsTArray<RefPtr<BrowsingContext>>& aChildren) { 1253 aChildren.AppendElements(Children()); 1254 } 1255 1256 Span<RefPtr<BrowsingContext>> BrowsingContext::NonSyntheticChildren() const { 1257 if (WindowContext* current = mCurrentWindowContext) { 1258 return current->NonSyntheticChildren(); 1259 } 1260 return Span<RefPtr<BrowsingContext>>(); 1261 } 1262 1263 BrowsingContext* BrowsingContext::NonSyntheticLightDOMChildAt( 1264 uint32_t aIndex) const { 1265 if (WindowContext* current = mCurrentWindowContext) { 1266 return current->NonSyntheticLightDOMChildAt(aIndex); 1267 } 1268 return nullptr; 1269 } 1270 1271 uint32_t BrowsingContext::NonSyntheticLightDOMChildrenCount() const { 1272 if (WindowContext* current = mCurrentWindowContext) { 1273 return current->NonSyntheticLightDOMChildrenCount(); 1274 } 1275 return 0; 1276 } 1277 1278 void BrowsingContext::GetWindowContexts( 1279 nsTArray<RefPtr<WindowContext>>& aWindows) { 1280 aWindows.AppendElements(mWindowContexts); 1281 } 1282 1283 void BrowsingContext::RegisterWindowContext(WindowContext* aWindow) { 1284 MOZ_ASSERT(!mWindowContexts.Contains(aWindow), 1285 "WindowContext already registered!"); 1286 MOZ_ASSERT(aWindow->GetBrowsingContext() == this); 1287 1288 mWindowContexts.AppendElement(aWindow); 1289 1290 // If the newly registered WindowContext is for our current inner window ID, 1291 // re-run the `DidSet` handler to re-establish the relationship. 1292 if (aWindow->InnerWindowId() == GetCurrentInnerWindowId()) { 1293 DidSet(FieldIndex<IDX_CurrentInnerWindowId>()); 1294 MOZ_DIAGNOSTIC_ASSERT(mCurrentWindowContext == aWindow); 1295 } 1296 } 1297 1298 void BrowsingContext::UnregisterWindowContext(WindowContext* aWindow) { 1299 MOZ_ASSERT(mWindowContexts.Contains(aWindow), 1300 "WindowContext not registered!"); 1301 mWindowContexts.RemoveElement(aWindow); 1302 1303 // If our currently active window was unregistered, clear our reference to it. 1304 if (aWindow == mCurrentWindowContext) { 1305 // Re-read our `CurrentInnerWindowId` value and use it to set 1306 // `mCurrentWindowContext`. As `aWindow` is now unregistered and discarded, 1307 // we won't find it, and the value will be cleared back to `nullptr`. 1308 DidSet(FieldIndex<IDX_CurrentInnerWindowId>()); 1309 MOZ_DIAGNOSTIC_ASSERT(mCurrentWindowContext == nullptr); 1310 } 1311 } 1312 1313 void BrowsingContext::PreOrderWalkVoid( 1314 const std::function<void(BrowsingContext*)>& aCallback) { 1315 aCallback(this); 1316 1317 AutoTArray<RefPtr<BrowsingContext>, 8> children; 1318 children.AppendElements(Children()); 1319 1320 for (auto& child : children) { 1321 child->PreOrderWalkVoid(aCallback); 1322 } 1323 } 1324 1325 BrowsingContext::WalkFlag BrowsingContext::PreOrderWalkFlag( 1326 const std::function<WalkFlag(BrowsingContext*)>& aCallback) { 1327 switch (aCallback(this)) { 1328 case WalkFlag::Skip: 1329 return WalkFlag::Next; 1330 case WalkFlag::Stop: 1331 return WalkFlag::Stop; 1332 case WalkFlag::Next: 1333 default: 1334 break; 1335 } 1336 1337 AutoTArray<RefPtr<BrowsingContext>, 8> children; 1338 children.AppendElements(Children()); 1339 1340 for (auto& child : children) { 1341 switch (child->PreOrderWalkFlag(aCallback)) { 1342 case WalkFlag::Stop: 1343 return WalkFlag::Stop; 1344 default: 1345 break; 1346 } 1347 } 1348 1349 return WalkFlag::Next; 1350 } 1351 1352 void BrowsingContext::PostOrderWalk( 1353 const std::function<void(BrowsingContext*)>& aCallback) { 1354 AutoTArray<RefPtr<BrowsingContext>, 8> children; 1355 children.AppendElements(Children()); 1356 1357 for (auto& child : children) { 1358 child->PostOrderWalk(aCallback); 1359 } 1360 1361 aCallback(this); 1362 } 1363 1364 void BrowsingContext::GetAllBrowsingContextsInSubtree( 1365 nsTArray<RefPtr<BrowsingContext>>& aBrowsingContexts) { 1366 PreOrderWalk([&](BrowsingContext* aContext) { 1367 aBrowsingContexts.AppendElement(aContext); 1368 }); 1369 } 1370 1371 BrowsingContext* BrowsingContext::FindChildWithName( 1372 const nsAString& aName, WindowGlobalChild& aRequestingWindow) { 1373 if (aName.IsEmpty()) { 1374 // You can't find a browsing context with the empty name. 1375 return nullptr; 1376 } 1377 1378 for (BrowsingContext* child : NonSyntheticChildren()) { 1379 if (child->NameEquals(aName) && aRequestingWindow.CanNavigate(child) && 1380 child->IsTargetable()) { 1381 return child; 1382 } 1383 } 1384 1385 return nullptr; 1386 } 1387 1388 BrowsingContext* BrowsingContext::FindWithSpecialName( 1389 const nsAString& aName, WindowGlobalChild& aRequestingWindow) { 1390 // TODO(farre): Neither BrowsingContext nor nsDocShell checks if the 1391 // browsing context pointed to by a special name is active. Should 1392 // it be? See Bug 1527913. 1393 if (aName.LowerCaseEqualsLiteral("_self")) { 1394 return this; 1395 } 1396 1397 if (aName.LowerCaseEqualsLiteral("_parent")) { 1398 if (BrowsingContext* parent = GetParent()) { 1399 return aRequestingWindow.CanNavigate(parent) ? parent : nullptr; 1400 } 1401 return this; 1402 } 1403 1404 if (aName.LowerCaseEqualsLiteral("_top")) { 1405 BrowsingContext* top = Top(); 1406 1407 return aRequestingWindow.CanNavigate(top) ? top : nullptr; 1408 } 1409 1410 return nullptr; 1411 } 1412 1413 BrowsingContext* BrowsingContext::FindWithNameInSubtree( 1414 const nsAString& aName, WindowGlobalChild* aRequestingWindow) { 1415 MOZ_DIAGNOSTIC_ASSERT(!aName.IsEmpty()); 1416 1417 if (NameEquals(aName) && 1418 (!aRequestingWindow || aRequestingWindow->CanNavigate(this)) && 1419 IsTargetable()) { 1420 return this; 1421 } 1422 1423 for (BrowsingContext* child : NonSyntheticChildren()) { 1424 if (BrowsingContext* found = 1425 child->FindWithNameInSubtree(aName, aRequestingWindow)) { 1426 return found; 1427 } 1428 } 1429 1430 return nullptr; 1431 } 1432 1433 bool BrowsingContext::IsSandboxedFrom(BrowsingContext* aTarget) { 1434 // If no target then not sandboxed. 1435 if (!aTarget) { 1436 return false; 1437 } 1438 1439 // We cannot be sandboxed from ourselves. 1440 if (aTarget == this) { 1441 return false; 1442 } 1443 1444 // Default the sandbox flags to our flags, so that if we can't retrieve the 1445 // active document, we will still enforce our own. 1446 uint32_t sandboxFlags = GetSandboxFlags(); 1447 if (mDocShell) { 1448 if (RefPtr<Document> doc = mDocShell->GetExtantDocument()) { 1449 sandboxFlags = doc->GetSandboxFlags(); 1450 } 1451 } 1452 1453 // If no flags, we are not sandboxed at all. 1454 if (!sandboxFlags) { 1455 return false; 1456 } 1457 1458 // If aTarget has an ancestor, it is not top level. 1459 if (RefPtr<BrowsingContext> ancestorOfTarget = aTarget->GetParent()) { 1460 do { 1461 // We are not sandboxed if we are an ancestor of target. 1462 if (ancestorOfTarget == this) { 1463 return false; 1464 } 1465 ancestorOfTarget = ancestorOfTarget->GetParent(); 1466 } while (ancestorOfTarget); 1467 1468 // Otherwise, we are sandboxed from aTarget. 1469 return true; 1470 } 1471 1472 // aTarget is top level, are we the "one permitted sandboxed 1473 // navigator", i.e. did we open aTarget? 1474 if (aTarget->GetOnePermittedSandboxedNavigatorId() == Id()) { 1475 return false; 1476 } 1477 1478 // If SANDBOXED_TOPLEVEL_NAVIGATION flag is not on, we are not sandboxed 1479 // from our top. 1480 if (!(sandboxFlags & SANDBOXED_TOPLEVEL_NAVIGATION) && aTarget == Top()) { 1481 return false; 1482 } 1483 1484 // If SANDBOXED_TOPLEVEL_NAVIGATION_USER_ACTIVATION flag is not on, we are not 1485 // sandboxed from our top if we have user interaction. We assume there is a 1486 // valid transient user gesture interaction if this check happens in the 1487 // target process given that we have checked in the triggering process 1488 // already. 1489 if (!(sandboxFlags & SANDBOXED_TOPLEVEL_NAVIGATION_USER_ACTIVATION) && 1490 mCurrentWindowContext && 1491 (!mCurrentWindowContext->IsInProcess() || 1492 mCurrentWindowContext->HasValidTransientUserGestureActivation()) && 1493 aTarget == Top()) { 1494 return false; 1495 } 1496 1497 // Otherwise, we are sandboxed from aTarget. 1498 return true; 1499 } 1500 1501 RefPtr<SessionStorageManager> BrowsingContext::GetSessionStorageManager() { 1502 RefPtr<SessionStorageManager>& manager = Top()->mSessionStorageManager; 1503 if (!manager) { 1504 manager = new SessionStorageManager(this); 1505 } 1506 return manager; 1507 } 1508 1509 bool BrowsingContext::CrossOriginIsolated() { 1510 MOZ_ASSERT(NS_IsMainThread()); 1511 1512 return StaticPrefs:: 1513 dom_postMessage_sharedArrayBuffer_withCOOP_COEP_AtStartup() && 1514 Top()->GetOpenerPolicy() == 1515 nsILoadInfo:: 1516 OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP && 1517 XRE_IsContentProcess() && 1518 StringBeginsWith(ContentChild::GetSingleton()->GetRemoteType(), 1519 WITH_COOP_COEP_REMOTE_TYPE_PREFIX); 1520 } 1521 1522 void BrowsingContext::SetTriggeringAndInheritPrincipals( 1523 nsIPrincipal* aTriggeringPrincipal, nsIPrincipal* aPrincipalToInherit, 1524 uint64_t aLoadIdentifier) { 1525 mTriggeringPrincipal = Some( 1526 PrincipalWithLoadIdentifierTuple(aTriggeringPrincipal, aLoadIdentifier)); 1527 if (aPrincipalToInherit) { 1528 mPrincipalToInherit = Some( 1529 PrincipalWithLoadIdentifierTuple(aPrincipalToInherit, aLoadIdentifier)); 1530 } 1531 } 1532 1533 std::tuple<nsCOMPtr<nsIPrincipal>, nsCOMPtr<nsIPrincipal>> 1534 BrowsingContext::GetTriggeringAndInheritPrincipalsForCurrentLoad() { 1535 nsCOMPtr<nsIPrincipal> triggeringPrincipal = 1536 GetSavedPrincipal(mTriggeringPrincipal); 1537 nsCOMPtr<nsIPrincipal> principalToInherit = 1538 GetSavedPrincipal(mPrincipalToInherit); 1539 return std::make_tuple(triggeringPrincipal, principalToInherit); 1540 } 1541 1542 nsIPrincipal* BrowsingContext::GetSavedPrincipal( 1543 Maybe<PrincipalWithLoadIdentifierTuple> aPrincipalTuple) { 1544 if (aPrincipalTuple) { 1545 nsCOMPtr<nsIPrincipal> principal; 1546 uint64_t loadIdentifier; 1547 std::tie(principal, loadIdentifier) = *aPrincipalTuple; 1548 // We want to return a principal only if the load identifier for it 1549 // matches the current one for this BC. 1550 if (auto current = GetCurrentLoadIdentifier(); 1551 current && *current == loadIdentifier) { 1552 return principal; 1553 } 1554 } 1555 return nullptr; 1556 } 1557 1558 BrowsingContext::~BrowsingContext() { 1559 MOZ_DIAGNOSTIC_ASSERT(!mParentWindow || 1560 !mParentWindow->mChildren.Contains(this)); 1561 MOZ_DIAGNOSTIC_ASSERT(!mGroup || !mGroup->Toplevels().Contains(this)); 1562 1563 mDeprioritizedLoadRunner.clear(); 1564 1565 if (sBrowsingContexts) { 1566 sBrowsingContexts->Remove(Id()); 1567 } 1568 UnregisterBrowserId(this); 1569 1570 ClearCachedValuesOfLocations(); 1571 mLocations.clear(); 1572 } 1573 1574 /* static */ 1575 void BrowsingContext::DiscardFromContentParent(ContentParent* aCP) { 1576 MOZ_ASSERT(XRE_IsParentProcess()); 1577 1578 if (sBrowsingContexts) { 1579 AutoTArray<RefPtr<BrowsingContext>, 8> toDiscard; 1580 for (const auto& data : sBrowsingContexts->Values()) { 1581 auto* bc = data->Canonical(); 1582 if (!bc->IsDiscarded() && bc->IsEmbeddedInProcess(aCP->ChildID())) { 1583 toDiscard.AppendElement(bc); 1584 } 1585 } 1586 1587 for (BrowsingContext* bc : toDiscard) { 1588 bc->Detach(/* aFromIPC */ true); 1589 } 1590 } 1591 } 1592 1593 nsISupports* BrowsingContext::GetParentObject() const { 1594 return xpc::NativeGlobal(xpc::PrivilegedJunkScope()); 1595 } 1596 1597 JSObject* BrowsingContext::WrapObject(JSContext* aCx, 1598 JS::Handle<JSObject*> aGivenProto) { 1599 return BrowsingContext_Binding::Wrap(aCx, this, aGivenProto); 1600 } 1601 1602 bool BrowsingContext::WriteStructuredClone(JSContext* aCx, 1603 JSStructuredCloneWriter* aWriter, 1604 StructuredCloneHolder* aHolder) { 1605 MOZ_DIAGNOSTIC_ASSERT(mEverAttached); 1606 return (JS_WriteUint32Pair(aWriter, SCTAG_DOM_BROWSING_CONTEXT, 0) && 1607 JS_WriteUint32Pair(aWriter, uint32_t(Id()), uint32_t(Id() >> 32))); 1608 } 1609 1610 /* static */ 1611 JSObject* BrowsingContext::ReadStructuredClone(JSContext* aCx, 1612 JSStructuredCloneReader* aReader, 1613 StructuredCloneHolder* aHolder) { 1614 uint32_t idLow = 0; 1615 uint32_t idHigh = 0; 1616 if (!JS_ReadUint32Pair(aReader, &idLow, &idHigh)) { 1617 return nullptr; 1618 } 1619 uint64_t id = uint64_t(idHigh) << 32 | idLow; 1620 1621 // Note: Do this check after reading our ID data. Returning null will abort 1622 // the decode operation anyway, but we should at least be as safe as possible. 1623 if (NS_WARN_IF(!NS_IsMainThread())) { 1624 MOZ_DIAGNOSTIC_CRASH( 1625 "We shouldn't be trying to decode a BrowsingContext " 1626 "on a background thread."); 1627 return nullptr; 1628 } 1629 1630 JS::Rooted<JS::Value> val(aCx, JS::NullValue()); 1631 // We'll get rooting hazard errors from the RefPtr destructor if it isn't 1632 // destroyed before we try to return a raw JSObject*, so create it in its own 1633 // scope. 1634 if (RefPtr<BrowsingContext> context = Get(id)) { 1635 if (!GetOrCreateDOMReflector(aCx, context, &val) || !val.isObject()) { 1636 return nullptr; 1637 } 1638 } 1639 return val.toObjectOrNull(); 1640 } 1641 1642 bool BrowsingContext::CanSetOriginAttributes() { 1643 // A discarded BrowsingContext has already been destroyed, and cannot modify 1644 // its OriginAttributes. 1645 if (NS_WARN_IF(IsDiscarded())) { 1646 return false; 1647 } 1648 1649 // Before attaching is the safest time to set OriginAttributes, and the only 1650 // allowed time for content BrowsingContexts. 1651 if (!EverAttached()) { 1652 return true; 1653 } 1654 1655 // Attached content BrowsingContexts may have been synced to other processes. 1656 if (NS_WARN_IF(IsContent())) { 1657 MOZ_CRASH(); 1658 return false; 1659 } 1660 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess()); 1661 1662 // Cannot set OriginAttributes after we've created our child BrowsingContext. 1663 if (NS_WARN_IF(!Children().IsEmpty())) { 1664 return false; 1665 } 1666 1667 // Only allow setting OriginAttributes if we have no associated document, or 1668 // the document is still `about:blank`. 1669 // TODO: Bug 1273058 - should have no document when setting origin attributes. 1670 if (WindowGlobalParent* window = Canonical()->GetCurrentWindowGlobal()) { 1671 if (nsIURI* uri = window->GetDocumentURI()) { 1672 MOZ_ASSERT(NS_IsAboutBlank(uri)); 1673 return NS_IsAboutBlank(uri); 1674 } 1675 } 1676 return true; 1677 } 1678 1679 Nullable<WindowProxyHolder> BrowsingContext::GetAssociatedWindow() { 1680 // nsILoadContext usually only returns same-process windows, 1681 // so we intentionally return nullptr if this BC is out of 1682 // process. 1683 if (IsInProcess()) { 1684 return WindowProxyHolder(this); 1685 } 1686 return nullptr; 1687 } 1688 1689 Nullable<WindowProxyHolder> BrowsingContext::GetTopWindow() { 1690 return Top()->GetAssociatedWindow(); 1691 } 1692 1693 Element* BrowsingContext::GetTopFrameElement() { 1694 return Top()->GetEmbedderElement(); 1695 } 1696 1697 void BrowsingContext::SetUsePrivateBrowsing(bool aUsePrivateBrowsing, 1698 ErrorResult& aError) { 1699 nsresult rv = SetUsePrivateBrowsing(aUsePrivateBrowsing); 1700 if (NS_FAILED(rv)) { 1701 aError.Throw(rv); 1702 } 1703 } 1704 1705 void BrowsingContext::SetUseTrackingProtectionWebIDL( 1706 bool aUseTrackingProtection, ErrorResult& aRv) { 1707 SetForceEnableTrackingProtection(aUseTrackingProtection, aRv); 1708 } 1709 1710 void BrowsingContext::GetOriginAttributes(JSContext* aCx, 1711 JS::MutableHandle<JS::Value> aVal, 1712 ErrorResult& aError) { 1713 AssertOriginAttributesMatchPrivateBrowsing(); 1714 1715 if (!ToJSValue(aCx, mOriginAttributes, aVal)) { 1716 aError.NoteJSContextException(aCx); 1717 } 1718 } 1719 1720 NS_IMETHODIMP BrowsingContext::GetAssociatedWindow( 1721 mozIDOMWindowProxy** aAssociatedWindow) { 1722 nsCOMPtr<mozIDOMWindowProxy> win = GetDOMWindow(); 1723 win.forget(aAssociatedWindow); 1724 return NS_OK; 1725 } 1726 1727 NS_IMETHODIMP BrowsingContext::GetTopWindow(mozIDOMWindowProxy** aTopWindow) { 1728 return Top()->GetAssociatedWindow(aTopWindow); 1729 } 1730 1731 NS_IMETHODIMP BrowsingContext::GetTopFrameElement(Element** aTopFrameElement) { 1732 RefPtr<Element> topFrameElement = GetTopFrameElement(); 1733 topFrameElement.forget(aTopFrameElement); 1734 return NS_OK; 1735 } 1736 1737 NS_IMETHODIMP BrowsingContext::GetIsContent(bool* aIsContent) { 1738 *aIsContent = IsContent(); 1739 return NS_OK; 1740 } 1741 1742 NS_IMETHODIMP BrowsingContext::GetUsePrivateBrowsing( 1743 bool* aUsePrivateBrowsing) { 1744 *aUsePrivateBrowsing = mPrivateBrowsingId > 0; 1745 return NS_OK; 1746 } 1747 1748 NS_IMETHODIMP BrowsingContext::SetUsePrivateBrowsing(bool aUsePrivateBrowsing) { 1749 if (!CanSetOriginAttributes()) { 1750 bool changed = aUsePrivateBrowsing != (mPrivateBrowsingId > 0); 1751 if (changed) { 1752 NS_WARNING("SetUsePrivateBrowsing when !CanSetOriginAttributes()"); 1753 } 1754 return changed ? NS_ERROR_FAILURE : NS_OK; 1755 } 1756 1757 return SetPrivateBrowsing(aUsePrivateBrowsing); 1758 } 1759 1760 NS_IMETHODIMP BrowsingContext::SetPrivateBrowsing(bool aPrivateBrowsing) { 1761 if (!CanSetOriginAttributes()) { 1762 NS_WARNING("Attempt to set PrivateBrowsing when !CanSetOriginAttributes"); 1763 return NS_ERROR_FAILURE; 1764 } 1765 1766 bool changed = aPrivateBrowsing != (mPrivateBrowsingId > 0); 1767 if (changed) { 1768 mPrivateBrowsingId = aPrivateBrowsing ? 1 : 0; 1769 if (IsContent()) { 1770 mOriginAttributes.SyncAttributesWithPrivateBrowsing(aPrivateBrowsing); 1771 } 1772 1773 if (XRE_IsParentProcess()) { 1774 Canonical()->AdjustPrivateBrowsingCount(aPrivateBrowsing); 1775 } 1776 } 1777 AssertOriginAttributesMatchPrivateBrowsing(); 1778 1779 if (changed && mDocShell) { 1780 nsDocShell::Cast(mDocShell)->NotifyPrivateBrowsingChanged(); 1781 } 1782 return NS_OK; 1783 } 1784 1785 NS_IMETHODIMP BrowsingContext::GetUseRemoteTabs(bool* aUseRemoteTabs) { 1786 *aUseRemoteTabs = mUseRemoteTabs; 1787 return NS_OK; 1788 } 1789 1790 NS_IMETHODIMP BrowsingContext::SetRemoteTabs(bool aUseRemoteTabs) { 1791 if (!CanSetOriginAttributes()) { 1792 NS_WARNING("Attempt to set RemoteTabs when !CanSetOriginAttributes"); 1793 return NS_ERROR_FAILURE; 1794 } 1795 1796 if (aUseRemoteTabs && !gIPCEnabledAnnotation) { 1797 gIPCEnabledAnnotation = true; 1798 } 1799 1800 // Don't allow non-remote tabs with remote subframes. 1801 if (NS_WARN_IF(!aUseRemoteTabs && mUseRemoteSubframes)) { 1802 return NS_ERROR_UNEXPECTED; 1803 } 1804 1805 mUseRemoteTabs = aUseRemoteTabs; 1806 return NS_OK; 1807 } 1808 1809 NS_IMETHODIMP BrowsingContext::GetUseRemoteSubframes( 1810 bool* aUseRemoteSubframes) { 1811 *aUseRemoteSubframes = mUseRemoteSubframes; 1812 return NS_OK; 1813 } 1814 1815 NS_IMETHODIMP BrowsingContext::SetRemoteSubframes(bool aUseRemoteSubframes) { 1816 if (!CanSetOriginAttributes()) { 1817 NS_WARNING("Attempt to set RemoteSubframes when !CanSetOriginAttributes"); 1818 return NS_ERROR_FAILURE; 1819 } 1820 1821 if (aUseRemoteSubframes && !gFissionEnabledAnnotation) { 1822 gFissionEnabledAnnotation = true; 1823 } 1824 1825 // Don't allow non-remote tabs with remote subframes. 1826 if (NS_WARN_IF(aUseRemoteSubframes && !mUseRemoteTabs)) { 1827 return NS_ERROR_UNEXPECTED; 1828 } 1829 1830 mUseRemoteSubframes = aUseRemoteSubframes; 1831 return NS_OK; 1832 } 1833 1834 NS_IMETHODIMP BrowsingContext::GetUseTrackingProtection( 1835 bool* aUseTrackingProtection) { 1836 *aUseTrackingProtection = false; 1837 1838 if (GetForceEnableTrackingProtection() || 1839 StaticPrefs::privacy_trackingprotection_enabled() || 1840 (UsePrivateBrowsing() && 1841 StaticPrefs::privacy_trackingprotection_pbmode_enabled())) { 1842 *aUseTrackingProtection = true; 1843 return NS_OK; 1844 } 1845 1846 if (GetParent()) { 1847 return GetParent()->GetUseTrackingProtection(aUseTrackingProtection); 1848 } 1849 1850 return NS_OK; 1851 } 1852 1853 NS_IMETHODIMP BrowsingContext::SetUseTrackingProtection( 1854 bool aUseTrackingProtection) { 1855 return SetForceEnableTrackingProtection(aUseTrackingProtection); 1856 } 1857 1858 NS_IMETHODIMP BrowsingContext::GetScriptableOriginAttributes( 1859 JSContext* aCx, JS::MutableHandle<JS::Value> aVal) { 1860 AssertOriginAttributesMatchPrivateBrowsing(); 1861 1862 bool ok = ToJSValue(aCx, mOriginAttributes, aVal); 1863 NS_ENSURE_TRUE(ok, NS_ERROR_FAILURE); 1864 return NS_OK; 1865 } 1866 1867 NS_IMETHODIMP_(void) 1868 BrowsingContext::GetOriginAttributes(OriginAttributes& aAttrs) { 1869 aAttrs = mOriginAttributes; 1870 AssertOriginAttributesMatchPrivateBrowsing(); 1871 } 1872 1873 nsresult BrowsingContext::SetOriginAttributes(const OriginAttributes& aAttrs) { 1874 if (!CanSetOriginAttributes()) { 1875 NS_WARNING("Attempt to set OriginAttributes when !CanSetOriginAttributes"); 1876 return NS_ERROR_FAILURE; 1877 } 1878 1879 AssertOriginAttributesMatchPrivateBrowsing(); 1880 mOriginAttributes = aAttrs; 1881 1882 bool isPrivate = mOriginAttributes.mPrivateBrowsingId != 1883 nsIScriptSecurityManager::DEFAULT_PRIVATE_BROWSING_ID; 1884 // Chrome Browsing Context can not contain OriginAttributes.mPrivateBrowsingId 1885 if (IsChrome() && isPrivate) { 1886 mOriginAttributes.mPrivateBrowsingId = 1887 nsIScriptSecurityManager::DEFAULT_PRIVATE_BROWSING_ID; 1888 } 1889 SetPrivateBrowsing(isPrivate); 1890 AssertOriginAttributesMatchPrivateBrowsing(); 1891 1892 return NS_OK; 1893 } 1894 1895 void BrowsingContext::AssertOriginAttributesMatchPrivateBrowsing() { 1896 // Chrome browsing contexts must not have a private browsing OriginAttribute 1897 // Content browsing contexts must maintain the equality: 1898 // mOriginAttributes.mPrivateBrowsingId == mPrivateBrowsingId 1899 if (IsChrome()) { 1900 MOZ_DIAGNOSTIC_ASSERT(mOriginAttributes.mPrivateBrowsingId == 0); 1901 } else { 1902 MOZ_DIAGNOSTIC_ASSERT(mOriginAttributes.mPrivateBrowsingId == 1903 mPrivateBrowsingId); 1904 } 1905 } 1906 1907 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(BrowsingContext) 1908 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 1909 NS_INTERFACE_MAP_ENTRY(nsILoadContext) 1910 NS_INTERFACE_MAP_ENTRY(nsISupports) 1911 NS_INTERFACE_MAP_END 1912 1913 NS_IMPL_CYCLE_COLLECTION_CLASS(BrowsingContext) 1914 1915 NS_IMPL_CYCLE_COLLECTING_ADDREF(BrowsingContext) 1916 NS_IMPL_CYCLE_COLLECTING_RELEASE(BrowsingContext) 1917 1918 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(BrowsingContext) 1919 NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER 1920 NS_IMPL_CYCLE_COLLECTION_TRACE_END 1921 1922 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(BrowsingContext) 1923 if (sBrowsingContexts) { 1924 sBrowsingContexts->Remove(tmp->Id()); 1925 } 1926 UnregisterBrowserId(tmp); 1927 1928 if (tmp->GetIsPopupSpam()) { 1929 PopupBlocker::UnregisterOpenPopupSpam(); 1930 // NOTE: Doesn't use SetIsPopupSpam, as it will be set all processes 1931 // automatically. 1932 tmp->mFields.SetWithoutSyncing<IDX_IsPopupSpam>(false); 1933 } 1934 1935 NS_IMPL_CYCLE_COLLECTION_UNLINK( 1936 mDocShell, mParentWindow, mGroup, mEmbedderElement, mWindowContexts, 1937 mCurrentWindowContext, mSessionStorageManager, mChildSessionHistory) 1938 NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER 1939 NS_IMPL_CYCLE_COLLECTION_UNLINK_END 1940 1941 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(BrowsingContext) 1942 NS_IMPL_CYCLE_COLLECTION_TRAVERSE( 1943 mDocShell, mParentWindow, mGroup, mEmbedderElement, mWindowContexts, 1944 mCurrentWindowContext, mSessionStorageManager, mChildSessionHistory) 1945 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END 1946 1947 static bool IsCertainlyAliveForCC(BrowsingContext* aContext) { 1948 return aContext->HasKnownLiveWrapper() || 1949 (AppShutdown::GetCurrentShutdownPhase() == 1950 ShutdownPhase::NotInShutdown && 1951 aContext->EverAttached() && !aContext->IsDiscarded()); 1952 } 1953 1954 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_BEGIN(BrowsingContext) 1955 if (IsCertainlyAliveForCC(tmp)) { 1956 if (tmp->PreservingWrapper()) { 1957 tmp->MarkWrapperLive(); 1958 } 1959 return true; 1960 } 1961 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_END 1962 1963 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_BEGIN(BrowsingContext) 1964 return IsCertainlyAliveForCC(tmp) && tmp->HasNothingToTrace(tmp); 1965 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_IN_CC_END 1966 1967 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(BrowsingContext) 1968 return IsCertainlyAliveForCC(tmp); 1969 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END 1970 1971 class RemoteLocationProxy 1972 : public RemoteObjectProxy<BrowsingContext::LocationProxy, 1973 Location_Binding::sCrossOriginProperties> { 1974 public: 1975 typedef RemoteObjectProxy Base; 1976 1977 constexpr RemoteLocationProxy() 1978 : RemoteObjectProxy(prototypes::id::Location) {} 1979 1980 void NoteChildren(JSObject* aProxy, 1981 nsCycleCollectionTraversalCallback& aCb) const override { 1982 auto location = 1983 static_cast<BrowsingContext::LocationProxy*>(GetNative(aProxy)); 1984 CycleCollectionNoteChild(aCb, location->GetBrowsingContext(), 1985 "JS::GetPrivate(obj)->GetBrowsingContext()"); 1986 } 1987 }; 1988 1989 static const RemoteLocationProxy sSingleton; 1990 1991 // Give RemoteLocationProxy 2 reserved slots, like the other wrappers, 1992 // so JSObject::swap can swap it with CrossCompartmentWrappers without requiring 1993 // malloc. 1994 template <> 1995 const JSClass RemoteLocationProxy::Base::sClass = 1996 PROXY_CLASS_DEF("Proxy", JSCLASS_HAS_RESERVED_SLOTS(2)); 1997 1998 void BrowsingContext::Location(JSContext* aCx, 1999 JS::MutableHandle<JSObject*> aLocation, 2000 ErrorResult& aError) { 2001 aError.MightThrowJSException(); 2002 sSingleton.GetProxyObject(aCx, &mLocation, /* aTransplantTo = */ nullptr, 2003 aLocation); 2004 if (!aLocation) { 2005 aError.StealExceptionFromJSContext(aCx); 2006 } 2007 } 2008 2009 bool BrowsingContext::RemoveRootFromBFCacheSync() { 2010 if (WindowContext* wc = GetParentWindowContext()) { 2011 if (RefPtr<Document> doc = wc->TopWindowContext()->GetDocument()) { 2012 return doc->RemoveFromBFCacheSync(); 2013 } 2014 } 2015 return false; 2016 } 2017 2018 nsresult BrowsingContext::CheckSandboxFlags(nsDocShellLoadState* aLoadState) { 2019 const auto& sourceBC = aLoadState->SourceBrowsingContext(); 2020 if (sourceBC.IsNull()) { 2021 return NS_OK; 2022 } 2023 2024 // We might be called after the source BC has been discarded, but before we've 2025 // destroyed our in-process instance of the BrowsingContext object in some 2026 // situations (e.g. after creating a new pop-up with window.open while the 2027 // window is being closed). In these situations we want to still perform the 2028 // sandboxing check against our in-process copy. If we've forgotten about the 2029 // context already, assume it is sanboxed. (bug 1643450) 2030 BrowsingContext* bc = sourceBC.GetMaybeDiscarded(); 2031 if (!bc || bc->IsSandboxedFrom(this)) { 2032 return NS_ERROR_DOM_SECURITY_ERR; 2033 } 2034 return NS_OK; 2035 } 2036 2037 nsresult BrowsingContext::CheckFramebusting(nsDocShellLoadState* aLoadState) { 2038 if (!StaticPrefs::dom_security_framebusting_intervention_enabled()) { 2039 return NS_OK; 2040 } 2041 2042 if (XRE_IsParentProcess()) { 2043 return NS_OK; 2044 } 2045 2046 // Only applies to top-level navigations. 2047 if (!IsTop()) { 2048 return NS_OK; 2049 } 2050 2051 if (aLoadState->HasValidUserGestureActivation()) { 2052 return NS_OK; 2053 } 2054 2055 const auto& sourceBC = aLoadState->SourceBrowsingContext(); 2056 if (sourceBC.IsNull()) { 2057 return NS_OK; 2058 } 2059 2060 if (BrowsingContext* bc = sourceBC.GetMaybeDiscarded()) { 2061 if (bc->IsFramebustingAllowed(this)) { 2062 return NS_OK; 2063 } 2064 2065 if (bc->GetDOMWindow()) { 2066 nsGlobalWindowOuter::Cast(bc->GetDOMWindow()) 2067 ->FireRedirectBlockedEvent(aLoadState->URI()); 2068 } 2069 2070 nsAutoCString frameURL; 2071 if (bc->GetDocument() && 2072 NS_SUCCEEDED( 2073 bc->GetDocument()->GetPrincipal()->GetAsciiSpec(frameURL))) { 2074 nsContentUtils::ReportToConsoleNonLocalized( 2075 NS_ConvertUTF8toUTF16(nsPrintfCString( 2076 R"(Attempting to navigate the top-level browsing context from )" 2077 R"(frame with url "%s" which is neither same-origin nor has )" 2078 R"(the required user interaction.)", 2079 frameURL.get())), 2080 nsIScriptError::errorFlag, "DOM"_ns, bc->GetDocument()); 2081 } 2082 } 2083 2084 return NS_ERROR_DOM_SECURITY_ERR; 2085 } 2086 2087 bool BrowsingContext::IsFramebustingAllowed(BrowsingContext* aTarget) { 2088 MOZ_ASSERT(aTarget->IsTop()); 2089 2090 if (aTarget->BrowserId() == BrowserId()) { 2091 return IsFramebustingAllowedInner() || IsPopupAllowed(); 2092 } 2093 2094 // We should be able to safely assume that the SOP has our back here 2095 // already. How else would this BrowsingContext have a reference? 2096 return true; 2097 } 2098 2099 bool BrowsingContext::IsFramebustingAllowedInner() { 2100 if (IsInProcess() && SameOriginWithTop()) { 2101 return true; 2102 } 2103 2104 // We get the sandbox flags from the load info since the CSP header 2105 // hasn't yet been processed at that time. The CSP sandbox directive makes 2106 // it possible for a document to grant itself "allow-top-navigation" 2107 // permissions by sending the appropiate header and we don't like that. 2108 Document* doc; 2109 nsIChannel* channel; 2110 if ((doc = GetExtantDocument()) && (channel = doc->GetChannel())) { 2111 nsCOMPtr<nsILoadInfo> loadInfo = channel->LoadInfo(); 2112 uint32_t sandboxFlags = loadInfo->GetSandboxFlags(); 2113 if (sandboxFlags && !(sandboxFlags & SANDBOXED_TOPLEVEL_NAVIGATION)) { 2114 BrowsingContext* parent = GetParent(); 2115 return !parent || parent->IsFramebustingAllowedInner(); 2116 } 2117 } 2118 2119 return false; 2120 } 2121 2122 nsresult BrowsingContext::LoadURI(nsDocShellLoadState* aLoadState, 2123 bool aSetNavigating) { 2124 // Per spec, most load attempts are silently ignored when a BrowsingContext is 2125 // null (which in our code corresponds to discarded), so we simply fail 2126 // silently in those cases. Regardless, we cannot trigger loads in/from 2127 // discarded BrowsingContexts via IPC, so we need to abort in any case. 2128 if (IsDiscarded()) { 2129 return NS_OK; 2130 } 2131 2132 MOZ_DIAGNOSTIC_ASSERT(aLoadState->TargetBrowsingContext().IsNull(), 2133 "Targeting occurs in InternalLoad"); 2134 aLoadState->AssertProcessCouldTriggerLoadIfSystem(); 2135 2136 // When this tab sets these load flags, we disable or force TRR for the 2137 // browsing context ensuring subsequent navigations will keep the same 2138 // TRR mode. 2139 if (aLoadState->HasLoadFlags(nsIWebNavigation::LOAD_FLAGS_DISABLE_TRR)) { 2140 (void)SetDefaultLoadFlags(GetDefaultLoadFlags() | 2141 nsIRequest::LOAD_TRR_DISABLED_MODE); 2142 } else if (aLoadState->HasLoadFlags(nsIWebNavigation::LOAD_FLAGS_FORCE_TRR)) { 2143 (void)SetDefaultLoadFlags(GetDefaultLoadFlags() | 2144 nsIRequest::LOAD_TRR_ONLY_MODE); 2145 } 2146 2147 if (mDocShell) { 2148 nsCOMPtr<nsIDocShell> docShell = mDocShell; 2149 2150 return docShell->LoadURI(aLoadState, aSetNavigating); 2151 } 2152 2153 // Note: We do this check both here and in `nsDocShell::InternalLoad`, since 2154 // document-specific sandbox flags are only available in the process 2155 // triggering the load, and we don't want the target process to have to trust 2156 // the triggering process to do the appropriate checks for the 2157 // BrowsingContext's sandbox flags. 2158 MOZ_TRY(CheckSandboxFlags(aLoadState)); 2159 SetTriggeringAndInheritPrincipals(aLoadState->TriggeringPrincipal(), 2160 aLoadState->PrincipalToInherit(), 2161 aLoadState->GetLoadIdentifier()); 2162 2163 const auto& sourceBC = aLoadState->SourceBrowsingContext(); 2164 2165 if (aLoadState->URI()->SchemeIs("javascript")) { 2166 if (!XRE_IsParentProcess()) { 2167 // Web content should only be able to load javascript: URIs into documents 2168 // whose principals the caller principal subsumes, which by definition 2169 // excludes any document in a cross-process BrowsingContext. 2170 return NS_ERROR_DOM_BAD_CROSS_ORIGIN_URI; 2171 } 2172 MOZ_DIAGNOSTIC_ASSERT(!sourceBC, 2173 "Should never see a cross-process javascript: load " 2174 "triggered from content"); 2175 } 2176 2177 // Note: We do this check both here and in `nsDocShell::InternalLoad`. 2178 // Same reason as for the sandbox flags. 2179 MOZ_TRY(CheckFramebusting(aLoadState)); 2180 2181 MOZ_DIAGNOSTIC_ASSERT(!sourceBC || sourceBC->Group() == Group()); 2182 if (sourceBC && sourceBC->IsInProcess()) { 2183 nsCOMPtr<nsPIDOMWindowOuter> win(sourceBC->GetDOMWindow()); 2184 if (WindowGlobalChild* wgc = 2185 win->GetCurrentInnerWindow()->GetWindowGlobalChild()) { 2186 if (!wgc->CanNavigate(this)) { 2187 return NS_ERROR_DOM_PROP_ACCESS_DENIED; 2188 } 2189 wgc->SendLoadURI(this, mozilla::WrapNotNull(aLoadState), aSetNavigating); 2190 } 2191 } else if (XRE_IsParentProcess()) { 2192 if (ContentParent* cp = Canonical()->GetContentParent()) { 2193 // Attempt to initiate this load immediately in the parent, if it succeeds 2194 // it'll return a unique identifier so that we can find it later. 2195 uint64_t loadIdentifier = 0; 2196 if (Canonical()->AttemptSpeculativeLoadInParent(aLoadState)) { 2197 MOZ_DIAGNOSTIC_ASSERT(GetCurrentLoadIdentifier().isSome()); 2198 loadIdentifier = GetCurrentLoadIdentifier().value(); 2199 aLoadState->SetChannelInitialized(true); 2200 } 2201 2202 cp->TransmitBlobDataIfBlobURL(aLoadState->URI()); 2203 2204 #ifdef ANDROID 2205 // Generate a unique android load identifier for this url load 2206 // Used to map back to the app link launch type in 2207 // ContentParent::RecordAndroidAppLinkTelemetry() so we can avoid 2208 // sending the app link launch type to the content process 2209 uint64_t androidLoadIdentifier = nsContentUtils::GenerateTabId(); 2210 MOZ_ALWAYS_SUCCEEDS( 2211 SetAndroidAppLinkLoadIdentifier(Some(androidLoadIdentifier))); 2212 2213 uint32_t appLinkLaunchType = aLoadState->GetAppLinkLaunchType(); 2214 cp->SetAndroidAppLinkLaunchType(androidLoadIdentifier, appLinkLaunchType); 2215 2216 // Record timing for cold app link launches 2217 constexpr uint32_t APPLINK_COLD = 1; 2218 if (appLinkLaunchType == APPLINK_COLD) { 2219 const TimeStamp loadUriTime = TimeStamp::Now(); 2220 2221 // Process creation to load URI timing 2222 const TimeStamp processCreationTime = TimeStamp::ProcessCreation(); 2223 if (!processCreationTime.IsNull()) { 2224 const TimeDuration delta = loadUriTime - processCreationTime; 2225 mozilla::glean::perf::cold_applink_process_launch_to_load_uri 2226 .AccumulateRawDuration(delta); 2227 2228 PROFILER_MARKER("Cold App Link Process Creation to Load URI", NETWORK, 2229 MarkerOptions(MarkerTiming::Interval( 2230 processCreationTime, loadUriTime)), 2231 Tracing, "AppLink"); 2232 } 2233 2234 // StartupTimeline::MAIN to load URI timing 2235 const TimeStamp mainTime = StartupTimeline::Get(StartupTimeline::MAIN); 2236 if (!mainTime.IsNull()) { 2237 const TimeDuration mainDelta = loadUriTime - mainTime; 2238 mozilla::glean::perf::cold_applink_main_to_load_uri 2239 .AccumulateRawDuration(mainDelta); 2240 2241 PROFILER_MARKER( 2242 "Cold App Link Main to Load URI", NETWORK, 2243 MarkerOptions(MarkerTiming::Interval(mainTime, loadUriTime)), 2244 Tracing, "AppLink"); 2245 } 2246 } 2247 2248 PROFILER_MARKER_FMT("BrowsingContext::LoadURI", NETWORK, {}, 2249 "android appLinkLaunchType {} URL {}", 2250 appLinkLaunchType, 2251 aLoadState->URI()->GetSpecOrDefault().get()); 2252 #endif 2253 2254 // Setup a confirmation callback once the content process receives this 2255 // load. Normally we'd expect a PDocumentChannel actor to have been 2256 // created to claim the load identifier by that time. If not, then it 2257 // won't be coming, so make sure we clean up and deregister. 2258 cp->SendLoadURI(this, mozilla::WrapNotNull(aLoadState), aSetNavigating) 2259 ->Then(GetMainThreadSerialEventTarget(), __func__, 2260 [loadIdentifier]( 2261 const PContentParent::LoadURIPromise::ResolveOrRejectValue& 2262 aValue) { 2263 if (loadIdentifier) { 2264 net::DocumentLoadListener::CleanupParentLoadAttempt( 2265 loadIdentifier); 2266 } 2267 }); 2268 } 2269 } else { 2270 if (!sourceBC) { 2271 return NS_ERROR_UNEXPECTED; 2272 } 2273 // If we're in a content process and the source BC is no longer in-process, 2274 // just fail silently. 2275 } 2276 return NS_OK; 2277 } 2278 2279 nsresult BrowsingContext::InternalLoad(nsDocShellLoadState* aLoadState) { 2280 if (IsDiscarded()) { 2281 return NS_OK; 2282 } 2283 SetTriggeringAndInheritPrincipals(aLoadState->TriggeringPrincipal(), 2284 aLoadState->PrincipalToInherit(), 2285 aLoadState->GetLoadIdentifier()); 2286 2287 MOZ_DIAGNOSTIC_ASSERT(aLoadState->Target().IsEmpty(), 2288 "should already have retargeted"); 2289 MOZ_DIAGNOSTIC_ASSERT(!aLoadState->TargetBrowsingContext().IsNull(), 2290 "should have target bc set"); 2291 MOZ_DIAGNOSTIC_ASSERT(aLoadState->TargetBrowsingContext() == this, 2292 "must be targeting this BrowsingContext"); 2293 aLoadState->AssertProcessCouldTriggerLoadIfSystem(); 2294 2295 if (mDocShell) { 2296 RefPtr<nsDocShell> docShell = nsDocShell::Cast(mDocShell); 2297 return docShell->InternalLoad(aLoadState); 2298 } 2299 2300 // Note: We do this check both here and in `nsDocShell::InternalLoad`, since 2301 // document-specific sandbox flags are only available in the process 2302 // triggering the load, and we don't want the target process to have to trust 2303 // the triggering process to do the appropriate checks for the 2304 // BrowsingContext's sandbox flags. 2305 MOZ_TRY(CheckSandboxFlags(aLoadState)); 2306 2307 const auto& sourceBC = aLoadState->SourceBrowsingContext(); 2308 2309 if (aLoadState->URI()->SchemeIs("javascript")) { 2310 if (!XRE_IsParentProcess()) { 2311 // Web content should only be able to load javascript: URIs into documents 2312 // whose principals the caller principal subsumes, which by definition 2313 // excludes any document in a cross-process BrowsingContext. 2314 return NS_ERROR_DOM_BAD_CROSS_ORIGIN_URI; 2315 } 2316 MOZ_DIAGNOSTIC_ASSERT(!sourceBC, 2317 "Should never see a cross-process javascript: load " 2318 "triggered from content"); 2319 } 2320 2321 // Note: We do this check both here and in `nsDocShell::InternalLoad`. 2322 // Same reason as for the sandbox flags. 2323 MOZ_TRY(CheckFramebusting(aLoadState)); 2324 2325 if (XRE_IsParentProcess()) { 2326 ContentParent* cp = Canonical()->GetContentParent(); 2327 if (!cp || !cp->CanSend()) { 2328 return NS_ERROR_FAILURE; 2329 } 2330 2331 MOZ_ALWAYS_SUCCEEDS( 2332 SetCurrentLoadIdentifier(Some(aLoadState->GetLoadIdentifier()))); 2333 (void)cp->SendInternalLoad(mozilla::WrapNotNull(aLoadState)); 2334 } else { 2335 MOZ_DIAGNOSTIC_ASSERT(sourceBC); 2336 MOZ_DIAGNOSTIC_ASSERT(sourceBC->Group() == Group()); 2337 2338 nsCOMPtr<nsPIDOMWindowOuter> win(sourceBC->GetDOMWindow()); 2339 WindowGlobalChild* wgc = 2340 win->GetCurrentInnerWindow()->GetWindowGlobalChild(); 2341 if (!wgc || !wgc->CanSend()) { 2342 return NS_ERROR_FAILURE; 2343 } 2344 if (!wgc->CanNavigate(this)) { 2345 return NS_ERROR_DOM_PROP_ACCESS_DENIED; 2346 } 2347 2348 MOZ_ALWAYS_SUCCEEDS( 2349 SetCurrentLoadIdentifier(Some(aLoadState->GetLoadIdentifier()))); 2350 wgc->SendInternalLoad(mozilla::WrapNotNull(aLoadState)); 2351 } 2352 2353 return NS_OK; 2354 } 2355 2356 already_AddRefed<nsDocShellLoadState> 2357 BrowsingContext::CheckURLAndCreateLoadState(nsIURI* aURI, 2358 nsIPrincipal& aSubjectPrincipal, 2359 Document* aSourceDocument, 2360 ErrorResult& aRv) { 2361 nsCOMPtr<nsIPrincipal> triggeringPrincipal; 2362 nsCOMPtr<nsIURI> sourceURI; 2363 ReferrerPolicy referrerPolicy = ReferrerPolicy::_empty; 2364 nsCOMPtr<nsIReferrerInfo> referrerInfo; 2365 2366 // Get security manager. 2367 nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); 2368 if (NS_WARN_IF(!ssm)) { 2369 aRv.Throw(NS_ERROR_UNEXPECTED); 2370 return nullptr; 2371 } 2372 2373 // Check to see if URI is allowed. We're not going to worry about a 2374 // window ID here because it's not 100% clear which window's id we 2375 // would want, and we're throwing a content-visible exception 2376 // anyway. 2377 nsresult rv = ssm->CheckLoadURIWithPrincipal( 2378 &aSubjectPrincipal, aURI, nsIScriptSecurityManager::STANDARD, 0); 2379 if (NS_WARN_IF(NS_FAILED(rv))) { 2380 nsAutoCString spec; 2381 aURI->GetSpec(spec); 2382 aRv.ThrowTypeError<MSG_URL_NOT_LOADABLE>(spec); 2383 return nullptr; 2384 } 2385 2386 // Create load info 2387 RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(aURI); 2388 2389 if (!aSourceDocument) { 2390 // No document; just use our subject principal as the triggering principal. 2391 loadState->SetTriggeringPrincipal(&aSubjectPrincipal); 2392 return loadState.forget(); 2393 } 2394 2395 nsCOMPtr<nsIURI> docOriginalURI, docCurrentURI, principalURI; 2396 docOriginalURI = aSourceDocument->GetOriginalURI(); 2397 docCurrentURI = aSourceDocument->GetDocumentURI(); 2398 nsCOMPtr<nsIPrincipal> principal = aSourceDocument->NodePrincipal(); 2399 2400 triggeringPrincipal = aSourceDocument->NodePrincipal(); 2401 referrerPolicy = aSourceDocument->GetReferrerPolicy(); 2402 2403 bool urisEqual = false; 2404 if (docOriginalURI && docCurrentURI && principal) { 2405 principal->EqualsURI(docOriginalURI, &urisEqual); 2406 } 2407 if (urisEqual) { 2408 referrerInfo = new ReferrerInfo(docCurrentURI, referrerPolicy); 2409 } else { 2410 principal->CreateReferrerInfo(referrerPolicy, getter_AddRefs(referrerInfo)); 2411 } 2412 loadState->SetTriggeringPrincipal(triggeringPrincipal); 2413 loadState->SetTriggeringSandboxFlags(aSourceDocument->GetSandboxFlags()); 2414 loadState->SetPolicyContainer(aSourceDocument->GetPolicyContainer()); 2415 if (referrerInfo) { 2416 loadState->SetReferrerInfo(referrerInfo); 2417 } 2418 loadState->SetHasValidUserGestureActivation( 2419 aSourceDocument->HasValidTransientUserGestureActivation()); 2420 2421 loadState->SetTextDirectiveUserActivation( 2422 aSourceDocument->ConsumeTextDirectiveUserActivation() || 2423 loadState->HasValidUserGestureActivation()); 2424 loadState->SetTriggeringWindowId(aSourceDocument->InnerWindowID()); 2425 loadState->SetTriggeringStorageAccess(aSourceDocument->UsingStorageAccess()); 2426 loadState->SetTriggeringClassificationFlags( 2427 aSourceDocument->GetScriptTrackingFlags()); 2428 2429 return loadState.forget(); 2430 } 2431 2432 // https://html.spec.whatwg.org/#navigate 2433 // In its current state, this method is not closely following the spec. 2434 // https://bugzil.la/1974717 tracks the work to align this method with the spec. 2435 void BrowsingContext::Navigate( 2436 nsIURI* aURI, Document* aSourceDocument, nsIPrincipal& aSubjectPrincipal, 2437 ErrorResult& aRv, NavigationHistoryBehavior aHistoryHandling, 2438 bool aNeedsCompletelyLoadedDocument, 2439 nsIStructuredCloneContainer* aNavigationAPIState, 2440 dom::NavigationAPIMethodTracker* aNavigationAPIMethodTracker) { 2441 MOZ_LOG_FMT(gNavigationAPILog, LogLevel::Debug, "Navigate to {} as {}", *aURI, 2442 aHistoryHandling); 2443 CallerType callerType = aSubjectPrincipal.IsSystemPrincipal() 2444 ? CallerType::System 2445 : CallerType::NonSystem; 2446 2447 nsresult rv = CheckNavigationRateLimit(callerType); 2448 if (NS_FAILED(rv)) { 2449 aRv.Throw(rv); 2450 return; 2451 } 2452 2453 RefPtr<nsDocShellLoadState> loadState = 2454 CheckURLAndCreateLoadState(aURI, aSubjectPrincipal, aSourceDocument, aRv); 2455 if (aRv.Failed()) { 2456 return; 2457 } 2458 2459 if (mozilla::SessionHistoryInParent()) { 2460 loadState->SetNeedsCompletelyLoadedDocument(aNeedsCompletelyLoadedDocument); 2461 loadState->SetHistoryBehavior(aHistoryHandling); 2462 } 2463 2464 if (aHistoryHandling == NavigationHistoryBehavior::Replace) { 2465 loadState->SetLoadType(LOAD_STOP_CONTENT_AND_REPLACE); 2466 } else { 2467 loadState->SetLoadType(LOAD_STOP_CONTENT); 2468 } 2469 2470 // Get the incumbent script's browsing context to set as source. 2471 nsCOMPtr<nsPIDOMWindowInner> sourceWindow = 2472 nsContentUtils::IncumbentInnerWindow(); 2473 if (sourceWindow) { 2474 WindowContext* context = sourceWindow->GetWindowContext(); 2475 loadState->SetSourceBrowsingContext(sourceWindow->GetBrowsingContext()); 2476 loadState->SetHasValidUserGestureActivation( 2477 context && context->HasValidTransientUserGestureActivation()); 2478 } 2479 2480 loadState->SetLoadFlags(nsIWebNavigation::LOAD_FLAGS_NONE); 2481 loadState->SetFirstParty(true); 2482 loadState->SetNavigationAPIState(aNavigationAPIState); 2483 loadState->SetNavigationAPIMethodTracker(aNavigationAPIMethodTracker); 2484 2485 rv = LoadURI(loadState); 2486 if (NS_WARN_IF(NS_FAILED(rv))) { 2487 if (rv == NS_ERROR_DOM_BAD_CROSS_ORIGIN_URI && 2488 loadState->URI()->SchemeIs("javascript")) { 2489 // Per spec[1], attempting to load a javascript: URI into a cross-origin 2490 // BrowsingContext is a no-op, and should not raise an exception. 2491 // Technically, Location setters run with exceptions enabled should only 2492 // throw an exception[2] when the caller is not allowed to navigate[3] the 2493 // target browsing context due to sandboxing flags or not being 2494 // closely-related enough, though in practice we currently throw for other 2495 // reasons as well. 2496 // 2497 // [1]: 2498 // https://html.spec.whatwg.org/multipage/browsing-the-web.html#javascript-protocol 2499 // [2]: 2500 // https://html.spec.whatwg.org/multipage/browsing-the-web.html#navigate 2501 // [3]: 2502 // https://html.spec.whatwg.org/multipage/browsers.html#allowed-to-navigate 2503 return; 2504 } 2505 aRv.Throw(rv); 2506 return; 2507 } 2508 2509 Document* doc = GetDocument(); 2510 if (doc && nsContentUtils::IsExternalProtocol(aURI)) { 2511 doc->EnsureNotEnteringAndExitFullscreen(); 2512 } 2513 } 2514 2515 void BrowsingContext::DisplayLoadError(const nsAString& aURI) { 2516 MOZ_LOG(GetLog(), LogLevel::Debug, ("DisplayLoadError")); 2517 MOZ_DIAGNOSTIC_ASSERT(!IsDiscarded()); 2518 MOZ_DIAGNOSTIC_ASSERT(mDocShell || XRE_IsParentProcess()); 2519 2520 if (mDocShell) { 2521 bool didDisplayLoadError = false; 2522 nsCOMPtr<nsIDocShell> docShell = mDocShell; 2523 docShell->DisplayLoadError(NS_ERROR_MALFORMED_URI, nullptr, 2524 PromiseFlatString(aURI).get(), nullptr, 2525 &didDisplayLoadError); 2526 } else { 2527 if (ContentParent* cp = Canonical()->GetContentParent()) { 2528 (void)cp->SendDisplayLoadError(this, PromiseFlatString(aURI)); 2529 } 2530 } 2531 } 2532 2533 WindowProxyHolder BrowsingContext::Window() { 2534 return WindowProxyHolder(Self()); 2535 } 2536 2537 WindowProxyHolder BrowsingContext::GetFrames(ErrorResult& aError) { 2538 return Window(); 2539 } 2540 2541 void BrowsingContext::Close(CallerType aCallerType, ErrorResult& aError) { 2542 if (mIsDiscarded) { 2543 return; 2544 } 2545 2546 if (IsSubframe()) { 2547 // .close() on frames is a no-op. 2548 return; 2549 } 2550 2551 if (GetDOMWindow()) { 2552 nsGlobalWindowOuter::Cast(GetDOMWindow()) 2553 ->CloseOuter(aCallerType == CallerType::System); 2554 return; 2555 } 2556 2557 // This is a bit of a hack for webcompat. Content needs to see an updated 2558 // |window.closed| value as early as possible, so we set this before we 2559 // actually send the DOMWindowClose event, which happens in the process where 2560 // the document for this browsing context is loaded. 2561 MOZ_ALWAYS_SUCCEEDS(SetClosed(true)); 2562 2563 if (ContentChild* cc = ContentChild::GetSingleton()) { 2564 cc->SendWindowClose(this, aCallerType == CallerType::System); 2565 } else if (ContentParent* cp = Canonical()->GetContentParent()) { 2566 (void)cp->SendWindowClose(this, aCallerType == CallerType::System); 2567 } 2568 } 2569 2570 template <typename FuncT> 2571 inline bool ApplyToDocumentsForPopup(Document* doc, FuncT func) { 2572 // HACK: Some pages using bogus library + UA sniffing call window.open() 2573 // from a blank iframe, only on Firefox, see bug 1685056. 2574 // 2575 // This is a hack-around to preserve behavior in that particular and 2576 // specific case, by consuming activation on the parent document, so we 2577 // don't care about the InProcessParent bits not being fission-safe or what 2578 // not. 2579 if (func(doc)) { 2580 return true; 2581 } 2582 if (!doc->IsInitialDocument()) { 2583 return false; 2584 } 2585 Document* parentDoc = doc->GetInProcessParentDocument(); 2586 if (!parentDoc || !parentDoc->NodePrincipal()->Equals(doc->NodePrincipal())) { 2587 return false; 2588 } 2589 return func(parentDoc); 2590 } 2591 2592 PopupBlocker::PopupControlState BrowsingContext::RevisePopupAbuseLevel( 2593 PopupBlocker::PopupControlState aControl) { 2594 if (!IsContent()) { 2595 return PopupBlocker::openAllowed; 2596 } 2597 2598 RefPtr<Document> doc = GetExtantDocument(); 2599 PopupBlocker::PopupControlState abuse = aControl; 2600 switch (abuse) { 2601 case PopupBlocker::openControlled: 2602 case PopupBlocker::openOverridden: 2603 if (IsPopupAllowed()) { 2604 // Go down one state enum step: 2605 // openControlled (1) -> openAllowed (0) 2606 // openOverridden (4) -> openAbused (3) 2607 abuse = PopupBlocker::PopupControlState(abuse - 1); 2608 } 2609 break; 2610 case PopupBlocker::openAbused: 2611 if (IsPopupAllowed() || 2612 (doc && doc->HasValidTransientUserGestureActivation())) { 2613 // Always go down to openControlled: 2614 // openAbused (3) -> openControlled (1), skip openBlocked (2) 2615 abuse = PopupBlocker::openControlled; 2616 } 2617 break; 2618 case PopupBlocker::openAllowed: 2619 break; 2620 case PopupBlocker::openBlocked: 2621 if (IsPopupAllowed() || 2622 (doc && doc->HasValidTransientUserGestureActivation())) { 2623 // Go down one state enum step: 2624 // openBlocked (2) -> openControlled (1) 2625 abuse = PopupBlocker::openControlled; 2626 } 2627 break; 2628 default: 2629 NS_WARNING("Strange PopupControlState!"); 2630 } 2631 2632 // limit the number of simultaneously open popups 2633 if (abuse == PopupBlocker::openAbused || abuse == PopupBlocker::openBlocked || 2634 abuse == PopupBlocker::openControlled) { 2635 int32_t popupMax = StaticPrefs::dom_popup_maximum(); 2636 if (popupMax >= 0 && 2637 PopupBlocker::GetOpenPopupSpamCount() >= (uint32_t)popupMax) { 2638 abuse = PopupBlocker::openOverridden; 2639 } 2640 } 2641 2642 // If we're currently in-process, attempt to consume transient user gesture 2643 // activations. 2644 if (doc) { 2645 auto ConsumeTransientUserActivationForMultiplePopupBlocking = 2646 [&]() -> bool { 2647 return ApplyToDocumentsForPopup(doc, [](Document* doc) { 2648 return doc->ConsumeTransientUserGestureActivation(); 2649 }); 2650 }; 2651 2652 // If this popup is allowed, let's block any other for this event, forcing 2653 // PopupBlocker::openBlocked state. 2654 if ((abuse == PopupBlocker::openAllowed || 2655 abuse == PopupBlocker::openControlled) && 2656 !IsPopupAllowed() && 2657 !ConsumeTransientUserActivationForMultiplePopupBlocking()) { 2658 nsContentUtils::ReportToConsole(nsIScriptError::warningFlag, "DOM"_ns, 2659 doc, nsContentUtils::eDOM_PROPERTIES, 2660 "MultiplePopupsBlockedNoUserActivation"); 2661 abuse = PopupBlocker::openBlocked; 2662 } 2663 } 2664 2665 return abuse; 2666 } 2667 2668 void BrowsingContext::GetUserActivationModifiersForPopup( 2669 UserActivation::Modifiers* aModifiers) { 2670 RefPtr<Document> doc = GetExtantDocument(); 2671 if (doc) { 2672 // Unlike RevisePopupAbuseLevel, modifiers can always be used regardless 2673 // of PopupControlState. 2674 (void)ApplyToDocumentsForPopup(doc, [&](Document* doc) { 2675 return doc->GetTransientUserGestureActivationModifiers(aModifiers); 2676 }); 2677 } 2678 } 2679 2680 void BrowsingContext::IncrementHistoryEntryCountForBrowsingContext() { 2681 (void)SetHistoryEntryCount(GetHistoryEntryCount() + 1); 2682 } 2683 2684 // https://wicg.github.io/document-picture-in-picture/#focusing-the-opener-window 2685 static bool ConsumePiPWindowTransientActivation(nsPIDOMWindowOuter* outer) { 2686 NS_ENSURE_TRUE(outer, false); 2687 2688 nsPIDOMWindowInner* inner = outer->GetCurrentInnerWindow(); 2689 NS_ENSURE_TRUE(inner, false); 2690 2691 DocumentPictureInPicture* dpip = inner->GetExtantDocumentPictureInPicture(); 2692 if (!dpip) { 2693 return false; 2694 } 2695 nsGlobalWindowInner* pipWindow = dpip->GetWindow(); 2696 if (!pipWindow) { 2697 return false; 2698 } 2699 2700 WindowContext* wc = pipWindow->GetWindowContext(); 2701 NS_ENSURE_TRUE(wc, false); 2702 2703 return wc->ConsumeTransientUserGestureActivation(); 2704 } 2705 2706 std::tuple<bool, bool> BrowsingContext::CanFocusCheck(CallerType aCallerType) { 2707 nsFocusManager* fm = nsFocusManager::GetFocusManager(); 2708 if (!fm) { 2709 return {false, false}; 2710 } 2711 2712 nsCOMPtr<nsPIDOMWindowInner> caller = do_QueryInterface(GetEntryGlobal()); 2713 BrowsingContext* callerBC = caller ? caller->GetBrowsingContext() : nullptr; 2714 RefPtr<BrowsingContext> openerBC = GetOpener(); 2715 MOZ_DIAGNOSTIC_ASSERT(!openerBC || openerBC->Group() == Group()); 2716 2717 // Enforce dom.disable_window_flip (for non-chrome), but still allow the 2718 // window which opened us to raise us at times when popups are allowed 2719 // (bugs 355482 and 369306). 2720 bool canFocus = aCallerType == CallerType::System || 2721 !Preferences::GetBool("dom.disable_window_flip", true); 2722 if (!canFocus && openerBC == callerBC) { 2723 canFocus = 2724 (callerBC ? callerBC : this) 2725 ->RevisePopupAbuseLevel(PopupBlocker::GetPopupControlState()) < 2726 PopupBlocker::openBlocked; 2727 } 2728 2729 // Allow the opener to get system focus if the PIP window has transient 2730 // activation 2731 if (!canFocus && IsTopContent() && 2732 ConsumePiPWindowTransientActivation(GetDOMWindow())) { 2733 canFocus = true; 2734 } 2735 2736 bool isActive = false; 2737 if (XRE_IsParentProcess()) { 2738 CanonicalBrowsingContext* chromeTop = Canonical()->TopCrossChromeBoundary(); 2739 nsCOMPtr<nsPIDOMWindowOuter> activeWindow = fm->GetActiveWindow(); 2740 isActive = activeWindow == chromeTop->GetDOMWindow(); 2741 } else { 2742 isActive = fm->GetActiveBrowsingContext() == Top(); 2743 } 2744 2745 return {canFocus, isActive}; 2746 } 2747 2748 void BrowsingContext::Focus(CallerType aCallerType, ErrorResult& aError) { 2749 // These checks need to happen before the RequestFrameFocus call, which 2750 // is why they are done in an untrusted process. If we wanted to enforce 2751 // these in the parent, we'd need to do the checks there _also_. 2752 // These should be kept in sync with nsGlobalWindowOuter::FocusOuter. 2753 2754 auto [canFocus, isActive] = CanFocusCheck(aCallerType); 2755 2756 if (!(canFocus || isActive)) { 2757 return; 2758 } 2759 2760 // Permission check passed 2761 2762 if (mEmbedderElement) { 2763 // Make the activeElement in this process update synchronously. 2764 nsContentUtils::RequestFrameFocus(*mEmbedderElement, true, aCallerType); 2765 } 2766 uint64_t actionId = nsFocusManager::GenerateFocusActionId(); 2767 if (ContentChild* cc = ContentChild::GetSingleton()) { 2768 cc->SendWindowFocus(this, aCallerType, actionId); 2769 } else if (ContentParent* cp = Canonical()->GetContentParent()) { 2770 (void)cp->SendWindowFocus(this, aCallerType, actionId); 2771 } 2772 } 2773 2774 bool BrowsingContext::CanBlurCheck(CallerType aCallerType) { 2775 // If dom.disable_window_flip == true, then content should not be allowed 2776 // to do blur (this would allow popunders, bug 369306) 2777 return aCallerType == CallerType::System || 2778 !Preferences::GetBool("dom.disable_window_flip", true); 2779 } 2780 2781 void BrowsingContext::Blur(CallerType aCallerType, ErrorResult& aError) { 2782 if (!CanBlurCheck(aCallerType)) { 2783 return; 2784 } 2785 2786 if (ContentChild* cc = ContentChild::GetSingleton()) { 2787 cc->SendWindowBlur(this, aCallerType); 2788 } else if (ContentParent* cp = Canonical()->GetContentParent()) { 2789 (void)cp->SendWindowBlur(this, aCallerType); 2790 } 2791 } 2792 2793 Nullable<WindowProxyHolder> BrowsingContext::GetWindow() { 2794 if (XRE_IsParentProcess() && !IsInProcess()) { 2795 return nullptr; 2796 } 2797 return WindowProxyHolder(this); 2798 } 2799 2800 Nullable<WindowProxyHolder> BrowsingContext::GetTop(ErrorResult& aError) { 2801 if (mIsDiscarded) { 2802 return nullptr; 2803 } 2804 2805 // We never return null or throw an error, but the implementation in 2806 // nsGlobalWindow does and we need to use the same signature. 2807 return WindowProxyHolder(Top()); 2808 } 2809 2810 void BrowsingContext::GetOpener(JSContext* aCx, 2811 JS::MutableHandle<JS::Value> aOpener, 2812 ErrorResult& aError) const { 2813 RefPtr<BrowsingContext> opener = GetOpener(); 2814 if (!opener) { 2815 aOpener.setNull(); 2816 return; 2817 } 2818 2819 if (!ToJSValue(aCx, WindowProxyHolder(opener), aOpener)) { 2820 aError.NoteJSContextException(aCx); 2821 } 2822 } 2823 2824 // We never throw an error, but the implementation in nsGlobalWindow does and 2825 // we need to use the same signature. 2826 Nullable<WindowProxyHolder> BrowsingContext::GetParent(ErrorResult& aError) { 2827 if (mIsDiscarded) { 2828 return nullptr; 2829 } 2830 2831 if (GetParent()) { 2832 return WindowProxyHolder(GetParent()); 2833 } 2834 return WindowProxyHolder(this); 2835 } 2836 2837 void BrowsingContext::PostMessageMoz(JSContext* aCx, 2838 JS::Handle<JS::Value> aMessage, 2839 const nsAString& aTargetOrigin, 2840 const Sequence<JSObject*>& aTransfer, 2841 nsIPrincipal& aSubjectPrincipal, 2842 ErrorResult& aError) { 2843 if (mIsDiscarded) { 2844 return; 2845 } 2846 2847 RefPtr<BrowsingContext> sourceBc; 2848 PostMessageData data; 2849 data.targetOrigin() = aTargetOrigin; 2850 data.subjectPrincipal() = &aSubjectPrincipal; 2851 RefPtr<nsGlobalWindowInner> callerInnerWindow; 2852 nsAutoCString scriptLocation; 2853 // We don't need to get the caller's agentClusterId since that is used for 2854 // checking whether it's okay to sharing memory (and it's not allowed to share 2855 // memory cross processes) 2856 if (!nsGlobalWindowOuter::GatherPostMessageData( 2857 aCx, aTargetOrigin, getter_AddRefs(sourceBc), data.origin(), 2858 getter_AddRefs(data.targetOriginURI()), 2859 getter_AddRefs(data.callerPrincipal()), 2860 getter_AddRefs(callerInnerWindow), getter_AddRefs(data.callerURI()), 2861 /* aCallerAgentClusterId */ nullptr, &scriptLocation, aError)) { 2862 return; 2863 } 2864 if (sourceBc && sourceBc->IsDiscarded()) { 2865 return; 2866 } 2867 data.source() = sourceBc; 2868 data.isFromPrivateWindow() = 2869 callerInnerWindow && 2870 nsScriptErrorBase::ComputeIsFromPrivateWindow(callerInnerWindow); 2871 data.innerWindowId() = callerInnerWindow ? callerInnerWindow->WindowID() : 0; 2872 data.scriptLocation() = scriptLocation; 2873 JS::Rooted<JS::Value> transferArray(aCx); 2874 aError = nsContentUtils::CreateJSValueFromSequenceOfObject(aCx, aTransfer, 2875 &transferArray); 2876 if (NS_WARN_IF(aError.Failed())) { 2877 return; 2878 } 2879 2880 JS::CloneDataPolicy clonePolicy; 2881 if (callerInnerWindow && callerInnerWindow->IsSharedMemoryAllowed()) { 2882 clonePolicy.allowSharedMemoryObjects(); 2883 } 2884 2885 // We will see if the message is required to be in the same process or it can 2886 // be in the different process after Write(). 2887 ipc::StructuredCloneData message = ipc::StructuredCloneData( 2888 StructuredCloneHolder::StructuredCloneScope::UnknownDestination, 2889 StructuredCloneHolder::TransferringSupported); 2890 message.Write(aCx, aMessage, transferArray, clonePolicy, aError); 2891 if (NS_WARN_IF(aError.Failed())) { 2892 return; 2893 } 2894 2895 ClonedOrErrorMessageData messageData; 2896 if (ContentChild* cc = ContentChild::GetSingleton()) { 2897 // The clone scope gets set when we write the message data based on the 2898 // requirements of that data that we're writing. 2899 // If the message data contains a shared memory object, then CloneScope 2900 // would return SameProcess. Otherwise, it returns DifferentProcess. 2901 if (message.CloneScope() == 2902 StructuredCloneHolder::StructuredCloneScope::DifferentProcess) { 2903 ClonedMessageData clonedMessageData; 2904 if (!message.BuildClonedMessageData(clonedMessageData)) { 2905 aError.Throw(NS_ERROR_FAILURE); 2906 return; 2907 } 2908 2909 messageData = std::move(clonedMessageData); 2910 } else { 2911 MOZ_ASSERT(message.CloneScope() == 2912 StructuredCloneHolder::StructuredCloneScope::SameProcess); 2913 2914 messageData = ErrorMessageData(); 2915 2916 nsContentUtils::ReportToConsole( 2917 nsIScriptError::warningFlag, "DOM Window"_ns, 2918 callerInnerWindow ? callerInnerWindow->GetDocument() : nullptr, 2919 nsContentUtils::eDOM_PROPERTIES, 2920 "PostMessageSharedMemoryObjectToCrossOriginWarning"); 2921 } 2922 2923 cc->SendWindowPostMessage(this, messageData, data); 2924 } else if (ContentParent* cp = Canonical()->GetContentParent()) { 2925 if (message.CloneScope() == 2926 StructuredCloneHolder::StructuredCloneScope::DifferentProcess) { 2927 ClonedMessageData clonedMessageData; 2928 if (!message.BuildClonedMessageData(clonedMessageData)) { 2929 aError.Throw(NS_ERROR_FAILURE); 2930 return; 2931 } 2932 2933 messageData = std::move(clonedMessageData); 2934 } else { 2935 MOZ_ASSERT(message.CloneScope() == 2936 StructuredCloneHolder::StructuredCloneScope::SameProcess); 2937 2938 messageData = ErrorMessageData(); 2939 2940 nsContentUtils::ReportToConsole( 2941 nsIScriptError::warningFlag, "DOM Window"_ns, 2942 callerInnerWindow ? callerInnerWindow->GetDocument() : nullptr, 2943 nsContentUtils::eDOM_PROPERTIES, 2944 "PostMessageSharedMemoryObjectToCrossOriginWarning"); 2945 } 2946 2947 (void)cp->SendWindowPostMessage(this, messageData, data); 2948 } 2949 } 2950 2951 void BrowsingContext::PostMessageMoz(JSContext* aCx, 2952 JS::Handle<JS::Value> aMessage, 2953 const WindowPostMessageOptions& aOptions, 2954 nsIPrincipal& aSubjectPrincipal, 2955 ErrorResult& aError) { 2956 PostMessageMoz(aCx, aMessage, aOptions.mTargetOrigin, aOptions.mTransfer, 2957 aSubjectPrincipal, aError); 2958 } 2959 2960 void BrowsingContext::SendCommitTransaction(ContentParent* aParent, 2961 const BaseTransaction& aTxn, 2962 uint64_t aEpoch) { 2963 (void)aParent->SendCommitBrowsingContextTransaction(this, aTxn, aEpoch); 2964 } 2965 2966 void BrowsingContext::SendCommitTransaction(ContentChild* aChild, 2967 const BaseTransaction& aTxn, 2968 uint64_t aEpoch) { 2969 aChild->SendCommitBrowsingContextTransaction(this, aTxn, aEpoch); 2970 } 2971 2972 BrowsingContext::IPCInitializer BrowsingContext::GetIPCInitializer() { 2973 MOZ_DIAGNOSTIC_ASSERT(mEverAttached); 2974 MOZ_DIAGNOSTIC_ASSERT(mType == Type::Content); 2975 2976 IPCInitializer init; 2977 init.mId = Id(); 2978 init.mParentId = mParentWindow ? mParentWindow->Id() : 0; 2979 init.mWindowless = mWindowless; 2980 init.mUseRemoteTabs = mUseRemoteTabs; 2981 init.mUseRemoteSubframes = mUseRemoteSubframes; 2982 init.mCreatedDynamically = mCreatedDynamically; 2983 init.mChildOffset = mChildOffset; 2984 init.mOriginAttributes = mOriginAttributes; 2985 if (mChildSessionHistory && mozilla::SessionHistoryInParent()) { 2986 init.mSessionHistoryIndex = mChildSessionHistory->Index(); 2987 init.mSessionHistoryCount = mChildSessionHistory->Count(); 2988 } 2989 init.mRequestContextId = mRequestContextId; 2990 init.mFields = mFields.RawValues(); 2991 return init; 2992 } 2993 2994 already_AddRefed<WindowContext> BrowsingContext::IPCInitializer::GetParent() { 2995 RefPtr<WindowContext> parent; 2996 if (mParentId != 0) { 2997 parent = WindowContext::GetById(mParentId); 2998 MOZ_RELEASE_ASSERT(parent); 2999 } 3000 return parent.forget(); 3001 } 3002 3003 already_AddRefed<BrowsingContext> BrowsingContext::IPCInitializer::GetOpener() { 3004 RefPtr<BrowsingContext> opener; 3005 if (GetOpenerId() != 0) { 3006 opener = BrowsingContext::Get(GetOpenerId()); 3007 MOZ_RELEASE_ASSERT(opener); 3008 } 3009 return opener.forget(); 3010 } 3011 3012 void BrowsingContext::StartDelayedAutoplayMediaComponents() { 3013 if (!mDocShell) { 3014 return; 3015 } 3016 AUTOPLAY_LOG("%s : StartDelayedAutoplayMediaComponents for bc 0x%08" PRIx64, 3017 XRE_IsParentProcess() ? "Parent" : "Child", Id()); 3018 mDocShell->StartDelayedAutoplayMediaComponents(); 3019 } 3020 3021 nsresult BrowsingContext::ResetGVAutoplayRequestStatus() { 3022 MOZ_ASSERT(IsTop(), 3023 "Should only set GVAudibleAutoplayRequestStatus in the top-level " 3024 "browsing context"); 3025 3026 Transaction txn; 3027 txn.SetGVAudibleAutoplayRequestStatus(GVAutoplayRequestStatus::eUNKNOWN); 3028 txn.SetGVInaudibleAutoplayRequestStatus(GVAutoplayRequestStatus::eUNKNOWN); 3029 return txn.Commit(this); 3030 } 3031 3032 template <typename Callback> 3033 void BrowsingContext::WalkPresContexts(Callback&& aCallback) { 3034 PreOrderWalk([&](BrowsingContext* aContext) { 3035 if (nsIDocShell* shell = aContext->GetDocShell()) { 3036 if (RefPtr pc = shell->GetPresContext()) { 3037 aCallback(pc.get()); 3038 } 3039 } 3040 }); 3041 } 3042 3043 void BrowsingContext::PresContextAffectingFieldChanged() { 3044 WalkPresContexts([&](nsPresContext* aPc) { 3045 aPc->RecomputeBrowsingContextDependentData(); 3046 }); 3047 } 3048 3049 void BrowsingContext::DidSet(FieldIndex<IDX_SessionStoreEpoch>, 3050 uint32_t aOldValue) { 3051 if (!mCurrentWindowContext) { 3052 return; 3053 } 3054 SessionStoreChild* sessionStoreChild = 3055 SessionStoreChild::From(mCurrentWindowContext->GetWindowGlobalChild()); 3056 if (!sessionStoreChild) { 3057 return; 3058 } 3059 3060 sessionStoreChild->SetEpoch(GetSessionStoreEpoch()); 3061 } 3062 3063 void BrowsingContext::DidSet(FieldIndex<IDX_GVAudibleAutoplayRequestStatus>) { 3064 MOZ_ASSERT(IsTop(), 3065 "Should only set GVAudibleAutoplayRequestStatus in the top-level " 3066 "browsing context"); 3067 } 3068 3069 void BrowsingContext::DidSet(FieldIndex<IDX_GVInaudibleAutoplayRequestStatus>) { 3070 MOZ_ASSERT(IsTop(), 3071 "Should only set GVAudibleAutoplayRequestStatus in the top-level " 3072 "browsing context"); 3073 } 3074 3075 bool BrowsingContext::CanSet(FieldIndex<IDX_ExplicitActive>, 3076 const ExplicitActiveStatus&, 3077 ContentParent* aSource) { 3078 return XRE_IsParentProcess() && IsTop() && !aSource; 3079 } 3080 3081 void BrowsingContext::DidSet(FieldIndex<IDX_ExplicitActive>, 3082 ExplicitActiveStatus aOldValue) { 3083 MOZ_ASSERT(IsTop()); 3084 3085 const bool isActive = IsActive(); 3086 const bool wasActive = [&] { 3087 if (aOldValue != ExplicitActiveStatus::None) { 3088 return aOldValue == ExplicitActiveStatus::Active; 3089 } 3090 return GetParent() && GetParent()->IsActive(); 3091 }(); 3092 3093 if (isActive == wasActive) { 3094 return; 3095 } 3096 3097 Group()->UpdateToplevelsSuspendedIfNeeded(); 3098 if (XRE_IsParentProcess()) { 3099 if (BrowserParent* bp = Canonical()->GetBrowserParent()) { 3100 bp->RecomputeProcessPriority(); 3101 #if defined(XP_WIN) && defined(ACCESSIBILITY) 3102 if (a11y::Compatibility::IsDolphin()) { 3103 // update active accessible documents on windows 3104 if (a11y::DocAccessibleParent* tabDoc = 3105 bp->GetTopLevelDocAccessible()) { 3106 HWND window = tabDoc->GetEmulatedWindowHandle(); 3107 MOZ_ASSERT(window); 3108 if (window) { 3109 if (isActive) { 3110 a11y::nsWinUtils::ShowNativeWindow(window); 3111 } else { 3112 a11y::nsWinUtils::HideNativeWindow(window); 3113 } 3114 } 3115 } 3116 } 3117 #endif 3118 } 3119 3120 // NOTE(emilio): Ideally we'd want to reuse the ExplicitActiveStatus::None 3121 // set-up, but that's non-trivial to do because in content processes we 3122 // can't access the top-cross-chrome-boundary bc. 3123 auto manageTopDescendant = [&](auto* aChild) { 3124 if (!aChild->ManuallyManagesActiveness()) { 3125 aChild->SetIsActiveInternal(isActive, IgnoreErrors()); 3126 if (BrowserParent* bp = aChild->GetBrowserParent()) { 3127 bp->SetRenderLayers(isActive); 3128 } 3129 } 3130 return CallState::Continue; 3131 }; 3132 Canonical()->CallOnTopDescendants( 3133 manageTopDescendant, 3134 CanonicalBrowsingContext::TopDescendantKind::NonNested); 3135 } 3136 3137 PreOrderWalk([&](BrowsingContext* aContext) { 3138 if (nsCOMPtr<nsIDocShell> ds = aContext->GetDocShell()) { 3139 if (auto* bc = BrowserChild::GetFrom(ds)) { 3140 bc->UpdateVisibility(); 3141 } 3142 nsDocShell::Cast(ds)->ActivenessMaybeChanged(); 3143 } 3144 }); 3145 } 3146 3147 void BrowsingContext::DidSet(FieldIndex<IDX_InRDMPane>, bool aOldValue) { 3148 MOZ_ASSERT(IsTop(), 3149 "Should only set InRDMPane in the top-level browsing context"); 3150 if (GetInRDMPane() == aOldValue) { 3151 return; 3152 } 3153 3154 // Reset screen orientation override when disabling RDM. 3155 if (!GetInRDMPane()) { 3156 ResetOrientationOverride(); 3157 } 3158 3159 PresContextAffectingFieldChanged(); 3160 } 3161 3162 void BrowsingContext::DidSet(FieldIndex<IDX_HasOrientationOverride>, 3163 bool aOldValue) { 3164 bool hasOrientationOverride = GetHasOrientationOverride(); 3165 OrientationType type = GetCurrentOrientationType(); 3166 float angle = GetCurrentOrientationAngle(); 3167 3168 PreOrderWalk([&](BrowsingContext* aBrowsingContext) { 3169 if (RefPtr<WindowContext> windowContext = 3170 aBrowsingContext->GetCurrentWindowContext()) { 3171 if (nsCOMPtr<nsPIDOMWindowInner> window = 3172 windowContext->GetInnerWindow()) { 3173 ScreenOrientation* orientation = 3174 nsGlobalWindowInner::Cast(window)->Screen()->Orientation(); 3175 3176 float screenOrientationAngle = 3177 orientation->DeviceAngle(CallerType::System); 3178 OrientationType screenOrientationType = 3179 orientation->DeviceType(CallerType::System); 3180 3181 bool overrideIsDifferentThanDevice = 3182 screenOrientationType != type || screenOrientationAngle != angle; 3183 3184 // Reset orientation override. 3185 if (!hasOrientationOverride && aOldValue) { 3186 (void)aBrowsingContext->SetCurrentOrientation(screenOrientationType, 3187 screenOrientationAngle); 3188 } else if (!aBrowsingContext->IsTop()) { 3189 // Sync orientation override in the existing frames. 3190 (void)aBrowsingContext->SetCurrentOrientation(type, angle); 3191 } 3192 3193 orientation->MaybeDispatchEventsForOverride( 3194 aBrowsingContext, aOldValue, overrideIsDifferentThanDevice); 3195 } 3196 } 3197 }); 3198 } 3199 3200 void BrowsingContext::DidSet(FieldIndex<IDX_ForceDesktopViewport>, 3201 bool aOldValue) { 3202 MOZ_ASSERT(IsTop(), "Should only set in the top-level browsing context"); 3203 if (ForceDesktopViewport() == aOldValue) { 3204 return; 3205 } 3206 PresContextAffectingFieldChanged(); 3207 if (nsIDocShell* shell = GetDocShell()) { 3208 if (RefPtr ps = shell->GetPresShell()) { 3209 ps->MaybeRecreateMobileViewportManager(/* aAfterInitialization= */ true); 3210 } 3211 } 3212 } 3213 3214 bool BrowsingContext::CanSet(FieldIndex<IDX_PageAwakeRequestCount>, 3215 uint32_t aNewValue, ContentParent* aSource) { 3216 return IsTop() && XRE_IsParentProcess() && !aSource; 3217 } 3218 3219 void BrowsingContext::DidSet(FieldIndex<IDX_PageAwakeRequestCount>, 3220 uint32_t aOldValue) { 3221 if (!IsTop() || aOldValue == GetPageAwakeRequestCount()) { 3222 return; 3223 } 3224 Group()->UpdateToplevelsSuspendedIfNeeded(); 3225 } 3226 3227 auto BrowsingContext::CanSet(FieldIndex<IDX_AllowJavascript>, bool aValue, 3228 ContentParent* aSource) -> CanSetResult { 3229 if (mozilla::SessionHistoryInParent()) { 3230 return XRE_IsParentProcess() && !aSource ? CanSetResult::Allow 3231 : CanSetResult::Deny; 3232 } 3233 3234 // Without Session History in Parent, session restore code still needs to set 3235 // this from content processes. 3236 return LegacyRevertIfNotOwningOrParentProcess(aSource); 3237 } 3238 3239 void BrowsingContext::DidSet(FieldIndex<IDX_AllowJavascript>, bool aOldValue) { 3240 RecomputeCanExecuteScripts(); 3241 } 3242 3243 void BrowsingContext::RecomputeCanExecuteScripts() { 3244 const bool old = mCanExecuteScripts; 3245 if (!AllowJavascript()) { 3246 // Scripting has been explicitly disabled on our BrowsingContext. 3247 mCanExecuteScripts = false; 3248 } else if (GetParentWindowContext()) { 3249 // Otherwise, inherit parent. 3250 mCanExecuteScripts = GetParentWindowContext()->CanExecuteScripts(); 3251 } else { 3252 // Otherwise, we're the root of the tree, and we haven't explicitly disabled 3253 // script. Allow. 3254 mCanExecuteScripts = true; 3255 } 3256 3257 if (old != mCanExecuteScripts) { 3258 for (WindowContext* wc : GetWindowContexts()) { 3259 wc->RecomputeCanExecuteScripts(); 3260 } 3261 } 3262 } 3263 3264 bool BrowsingContext::InactiveForSuspend() const { 3265 if (!StaticPrefs::dom_suspend_inactive_enabled()) { 3266 return false; 3267 } 3268 // We should suspend a page only when it's inactive and doesn't have any awake 3269 // request that is used to prevent page from being suspended because web page 3270 // might still need to run their script. Eg. waiting for media keys to resume 3271 // media, playing web audio, waiting in a video call conference room. 3272 return !IsActive() && GetPageAwakeRequestCount() == 0; 3273 } 3274 3275 bool BrowsingContext::CanSet(FieldIndex<IDX_TouchEventsOverrideInternal>, 3276 dom::TouchEventsOverride, ContentParent* aSource) { 3277 return XRE_IsParentProcess() && !aSource; 3278 } 3279 3280 void BrowsingContext::DidSet(FieldIndex<IDX_TouchEventsOverrideInternal>, 3281 dom::TouchEventsOverride&& aOldValue) { 3282 if (GetTouchEventsOverrideInternal() == aOldValue) { 3283 return; 3284 } 3285 WalkPresContexts([&](nsPresContext* aPc) { 3286 aPc->MediaFeatureValuesChanged( 3287 {MediaFeatureChangeReason::SystemMetricsChange}, 3288 // We're already iterating through sub documents, so we don't need to 3289 // propagate the change again. 3290 MediaFeatureChangePropagation::JustThisDocument); 3291 }); 3292 } 3293 3294 void BrowsingContext::DidSet(FieldIndex<IDX_EmbedderColorSchemes>, 3295 EmbedderColorSchemes&& aOldValue) { 3296 if (GetEmbedderColorSchemes() == aOldValue) { 3297 return; 3298 } 3299 PresContextAffectingFieldChanged(); 3300 } 3301 3302 void BrowsingContext::DidSet(FieldIndex<IDX_PrefersColorSchemeOverride>, 3303 dom::PrefersColorSchemeOverride aOldValue) { 3304 MOZ_ASSERT(IsTop()); 3305 if (PrefersColorSchemeOverride() == aOldValue) { 3306 return; 3307 } 3308 PresContextAffectingFieldChanged(); 3309 } 3310 3311 void BrowsingContext::DidSet(FieldIndex<IDX_ForcedColorsOverride>, 3312 dom::ForcedColorsOverride aOldValue) { 3313 MOZ_ASSERT(IsTop()); 3314 if (ForcedColorsOverride() == aOldValue) { 3315 return; 3316 } 3317 PresContextAffectingFieldChanged(); 3318 } 3319 3320 void BrowsingContext::DidSet(FieldIndex<IDX_LanguageOverride>, 3321 nsCString&& aOldValue) { 3322 MOZ_ASSERT(IsTop()); 3323 3324 const nsCString& languageOverride = GetLanguageOverride(); 3325 3326 PreOrderWalk([&](BrowsingContext* aBrowsingContext) { 3327 if (RefPtr<WindowContext> windowContext = 3328 aBrowsingContext->GetCurrentWindowContext()) { 3329 if (nsCOMPtr<nsPIDOMWindowInner> window = 3330 windowContext->GetInnerWindow()) { 3331 JSObject* global = 3332 nsGlobalWindowInner::Cast(window)->GetGlobalJSObject(); 3333 JS::Realm* realm = JS::GetObjectRealmOrNull(global); 3334 3335 if (mDefaultLocale == nullptr) { 3336 AutoJSAPI jsapi; 3337 if (jsapi.Init(window)) { 3338 JSContext* context = jsapi.cx(); 3339 mDefaultLocale = JS_GetDefaultLocale(context); 3340 } 3341 } 3342 3343 if (languageOverride.IsEmpty()) { 3344 JS::SetRealmLocaleOverride(realm, mDefaultLocale.get()); 3345 mDefaultLocale = nullptr; 3346 } else { 3347 JS::SetRealmLocaleOverride( 3348 realm, PromiseFlatCString(languageOverride).get()); 3349 } 3350 3351 if (Navigator* navigator = window->Navigator()) { 3352 navigator->ClearLanguageCache(); 3353 } 3354 } 3355 } 3356 }); 3357 } 3358 3359 void BrowsingContext::DidSet(FieldIndex<IDX_MediumOverride>, 3360 nsString&& aOldValue) { 3361 MOZ_ASSERT(IsTop()); 3362 if (GetMediumOverride() == aOldValue) { 3363 return; 3364 } 3365 PresContextAffectingFieldChanged(); 3366 } 3367 3368 void BrowsingContext::DidSet(FieldIndex<IDX_DisplayMode>, 3369 enum DisplayMode aOldValue) { 3370 MOZ_ASSERT(IsTop()); 3371 3372 if (GetDisplayMode() == aOldValue) { 3373 return; 3374 } 3375 3376 WalkPresContexts([&](nsPresContext* aPc) { 3377 aPc->MediaFeatureValuesChanged( 3378 {MediaFeatureChangeReason::DisplayModeChange}, 3379 // We're already iterating through sub documents, so we don't need 3380 // to propagate the change again. 3381 // 3382 // Images and other resources don't change their display-mode 3383 // evaluation, display-mode is a property of the browsing context. 3384 MediaFeatureChangePropagation::JustThisDocument); 3385 }); 3386 } 3387 3388 void BrowsingContext::DidSet(FieldIndex<IDX_Muted>) { 3389 MOZ_ASSERT(IsTop(), "Set muted flag on non top-level context!"); 3390 USER_ACTIVATION_LOG("Set audio muted %d for %s browsing context 0x%08" PRIx64, 3391 GetMuted(), XRE_IsParentProcess() ? "Parent" : "Child", 3392 Id()); 3393 PreOrderWalk([&](BrowsingContext* aContext) { 3394 nsPIDOMWindowOuter* win = aContext->GetDOMWindow(); 3395 if (win) { 3396 win->RefreshMediaElementsVolume(); 3397 } 3398 }); 3399 } 3400 3401 bool BrowsingContext::CanSet(FieldIndex<IDX_IsAppTab>, const bool& aValue, 3402 ContentParent* aSource) { 3403 return XRE_IsParentProcess() && !aSource && IsTop(); 3404 } 3405 3406 bool BrowsingContext::CanSet(FieldIndex<IDX_HasSiblings>, const bool& aValue, 3407 ContentParent* aSource) { 3408 return XRE_IsParentProcess() && !aSource && IsTop(); 3409 } 3410 3411 bool BrowsingContext::CanSet(FieldIndex<IDX_ShouldDelayMediaFromStart>, 3412 const bool& aValue, ContentParent* aSource) { 3413 return IsTop(); 3414 } 3415 3416 void BrowsingContext::DidSet(FieldIndex<IDX_ShouldDelayMediaFromStart>, 3417 bool aOldValue) { 3418 MOZ_ASSERT(IsTop(), "Set attribute on non top-level context!"); 3419 if (aOldValue == GetShouldDelayMediaFromStart()) { 3420 return; 3421 } 3422 if (!GetShouldDelayMediaFromStart()) { 3423 PreOrderWalk([&](BrowsingContext* aContext) { 3424 if (nsPIDOMWindowOuter* win = aContext->GetDOMWindow()) { 3425 win->ActivateMediaComponents(); 3426 } 3427 }); 3428 } 3429 } 3430 3431 bool BrowsingContext::CanSet(FieldIndex<IDX_OverrideDPPX>, const float& aValue, 3432 ContentParent* aSource) { 3433 return XRE_IsParentProcess() && !aSource && IsTop(); 3434 } 3435 3436 void BrowsingContext::DidSet(FieldIndex<IDX_OverrideDPPX>, float aOldValue) { 3437 MOZ_ASSERT(IsTop()); 3438 if (GetOverrideDPPX() == aOldValue) { 3439 return; 3440 } 3441 PresContextAffectingFieldChanged(); 3442 } 3443 3444 void BrowsingContext::SetCustomUserAgent(const nsAString& aUserAgent, 3445 ErrorResult& aRv) { 3446 Top()->SetUserAgentOverride(aUserAgent, aRv); 3447 } 3448 3449 nsresult BrowsingContext::SetCustomUserAgent(const nsAString& aUserAgent) { 3450 return Top()->SetUserAgentOverride(aUserAgent); 3451 } 3452 3453 void BrowsingContext::DidSet(FieldIndex<IDX_UserAgentOverride>) { 3454 MOZ_ASSERT(IsTop()); 3455 3456 PreOrderWalk([&](BrowsingContext* aContext) { 3457 nsIDocShell* shell = aContext->GetDocShell(); 3458 if (shell) { 3459 shell->ClearCachedUserAgent(); 3460 } 3461 3462 if (nsCOMPtr<Document> doc = aContext->GetExtantDocument()) { 3463 if (nsCOMPtr<nsIHttpChannel> httpChannel = 3464 do_QueryInterface(doc->GetChannel())) { 3465 (void)httpChannel->SetIsUserAgentHeaderOutdated(true); 3466 } 3467 } 3468 }); 3469 } 3470 3471 bool BrowsingContext::CanSet(FieldIndex<IDX_IsInBFCache>, bool, 3472 ContentParent* aSource) { 3473 return IsTop() && !aSource && mozilla::BFCacheInParent(); 3474 } 3475 3476 void BrowsingContext::DidSet(FieldIndex<IDX_IsInBFCache>) { 3477 MOZ_RELEASE_ASSERT(mozilla::BFCacheInParent()); 3478 MOZ_DIAGNOSTIC_ASSERT(IsTop()); 3479 3480 const bool isInBFCache = GetIsInBFCache(); 3481 if (!isInBFCache) { 3482 UpdateCurrentTopByBrowserId(this); 3483 PreOrderWalk([&](BrowsingContext* aContext) { 3484 aContext->mIsInBFCache = false; 3485 nsCOMPtr<nsIDocShell> shell = aContext->GetDocShell(); 3486 if (shell) { 3487 nsDocShell::Cast(shell)->ThawFreezeNonRecursive(true); 3488 } 3489 }); 3490 } 3491 3492 if (isInBFCache && XRE_IsContentProcess() && mDocShell) { 3493 nsDocShell::Cast(mDocShell)->MaybeDisconnectChildListenersOnPageHide(); 3494 } 3495 3496 if (isInBFCache) { 3497 PreOrderWalk([&](BrowsingContext* aContext) { 3498 nsCOMPtr<nsIDocShell> shell = aContext->GetDocShell(); 3499 if (shell) { 3500 nsDocShell::Cast(shell)->FirePageHideShowNonRecursive(false); 3501 } 3502 }); 3503 } else { 3504 PostOrderWalk([&](BrowsingContext* aContext) { 3505 nsCOMPtr<nsIDocShell> shell = aContext->GetDocShell(); 3506 if (shell) { 3507 nsDocShell::Cast(shell)->FirePageHideShowNonRecursive(true); 3508 } 3509 }); 3510 } 3511 3512 if (isInBFCache) { 3513 PreOrderWalk([&](BrowsingContext* aContext) { 3514 nsCOMPtr<nsIDocShell> shell = aContext->GetDocShell(); 3515 if (shell) { 3516 nsDocShell::Cast(shell)->ThawFreezeNonRecursive(false); 3517 if (nsPresContext* pc = shell->GetPresContext()) { 3518 pc->EventStateManager()->ResetHoverState(); 3519 } 3520 } 3521 aContext->mIsInBFCache = true; 3522 Document* doc = aContext->GetDocument(); 3523 if (doc) { 3524 // Notifying needs to happen after mIsInBFCache is set to true. 3525 doc->NotifyActivityChanged(); 3526 } 3527 }); 3528 3529 if (XRE_IsParentProcess()) { 3530 if (mCurrentWindowContext && 3531 mCurrentWindowContext->Canonical()->Fullscreen()) { 3532 mCurrentWindowContext->Canonical()->ExitTopChromeDocumentFullscreen(); 3533 } 3534 } 3535 } 3536 } 3537 3538 void BrowsingContext::DidSet(FieldIndex<IDX_IsSyntheticDocumentContainer>) { 3539 if (WindowContext* parentWindowContext = GetParentWindowContext()) { 3540 parentWindowContext->UpdateChildSynthetic( 3541 this, GetIsSyntheticDocumentContainer()); 3542 } 3543 } 3544 3545 void BrowsingContext::SetCustomPlatform(const nsAString& aPlatform, 3546 ErrorResult& aRv) { 3547 Top()->SetPlatformOverride(aPlatform, aRv); 3548 } 3549 3550 void BrowsingContext::DidSet(FieldIndex<IDX_PlatformOverride>) { 3551 MOZ_ASSERT(IsTop()); 3552 3553 PreOrderWalk([&](BrowsingContext* aContext) { 3554 nsIDocShell* shell = aContext->GetDocShell(); 3555 if (shell) { 3556 shell->ClearCachedPlatform(); 3557 } 3558 }); 3559 } 3560 3561 auto BrowsingContext::LegacyRevertIfNotOwningOrParentProcess( 3562 ContentParent* aSource) -> CanSetResult { 3563 if (aSource) { 3564 MOZ_ASSERT(XRE_IsParentProcess()); 3565 3566 if (!Canonical()->IsOwnedByProcess(aSource->ChildID())) { 3567 return CanSetResult::Revert; 3568 } 3569 } else if (!IsInProcess() && !XRE_IsParentProcess()) { 3570 // Don't allow this to be set from content processes that 3571 // don't own the BrowsingContext. 3572 return CanSetResult::Deny; 3573 } 3574 3575 return CanSetResult::Allow; 3576 } 3577 3578 bool BrowsingContext::CanSet(FieldIndex<IDX_IsActiveBrowserWindowInternal>, 3579 const bool& aValue, ContentParent* aSource) { 3580 // Should only be set in the parent process. 3581 return XRE_IsParentProcess() && !aSource && IsTop(); 3582 } 3583 3584 void BrowsingContext::DidSet(FieldIndex<IDX_IsActiveBrowserWindowInternal>, 3585 bool aOldValue) { 3586 bool isActivateEvent = GetIsActiveBrowserWindowInternal(); 3587 // The browser window containing this context has changed 3588 // activation state so update window inactive document states 3589 // for all in-process documents. 3590 PreOrderWalk([isActivateEvent](BrowsingContext* aContext) { 3591 if (RefPtr<Document> doc = aContext->GetExtantDocument()) { 3592 doc->UpdateDocumentStates(DocumentState::WINDOW_INACTIVE, true); 3593 3594 RefPtr<nsPIDOMWindowInner> win = doc->GetInnerWindow(); 3595 if (win) { 3596 RefPtr<MediaDevices> devices; 3597 if (isActivateEvent && (devices = win->GetExtantMediaDevices())) { 3598 devices->BrowserWindowBecameActive(); 3599 } 3600 3601 if (XRE_IsContentProcess() && 3602 (!aContext->GetParent() || !aContext->GetParent()->IsInProcess())) { 3603 // Send the inner window an activate/deactivate event if 3604 // the context is the top of a sub-tree of in-process 3605 // contexts. 3606 nsContentUtils::DispatchEventOnlyToChrome( 3607 doc, nsGlobalWindowInner::Cast(win), 3608 isActivateEvent ? u"activate"_ns : u"deactivate"_ns, 3609 CanBubble::eYes, Cancelable::eYes, nullptr); 3610 } 3611 } 3612 } 3613 }); 3614 } 3615 3616 bool BrowsingContext::CanSet(FieldIndex<IDX_OpenerPolicy>, 3617 nsILoadInfo::CrossOriginOpenerPolicy aPolicy, 3618 ContentParent* aSource) { 3619 // A potentially cross-origin isolated BC can't change opener policy, nor can 3620 // a BC become potentially cross-origin isolated. An unchanged policy is 3621 // always OK. 3622 return GetOpenerPolicy() == aPolicy || 3623 (GetOpenerPolicy() != 3624 nsILoadInfo:: 3625 OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP && 3626 aPolicy != 3627 nsILoadInfo:: 3628 OPENER_POLICY_SAME_ORIGIN_EMBEDDER_POLICY_REQUIRE_CORP); 3629 } 3630 3631 auto BrowsingContext::CanSet(FieldIndex<IDX_AllowContentRetargeting>, 3632 const bool& aAllowContentRetargeting, 3633 ContentParent* aSource) -> CanSetResult { 3634 return LegacyRevertIfNotOwningOrParentProcess(aSource); 3635 } 3636 3637 auto BrowsingContext::CanSet(FieldIndex<IDX_AllowContentRetargetingOnChildren>, 3638 const bool& aAllowContentRetargetingOnChildren, 3639 ContentParent* aSource) -> CanSetResult { 3640 return LegacyRevertIfNotOwningOrParentProcess(aSource); 3641 } 3642 3643 bool BrowsingContext::CanSet(FieldIndex<IDX_FullscreenAllowedByOwner>, 3644 const bool& aAllowed, ContentParent* aSource) { 3645 return CheckOnlyEmbedderCanSet(aSource); 3646 } 3647 3648 bool BrowsingContext::CanSet(FieldIndex<IDX_UseErrorPages>, 3649 const bool& aUseErrorPages, 3650 ContentParent* aSource) { 3651 return CheckOnlyEmbedderCanSet(aSource); 3652 } 3653 3654 TouchEventsOverride BrowsingContext::TouchEventsOverride() const { 3655 for (const auto* bc = this; bc; bc = bc->GetParent()) { 3656 auto tev = bc->GetTouchEventsOverrideInternal(); 3657 if (tev != dom::TouchEventsOverride::None) { 3658 return tev; 3659 } 3660 } 3661 return dom::TouchEventsOverride::None; 3662 } 3663 3664 bool BrowsingContext::TargetTopLevelLinkClicksToBlank() const { 3665 return Top()->GetTargetTopLevelLinkClicksToBlankInternal(); 3666 } 3667 3668 // We map `watchedByDevTools` WebIDL attribute to `watchedByDevToolsInternal` 3669 // BC field. And we map it to the top level BrowsingContext. 3670 bool BrowsingContext::WatchedByDevTools() { 3671 return Top()->GetWatchedByDevToolsInternal(); 3672 } 3673 3674 // Enforce that the watchedByDevTools BC field can only be set on the top level 3675 // Browsing Context. 3676 bool BrowsingContext::CanSet(FieldIndex<IDX_WatchedByDevToolsInternal>, 3677 const bool& aWatchedByDevTools, 3678 ContentParent* aSource) { 3679 return IsTop(); 3680 } 3681 void BrowsingContext::SetWatchedByDevTools(bool aWatchedByDevTools, 3682 ErrorResult& aRv) { 3683 if (!IsTop()) { 3684 aRv.ThrowInvalidModificationError( 3685 "watchedByDevTools can only be set on top BrowsingContext"); 3686 return; 3687 } 3688 SetWatchedByDevToolsInternal(aWatchedByDevTools, aRv); 3689 } 3690 3691 RefPtr<nsGeolocationService> BrowsingContext::GetGeolocationServiceOverride() { 3692 // Override can be set only to the top-level browsing context, 3693 // but when the geolocation coordinates are requested for iframe, 3694 // we should return the override which is set for its top-level context. 3695 return Top()->mGeolocationServiceOverride; 3696 } 3697 3698 void BrowsingContext::SetGeolocationServiceOverride( 3699 const Optional<nsIDOMGeoPosition*>& aGeolocationOverride) { 3700 MOZ_ASSERT( 3701 IsTop(), 3702 "Should only set GeolocationServiceOverride in the top browsing context"); 3703 if (aGeolocationOverride.WasPassed()) { 3704 if (!mGeolocationServiceOverride) { 3705 mGeolocationServiceOverride = new nsGeolocationService(); 3706 mGeolocationServiceOverride->Init(); 3707 } 3708 mGeolocationServiceOverride->Update(aGeolocationOverride.Value()); 3709 } else if (RefPtr<nsGeolocationService> serviceOverride = 3710 mGeolocationServiceOverride.forget()) { 3711 // Create an original service and move the locators. 3712 RefPtr<nsGeolocationService> service = 3713 nsGeolocationService::GetGeolocationService(); 3714 serviceOverride->MoveLocators(service); 3715 } 3716 } 3717 3718 void BrowsingContext::DidSet(FieldIndex<IDX_TimezoneOverride>, 3719 nsString&& aOldValue) { 3720 MOZ_ASSERT(IsTop()); 3721 3722 PreOrderWalk([&](BrowsingContext* aBrowsingContext) { 3723 if (RefPtr<WindowContext> windowContext = 3724 aBrowsingContext->GetCurrentWindowContext()) { 3725 if (nsCOMPtr<nsPIDOMWindowInner> window = 3726 windowContext->GetInnerWindow()) { 3727 JSObject* global = 3728 nsGlobalWindowInner::Cast(window)->GetGlobalJSObject(); 3729 JS::Realm* realm = JS::GetObjectRealmOrNull(global); 3730 3731 if (GetTimezoneOverride().IsEmpty()) { 3732 JS::SetRealmTimezoneOverride(realm, nullptr); 3733 } else { 3734 JS::SetRealmTimezoneOverride( 3735 realm, NS_ConvertUTF16toUTF8(GetTimezoneOverride()).get()); 3736 } 3737 } 3738 } 3739 }); 3740 } 3741 3742 auto BrowsingContext::CanSet(FieldIndex<IDX_DefaultLoadFlags>, 3743 const uint32_t& aDefaultLoadFlags, 3744 ContentParent* aSource) -> CanSetResult { 3745 // Bug 1623565 - Are these flags only used by the debugger, which makes it 3746 // possible that this field can only be settable by the parent process? 3747 return LegacyRevertIfNotOwningOrParentProcess(aSource); 3748 } 3749 3750 void BrowsingContext::DidSet(FieldIndex<IDX_DefaultLoadFlags>) { 3751 auto loadFlags = GetDefaultLoadFlags(); 3752 if (GetDocShell()) { 3753 nsDocShell::Cast(GetDocShell())->SetLoadGroupDefaultLoadFlags(loadFlags); 3754 } 3755 3756 if (XRE_IsParentProcess()) { 3757 PreOrderWalk([&](BrowsingContext* aContext) { 3758 if (aContext != this) { 3759 // Setting load flags on a discarded context has no effect. 3760 (void)aContext->SetDefaultLoadFlags(loadFlags); 3761 } 3762 }); 3763 } 3764 } 3765 3766 bool BrowsingContext::CanSet(FieldIndex<IDX_UseGlobalHistory>, 3767 const bool& aUseGlobalHistory, 3768 ContentParent* aSource) { 3769 // Should only be set in the parent process. 3770 // return XRE_IsParentProcess() && !aSource; 3771 return true; 3772 } 3773 3774 auto BrowsingContext::CanSet(FieldIndex<IDX_UserAgentOverride>, 3775 const nsString& aUserAgent, ContentParent* aSource) 3776 -> CanSetResult { 3777 if (!IsTop()) { 3778 return CanSetResult::Deny; 3779 } 3780 3781 return LegacyRevertIfNotOwningOrParentProcess(aSource); 3782 } 3783 3784 auto BrowsingContext::CanSet(FieldIndex<IDX_PlatformOverride>, 3785 const nsString& aPlatform, ContentParent* aSource) 3786 -> CanSetResult { 3787 if (!IsTop()) { 3788 return CanSetResult::Deny; 3789 } 3790 3791 return LegacyRevertIfNotOwningOrParentProcess(aSource); 3792 } 3793 3794 bool BrowsingContext::CheckOnlyEmbedderCanSet(ContentParent* aSource) { 3795 if (XRE_IsParentProcess()) { 3796 uint64_t childId = aSource ? aSource->ChildID() : 0; 3797 return Canonical()->IsEmbeddedInProcess(childId); 3798 } 3799 return mEmbeddedByThisProcess; 3800 } 3801 3802 bool BrowsingContext::CanSet(FieldIndex<IDX_EmbedderInnerWindowId>, 3803 const uint64_t& aValue, ContentParent* aSource) { 3804 // If we have a parent window, our embedder inner window ID must match it. 3805 if (mParentWindow) { 3806 return mParentWindow->Id() == aValue; 3807 } 3808 3809 // For toplevel BrowsingContext instances, this value may only be set by the 3810 // parent process, or initialized to `0`. 3811 return CheckOnlyEmbedderCanSet(aSource); 3812 } 3813 3814 bool BrowsingContext::CanSet(FieldIndex<IDX_EmbedderElementType>, 3815 const Maybe<nsString>&, ContentParent* aSource) { 3816 return CheckOnlyEmbedderCanSet(aSource); 3817 } 3818 3819 auto BrowsingContext::CanSet(FieldIndex<IDX_CurrentInnerWindowId>, 3820 const uint64_t& aValue, ContentParent* aSource) 3821 -> CanSetResult { 3822 // Generally allow clearing this. We may want to be more precise about this 3823 // check in the future. 3824 if (aValue == 0) { 3825 return CanSetResult::Allow; 3826 } 3827 3828 // We must have access to the specified context. 3829 RefPtr<WindowContext> window = WindowContext::GetById(aValue); 3830 if (!window || window->GetBrowsingContext() != this) { 3831 return CanSetResult::Deny; 3832 } 3833 3834 if (aSource) { 3835 // If the sending process is no longer the current owner, revert 3836 MOZ_ASSERT(XRE_IsParentProcess()); 3837 if (!Canonical()->IsOwnedByProcess(aSource->ChildID())) { 3838 return CanSetResult::Revert; 3839 } 3840 } else if (XRE_IsContentProcess() && !IsOwnedByProcess()) { 3841 return CanSetResult::Deny; 3842 } 3843 3844 return CanSetResult::Allow; 3845 } 3846 3847 bool BrowsingContext::CanSet(FieldIndex<IDX_ParentInitiatedNavigationEpoch>, 3848 const uint64_t& aValue, ContentParent* aSource) { 3849 return XRE_IsParentProcess() && !aSource; 3850 } 3851 3852 void BrowsingContext::DidSet(FieldIndex<IDX_CurrentInnerWindowId>) { 3853 RefPtr<WindowContext> prevWindowContext = mCurrentWindowContext.forget(); 3854 mCurrentWindowContext = WindowContext::GetById(GetCurrentInnerWindowId()); 3855 MOZ_ASSERT( 3856 !mCurrentWindowContext || mWindowContexts.Contains(mCurrentWindowContext), 3857 "WindowContext not registered?"); 3858 3859 // Clear our cached `children` value, to ensure that JS sees the up-to-date 3860 // value. 3861 BrowsingContext_Binding::ClearCachedChildrenValue(this); 3862 3863 if (XRE_IsParentProcess()) { 3864 if (prevWindowContext != mCurrentWindowContext) { 3865 if (prevWindowContext) { 3866 prevWindowContext->Canonical()->DidBecomeCurrentWindowGlobal(false); 3867 } 3868 if (mCurrentWindowContext) { 3869 // We set a timer when we set the current inner window. This 3870 // will then flush the session storage to session store to 3871 // make sure that we don't miss to store session storage to 3872 // session store that is a result of navigation. This is due 3873 // to Bug 1700623. We wish to fix this in Bug 1711886, where 3874 // making sure to store everything would make this timer 3875 // unnecessary. 3876 Canonical()->MaybeScheduleSessionStoreUpdate(); 3877 mCurrentWindowContext->Canonical()->DidBecomeCurrentWindowGlobal(true); 3878 } 3879 } 3880 BrowserParent::UpdateFocusFromBrowsingContext(); 3881 } 3882 } 3883 3884 bool BrowsingContext::CanSet(FieldIndex<IDX_IsPopupSpam>, const bool& aValue, 3885 ContentParent* aSource) { 3886 // Ensure that we only mark a browsing context as popup spam once and never 3887 // unmark it. 3888 return aValue && !GetIsPopupSpam(); 3889 } 3890 3891 void BrowsingContext::DidSet(FieldIndex<IDX_IsPopupSpam>) { 3892 if (GetIsPopupSpam()) { 3893 PopupBlocker::RegisterOpenPopupSpam(); 3894 } 3895 } 3896 3897 bool BrowsingContext::CanSet(FieldIndex<IDX_MessageManagerGroup>, 3898 const nsString& aMessageManagerGroup, 3899 ContentParent* aSource) { 3900 // Should only be set in the parent process on toplevel. 3901 return XRE_IsParentProcess() && !aSource && IsTopContent(); 3902 } 3903 3904 bool BrowsingContext::CanSet( 3905 FieldIndex<IDX_OrientationLock>, 3906 const mozilla::hal::ScreenOrientation& aOrientationLock, 3907 ContentParent* aSource) { 3908 return IsTop(); 3909 } 3910 3911 bool BrowsingContext::IsLoading() { 3912 if (GetLoading()) { 3913 return true; 3914 } 3915 3916 // If we're in the same process as the page, we're possibly just 3917 // updating the flag. 3918 nsIDocShell* shell = GetDocShell(); 3919 if (shell) { 3920 Document* doc = shell->GetDocument(); 3921 return doc && doc->GetReadyStateEnum() < Document::READYSTATE_COMPLETE; 3922 } 3923 3924 return false; 3925 } 3926 3927 void BrowsingContext::DidSet(FieldIndex<IDX_Loading>) { 3928 if (mFields.Get<IDX_Loading>()) { 3929 return; 3930 } 3931 3932 while (!mDeprioritizedLoadRunner.isEmpty()) { 3933 nsCOMPtr<nsIRunnable> runner = mDeprioritizedLoadRunner.popFirst(); 3934 NS_DispatchToCurrentThread(runner.forget()); 3935 } 3936 3937 if (IsTop()) { 3938 Group()->FlushPostMessageEvents(); 3939 } 3940 } 3941 3942 // Inform the Document for this context of the (potential) change in 3943 // loading state 3944 void BrowsingContext::DidSet(FieldIndex<IDX_AncestorLoading>) { 3945 nsPIDOMWindowOuter* outer = GetDOMWindow(); 3946 if (!outer) { 3947 MOZ_LOG(gTimeoutDeferralLog, mozilla::LogLevel::Debug, 3948 ("DidSetAncestorLoading BC: %p -- No outer window", (void*)this)); 3949 return; 3950 } 3951 Document* document = nsGlobalWindowOuter::Cast(outer)->GetExtantDoc(); 3952 if (document) { 3953 MOZ_LOG(gTimeoutDeferralLog, mozilla::LogLevel::Debug, 3954 ("DidSetAncestorLoading BC: %p -- NotifyLoading(%d, %d, %d)", 3955 (void*)this, GetAncestorLoading(), document->GetReadyStateEnum(), 3956 document->GetReadyStateEnum())); 3957 document->NotifyLoading(GetAncestorLoading(), document->GetReadyStateEnum(), 3958 document->GetReadyStateEnum()); 3959 } 3960 } 3961 3962 void BrowsingContext::DidSet(FieldIndex<IDX_AuthorStyleDisabledDefault>) { 3963 MOZ_ASSERT(IsTop(), 3964 "Should only set AuthorStyleDisabledDefault in the top " 3965 "browsing context"); 3966 3967 // We don't need to handle changes to this field, since PageStyleChild.sys.mjs 3968 // will respond to the PageStyle:Disable message in all content processes. 3969 // 3970 // But we store the state here on the top BrowsingContext so that the 3971 // docshell has somewhere to look for the current author style disabling 3972 // state when new iframes are inserted. 3973 } 3974 3975 void BrowsingContext::DidSet(FieldIndex<IDX_TextZoom>, float aOldValue) { 3976 if (GetTextZoom() == aOldValue) { 3977 return; 3978 } 3979 3980 if (IsInProcess()) { 3981 if (nsIDocShell* shell = GetDocShell()) { 3982 if (nsPresContext* pc = shell->GetPresContext()) { 3983 pc->RecomputeBrowsingContextDependentData(); 3984 } 3985 } 3986 3987 for (BrowsingContext* child : Children()) { 3988 // Setting text zoom on a discarded context has no effect. 3989 (void)child->SetTextZoom(GetTextZoom()); 3990 } 3991 } 3992 3993 if (IsTop() && XRE_IsParentProcess()) { 3994 if (Element* element = GetEmbedderElement()) { 3995 AsyncEventDispatcher::RunDOMEventWhenSafe(*element, u"TextZoomChange"_ns, 3996 CanBubble::eYes, 3997 ChromeOnlyDispatch::eYes); 3998 } 3999 } 4000 } 4001 4002 // TODO(emilio): It'd be potentially nicer and cheaper to allow to set this only 4003 // on the Top() browsing context, but there are a lot of tests that rely on 4004 // zooming a subframe so... 4005 void BrowsingContext::DidSet(FieldIndex<IDX_FullZoom>, float aOldValue) { 4006 if (GetFullZoom() == aOldValue) { 4007 return; 4008 } 4009 4010 if (IsInProcess()) { 4011 if (nsIDocShell* shell = GetDocShell()) { 4012 if (nsPresContext* pc = shell->GetPresContext()) { 4013 pc->RecomputeBrowsingContextDependentData(); 4014 } 4015 } 4016 4017 for (BrowsingContext* child : Children()) { 4018 // When passing the outer document's full-zoom down to the inner 4019 // document, scale by the effective CSS 'zoom' on the embedder element: 4020 auto fullZoom = GetFullZoom(); 4021 if (auto* elem = child->GetEmbedderElement()) { 4022 if (auto* frame = elem->GetPrimaryFrame()) { 4023 fullZoom = frame->Style()->EffectiveZoom().Zoom(fullZoom); 4024 } 4025 } 4026 // Setting full zoom on a discarded context has no effect. 4027 (void)child->SetFullZoom(fullZoom); 4028 } 4029 } 4030 4031 if (IsTop() && XRE_IsParentProcess()) { 4032 if (Element* element = GetEmbedderElement()) { 4033 AsyncEventDispatcher::RunDOMEventWhenSafe(*element, u"FullZoomChange"_ns, 4034 CanBubble::eYes, 4035 ChromeOnlyDispatch::eYes); 4036 } 4037 } 4038 } 4039 4040 void BrowsingContext::AddDeprioritizedLoadRunner(nsIRunnable* aRunner) { 4041 MOZ_ASSERT(IsLoading()); 4042 MOZ_ASSERT(Top() == this); 4043 4044 RefPtr<DeprioritizedLoadRunner> runner = new DeprioritizedLoadRunner(aRunner); 4045 mDeprioritizedLoadRunner.insertBack(runner); 4046 NS_DispatchToCurrentThreadQueue(runner.forget(), EventQueuePriority::Low); 4047 } 4048 4049 bool BrowsingContext::IsDynamic() const { 4050 const BrowsingContext* current = this; 4051 do { 4052 if (current->CreatedDynamically()) { 4053 return true; 4054 } 4055 } while ((current = current->GetParent())); 4056 4057 return false; 4058 } 4059 4060 bool BrowsingContext::GetOffsetPath(nsTArray<uint32_t>& aPath) const { 4061 for (const BrowsingContext* current = this; current && current->GetParent(); 4062 current = current->GetParent()) { 4063 if (current->CreatedDynamically()) { 4064 return false; 4065 } 4066 aPath.AppendElement(current->ChildOffset()); 4067 } 4068 return true; 4069 } 4070 4071 void BrowsingContext::GetHistoryID(JSContext* aCx, 4072 JS::MutableHandle<JS::Value> aVal, 4073 ErrorResult& aError) { 4074 if (!xpc::ID2JSValue(aCx, GetHistoryID(), aVal)) { 4075 aError.Throw(NS_ERROR_OUT_OF_MEMORY); 4076 } 4077 } 4078 4079 void BrowsingContext::InitSessionHistory() { 4080 MOZ_ASSERT(!IsDiscarded()); 4081 MOZ_ASSERT(IsTop()); 4082 MOZ_ASSERT(EverAttached()); 4083 4084 if (!GetHasSessionHistory()) { 4085 MOZ_ALWAYS_SUCCEEDS(SetHasSessionHistory(true)); 4086 } 4087 } 4088 4089 ChildSHistory* BrowsingContext::GetChildSessionHistory() { 4090 if (!mozilla::SessionHistoryInParent()) { 4091 // For now we're checking that the session history object for the child 4092 // process is available before returning the ChildSHistory object, because 4093 // it is the actual implementation that ChildSHistory forwards to. This can 4094 // be removed once session history is stored exclusively in the parent 4095 // process. 4096 return mChildSessionHistory && mChildSessionHistory->IsInProcess() 4097 ? mChildSessionHistory.get() 4098 : nullptr; 4099 } 4100 4101 return mChildSessionHistory; 4102 } 4103 4104 void BrowsingContext::CreateChildSHistory() { 4105 MOZ_ASSERT(IsTop()); 4106 MOZ_ASSERT(GetHasSessionHistory()); 4107 MOZ_ASSERT(!mChildSessionHistory); 4108 4109 // Because session history is global in a browsing context tree, every process 4110 // that has access to a browsing context tree needs access to its session 4111 // history. That is why we create the ChildSHistory object in every process 4112 // where we have access to this browsing context (which is the top one). 4113 mChildSessionHistory = new ChildSHistory(this); 4114 4115 // If the top browsing context (this one) is loaded in this process then we 4116 // also create the session history implementation for the child process. 4117 // This can be removed once session history is stored exclusively in the 4118 // parent process. 4119 mChildSessionHistory->SetIsInProcess(IsInProcess()); 4120 } 4121 4122 void BrowsingContext::DidSet(FieldIndex<IDX_HasSessionHistory>, 4123 bool aOldValue) { 4124 MOZ_ASSERT(GetHasSessionHistory() || !aOldValue, 4125 "We don't support turning off session history."); 4126 4127 if (GetHasSessionHistory() && !aOldValue) { 4128 CreateChildSHistory(); 4129 } 4130 } 4131 4132 bool BrowsingContext::CanSet( 4133 FieldIndex<IDX_TargetTopLevelLinkClicksToBlankInternal>, 4134 const bool& aTargetTopLevelLinkClicksToBlankInternal, 4135 ContentParent* aSource) { 4136 return XRE_IsParentProcess() && !aSource && IsTop(); 4137 } 4138 4139 bool BrowsingContext::CanSet(FieldIndex<IDX_BrowserId>, const uint32_t& aValue, 4140 ContentParent* aSource) { 4141 // We should only be able to set this for toplevel contexts which don't have 4142 // an ID yet. 4143 return GetBrowserId() == 0 && IsTop() && Children().IsEmpty(); 4144 } 4145 4146 bool BrowsingContext::CanSet(FieldIndex<IDX_PendingInitialization>, 4147 bool aNewValue, ContentParent* aSource) { 4148 // Can only be cleared from `true` to `false`, and should only ever be set on 4149 // the toplevel BrowsingContext. 4150 return IsTop() && GetPendingInitialization() && !aNewValue; 4151 } 4152 4153 bool BrowsingContext::CanSet(FieldIndex<IDX_TopLevelCreatedByWebContent>, 4154 const bool& aNewValue, ContentParent* aSource) { 4155 // Should only be set after creation in the parent process. 4156 return XRE_IsParentProcess() && !aSource && IsTop(); 4157 } 4158 4159 bool BrowsingContext::CanSet(FieldIndex<IDX_HasRestoreData>, bool aNewValue, 4160 ContentParent* aSource) { 4161 return IsTop(); 4162 } 4163 4164 bool BrowsingContext::CanSet(FieldIndex<IDX_IsUnderHiddenEmbedderElement>, 4165 const bool& aIsUnderHiddenEmbedderElement, 4166 ContentParent* aSource) { 4167 return true; 4168 } 4169 4170 bool BrowsingContext::CanSet(FieldIndex<IDX_ForceOffline>, bool aNewValue, 4171 ContentParent* aSource) { 4172 return XRE_IsParentProcess() && !aSource; 4173 } 4174 4175 void BrowsingContext::DidSet(FieldIndex<IDX_IsUnderHiddenEmbedderElement>, 4176 bool aOldValue) { 4177 nsIDocShell* shell = GetDocShell(); 4178 if (!shell) { 4179 return; 4180 } 4181 const bool newValue = IsUnderHiddenEmbedderElement(); 4182 if (NS_WARN_IF(aOldValue == newValue)) { 4183 return; 4184 } 4185 4186 if (auto* bc = BrowserChild::GetFrom(shell)) { 4187 bc->UpdateVisibility(); 4188 } 4189 4190 if (PresShell* presShell = shell->GetPresShell()) { 4191 presShell->SetIsUnderHiddenEmbedderElement(newValue); 4192 } 4193 4194 // Propagate to children. 4195 auto PropagateToChild = [&newValue](BrowsingContext* aChild) { 4196 Element* embedderElement = aChild->GetEmbedderElement(); 4197 if (!embedderElement) { 4198 // TODO: We shouldn't need to null check here since `child` and the 4199 // element returned by `child->GetEmbedderElement()` are in our 4200 // process (the actual browsing context represented by `child` may not 4201 // be, but that doesn't matter). However, there are currently a very 4202 // small number of crashes due to `embedderElement` being null, somehow 4203 // - see bug 1551241. For now we wallpaper the crash. 4204 return CallState::Continue; 4205 } 4206 4207 bool embedderFrameIsHidden = true; 4208 if (auto* embedderFrame = embedderElement->GetPrimaryFrame()) { 4209 embedderFrameIsHidden = !embedderFrame->StyleVisibility()->IsVisible(); 4210 } 4211 4212 bool hidden = newValue || embedderFrameIsHidden; 4213 if (aChild->IsUnderHiddenEmbedderElement() != hidden) { 4214 (void)aChild->SetIsUnderHiddenEmbedderElement(hidden); 4215 } 4216 4217 return CallState::Continue; 4218 }; 4219 4220 for (BrowsingContext* child : Children()) { 4221 PropagateToChild(child); 4222 } 4223 4224 if (XRE_IsParentProcess()) { 4225 Canonical()->CallOnTopDescendants( 4226 PropagateToChild, 4227 CanonicalBrowsingContext::TopDescendantKind::ChildrenOnly); 4228 } 4229 } 4230 4231 void BrowsingContext::DidSet(FieldIndex<IDX_ForceOffline>, bool aOldValue) { 4232 const bool newValue = ForceOffline(); 4233 if (newValue == aOldValue) { 4234 return; 4235 } 4236 PreOrderWalk([&](BrowsingContext* aBrowsingContext) { 4237 if (RefPtr<WindowContext> windowContext = 4238 aBrowsingContext->GetCurrentWindowContext()) { 4239 if (nsCOMPtr<nsPIDOMWindowInner> window = 4240 windowContext->GetInnerWindow()) { 4241 nsGlobalWindowInner::Cast(window)->FireOfflineStatusEventIfChanged(); 4242 } 4243 } 4244 }); 4245 } 4246 4247 bool BrowsingContext::IsPopupAllowed() { 4248 for (auto* context = GetCurrentWindowContext(); context; 4249 context = context->GetParentWindowContext()) { 4250 if (context->CanShowPopup()) { 4251 return true; 4252 } 4253 } 4254 4255 return false; 4256 } 4257 4258 /* static */ 4259 bool BrowsingContext::ShouldAddEntryForRefresh( 4260 nsIURI* aPreviousURI, const SessionHistoryInfo& aInfo) { 4261 return ShouldAddEntryForRefresh(aPreviousURI, aInfo.GetURI(), 4262 aInfo.HasPostData()); 4263 } 4264 4265 /* static */ 4266 bool BrowsingContext::ShouldAddEntryForRefresh(nsIURI* aPreviousURI, 4267 nsIURI* aNewURI, 4268 bool aHasPostData) { 4269 if (aHasPostData) { 4270 return true; 4271 } 4272 4273 bool equalsURI = false; 4274 if (aPreviousURI) { 4275 aPreviousURI->Equals(aNewURI, &equalsURI); 4276 } 4277 return !equalsURI; 4278 } 4279 4280 bool BrowsingContext::AddSHEntryWouldIncreaseLength( 4281 SessionHistoryInfo* aCurrentEntry) const { 4282 // nsSHistory::AddEntry and AddNestedSHEntry do a replace load if the current 4283 // entry is marked as transient. 4284 const bool isCurrentTransientEntry = 4285 aCurrentEntry && aCurrentEntry->IsTransient(); 4286 4287 // If this is the first entry for an iframe, it would be added to the parent 4288 // entry instead of creating a new top-level entry. 4289 const bool wouldAddToParentEntry = !IsTop() && !aCurrentEntry; 4290 4291 return !isCurrentTransientEntry && !wouldAddToParentEntry; 4292 } 4293 4294 void BrowsingContext::SessionHistoryCommit( 4295 const LoadingSessionHistoryInfo& aInfo, uint32_t aLoadType, 4296 nsIURI* aPreviousURI, SessionHistoryInfo* aPreviousActiveEntry, 4297 bool aCloneEntryChildren, bool aChannelExpired, uint32_t aCacheKey) { 4298 nsID changeID = {}; 4299 if (XRE_IsContentProcess()) { 4300 RefPtr<ChildSHistory> rootSH = Top()->GetChildSessionHistory(); 4301 if (rootSH) { 4302 if (!aInfo.mLoadIsFromSessionHistory) { 4303 // We try to mimic as closely as possible whether 4304 // CanonicalBrowsingContext::SessionHistoryCommit will increase 4305 // the session history length. 4306 // It is possible that this leads to wrong length temporarily, but 4307 // so would not having these checks. 4308 // The child process does not have access to the current entry, so we 4309 // use the previous active entry as the best approximation. When that's 4310 // not the current entry then the length might be wrong briefly, until 4311 // the parent process commits the actual length. 4312 const bool isReplaceLoad = LOAD_TYPE_HAS_FLAGS( 4313 aLoadType, nsIWebNavigation::LOAD_FLAGS_REPLACE_HISTORY), 4314 isRefreshLoad = LOAD_TYPE_HAS_FLAGS( 4315 aLoadType, nsIWebNavigation::LOAD_FLAGS_IS_REFRESH); 4316 if (!isReplaceLoad && 4317 AddSHEntryWouldIncreaseLength(aPreviousActiveEntry) && 4318 ShouldUpdateSessionHistory(aLoadType) && 4319 (!isRefreshLoad || 4320 ShouldAddEntryForRefresh(aPreviousURI, aInfo.mInfo))) { 4321 changeID = rootSH->AddPendingHistoryChange(); 4322 } 4323 } else { 4324 // History load doesn't change the length, only index. 4325 changeID = rootSH->AddPendingHistoryChange(aInfo.mOffset, 0); 4326 } 4327 } 4328 ContentChild* cc = ContentChild::GetSingleton(); 4329 (void)cc->SendHistoryCommit(this, aInfo.mLoadId, changeID, aLoadType, 4330 aCloneEntryChildren, aChannelExpired, 4331 aCacheKey); 4332 } else { 4333 Canonical()->SessionHistoryCommit(aInfo.mLoadId, changeID, aLoadType, 4334 aCloneEntryChildren, aChannelExpired, 4335 aCacheKey); 4336 } 4337 } 4338 4339 void BrowsingContext::SetActiveSessionHistoryEntry( 4340 const Maybe<nsPoint>& aPreviousScrollPos, SessionHistoryInfo* aInfo, 4341 SessionHistoryInfo* aPreviousActiveEntry, uint32_t aLoadType, 4342 uint32_t aUpdatedCacheKey, bool aUpdateLength) { 4343 if (IsTop() && 4344 !nsDocShell::ShouldAddToSessionHistory(aInfo->GetURI(), nullptr)) { 4345 aInfo->SetTransient(); 4346 } 4347 if (XRE_IsContentProcess()) { 4348 // XXX Why we update cache key only in content process case? 4349 if (aUpdatedCacheKey != 0) { 4350 aInfo->SetCacheKey(aUpdatedCacheKey); 4351 } 4352 4353 nsID changeID = {}; 4354 if (aUpdateLength) { 4355 RefPtr<ChildSHistory> shistory = Top()->GetChildSessionHistory(); 4356 if (shistory) { 4357 // We try to mimic what will happen in 4358 // CanonicalBrowsingContext::SendSetActiveSessionHistoryEntry 4359 if (AddSHEntryWouldIncreaseLength(aPreviousActiveEntry)) { 4360 changeID = shistory->AddPendingHistoryChange(); 4361 } 4362 } 4363 } 4364 ContentChild::GetSingleton()->SendSetActiveSessionHistoryEntry( 4365 this, aPreviousScrollPos, *aInfo, aLoadType, aUpdatedCacheKey, 4366 changeID); 4367 } else { 4368 Canonical()->SetActiveSessionHistoryEntry( 4369 aPreviousScrollPos, aInfo, aLoadType, aUpdatedCacheKey, nsID()); 4370 } 4371 } 4372 4373 void BrowsingContext::ReplaceActiveSessionHistoryEntry( 4374 SessionHistoryInfo* aInfo) { 4375 if (XRE_IsContentProcess()) { 4376 ContentChild::GetSingleton()->SendReplaceActiveSessionHistoryEntry(this, 4377 *aInfo); 4378 } else { 4379 Canonical()->ReplaceActiveSessionHistoryEntry(aInfo); 4380 } 4381 } 4382 4383 void BrowsingContext::RemoveDynEntriesFromActiveSessionHistoryEntry() { 4384 if (XRE_IsContentProcess()) { 4385 ContentChild::GetSingleton() 4386 ->SendRemoveDynEntriesFromActiveSessionHistoryEntry(this); 4387 } else { 4388 Canonical()->RemoveDynEntriesFromActiveSessionHistoryEntry(); 4389 } 4390 } 4391 4392 void BrowsingContext::RemoveFromSessionHistory(const nsID& aChangeID) { 4393 if (XRE_IsContentProcess()) { 4394 ContentChild::GetSingleton()->SendRemoveFromSessionHistory(this, aChangeID); 4395 } else { 4396 Canonical()->RemoveFromSessionHistory(aChangeID); 4397 } 4398 } 4399 4400 void BrowsingContext::HistoryGo( 4401 int32_t aOffset, uint64_t aHistoryEpoch, bool aRequireUserInteraction, 4402 bool aUserActivation, std::function<void(Maybe<int32_t>&&)>&& aResolver) { 4403 // We always pass checkForCancelation as true here, since we'll never call 4404 // HistoryGo in a context where we beforehand know that we want to skip 4405 // onbeforeunload or onnavigate. 4406 bool checkForCancelation = true; 4407 if (XRE_IsContentProcess()) { 4408 ContentChild::GetSingleton()->SendHistoryGo( 4409 this, aOffset, aHistoryEpoch, aRequireUserInteraction, aUserActivation, 4410 checkForCancelation, std::move(aResolver), 4411 [](mozilla::ipc:: 4412 ResponseRejectReason) { /* FIXME Is ignoring this fine? */ }); 4413 } else { 4414 RefPtr<CanonicalBrowsingContext> self = Canonical(); 4415 aResolver(self->HistoryGo(aOffset, aHistoryEpoch, aRequireUserInteraction, 4416 aUserActivation, checkForCancelation, 4417 self->GetContentParent() 4418 ? Some(self->GetContentParent()->ChildID()) 4419 : Nothing())); 4420 } 4421 } 4422 4423 void BrowsingContext::NavigationTraverse( 4424 const nsID& aKey, uint64_t aHistoryEpoch, bool aUserActivation, 4425 bool aCheckForCancelation, std::function<void(nsresult)>&& aResolver) { 4426 if (XRE_IsContentProcess()) { 4427 ContentChild::GetSingleton()->SendNavigationTraverse( 4428 this, aKey, aHistoryEpoch, aUserActivation, aCheckForCancelation, 4429 std::move(aResolver), 4430 [](mozilla::ipc:: 4431 ResponseRejectReason) { /* FIXME Is ignoring this fine? */ }); 4432 } else { 4433 RefPtr<CanonicalBrowsingContext> self = Canonical(); 4434 self->NavigationTraverse( 4435 aKey, aHistoryEpoch, aUserActivation, aCheckForCancelation, 4436 self->GetContentParent() ? Some(self->GetContentParent()->ChildID()) 4437 : Nothing(), 4438 std::move(aResolver)); 4439 } 4440 } 4441 4442 void BrowsingContext::SetChildSHistory(ChildSHistory* aChildSHistory) { 4443 mChildSessionHistory = aChildSHistory; 4444 mChildSessionHistory->SetBrowsingContext(this); 4445 mFields.SetWithoutSyncing<IDX_HasSessionHistory>(true); 4446 } 4447 4448 bool BrowsingContext::ShouldUpdateSessionHistory(uint32_t aLoadType) { 4449 // We don't update session history on reload unless we're loading 4450 // an iframe in shift-reload case. 4451 return nsDocShell::ShouldUpdateGlobalHistory(aLoadType) && 4452 (!(aLoadType & nsIDocShell::LOAD_CMD_RELOAD) || 4453 (IsForceReloadType(aLoadType) && IsSubframe())); 4454 } 4455 4456 nsresult BrowsingContext::CheckNavigationRateLimit(CallerType aCallerType) { 4457 // We only rate limit non system callers 4458 if (aCallerType == CallerType::System) { 4459 return NS_OK; 4460 } 4461 4462 // Fetch rate limiting preferences 4463 uint32_t limitCount = StaticPrefs::dom_navigation_navigationRateLimit_count(); 4464 uint32_t timeSpanSeconds = 4465 StaticPrefs::dom_navigation_navigationRateLimit_timespan(); 4466 4467 // Disable throttling if either of the preferences is set to 0. 4468 if (limitCount == 0 || timeSpanSeconds == 0) { 4469 return NS_OK; 4470 } 4471 4472 TimeDuration throttleSpan = TimeDuration::FromSeconds(timeSpanSeconds); 4473 4474 if (mNavigationRateLimitSpanStart.IsNull() || 4475 ((TimeStamp::Now() - mNavigationRateLimitSpanStart) > throttleSpan)) { 4476 // Initial call or timespan exceeded, reset counter and timespan. 4477 mNavigationRateLimitSpanStart = TimeStamp::Now(); 4478 mNavigationRateLimitCount = 1; 4479 return NS_OK; 4480 } 4481 4482 if (mNavigationRateLimitCount >= limitCount) { 4483 // Rate limit reached 4484 4485 Document* doc = GetDocument(); 4486 if (doc) { 4487 nsContentUtils::ReportToConsole(nsIScriptError::errorFlag, "DOM"_ns, doc, 4488 nsContentUtils::eDOM_PROPERTIES, 4489 "LocChangeFloodingPrevented"); 4490 } 4491 4492 return NS_ERROR_DOM_SECURITY_ERR; 4493 } 4494 4495 mNavigationRateLimitCount++; 4496 return NS_OK; 4497 } 4498 4499 void BrowsingContext::ResetNavigationRateLimit() { 4500 // Resetting the timestamp object will cause the check function to 4501 // init again and reset the rate limit. 4502 mNavigationRateLimitSpanStart = TimeStamp(); 4503 } 4504 4505 void BrowsingContext::LocationCreated(dom::Location* aLocation) { 4506 MOZ_ASSERT(!aLocation->isInList()); 4507 mLocations.insertBack(aLocation); 4508 } 4509 4510 void BrowsingContext::ClearCachedValuesOfLocations() { 4511 for (dom::Location* loc = mLocations.getFirst(); loc; loc = loc->getNext()) { 4512 loc->ClearCachedValues(); 4513 } 4514 } 4515 4516 // https://html.spec.whatwg.org/multipage/interaction.html#consume-history-action-user-activation 4517 // Step 3 onward 4518 void BrowsingContext::ConsumeHistoryActivation() { 4519 // 3. Let navigables be the inclusive descendant navigables of top's active 4520 // document. 4521 // 4. Let windows be the list of Window objects constructed by taking the 4522 // active window of each item in navigables. 4523 PreOrderWalk([&](BrowsingContext* aBrowsingContext) { 4524 RefPtr<WindowContext> windowContext = 4525 aBrowsingContext->GetCurrentWindowContext(); 4526 // 5. For each window in windows, set window's last history-action 4527 // activation timestamp to window's last activation timestamp. 4528 if (aBrowsingContext->IsInProcess() && windowContext && 4529 windowContext->GetUserActivationState() == 4530 UserActivation::State::FullActivated) { 4531 windowContext->UpdateLastHistoryActivation(); 4532 } 4533 }); 4534 } 4535 4536 void BrowsingContext::SynchronizeNavigationAPIState( 4537 nsIStructuredCloneContainer* aState) { 4538 if (!aState) { 4539 return; 4540 } 4541 4542 if (XRE_IsContentProcess()) { 4543 ClonedMessageData data; 4544 DebugOnly<bool> result = static_cast<nsStructuredCloneContainer*>(aState) 4545 ->BuildClonedMessageData(data); 4546 MOZ_ASSERT(result); 4547 4548 MOZ_ASSERT(ContentChild::GetSingleton()); 4549 ContentChild::GetSingleton()->SendSynchronizeNavigationAPIState(this, data); 4550 } else { 4551 Canonical()->SynchronizeNavigationAPIState(aState); 4552 } 4553 } 4554 4555 } // namespace dom 4556 } // namespace mozilla 4557 4558 namespace IPC { 4559 4560 using mozilla::dom::BrowsingContext; 4561 using mozilla::dom::MaybeDiscarded; 4562 4563 void ParamTraits<MaybeDiscarded<BrowsingContext>>::Write( 4564 IPC::MessageWriter* aWriter, 4565 const MaybeDiscarded<BrowsingContext>& aParam) { 4566 MOZ_DIAGNOSTIC_ASSERT(!aParam.GetMaybeDiscarded() || 4567 aParam.GetMaybeDiscarded()->EverAttached()); 4568 uint64_t id = aParam.ContextId(); 4569 WriteParam(aWriter, id); 4570 } 4571 4572 bool ParamTraits<MaybeDiscarded<BrowsingContext>>::Read( 4573 IPC::MessageReader* aReader, MaybeDiscarded<BrowsingContext>* aResult) { 4574 uint64_t id = 0; 4575 if (!ReadParam(aReader, &id)) { 4576 return false; 4577 } 4578 4579 if (id == 0) { 4580 *aResult = nullptr; 4581 } else if (RefPtr<BrowsingContext> bc = BrowsingContext::Get(id)) { 4582 *aResult = std::move(bc); 4583 } else { 4584 aResult->SetDiscarded(id); 4585 } 4586 return true; 4587 } 4588 4589 void ParamTraits<BrowsingContext::IPCInitializer>::Write( 4590 IPC::MessageWriter* aWriter, const paramType& aInit) { 4591 // Write actor ID parameters. 4592 WriteParam(aWriter, aInit.mId); 4593 WriteParam(aWriter, aInit.mParentId); 4594 WriteParam(aWriter, aInit.mWindowless); 4595 WriteParam(aWriter, aInit.mUseRemoteTabs); 4596 WriteParam(aWriter, aInit.mUseRemoteSubframes); 4597 WriteParam(aWriter, aInit.mCreatedDynamically); 4598 WriteParam(aWriter, aInit.mChildOffset); 4599 WriteParam(aWriter, aInit.mOriginAttributes); 4600 WriteParam(aWriter, aInit.mRequestContextId); 4601 WriteParam(aWriter, aInit.mSessionHistoryIndex); 4602 WriteParam(aWriter, aInit.mSessionHistoryCount); 4603 WriteParam(aWriter, aInit.mFields); 4604 } 4605 4606 bool ParamTraits<BrowsingContext::IPCInitializer>::Read( 4607 IPC::MessageReader* aReader, paramType* aInit) { 4608 // Read actor ID parameters. 4609 return ReadParam(aReader, &aInit->mId) && 4610 ReadParam(aReader, &aInit->mParentId) && 4611 ReadParam(aReader, &aInit->mWindowless) && 4612 ReadParam(aReader, &aInit->mUseRemoteTabs) && 4613 ReadParam(aReader, &aInit->mUseRemoteSubframes) && 4614 ReadParam(aReader, &aInit->mCreatedDynamically) && 4615 ReadParam(aReader, &aInit->mChildOffset) && 4616 ReadParam(aReader, &aInit->mOriginAttributes) && 4617 ReadParam(aReader, &aInit->mRequestContextId) && 4618 ReadParam(aReader, &aInit->mSessionHistoryIndex) && 4619 ReadParam(aReader, &aInit->mSessionHistoryCount) && 4620 ReadParam(aReader, &aInit->mFields); 4621 } 4622 4623 template struct ParamTraits<BrowsingContext::BaseTransaction>; 4624 4625 } // namespace IPC