nsFrameLoader.cpp (135875B)
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 /* 8 * Class for managing loading of a subframe (creation of the docshell, 9 * handling of loads in it, recursion-checking). 10 */ 11 12 #include "nsFrameLoader.h" 13 14 #include "BrowserParent.h" 15 #include "ContentParent.h" 16 #include "InProcessBrowserChildMessageManager.h" 17 #include "ReferrerInfo.h" 18 #include "base/basictypes.h" 19 #include "buildid_section.h" 20 #include "jsapi.h" 21 #include "mozilla/AsyncEventDispatcher.h" 22 #include "mozilla/BasePrincipal.h" 23 #include "mozilla/ContentPrincipal.h" 24 #include "mozilla/ExpandedPrincipal.h" 25 #include "mozilla/FlushType.h" 26 #include "mozilla/HTMLEditor.h" 27 #include "mozilla/NullPrincipal.h" 28 #include "mozilla/Preferences.h" 29 #include "mozilla/PresShell.h" 30 #include "mozilla/PresShellInlines.h" 31 #include "mozilla/ProcessPriorityManager.h" 32 #include "mozilla/ProfilerLabels.h" 33 #include "mozilla/ScopeExit.h" 34 #include "mozilla/ScrollContainerFrame.h" 35 #include "mozilla/StaticPrefs_fission.h" 36 #include "mozilla/WebBrowserPersistLocalDocument.h" 37 #include "mozilla/dom/BrowserBridgeChild.h" 38 #include "mozilla/dom/BrowserBridgeHost.h" 39 #include "mozilla/dom/BrowserHost.h" 40 #include "mozilla/dom/BrowsingContext.h" 41 #include "mozilla/dom/BrowsingContextGroup.h" 42 #include "mozilla/dom/CanonicalBrowsingContext.h" 43 #include "mozilla/dom/ChildSHistory.h" 44 #include "mozilla/dom/ChromeMessageSender.h" 45 #include "mozilla/dom/ContentChild.h" 46 #include "mozilla/dom/ContentProcessManager.h" 47 #include "mozilla/dom/CustomEvent.h" 48 #include "mozilla/dom/Document.h" 49 #include "mozilla/dom/Element.h" 50 #include "mozilla/dom/FrameCrashedEvent.h" 51 #include "mozilla/dom/FrameLoaderBinding.h" 52 #include "mozilla/dom/HTMLBodyElement.h" 53 #include "mozilla/dom/HTMLIFrameElement.h" 54 #include "mozilla/dom/InProcessChild.h" 55 #include "mozilla/dom/MozFrameLoaderOwnerBinding.h" 56 #include "mozilla/dom/PBackgroundSessionStorageCache.h" 57 #include "mozilla/dom/PBrowser.h" 58 #include "mozilla/dom/PolicyContainer.h" 59 #include "mozilla/dom/Promise.h" 60 #include "mozilla/dom/PromiseNativeHandler.h" 61 #include "mozilla/dom/SessionHistoryEntry.h" 62 #include "mozilla/dom/SessionStorageManager.h" 63 #include "mozilla/dom/SessionStoreChild.h" 64 #include "mozilla/dom/SessionStoreParent.h" 65 #include "mozilla/dom/SessionStoreUtils.h" 66 #include "mozilla/dom/WindowGlobalParent.h" 67 #include "mozilla/dom/XULFrameElement.h" 68 #include "mozilla/dom/ipc/StructuredCloneData.h" 69 #include "mozilla/gfx/CrossProcessPaint.h" 70 #include "mozilla/ipc/BackgroundChild.h" 71 #include "mozilla/ipc/BackgroundUtils.h" 72 #include "mozilla/ipc/PBackgroundChild.h" 73 #include "mozilla/layers/CompositorBridgeChild.h" 74 #include "mozilla/toolkit/library/buildid_reader_ffi.h" 75 #include "nsAppRunner.h" 76 #include "nsContentUtils.h" 77 #include "nsDirectoryService.h" 78 #include "nsDirectoryServiceDefs.h" 79 #include "nsDocShell.h" 80 #include "nsDocShellLoadState.h" 81 #include "nsError.h" 82 #include "nsFocusManager.h" 83 #include "nsFrameLoaderOwner.h" 84 #include "nsGenericHTMLFrameElement.h" 85 #include "nsGkAtoms.h" 86 #include "nsGlobalWindowInner.h" 87 #include "nsGlobalWindowOuter.h" 88 #include "nsHTMLDocument.h" 89 #include "nsIAppWindow.h" 90 #include "nsIBaseWindow.h" 91 #include "nsIBrowser.h" 92 #include "nsIContentInlines.h" 93 #include "nsIDocShell.h" 94 #include "nsIDocShellTreeOwner.h" 95 #include "nsIDocumentViewer.h" 96 #include "nsIFrame.h" 97 #include "nsIINIParser.h" 98 #include "nsIOpenWindowInfo.h" 99 #include "nsIPrintSettings.h" 100 #include "nsIPrintSettingsService.h" 101 #include "nsISHistory.h" 102 #include "nsIScriptError.h" 103 #include "nsIScriptGlobalObject.h" 104 #include "nsIScriptSecurityManager.h" 105 #include "nsIURI.h" 106 #include "nsIWebNavigation.h" 107 #include "nsIWebProgress.h" 108 #include "nsIWidget.h" 109 #include "nsIXULRuntime.h" 110 #include "nsLayoutUtils.h" 111 #include "nsNameSpaceManager.h" 112 #include "nsNetUtil.h" 113 #include "nsOpenWindowInfo.h" 114 #include "nsPIDOMWindow.h" 115 #include "nsPIWindowRoot.h" 116 #include "nsQueryObject.h" 117 #include "nsSandboxFlags.h" 118 #include "nsSubDocumentFrame.h" 119 #include "nsThreadUtils.h" 120 #include "nsUnicharUtils.h" 121 #include "nsXPCOMPrivate.h" // for XUL_DLL 122 #include "nsXULPopupManager.h" 123 #include "prenv.h" 124 125 #ifdef NS_PRINTING 126 # include "nsIWebBrowserPrint.h" 127 #endif 128 129 #if defined(MOZ_TELEMETRY_REPORTING) 130 # include "mozilla/glean/DomMetrics.h" 131 #endif // defined(MOZ_TELEMETRY_REPORTING) 132 133 using namespace mozilla; 134 using namespace mozilla::hal; 135 using namespace mozilla::dom; 136 using namespace mozilla::dom::ipc; 137 using namespace mozilla::ipc; 138 using namespace mozilla::layers; 139 using namespace mozilla::layout; 140 using ViewID = ScrollableLayerGuid::ViewID; 141 142 using PrintPreviewResolver = std::function<void(const PrintPreviewResultInfo&)>; 143 144 // Bug 8065: Limit content frame depth to some reasonable level. This 145 // does not count chrome frames when determining depth, nor does it 146 // prevent chrome recursion. Number is fairly arbitrary, but meant to 147 // keep number of shells to a reasonable number on accidental recursion with a 148 // small (but not 1) branching factor. With large branching factors the number 149 // of shells can rapidly become huge and run us out of memory. To solve that, 150 // we'd need to re-institute a fixed version of bug 98158. 151 #define MAX_DEPTH_CONTENT_FRAMES 10 152 153 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsFrameLoader, mPendingBrowsingContext, 154 mMessageManager, mChildMessageManager, 155 mRemoteBrowser, mSessionStoreChild) 156 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFrameLoader) 157 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFrameLoader) 158 159 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFrameLoader) 160 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY 161 NS_INTERFACE_MAP_ENTRY_CONCRETE(nsFrameLoader) 162 NS_INTERFACE_MAP_ENTRY(nsIMutationObserver) 163 NS_INTERFACE_MAP_ENTRY(nsISupports) 164 NS_INTERFACE_MAP_END 165 166 nsFrameLoader::nsFrameLoader(Element* aOwner, BrowsingContext* aBrowsingContext, 167 bool aIsRemoteFrame, bool aNetworkCreated) 168 : mPendingBrowsingContext(aBrowsingContext), 169 mOwnerContent(aOwner), 170 mPendingSwitchID(0), 171 mChildID(0), 172 mRemoteType(NOT_REMOTE_TYPE), 173 mInitialized(false), 174 mDepthTooGreat(false), 175 mIsTopLevelContent(false), 176 mDestroyCalled(false), 177 mNeedsAsyncDestroy(false), 178 mInSwap(false), 179 mInShow(false), 180 mHideCalled(false), 181 mNetworkCreated(aNetworkCreated), 182 mLoadingOriginalSrc(false), 183 mShouldCheckForRecursion(false), 184 mRemoteBrowserShown(false), 185 mRemoteBrowserSized(false), 186 mIsRemoteFrame(aIsRemoteFrame), 187 mWillChangeProcess(false), 188 mObservingOwnerContent(false), 189 mHadDetachedFrame(false), 190 mTabProcessCrashFired(false) { 191 nsCOMPtr<nsFrameLoaderOwner> owner = do_QueryInterface(aOwner); 192 owner->AttachFrameLoader(this); 193 } 194 195 nsFrameLoader::~nsFrameLoader() { 196 if (mMessageManager) { 197 mMessageManager->Disconnect(); 198 } 199 200 MOZ_ASSERT(!mOwnerContent); 201 MOZ_RELEASE_ASSERT(mDestroyCalled); 202 } 203 204 static nsAtom* TypeAttrName(Element* aOwnerContent) { 205 return aOwnerContent->IsXULElement() ? nsGkAtoms::type 206 : nsGkAtoms::mozframetype; 207 } 208 209 static void GetFrameName(Element* aOwnerContent, nsAString& aFrameName) { 210 int32_t namespaceID = aOwnerContent->GetNameSpaceID(); 211 if (namespaceID == kNameSpaceID_XHTML && !aOwnerContent->IsInHTMLDocument()) { 212 aOwnerContent->GetAttr(nsGkAtoms::id, aFrameName); 213 } else { 214 aOwnerContent->GetAttr(nsGkAtoms::name, aFrameName); 215 // XXX if no NAME then use ID, after a transition period this will be 216 // changed so that XUL only uses ID too (bug 254284). 217 if (aFrameName.IsEmpty() && namespaceID == kNameSpaceID_XUL) { 218 aOwnerContent->GetAttr(nsGkAtoms::id, aFrameName); 219 } 220 } 221 } 222 223 // If this method returns true, the nsFrameLoader will act as a boundary, as is 224 // the case for <iframe mozbrowser> and <browser type="content"> elements. 225 // 226 // # Historical Notes (10 April 2019) 227 // 228 // In the past, this boundary was defined by the "typeContent" and "typeChrome" 229 // nsIDocShellTreeItem types. There was only ever a single split in the tree, 230 // and it occurred at the boundary between these two types of docshells. When 231 // <iframe mozbrowser> was introduced, it was given special casing to make it 232 // act like a second boundary, without having to change the existing code. 233 // 234 // The about:addons page, which is loaded within a content browser, then added a 235 // remote <browser type="content" remote="true"> element. When remote, this 236 // would also act as a mechanism for creating a disjoint tree, due to the 237 // process keeping the embedder and embedee separate. 238 // 239 // However, when initial out-of-process iframe support was implemented, this 240 // codepath became a risk, as it could've caused the oop iframe remote 241 // WindowProxy code to be activated for the addons page. This was fixed by 242 // extendng the isolation logic previously reserved to <iframe mozbrowser> to 243 // also cover <browser> elements with the explicit `remote` property loaded in 244 // content. 245 // 246 // To keep these boundaries clear, and allow them to work in a cross-process 247 // manner, they are no longer handled by typeContent and typeChrome. Instead, 248 // the actual BrowsingContext tree is broken at these edges. 249 static bool IsTopContent(BrowsingContext* aParent, Element* aOwner) { 250 if (XRE_IsContentProcess()) { 251 return false; 252 } 253 254 if (aParent->IsContent()) { 255 // If we're already in content, we may still want to create a new 256 // BrowsingContext tree if our element is a xul browser element with a 257 // `remote="true"` marker. 258 return aOwner->IsXULElement() && 259 aOwner->AttrValueIs(kNameSpaceID_None, nsGkAtoms::remote, 260 nsGkAtoms::_true, eCaseMatters); 261 } 262 263 // If we're in a chrome context, we want to start a new tree if we are an 264 // element with a `type="content"` marker. 265 return aOwner->AttrValueIs(kNameSpaceID_None, TypeAttrName(aOwner), 266 nsGkAtoms::content, eIgnoreCase); 267 } 268 269 static already_AddRefed<BrowsingContext> CreateBrowsingContext( 270 Element* aOwner, nsIOpenWindowInfo* aOpenWindowInfo, 271 BrowsingContextGroup* aSpecificGroup, bool aNetworkCreated = false) { 272 MOZ_ASSERT(!aOpenWindowInfo || !aSpecificGroup, 273 "Only one of SpecificGroup and OpenWindowInfo may be provided!"); 274 275 // If we've got a pending BrowserParent from the content process, use the 276 // BrowsingContext which was created for it. 277 if (aOpenWindowInfo && aOpenWindowInfo->GetNextRemoteBrowser()) { 278 MOZ_ASSERT(XRE_IsParentProcess()); 279 return do_AddRef( 280 aOpenWindowInfo->GetNextRemoteBrowser()->GetBrowsingContext()); 281 } 282 283 RefPtr<BrowsingContext> opener; 284 if (aOpenWindowInfo && !aOpenWindowInfo->GetForceNoOpener()) { 285 opener = aOpenWindowInfo->GetParent(); 286 if (opener) { 287 // Must create BrowsingContext with opener in-process. 288 MOZ_ASSERT(opener->IsInProcess()); 289 290 // This can only happen when the opener was closed from a nested event 291 // loop in the window provider code, and only when the open was triggered 292 // by a non-e10s tab, and the new tab is being opened in a new browser 293 // window. Since it is a corner case among corner cases, and the opener 294 // window will appear to be null to consumers after it is discarded 295 // anyway, just drop the opener entirely. 296 if (opener->IsDiscarded()) { 297 NS_WARNING( 298 "Opener was closed from a nested event loop in the parent process. " 299 "Please fix this."); 300 opener = nullptr; 301 } 302 } 303 } 304 305 RefPtr<nsGlobalWindowInner> parentInner = 306 nsGlobalWindowInner::Cast(aOwner->OwnerDoc()->GetInnerWindow()); 307 if (NS_WARN_IF(!parentInner) || parentInner->IsDying()) { 308 return nullptr; 309 } 310 311 BrowsingContext* parentBC = parentInner->GetBrowsingContext(); 312 if (NS_WARN_IF(!parentBC) || parentBC->IsDiscarded()) { 313 return nullptr; 314 } 315 316 // Determine the frame name for the new browsing context. 317 nsAutoString frameName; 318 GetFrameName(aOwner, frameName); 319 320 // Create our BrowsingContext without immediately attaching it. It's possible 321 // that no DocShell or remote browser will ever be created for this 322 // FrameLoader, particularly if the document that we were created for is not 323 // currently active. And in that latter case, if we try to attach our BC now, 324 // it will wind up attached as a child of the currently active inner window 325 // for the BrowsingContext, and cause no end of trouble. 326 if (IsTopContent(parentBC, aOwner)) { 327 BrowsingContext::CreateDetachedOptions options; 328 if (aOpenWindowInfo) { 329 options.topLevelCreatedByWebContent = 330 aOpenWindowInfo->GetIsTopLevelCreatedByWebContent(); 331 } 332 options.windowless = parentBC->Windowless(); 333 334 // Create toplevel context without a parent & as Type::Content. 335 return BrowsingContext::CreateDetached( 336 nullptr, opener, aSpecificGroup, frameName, 337 BrowsingContext::Type::Content, options); 338 } 339 340 MOZ_ASSERT(!aOpenWindowInfo, 341 "Can't have openWindowInfo for non-toplevel context"); 342 343 MOZ_ASSERT(!aSpecificGroup, 344 "Can't force BrowsingContextGroup for non-toplevel context"); 345 return BrowsingContext::CreateDetached( 346 parentInner, nullptr, nullptr, frameName, parentBC->GetType(), 347 {.createdDynamically = !aNetworkCreated, 348 .windowless = parentBC->Windowless()}); 349 } 350 351 static bool InitialLoadIsRemote(Element* aOwner) { 352 // The initial load in an content process iframe should never be made remote. 353 // Content process iframes always become remote due to navigation. 354 if (XRE_IsContentProcess()) { 355 return false; 356 } 357 358 // Otherwise, we're remote if we have "remote=true" and we're a XUL element. 359 return (aOwner->GetNameSpaceID() == kNameSpaceID_XUL) && 360 aOwner->AttrValueIs(kNameSpaceID_None, nsGkAtoms::remote, 361 nsGkAtoms::_true, eCaseMatters); 362 } 363 364 static already_AddRefed<BrowsingContextGroup> InitialBrowsingContextGroup( 365 Element* aOwner) { 366 nsAutoString attrString; 367 if (aOwner->GetNameSpaceID() != kNameSpaceID_XUL || 368 !aOwner->GetAttr(nsGkAtoms::initialBrowsingContextGroupId, attrString)) { 369 return nullptr; 370 } 371 372 // It's OK to read the attribute using a signed 64-bit integer parse, as an ID 373 // generated using `nsContentUtils::GenerateProcessSpecificId` (like BCG IDs) 374 // will only ever use 53 bits of precision, so it can be round-tripped through 375 // a JS number. 376 nsresult rv = NS_OK; 377 int64_t signedGroupId = attrString.ToInteger64(&rv, 10); 378 if (NS_FAILED(rv) || signedGroupId <= 0) { 379 MOZ_DIAGNOSTIC_ASSERT( 380 false, "we intended to have a particular id, but failed to parse it!"); 381 return nullptr; 382 } 383 384 return BrowsingContextGroup::GetOrCreate(uint64_t(signedGroupId)); 385 } 386 387 already_AddRefed<nsFrameLoader> nsFrameLoader::Create( 388 Element* aOwner, bool aNetworkCreated, nsIOpenWindowInfo* aOpenWindowInfo) { 389 NS_ENSURE_TRUE(aOwner, nullptr); 390 Document* doc = aOwner->OwnerDoc(); 391 392 // We never create nsFrameLoaders for elements in resource documents. 393 // 394 // We never create nsFrameLoaders for elements in data documents, unless the 395 // document is a static document. 396 // Static documents are an exception because any sub-documents need an 397 // nsFrameLoader to keep the relevant docShell alive, even though the 398 // nsFrameLoader isn't used to load anything (the sub-document is created by 399 // the static clone process). 400 // 401 // We never create nsFrameLoaders for elements that are not 402 // in-composed-document, unless the element belongs to a static document. 403 // Static documents are an exception because this method is called at a point 404 // in the static clone process before aOwner has been inserted into its 405 // document. For other types of documents this wouldn't be a problem since 406 // we'd create the nsFrameLoader as necessary after aOwner is inserted into a 407 // document, but the mechanisms that take care of that don't apply for static 408 // documents so we need to create the nsFrameLoader now. (This isn't wasteful 409 // since for a static document we know aOwner will end up in a document and 410 // the nsFrameLoader will be used for its docShell.) 411 // 412 NS_ENSURE_TRUE(!doc->IsResourceDoc() && 413 ((!doc->IsLoadedAsData() && aOwner->IsInComposedDoc()) || 414 doc->IsStaticDocument()), 415 nullptr); 416 417 RefPtr<BrowsingContextGroup> group = InitialBrowsingContextGroup(aOwner); 418 RefPtr<BrowsingContext> context = 419 CreateBrowsingContext(aOwner, aOpenWindowInfo, group, aNetworkCreated); 420 NS_ENSURE_TRUE(context, nullptr); 421 422 if (XRE_IsParentProcess() && aOpenWindowInfo) { 423 MOZ_ASSERT(context->IsTopContent()); 424 if (RefPtr<BrowsingContext> crossGroupOpener = 425 aOpenWindowInfo->GetParent()) { 426 context->Canonical()->SetCrossGroupOpenerId(crossGroupOpener->Id()); 427 } 428 } 429 430 bool isRemoteFrame = InitialLoadIsRemote(aOwner); 431 RefPtr<nsFrameLoader> fl = 432 new nsFrameLoader(aOwner, context, isRemoteFrame, aNetworkCreated); 433 fl->mOpenWindowInfo = aOpenWindowInfo; 434 435 // If this is a toplevel initial remote frame, we're looking at a browser 436 // loaded in the parent process. Pull the remote type attribute off of the 437 // <browser> element to determine which remote type it should be loaded in, or 438 // use `DEFAULT_REMOTE_TYPE` if we can't tell. 439 if (isRemoteFrame) { 440 MOZ_ASSERT(XRE_IsParentProcess()); 441 nsAutoString remoteType; 442 if (aOwner->GetAttr(nsGkAtoms::RemoteType, remoteType) && 443 !remoteType.IsEmpty()) { 444 CopyUTF16toUTF8(remoteType, fl->mRemoteType); 445 } else { 446 fl->mRemoteType = DEFAULT_REMOTE_TYPE; 447 } 448 } 449 return fl.forget(); 450 } 451 452 /* static */ 453 already_AddRefed<nsFrameLoader> nsFrameLoader::Recreate( 454 mozilla::dom::Element* aOwner, BrowsingContext* aContext, 455 BrowsingContextGroup* aSpecificGroup, 456 const NavigationIsolationOptions& aRemotenessOptions, bool aIsRemote, 457 bool aNetworkCreated, bool aPreserveContext) { 458 NS_ENSURE_TRUE(aOwner, nullptr); 459 460 #ifdef DEBUG 461 // This version of Create is only called for Remoteness updates, so we can 462 // assume we need a FrameLoader here and skip the check in the other Create. 463 Document* doc = aOwner->OwnerDoc(); 464 MOZ_ASSERT(!doc->IsResourceDoc()); 465 MOZ_ASSERT((!doc->IsLoadedAsData() && aOwner->IsInComposedDoc()) || 466 doc->IsStaticDocument()); 467 #endif 468 469 RefPtr<BrowsingContext> context = aContext; 470 if (!context || !aPreserveContext) { 471 context = CreateBrowsingContext(aOwner, /* openWindowInfo */ nullptr, 472 aSpecificGroup); 473 if (aContext) { 474 MOZ_ASSERT( 475 XRE_IsParentProcess(), 476 "Recreating browing contexts only supported in the parent process"); 477 aContext->Canonical()->SynchronizeLayoutHistoryState(); 478 aContext->Canonical()->ReplacedBy(context->Canonical(), 479 aRemotenessOptions); 480 } 481 } 482 NS_ENSURE_TRUE(context, nullptr); 483 484 RefPtr<nsFrameLoader> fl = 485 new nsFrameLoader(aOwner, context, aIsRemote, aNetworkCreated); 486 return fl.forget(); 487 } 488 489 void nsFrameLoader::LoadFrame(bool aOriginalSrc, 490 bool aShouldCheckForRecursion) { 491 if (NS_WARN_IF(!mOwnerContent)) { 492 return; 493 } 494 495 nsAutoString src; 496 nsCOMPtr<nsIPrincipal> principal; 497 nsCOMPtr<nsIPolicyContainer> policyContainer; 498 499 bool isSrcdoc = mOwnerContent->IsHTMLElement(nsGkAtoms::iframe) && 500 mOwnerContent->HasAttr(nsGkAtoms::srcdoc); 501 if (isSrcdoc) { 502 src.AssignLiteral("about:srcdoc"); 503 principal = mOwnerContent->NodePrincipal(); 504 policyContainer = mOwnerContent->GetPolicyContainer(); 505 } else { 506 GetURL(src, getter_AddRefs(principal), getter_AddRefs(policyContainer)); 507 508 src.Trim(" \t\n\r"); 509 510 if (src.IsEmpty()) { 511 // If the frame is a XUL element and has the attribute 'nodefaultsrc=true' 512 // then we will not use 'about:blank' as fallback but return early without 513 // starting a load if no 'src' attribute is given (or it's empty). 514 if (mOwnerContent->IsXULElement() && 515 mOwnerContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::nodefaultsrc, 516 nsGkAtoms::_true, eCaseMatters)) { 517 return; 518 } 519 src.AssignLiteral("about:blank"); 520 principal = mOwnerContent->NodePrincipal(); 521 policyContainer = mOwnerContent->GetPolicyContainer(); 522 } 523 } 524 525 Document* doc = mOwnerContent->OwnerDoc(); 526 if (doc->IsStaticDocument()) { 527 return; 528 } 529 530 // If we are being loaded by a lazy loaded iframe, use its base URI first 531 // instead of the current base URI. 532 auto* lazyBaseURI = GetLazyLoadFrameResumptionState().mBaseURI.get(); 533 nsIURI* baseURI = lazyBaseURI ? lazyBaseURI : mOwnerContent->GetBaseURI(); 534 535 auto encoding = doc->GetDocumentCharacterSet(); 536 537 nsCOMPtr<nsIURI> uri; 538 nsresult rv = NS_NewURI(getter_AddRefs(uri), src, encoding, baseURI); 539 540 // If the URI was malformed, try to recover by loading about:blank. 541 if (rv == NS_ERROR_MALFORMED_URI) { 542 rv = NS_NewURI(getter_AddRefs(uri), u"about:blank"_ns, encoding, baseURI); 543 } 544 545 if (NS_SUCCEEDED(rv)) { 546 rv = LoadURI(uri, principal, policyContainer, aOriginalSrc, 547 aShouldCheckForRecursion); 548 } 549 550 if (NS_FAILED(rv)) { 551 FireErrorEvent(); 552 } 553 } 554 555 void nsFrameLoader::ConfigRemoteProcess(const nsACString& aRemoteType, 556 ContentParent* aContentParent) { 557 MOZ_DIAGNOSTIC_ASSERT(IsRemoteFrame(), "Must be a remote frame"); 558 MOZ_DIAGNOSTIC_ASSERT(!mRemoteBrowser, "Must not have a browser yet"); 559 MOZ_DIAGNOSTIC_ASSERT_IF(aContentParent, 560 aContentParent->GetRemoteType() == aRemoteType); 561 562 mRemoteType = aRemoteType; 563 mChildID = aContentParent ? aContentParent->ChildID() : 0; 564 } 565 566 void nsFrameLoader::FireErrorEvent() { 567 if (!mOwnerContent) { 568 return; 569 } 570 RefPtr<AsyncEventDispatcher> loadBlockingAsyncDispatcher = 571 new LoadBlockingAsyncEventDispatcher( 572 mOwnerContent, u"error"_ns, CanBubble::eNo, ChromeOnlyDispatch::eNo); 573 loadBlockingAsyncDispatcher->PostDOMEvent(); 574 } 575 576 nsresult nsFrameLoader::LoadURI(nsIURI* aURI, 577 nsIPrincipal* aTriggeringPrincipal, 578 nsIPolicyContainer* aPolicyContainer, 579 bool aOriginalSrc, 580 bool aShouldCheckForRecursion) { 581 if (!aURI) return NS_ERROR_INVALID_POINTER; 582 NS_ENSURE_STATE(!mDestroyCalled && mOwnerContent); 583 MOZ_ASSERT( 584 aTriggeringPrincipal, 585 "Must have an explicit triggeringPrincipal to nsFrameLoader::LoadURI."); 586 587 mLoadingOriginalSrc = aOriginalSrc; 588 mShouldCheckForRecursion = aShouldCheckForRecursion; 589 590 nsCOMPtr<Document> doc = mOwnerContent->OwnerDoc(); 591 592 nsresult rv; 593 rv = CheckURILoad(aURI, aTriggeringPrincipal); 594 NS_ENSURE_SUCCESS(rv, rv); 595 596 mURIToLoad = aURI; 597 mTriggeringPrincipal = aTriggeringPrincipal; 598 mPolicyContainer = aPolicyContainer; 599 rv = doc->InitializeFrameLoader(this); 600 if (NS_FAILED(rv)) { 601 mURIToLoad = nullptr; 602 mTriggeringPrincipal = nullptr; 603 mPolicyContainer = nullptr; 604 } 605 return rv; 606 } 607 608 void nsFrameLoader::ResumeLoad(uint64_t aPendingSwitchID) { 609 Document* doc = mOwnerContent->OwnerDoc(); 610 if (doc->IsStaticDocument()) { 611 // Static doc shouldn't load sub-documents. 612 return; 613 } 614 615 if (NS_WARN_IF(mDestroyCalled || !mOwnerContent)) { 616 FireErrorEvent(); 617 return; 618 } 619 620 mLoadingOriginalSrc = false; 621 mShouldCheckForRecursion = false; 622 mURIToLoad = nullptr; 623 mPendingSwitchID = aPendingSwitchID; 624 mTriggeringPrincipal = mOwnerContent->NodePrincipal(); 625 mPolicyContainer = mOwnerContent->GetPolicyContainer(); 626 627 nsresult rv = doc->InitializeFrameLoader(this); 628 if (NS_FAILED(rv)) { 629 mPendingSwitchID = 0; 630 mTriggeringPrincipal = nullptr; 631 mPolicyContainer = nullptr; 632 633 FireErrorEvent(); 634 } 635 } 636 637 nsresult nsFrameLoader::ReallyStartLoading() { 638 nsresult rv = ReallyStartLoadingInternal(); 639 if (NS_FAILED(rv)) { 640 FireErrorEvent(); 641 } 642 643 return rv; 644 } 645 646 nsresult nsFrameLoader::ReallyStartLoadingInternal() { 647 NS_ENSURE_STATE((mURIToLoad || mPendingSwitchID) && mOwnerContent && 648 mOwnerContent->IsInComposedDoc()); 649 650 AUTO_PROFILER_LABEL("nsFrameLoader::ReallyStartLoadingInternal", OTHER); 651 RefPtr<nsDocShellLoadState> loadState; 652 if (!mPendingSwitchID) { 653 loadState = new nsDocShellLoadState(mURIToLoad); 654 loadState->SetOriginalFrameSrc(mLoadingOriginalSrc); 655 loadState->SetShouldCheckForRecursion(mShouldCheckForRecursion); 656 657 // The triggering principal could be null if the frame is loaded other 658 // than the src attribute, for example, the frame is sandboxed. In that 659 // case we use the principal of the owner content, which is needed to 660 // prevent XSS attaches on documents loaded in subframes. 661 if (mTriggeringPrincipal) { 662 loadState->SetTriggeringPrincipal(mTriggeringPrincipal); 663 } else { 664 loadState->SetTriggeringPrincipal(mOwnerContent->NodePrincipal()); 665 } 666 667 // If we have an explicit policyContainer, we set it. If not, we only query 668 // it from the document in case there was no explicit triggeringPrincipal. 669 // Otherwise it's possible that the original triggeringPrincipal did not 670 // have a policyContainer which causes the policyContainer on the Principal 671 // and explicit policyContainer to be out of sync. 672 if (mPolicyContainer) { 673 loadState->SetPolicyContainer(mPolicyContainer); 674 } else if (!mTriggeringPrincipal) { 675 nsCOMPtr<nsIPolicyContainer> policyContainer = 676 mOwnerContent->GetPolicyContainer(); 677 loadState->SetPolicyContainer(policyContainer); 678 } 679 680 nsAutoString srcdoc; 681 bool isSrcdoc = mOwnerContent->IsHTMLElement(nsGkAtoms::iframe) && 682 mOwnerContent->GetAttr(nsGkAtoms::srcdoc, srcdoc); 683 684 if (isSrcdoc) { 685 loadState->SetSrcdocData(srcdoc); 686 loadState->SetBaseURI(mOwnerContent->GetBaseURI()); 687 } 688 689 auto referrerInfo = MakeRefPtr<ReferrerInfo>( 690 *mOwnerContent, GetLazyLoadFrameResumptionState().mReferrerPolicy); 691 loadState->SetReferrerInfo(referrerInfo); 692 693 loadState->SetIsFromProcessingFrameAttributes(); 694 695 // Default flags: 696 int32_t flags = nsIWebNavigation::LOAD_FLAGS_NONE; 697 loadState->SetLoadFlags(flags); 698 699 loadState->SetFirstParty(false); 700 701 Document* ownerDoc = mOwnerContent->OwnerDoc(); 702 if (ownerDoc) { 703 loadState->SetTriggeringStorageAccess(ownerDoc->UsingStorageAccess()); 704 loadState->SetTriggeringWindowId(ownerDoc->InnerWindowID()); 705 loadState->SetTriggeringClassificationFlags( 706 ownerDoc->GetScriptTrackingFlags()); 707 } 708 709 // If we're loading the default about:blank document in a <browser> element, 710 // prevent the load from causing a process switch by explicitly overriding 711 // remote type selection. 712 if (mPendingBrowsingContext->IsTopContent() && 713 mOwnerContent->IsXULElement(nsGkAtoms::browser) && 714 NS_IsAboutBlank(mURIToLoad) && 715 loadState->TriggeringPrincipal()->IsSystemPrincipal()) { 716 loadState->SetRemoteTypeOverride(mRemoteType); 717 } 718 } 719 720 if (IsRemoteFrame()) { 721 if (!EnsureRemoteBrowser()) { 722 NS_WARNING("Couldn't create child process for iframe."); 723 return NS_ERROR_FAILURE; 724 } 725 726 if (mPendingSwitchID) { 727 mRemoteBrowser->ResumeLoad(mPendingSwitchID); 728 mPendingSwitchID = 0; 729 } else { 730 mRemoteBrowser->LoadURL(loadState); 731 } 732 733 if (!mRemoteBrowserShown) { 734 // This can fail if it's too early to show the frame, we will retry later. 735 (void)ShowRemoteFrame( 736 /* aFrame = */ do_QueryFrame(GetPrimaryFrameOfOwningContent())); 737 } 738 739 return NS_OK; 740 } 741 742 nsresult rv = MaybeCreateDocShell(); 743 if (NS_FAILED(rv)) { 744 return rv; 745 } 746 MOZ_ASSERT(GetDocShell(), 747 "MaybeCreateDocShell succeeded with a null docShell"); 748 749 // If we have a pending switch, just resume our load. 750 if (mPendingSwitchID) { 751 bool tmpState = mNeedsAsyncDestroy; 752 mNeedsAsyncDestroy = true; 753 rv = GetDocShell()->ResumeRedirectedLoad(mPendingSwitchID, -1); 754 mNeedsAsyncDestroy = tmpState; 755 mPendingSwitchID = 0; 756 return rv; 757 } 758 759 // Just to be safe, recheck uri. 760 rv = CheckURILoad(mURIToLoad, mTriggeringPrincipal); 761 NS_ENSURE_SUCCESS(rv, rv); 762 763 mLoadingOriginalSrc = false; 764 mShouldCheckForRecursion = false; 765 766 // Kick off the load... 767 bool tmpState = mNeedsAsyncDestroy; 768 // Sync destroy should be possible from the sync about:blank load event 769 mNeedsAsyncDestroy = !NS_IsAboutBlankAllowQueryAndFragment(mURIToLoad); 770 771 RefPtr<nsDocShell> docShell = GetDocShell(); 772 rv = docShell->LoadURI(loadState, false); 773 mNeedsAsyncDestroy = tmpState; 774 mURIToLoad = nullptr; 775 NS_ENSURE_SUCCESS(rv, rv); 776 777 return NS_OK; 778 } 779 780 nsresult nsFrameLoader::CheckURILoad(nsIURI* aURI, 781 nsIPrincipal* aTriggeringPrincipal) { 782 // Check for security. The fun part is trying to figure out what principals 783 // to use. The way I figure it, if we're doing a LoadFrame() accidentally 784 // (eg someone created a frame/iframe node, we're being parsed, XUL iframes 785 // are being reframed, etc.) then we definitely want to use the node 786 // principal of mOwnerContent for security checks. If, on the other hand, 787 // someone's setting the src on our owner content, or created it via script, 788 // or whatever, then they can clearly access it... and we should still use 789 // the principal of mOwnerContent. I don't think that leads to privilege 790 // escalation, and it's reasonably guaranteed to not lead to XSS issues 791 // (since caller can already access mOwnerContent in this case). So just use 792 // the principal of mOwnerContent no matter what. If script wants to run 793 // things with its own permissions, which differ from those of mOwnerContent 794 // (which means the script is privileged in some way) it should set 795 // window.location instead. 796 nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager(); 797 798 // Get our principal 799 nsIPrincipal* principal = 800 (aTriggeringPrincipal ? aTriggeringPrincipal 801 : mOwnerContent->NodePrincipal()); 802 803 // Check if we are allowed to load absURL 804 nsresult rv = secMan->CheckLoadURIWithPrincipal( 805 principal, aURI, nsIScriptSecurityManager::STANDARD, 806 mOwnerContent->OwnerDoc()->InnerWindowID()); 807 if (NS_FAILED(rv)) { 808 return rv; // We're not 809 } 810 811 // Bail out if this is an infinite recursion scenario 812 if (IsRemoteFrame()) { 813 return NS_OK; 814 } 815 return CheckForRecursiveLoad(aURI); 816 } 817 818 nsDocShell* nsFrameLoader::GetDocShell(ErrorResult& aRv) { 819 if (IsRemoteFrame()) { 820 return nullptr; 821 } 822 823 // If we have an owner, make sure we have a docshell and return 824 // that. If not, we're most likely in the middle of being torn down, 825 // then we just return null. 826 if (mOwnerContent) { 827 nsresult rv = MaybeCreateDocShell(); 828 if (NS_FAILED(rv)) { 829 aRv.Throw(rv); 830 return nullptr; 831 } 832 MOZ_ASSERT(GetDocShell(), 833 "MaybeCreateDocShell succeeded, but null docShell"); 834 } 835 836 return GetDocShell(); 837 } 838 839 static void SetTreeOwnerAndChromeEventHandlerOnDocshellTree( 840 nsIDocShellTreeItem* aItem, nsIDocShellTreeOwner* aOwner, 841 EventTarget* aHandler) { 842 MOZ_ASSERT(aItem, "Must have item"); 843 844 aItem->SetTreeOwner(aOwner); 845 846 int32_t childCount = 0; 847 aItem->GetInProcessChildCount(&childCount); 848 for (int32_t i = 0; i < childCount; ++i) { 849 nsCOMPtr<nsIDocShellTreeItem> item; 850 aItem->GetInProcessChildAt(i, getter_AddRefs(item)); 851 if (aHandler) { 852 nsCOMPtr<nsIDocShell> shell(do_QueryInterface(item)); 853 shell->SetChromeEventHandler(aHandler); 854 } 855 SetTreeOwnerAndChromeEventHandlerOnDocshellTree(item, aOwner, aHandler); 856 } 857 } 858 859 #if defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED) 860 static bool CheckDocShellType(mozilla::dom::Element* aOwnerContent, 861 nsIDocShellTreeItem* aDocShell, nsAtom* aAtom) { 862 bool isContent = aOwnerContent->AttrValueIs(kNameSpaceID_None, aAtom, 863 nsGkAtoms::content, eIgnoreCase); 864 865 if (isContent) { 866 return aDocShell->ItemType() == nsIDocShellTreeItem::typeContent; 867 } 868 869 nsCOMPtr<nsIDocShellTreeItem> parent; 870 aDocShell->GetInProcessParent(getter_AddRefs(parent)); 871 872 return parent && parent->ItemType() == aDocShell->ItemType(); 873 } 874 #endif // defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED) 875 876 /** 877 * Hook up a given TreeItem to its tree owner. aItem's type must have already 878 * been set, and it should already be part of the DocShellTree. 879 * @param aItem the treeitem we're working with 880 * @param aTreeOwner the relevant treeowner; might be null 881 */ 882 void nsFrameLoader::AddTreeItemToTreeOwner(nsIDocShellTreeItem* aItem, 883 nsIDocShellTreeOwner* aOwner) { 884 MOZ_ASSERT(aItem, "Must have docshell treeitem"); 885 MOZ_ASSERT(mOwnerContent, "Must have owning content"); 886 887 MOZ_DIAGNOSTIC_ASSERT( 888 CheckDocShellType(mOwnerContent, aItem, TypeAttrName(mOwnerContent)), 889 "Correct ItemType should be set when creating BrowsingContext"); 890 891 if (mIsTopLevelContent) { 892 bool is_primary = mOwnerContent->AttrValueIs( 893 kNameSpaceID_None, nsGkAtoms::primary, nsGkAtoms::_true, eIgnoreCase); 894 if (aOwner) { 895 mOwnerContent->AddMutationObserver(this); 896 mObservingOwnerContent = true; 897 aOwner->ContentShellAdded(aItem, is_primary); 898 } 899 } 900 } 901 902 static bool AllDescendantsOfType(BrowsingContext* aParent, 903 BrowsingContext::Type aType) { 904 for (auto& child : aParent->Children()) { 905 if (child->GetType() != aType || !AllDescendantsOfType(child, aType)) { 906 return false; 907 } 908 } 909 910 return true; 911 } 912 913 void nsFrameLoader::MaybeShowFrame() { 914 nsIFrame* frame = GetPrimaryFrameOfOwningContent(); 915 if (frame) { 916 nsSubDocumentFrame* subDocFrame = do_QueryFrame(frame); 917 if (subDocFrame) { 918 subDocFrame->MaybeShowViewer(); 919 } 920 } 921 } 922 923 static ScrollbarPreference GetScrollbarPreference(const Element* aOwner) { 924 if (!aOwner) { 925 return ScrollbarPreference::Auto; 926 } 927 const nsAttrValue* attrValue = aOwner->GetParsedAttr(nsGkAtoms::scrolling); 928 return nsGenericHTMLFrameElement::MapScrollingAttribute(attrValue); 929 } 930 931 static CSSIntSize GetMarginAttributes(const Element* aOwner) { 932 CSSIntSize result(-1, -1); 933 auto* content = nsGenericHTMLElement::FromNodeOrNull(aOwner); 934 if (!content) { 935 return result; 936 } 937 const nsAttrValue* attr = content->GetParsedAttr(nsGkAtoms::marginwidth); 938 if (attr && attr->Type() == nsAttrValue::eInteger) { 939 result.width = attr->GetIntegerValue(); 940 } 941 attr = content->GetParsedAttr(nsGkAtoms::marginheight); 942 if (attr && attr->Type() == nsAttrValue::eInteger) { 943 result.height = attr->GetIntegerValue(); 944 } 945 return result; 946 } 947 948 bool nsFrameLoader::Show(nsSubDocumentFrame* aFrame) { 949 if (mInShow) { 950 return false; 951 } 952 mInShow = true; 953 954 auto resetInShow = mozilla::MakeScopeExit([&] { mInShow = false; }); 955 if (IsRemoteFrame()) { 956 return ShowRemoteFrame(aFrame); 957 } 958 const LayoutDeviceIntSize size = aFrame->GetInitialSubdocumentSize(); 959 960 nsresult rv = MaybeCreateDocShell(); 961 if (NS_FAILED(rv)) { 962 return false; 963 } 964 nsDocShell* ds = GetDocShell(); 965 MOZ_ASSERT(ds, "MaybeCreateDocShell succeeded, but null docShell"); 966 if (!ds) { 967 return false; 968 } 969 970 ds->SetScrollbarPreference(GetScrollbarPreference(mOwnerContent)); 971 const bool marginsChanged = 972 ds->UpdateFrameMargins(GetMarginAttributes(mOwnerContent)); 973 974 // If we already have a pres shell (which can happen with <object> / <embed>) 975 // then hook it up to the frame. 976 if (PresShell* presShell = ds->GetPresShell()) { 977 // Ensure root scroll frame is reflowed in case margins have changed. 978 if (marginsChanged) { 979 if (nsIFrame* rootScrollContainerFrame = 980 presShell->GetRootScrollContainerFrame()) { 981 presShell->FrameNeedsReflow(rootScrollContainerFrame, 982 IntrinsicDirty::None, NS_FRAME_IS_DIRTY); 983 } 984 } 985 aFrame->EnsureEmbeddingPresShell(presShell); 986 } 987 988 RefPtr<nsDocShell> baseWindow = GetDocShell(); 989 MOZ_ASSERT(ds == baseWindow, "How did the docshell change?"); 990 baseWindow->InitWindow(nullptr, 0, 0, size.width, size.height, nullptr, 991 nullptr); 992 baseWindow->SetVisibility(true); 993 NS_ENSURE_TRUE(GetDocShell(), false); 994 995 // Trigger editor re-initialization if midas is turned on in the sub-document. 996 // This shouldn't be necessary, but given the way our editor works, it is. 997 // 998 // See https://bugzilla.mozilla.org/show_bug.cgi?id=284245 999 // 1000 // FIXME(emilio): This is a massive hack, plus probably not the right place to 1001 // do it. Should probably be done in Document::CreatePresShell? 1002 if (RefPtr<PresShell> presShell = GetDocShell()->GetPresShell()) { 1003 Document* doc = presShell->GetDocument(); 1004 nsHTMLDocument* htmlDoc = 1005 doc && doc->IsHTMLOrXHTML() ? doc->AsHTMLDocument() : nullptr; 1006 1007 if (htmlDoc) { 1008 nsAutoString designMode; 1009 htmlDoc->GetDesignMode(designMode); 1010 1011 if (designMode.EqualsLiteral("on")) { 1012 // Hold on to the editor object to let the document reattach to the 1013 // same editor object, instead of creating a new one. 1014 RefPtr<HTMLEditor> htmlEditor = GetDocShell()->GetHTMLEditor(); 1015 (void)htmlEditor; 1016 htmlDoc->SetDesignMode(u"off"_ns, Nothing(), IgnoreErrors()); 1017 1018 htmlDoc->SetDesignMode(u"on"_ns, Nothing(), IgnoreErrors()); 1019 } else { 1020 // Re-initialize the presentation for contenteditable documents 1021 bool editable = false, hasEditingSession = false; 1022 GetDocShell()->GetEditable(&editable); 1023 GetDocShell()->GetHasEditingSession(&hasEditingSession); 1024 RefPtr<HTMLEditor> htmlEditor = GetDocShell()->GetHTMLEditor(); 1025 if (editable && hasEditingSession && htmlEditor) { 1026 htmlEditor->PostCreate(); 1027 } 1028 } 1029 } 1030 } 1031 1032 mInShow = false; 1033 if (mHideCalled) { 1034 mHideCalled = false; 1035 Hide(); 1036 return false; 1037 } 1038 return true; 1039 } 1040 1041 void nsFrameLoader::MarginsChanged() { 1042 // We assume that the margins are always zero for remote frames. 1043 if (IsRemoteFrame()) { 1044 return; 1045 } 1046 1047 nsDocShell* docShell = GetDocShell(); 1048 // If there's no docshell, we're probably not up and running yet. 1049 // nsFrameLoader::Show() will take care of setting the right 1050 // margins. 1051 if (!docShell) { 1052 return; 1053 } 1054 1055 if (!docShell->UpdateFrameMargins(GetMarginAttributes(mOwnerContent))) { 1056 return; 1057 } 1058 1059 // There's a cached property declaration block 1060 // that needs to be updated 1061 if (Document* doc = docShell->GetDocument()) { 1062 for (nsINode* cur = doc; cur; cur = cur->GetNextNode()) { 1063 if (auto* body = HTMLBodyElement::FromNode(cur)) { 1064 body->FrameMarginsChanged(); 1065 } 1066 } 1067 } 1068 } 1069 1070 bool nsFrameLoader::ShowRemoteFrame(nsSubDocumentFrame* aFrame) { 1071 AUTO_PROFILER_LABEL("nsFrameLoader::ShowRemoteFrame", OTHER); 1072 NS_ASSERTION(IsRemoteFrame(), 1073 "ShowRemote only makes sense on remote frames."); 1074 1075 if (!EnsureRemoteBrowser()) { 1076 NS_ERROR("Couldn't create child process."); 1077 return false; 1078 } 1079 1080 const bool hasSize = 1081 aFrame && !aFrame->HasAnyStateBits(NS_FRAME_FIRST_REFLOW); 1082 1083 // FIXME/bug 589337: Show()/Hide() is pretty expensive for 1084 // cross-process layers; need to figure out what behavior we really 1085 // want here. For now, hack. 1086 if (!mRemoteBrowserShown) { 1087 if (!mOwnerContent || !mOwnerContent->GetComposedDoc()) { 1088 return false; 1089 } 1090 1091 // We never want to host remote frameloaders in simple popups, like menus. 1092 nsIWidget* widget = nsContentUtils::WidgetForContent(mOwnerContent); 1093 if (!widget || widget->IsSmallPopup()) { 1094 return false; 1095 } 1096 1097 if (BrowserHost* bh = mRemoteBrowser->AsBrowserHost()) { 1098 RefPtr<BrowsingContext> bc = bh->GetBrowsingContext()->Top(); 1099 1100 // Set to the current activation of the window. 1101 bc->SetIsActiveBrowserWindow(bc->GetIsActiveBrowserWindow()); 1102 } 1103 1104 nsCOMPtr<nsISupports> container = mOwnerContent->OwnerDoc()->GetContainer(); 1105 nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(container); 1106 nsCOMPtr<nsIWidget> mainWidget; 1107 baseWindow->GetMainWidget(getter_AddRefs(mainWidget)); 1108 nsSizeMode sizeMode = 1109 mainWidget ? mainWidget->SizeMode() : nsSizeMode_Normal; 1110 const auto size = 1111 hasSize ? aFrame->GetSubdocumentSize() : LayoutDeviceIntSize(); 1112 OwnerShowInfo info(size, GetScrollbarPreference(mOwnerContent), sizeMode); 1113 if (!mRemoteBrowser->Show(info)) { 1114 return false; 1115 } 1116 mRemoteBrowserShown = true; 1117 mRemoteBrowserSized = hasSize; 1118 1119 // This notification doesn't apply to fission, apparently. 1120 if (!GetBrowserBridgeChild()) { 1121 if (nsCOMPtr<nsIObserverService> os = services::GetObserverService()) { 1122 os->NotifyObservers(ToSupports(this), "remote-browser-shown", nullptr); 1123 } 1124 } 1125 } else if (hasSize) { 1126 NS_ENSURE_SUCCESS(UpdatePositionAndSize(aFrame), false); 1127 } 1128 1129 return true; 1130 } 1131 1132 void nsFrameLoader::Hide() { 1133 if (mHideCalled) { 1134 return; 1135 } 1136 if (mInShow) { 1137 mHideCalled = true; 1138 return; 1139 } 1140 1141 if (mRemoteBrowser) { 1142 mRemoteBrowser->UpdateEffects(EffectsInfo::FullyHidden()); 1143 } 1144 1145 if (!GetDocShell()) { 1146 return; 1147 } 1148 1149 nsCOMPtr<nsIDocumentViewer> viewer; 1150 GetDocShell()->GetDocViewer(getter_AddRefs(viewer)); 1151 if (viewer) viewer->SetSticky(false); 1152 1153 RefPtr<nsDocShell> baseWin = GetDocShell(); 1154 baseWin->SetVisibility(false); 1155 baseWin->SetParentWidget(nullptr); 1156 } 1157 1158 void nsFrameLoader::ForceLayoutIfNecessary() { 1159 nsIFrame* frame = GetPrimaryFrameOfOwningContent(); 1160 if (!frame) { 1161 return; 1162 } 1163 1164 nsPresContext* presContext = frame->PresContext(); 1165 if (!presContext) { 1166 return; 1167 } 1168 1169 // Only force the layout flush if the frameloader hasn't ever been 1170 // run through layout. 1171 if (frame->HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) { 1172 if (RefPtr<PresShell> presShell = presContext->GetPresShell()) { 1173 presShell->FlushPendingNotifications(FlushType::Layout); 1174 } 1175 } 1176 } 1177 1178 nsresult nsFrameLoader::SwapWithOtherRemoteLoader( 1179 nsFrameLoader* aOther, nsFrameLoaderOwner* aThisOwner, 1180 nsFrameLoaderOwner* aOtherOwner) { 1181 MOZ_ASSERT(NS_IsMainThread()); 1182 1183 #ifdef DEBUG 1184 RefPtr<nsFrameLoader> first = aThisOwner->GetFrameLoader(); 1185 RefPtr<nsFrameLoader> second = aOtherOwner->GetFrameLoader(); 1186 MOZ_ASSERT(first == this, "aThisOwner must own this"); 1187 MOZ_ASSERT(second == aOther, "aOtherOwner must own aOther"); 1188 #endif 1189 1190 Element* ourContent = mOwnerContent; 1191 Element* otherContent = aOther->mOwnerContent; 1192 1193 if (!ourContent || !otherContent) { 1194 // Can't handle this 1195 return NS_ERROR_NOT_IMPLEMENTED; 1196 } 1197 1198 // Make sure there are no same-origin issues 1199 bool equal; 1200 nsresult rv = ourContent->NodePrincipal()->Equals( 1201 otherContent->NodePrincipal(), &equal); 1202 if (NS_FAILED(rv) || !equal) { 1203 // Security problems loom. Just bail on it all 1204 return NS_ERROR_DOM_SECURITY_ERR; 1205 } 1206 1207 Document* ourDoc = ourContent->GetComposedDoc(); 1208 Document* otherDoc = otherContent->GetComposedDoc(); 1209 if (!ourDoc || !otherDoc) { 1210 // Again, how odd, given that we had docshells 1211 return NS_ERROR_NOT_IMPLEMENTED; 1212 } 1213 1214 PresShell* ourPresShell = ourDoc->GetPresShell(); 1215 PresShell* otherPresShell = otherDoc->GetPresShell(); 1216 if (!ourPresShell || !otherPresShell) { 1217 return NS_ERROR_NOT_IMPLEMENTED; 1218 } 1219 1220 auto* browserParent = GetBrowserParent(); 1221 auto* otherBrowserParent = aOther->GetBrowserParent(); 1222 1223 if (!browserParent || !otherBrowserParent) { 1224 return NS_ERROR_NOT_IMPLEMENTED; 1225 } 1226 1227 RefPtr<BrowsingContext> ourBc = browserParent->GetBrowsingContext(); 1228 RefPtr<BrowsingContext> otherBc = otherBrowserParent->GetBrowsingContext(); 1229 1230 // When we swap docShells, maybe we have to deal with a new page created just 1231 // for this operation. In this case, the browser code should already have set 1232 // the correct userContextId attribute value in the owning element, but our 1233 // docShell, that has been created way before) doesn't know that that 1234 // happened. 1235 // This is the reason why now we must retrieve the correct value from the 1236 // usercontextid attribute before comparing our originAttributes with the 1237 // other one. 1238 OriginAttributes ourOriginAttributes = ourBc->OriginAttributesRef(); 1239 rv = PopulateOriginContextIdsFromAttributes(ourOriginAttributes); 1240 NS_ENSURE_SUCCESS(rv, rv); 1241 1242 OriginAttributes otherOriginAttributes = otherBc->OriginAttributesRef(); 1243 rv = aOther->PopulateOriginContextIdsFromAttributes(otherOriginAttributes); 1244 NS_ENSURE_SUCCESS(rv, rv); 1245 1246 if (!ourOriginAttributes.EqualsIgnoringFPD(otherOriginAttributes)) { 1247 return NS_ERROR_NOT_IMPLEMENTED; 1248 } 1249 1250 bool ourHasHistory = mIsTopLevelContent && 1251 ourContent->IsXULElement(nsGkAtoms::browser) && 1252 !ourContent->HasAttr(nsGkAtoms::disablehistory); 1253 bool otherHasHistory = aOther->mIsTopLevelContent && 1254 otherContent->IsXULElement(nsGkAtoms::browser) && 1255 !otherContent->HasAttr(nsGkAtoms::disablehistory); 1256 if (ourHasHistory != otherHasHistory) { 1257 return NS_ERROR_NOT_IMPLEMENTED; 1258 } 1259 1260 if (mInSwap || aOther->mInSwap) { 1261 return NS_ERROR_NOT_IMPLEMENTED; 1262 } 1263 mInSwap = aOther->mInSwap = true; 1264 1265 // NOTE(emilio): This doesn't have to flush because the caller does already. 1266 nsIFrame* ourFrame = ourContent->GetPrimaryFrame(); 1267 nsIFrame* otherFrame = otherContent->GetPrimaryFrame(); 1268 if (!ourFrame || !otherFrame) { 1269 mInSwap = aOther->mInSwap = false; 1270 return NS_ERROR_NOT_IMPLEMENTED; 1271 } 1272 1273 nsSubDocumentFrame* ourFrameFrame = do_QueryFrame(ourFrame); 1274 if (!ourFrameFrame) { 1275 mInSwap = aOther->mInSwap = false; 1276 return NS_ERROR_NOT_IMPLEMENTED; 1277 } 1278 1279 rv = ourFrameFrame->BeginSwapDocShells(otherFrame); 1280 if (NS_FAILED(rv)) { 1281 mInSwap = aOther->mInSwap = false; 1282 return rv; 1283 } 1284 1285 nsCOMPtr<nsIBrowserDOMWindow> otherBrowserDOMWindow = 1286 otherBrowserParent->GetBrowserDOMWindow(); 1287 nsCOMPtr<nsIBrowserDOMWindow> browserDOMWindow = 1288 browserParent->GetBrowserDOMWindow(); 1289 1290 if (!!otherBrowserDOMWindow != !!browserDOMWindow) { 1291 return NS_ERROR_NOT_IMPLEMENTED; 1292 } 1293 1294 otherBrowserParent->SetBrowserDOMWindow(browserDOMWindow); 1295 browserParent->SetBrowserDOMWindow(otherBrowserDOMWindow); 1296 1297 MaybeUpdatePrimaryBrowserParent(eBrowserParentRemoved); 1298 aOther->MaybeUpdatePrimaryBrowserParent(eBrowserParentRemoved); 1299 1300 if (mozilla::BFCacheInParent() && XRE_IsParentProcess()) { 1301 // nsFrameLoaders in session history can't be moved to another owner since 1302 // there are no corresponging message managers on which swap can be done. 1303 // See the line mMessageManager.swap(aOther->mMessageManager); below. 1304 auto evict = [](nsFrameLoader* aFrameLoader) { 1305 if (BrowsingContext* bc = 1306 aFrameLoader->GetMaybePendingBrowsingContext()) { 1307 nsCOMPtr<nsISHistory> shistory = bc->Canonical()->GetSessionHistory(); 1308 if (shistory) { 1309 shistory->EvictAllDocumentViewers(); 1310 } 1311 } 1312 }; 1313 evict(this); 1314 evict(aOther); 1315 } 1316 1317 SetOwnerContent(otherContent); 1318 aOther->SetOwnerContent(ourContent); 1319 1320 browserParent->SetOwnerElement(otherContent); 1321 otherBrowserParent->SetOwnerElement(ourContent); 1322 1323 // Update window activation state for the swapped owner content. 1324 bool ourActive = otherBc->GetIsActiveBrowserWindow(); 1325 bool otherActive = ourBc->GetIsActiveBrowserWindow(); 1326 if (ourBc->IsTop()) { 1327 ourBc->SetIsActiveBrowserWindow(otherActive); 1328 } 1329 if (otherBc->IsTop()) { 1330 otherBc->SetIsActiveBrowserWindow(ourActive); 1331 } 1332 1333 MaybeUpdatePrimaryBrowserParent(eBrowserParentChanged); 1334 aOther->MaybeUpdatePrimaryBrowserParent(eBrowserParentChanged); 1335 1336 RefPtr<nsFrameMessageManager> ourMessageManager = mMessageManager; 1337 RefPtr<nsFrameMessageManager> otherMessageManager = aOther->mMessageManager; 1338 // Swap and setup things in parent message managers. 1339 if (ourMessageManager) { 1340 ourMessageManager->SetCallback(aOther); 1341 } 1342 if (otherMessageManager) { 1343 otherMessageManager->SetCallback(this); 1344 } 1345 mMessageManager.swap(aOther->mMessageManager); 1346 1347 // XXXsmaug what should be done to JSWindowActorParent objects when swapping 1348 // frameloaders? Currently they leak very easily, bug 1697918. 1349 1350 // Perform the actual swap of the internal refptrs. We keep a strong reference 1351 // to ourselves to make sure we don't die while we overwrite our reference to 1352 // ourself. 1353 RefPtr<nsFrameLoader> kungFuDeathGrip(this); 1354 aThisOwner->SetFrameLoader(aOther); 1355 aOtherOwner->SetFrameLoader(kungFuDeathGrip); 1356 1357 ourFrameFrame->EndSwapDocShells(otherFrame); 1358 1359 ourPresShell->BackingScaleFactorChanged(); 1360 otherPresShell->BackingScaleFactorChanged(); 1361 1362 mInSwap = aOther->mInSwap = false; 1363 1364 // Send an updated tab context since owner content type may have changed. 1365 MutableTabContext ourContext; 1366 rv = GetNewTabContext(&ourContext); 1367 if (NS_WARN_IF(NS_FAILED(rv))) { 1368 return rv; 1369 } 1370 MutableTabContext otherContext; 1371 rv = aOther->GetNewTabContext(&otherContext); 1372 if (NS_WARN_IF(NS_FAILED(rv))) { 1373 return rv; 1374 } 1375 1376 (void)browserParent->SendSwappedWithOtherRemoteLoader( 1377 ourContext.AsIPCTabContext()); 1378 (void)otherBrowserParent->SendSwappedWithOtherRemoteLoader( 1379 otherContext.AsIPCTabContext()); 1380 // These might have moved to a new window, so make sure they have 1381 // the appropriate priority (bug 1896172) 1382 browserParent->RecomputeProcessPriority(); 1383 otherBrowserParent->RecomputeProcessPriority(); 1384 return NS_OK; 1385 } 1386 1387 class MOZ_RAII AutoResetInFrameSwap final { 1388 public: 1389 AutoResetInFrameSwap(nsFrameLoader* aThisFrameLoader, 1390 nsFrameLoader* aOtherFrameLoader, 1391 nsDocShell* aThisDocShell, nsDocShell* aOtherDocShell, 1392 EventTarget* aThisEventTarget, 1393 EventTarget* aOtherEventTarget) 1394 : mThisFrameLoader(aThisFrameLoader), 1395 mOtherFrameLoader(aOtherFrameLoader), 1396 mThisDocShell(aThisDocShell), 1397 mOtherDocShell(aOtherDocShell), 1398 mThisEventTarget(aThisEventTarget), 1399 mOtherEventTarget(aOtherEventTarget) { 1400 mThisFrameLoader->mInSwap = true; 1401 mOtherFrameLoader->mInSwap = true; 1402 mThisDocShell->SetInFrameSwap(true); 1403 mOtherDocShell->SetInFrameSwap(true); 1404 1405 // Fire pageshow events on still-loading pages, and then fire pagehide 1406 // events. Note that we do NOT fire these in the normal way, but just fire 1407 // them on the chrome event handlers. 1408 nsContentUtils::FirePageShowEventForFrameLoaderSwap( 1409 mThisDocShell, mThisEventTarget, false); 1410 nsContentUtils::FirePageShowEventForFrameLoaderSwap( 1411 mOtherDocShell, mOtherEventTarget, false); 1412 nsContentUtils::FirePageHideEventForFrameLoaderSwap(mThisDocShell, 1413 mThisEventTarget); 1414 nsContentUtils::FirePageHideEventForFrameLoaderSwap(mOtherDocShell, 1415 mOtherEventTarget); 1416 } 1417 1418 ~AutoResetInFrameSwap() { 1419 nsContentUtils::FirePageShowEventForFrameLoaderSwap(mThisDocShell, 1420 mThisEventTarget, true); 1421 nsContentUtils::FirePageShowEventForFrameLoaderSwap( 1422 mOtherDocShell, mOtherEventTarget, true); 1423 1424 mThisFrameLoader->mInSwap = false; 1425 mOtherFrameLoader->mInSwap = false; 1426 mThisDocShell->SetInFrameSwap(false); 1427 mOtherDocShell->SetInFrameSwap(false); 1428 1429 // This is needed to get visibility state right in cases when we swapped a 1430 // visible tab (foreground in visible window) with a non-visible tab. 1431 if (RefPtr<Document> doc = mThisDocShell->GetDocument()) { 1432 doc->UpdateVisibilityState(); 1433 } 1434 if (RefPtr<Document> doc = mOtherDocShell->GetDocument()) { 1435 doc->UpdateVisibilityState(); 1436 } 1437 } 1438 1439 private: 1440 RefPtr<nsFrameLoader> mThisFrameLoader; 1441 RefPtr<nsFrameLoader> mOtherFrameLoader; 1442 RefPtr<nsDocShell> mThisDocShell; 1443 RefPtr<nsDocShell> mOtherDocShell; 1444 nsCOMPtr<EventTarget> mThisEventTarget; 1445 nsCOMPtr<EventTarget> mOtherEventTarget; 1446 }; 1447 1448 nsresult nsFrameLoader::SwapWithOtherLoader(nsFrameLoader* aOther, 1449 nsFrameLoaderOwner* aThisOwner, 1450 nsFrameLoaderOwner* aOtherOwner) { 1451 #ifdef DEBUG 1452 RefPtr<nsFrameLoader> first = aThisOwner->GetFrameLoader(); 1453 RefPtr<nsFrameLoader> second = aOtherOwner->GetFrameLoader(); 1454 MOZ_ASSERT(first == this, "aThisOwner must own this"); 1455 MOZ_ASSERT(second == aOther, "aOtherOwner must own aOther"); 1456 #endif 1457 1458 NS_ENSURE_STATE(!mInShow && !aOther->mInShow); 1459 1460 if (IsRemoteFrame() != aOther->IsRemoteFrame()) { 1461 NS_WARNING( 1462 "Swapping remote and non-remote frames is not currently supported"); 1463 return NS_ERROR_NOT_IMPLEMENTED; 1464 } 1465 1466 RefPtr<Element> ourContent = mOwnerContent; 1467 RefPtr<Element> otherContent = aOther->mOwnerContent; 1468 if (!ourContent || !otherContent) { 1469 // Can't handle this 1470 return NS_ERROR_NOT_IMPLEMENTED; 1471 } 1472 1473 nsIFrame* ourFrame = ourContent->GetPrimaryFrame(FlushType::Frames); 1474 nsIFrame* otherFrame = otherContent->GetPrimaryFrame(FlushType::Frames); 1475 if (!ourFrame || !otherFrame) { 1476 return NS_ERROR_NOT_IMPLEMENTED; 1477 } 1478 1479 // Ensure the flushes above haven't changed all the world. 1480 if (ourContent != mOwnerContent || otherContent != aOther->mOwnerContent) { 1481 return NS_ERROR_NOT_IMPLEMENTED; 1482 } 1483 1484 bool ourHasSrcdoc = ourContent->IsHTMLElement(nsGkAtoms::iframe) && 1485 ourContent->HasAttr(nsGkAtoms::srcdoc); 1486 bool otherHasSrcdoc = otherContent->IsHTMLElement(nsGkAtoms::iframe) && 1487 otherContent->HasAttr(nsGkAtoms::srcdoc); 1488 if (ourHasSrcdoc || otherHasSrcdoc) { 1489 // Ignore this case entirely for now, since we support XUL <-> HTML swapping 1490 return NS_ERROR_NOT_IMPLEMENTED; 1491 } 1492 1493 bool ourFullscreenAllowed = ourContent->IsXULElement(); 1494 bool otherFullscreenAllowed = otherContent->IsXULElement(); 1495 if (ourFullscreenAllowed != otherFullscreenAllowed) { 1496 return NS_ERROR_NOT_IMPLEMENTED; 1497 } 1498 1499 nsILoadContext* ourLoadContext = ourContent->OwnerDoc()->GetLoadContext(); 1500 nsILoadContext* otherLoadContext = otherContent->OwnerDoc()->GetLoadContext(); 1501 MOZ_ASSERT(ourLoadContext && otherLoadContext, 1502 "Swapping frames within dead documents?"); 1503 if (ourLoadContext->UseRemoteTabs() != otherLoadContext->UseRemoteTabs()) { 1504 NS_WARNING("Can't swap between e10s and non-e10s windows"); 1505 return NS_ERROR_NOT_IMPLEMENTED; 1506 } 1507 if (ourLoadContext->UseRemoteSubframes() != 1508 otherLoadContext->UseRemoteSubframes()) { 1509 NS_WARNING("Can't swap between fission and non-fission windows"); 1510 return NS_ERROR_NOT_IMPLEMENTED; 1511 } 1512 1513 // Divert to a separate path for the remaining steps in the remote case 1514 if (IsRemoteFrame()) { 1515 MOZ_ASSERT(aOther->IsRemoteFrame()); 1516 return SwapWithOtherRemoteLoader(aOther, aThisOwner, aOtherOwner); 1517 } 1518 1519 // Make sure there are no same-origin issues 1520 bool equal; 1521 nsresult rv = ourContent->NodePrincipal()->Equals( 1522 otherContent->NodePrincipal(), &equal); 1523 if (NS_FAILED(rv) || !equal) { 1524 // Security problems loom. Just bail on it all 1525 return NS_ERROR_DOM_SECURITY_ERR; 1526 } 1527 1528 RefPtr<nsDocShell> ourDocshell = GetExistingDocShell(); 1529 RefPtr<nsDocShell> otherDocshell = aOther->GetExistingDocShell(); 1530 if (!ourDocshell || !otherDocshell) { 1531 // How odd 1532 return NS_ERROR_NOT_IMPLEMENTED; 1533 } 1534 1535 // To avoid having to mess with session history, avoid swapping 1536 // frameloaders that don't correspond to root same-type docshells, 1537 // unless both roots have session history disabled. 1538 nsCOMPtr<nsIDocShellTreeItem> ourRootTreeItem, otherRootTreeItem; 1539 ourDocshell->GetInProcessSameTypeRootTreeItem( 1540 getter_AddRefs(ourRootTreeItem)); 1541 otherDocshell->GetInProcessSameTypeRootTreeItem( 1542 getter_AddRefs(otherRootTreeItem)); 1543 nsCOMPtr<nsIWebNavigation> ourRootWebnav = do_QueryInterface(ourRootTreeItem); 1544 nsCOMPtr<nsIWebNavigation> otherRootWebnav = 1545 do_QueryInterface(otherRootTreeItem); 1546 1547 if (!ourRootWebnav || !otherRootWebnav) { 1548 return NS_ERROR_NOT_IMPLEMENTED; 1549 } 1550 1551 RefPtr<ChildSHistory> ourHistory = ourRootWebnav->GetSessionHistory(); 1552 RefPtr<ChildSHistory> otherHistory = otherRootWebnav->GetSessionHistory(); 1553 1554 if ((ourRootTreeItem != ourDocshell || otherRootTreeItem != otherDocshell) && 1555 (ourHistory || otherHistory)) { 1556 return NS_ERROR_NOT_IMPLEMENTED; 1557 } 1558 1559 RefPtr<BrowsingContext> ourBc = ourDocshell->GetBrowsingContext(); 1560 RefPtr<BrowsingContext> otherBc = otherDocshell->GetBrowsingContext(); 1561 1562 // Also make sure that the two BrowsingContexts are the same type. Otherwise 1563 // swapping is certainly not safe. If this needs to be changed then 1564 // the code below needs to be audited as it assumes identical types. 1565 if (ourBc->GetType() != otherBc->GetType()) { 1566 return NS_ERROR_NOT_IMPLEMENTED; 1567 } 1568 1569 // We ensure that BCs are either both top frames or both subframes. 1570 if (ourBc->IsTop() != otherBc->IsTop()) { 1571 return NS_ERROR_NOT_IMPLEMENTED; 1572 } 1573 1574 // One more twist here. Setting up the right treeowners in a heterogeneous 1575 // tree is a bit of a pain. So make sure that if `ourBc->GetType()` is not 1576 // nsIDocShellTreeItem::typeContent then all of our descendants are the same 1577 // type as us. 1578 if (!ourBc->IsContent() && 1579 (!AllDescendantsOfType(ourBc, ourBc->GetType()) || 1580 !AllDescendantsOfType(otherBc, otherBc->GetType()))) { 1581 return NS_ERROR_NOT_IMPLEMENTED; 1582 } 1583 1584 // Save off the tree owners, frame elements, chrome event handlers, and 1585 // docshell and document parents before doing anything else. 1586 nsCOMPtr<nsIDocShellTreeOwner> ourOwner, otherOwner; 1587 ourDocshell->GetTreeOwner(getter_AddRefs(ourOwner)); 1588 otherDocshell->GetTreeOwner(getter_AddRefs(otherOwner)); 1589 // Note: it's OK to have null treeowners. 1590 1591 nsCOMPtr<nsIDocShellTreeItem> ourParentItem, otherParentItem; 1592 ourDocshell->GetInProcessParent(getter_AddRefs(ourParentItem)); 1593 otherDocshell->GetInProcessParent(getter_AddRefs(otherParentItem)); 1594 if (!ourParentItem || !otherParentItem) { 1595 return NS_ERROR_NOT_IMPLEMENTED; 1596 } 1597 1598 nsCOMPtr<nsPIDOMWindowOuter> ourWindow = ourDocshell->GetWindow(); 1599 nsCOMPtr<nsPIDOMWindowOuter> otherWindow = otherDocshell->GetWindow(); 1600 1601 nsCOMPtr<Element> ourFrameElement = ourWindow->GetFrameElementInternal(); 1602 nsCOMPtr<Element> otherFrameElement = otherWindow->GetFrameElementInternal(); 1603 1604 nsCOMPtr<EventTarget> ourChromeEventHandler = 1605 ourWindow->GetChromeEventHandler(); 1606 nsCOMPtr<EventTarget> otherChromeEventHandler = 1607 otherWindow->GetChromeEventHandler(); 1608 1609 nsCOMPtr<EventTarget> ourEventTarget = ourWindow->GetParentTarget(); 1610 nsCOMPtr<EventTarget> otherEventTarget = otherWindow->GetParentTarget(); 1611 1612 NS_ASSERTION(SameCOMIdentity(ourFrameElement, ourContent) && 1613 SameCOMIdentity(otherFrameElement, otherContent) && 1614 SameCOMIdentity(ourChromeEventHandler, ourContent) && 1615 SameCOMIdentity(otherChromeEventHandler, otherContent), 1616 "How did that happen, exactly?"); 1617 1618 nsCOMPtr<Document> ourChildDocument = ourWindow->GetExtantDoc(); 1619 nsCOMPtr<Document> otherChildDocument = otherWindow->GetExtantDoc(); 1620 if (!ourChildDocument || !otherChildDocument) { 1621 // This shouldn't be happening 1622 return NS_ERROR_NOT_IMPLEMENTED; 1623 } 1624 1625 nsCOMPtr<Document> ourParentDocument = 1626 ourChildDocument->GetInProcessParentDocument(); 1627 nsCOMPtr<Document> otherParentDocument = 1628 otherChildDocument->GetInProcessParentDocument(); 1629 1630 // Make sure to swap docshells between the two frames. 1631 Document* ourDoc = ourContent->GetComposedDoc(); 1632 Document* otherDoc = otherContent->GetComposedDoc(); 1633 if (!ourDoc || !otherDoc) { 1634 // Again, how odd, given that we had docshells 1635 return NS_ERROR_NOT_IMPLEMENTED; 1636 } 1637 1638 NS_ASSERTION(ourDoc == ourParentDocument, "Unexpected parent document"); 1639 NS_ASSERTION(otherDoc == otherParentDocument, "Unexpected parent document"); 1640 1641 PresShell* ourPresShell = ourDoc->GetPresShell(); 1642 PresShell* otherPresShell = otherDoc->GetPresShell(); 1643 if (!ourPresShell || !otherPresShell) { 1644 return NS_ERROR_NOT_IMPLEMENTED; 1645 } 1646 1647 // When we swap docShells, maybe we have to deal with a new page created just 1648 // for this operation. In this case, the browser code should already have set 1649 // the correct userContextId attribute value in the owning element, but our 1650 // docShell, that has been created way before) doesn't know that that 1651 // happened. 1652 // This is the reason why now we must retrieve the correct value from the 1653 // usercontextid attribute before comparing our originAttributes with the 1654 // other one. 1655 OriginAttributes ourOriginAttributes = ourDocshell->GetOriginAttributes(); 1656 rv = PopulateOriginContextIdsFromAttributes(ourOriginAttributes); 1657 NS_ENSURE_SUCCESS(rv, rv); 1658 1659 OriginAttributes otherOriginAttributes = otherDocshell->GetOriginAttributes(); 1660 rv = aOther->PopulateOriginContextIdsFromAttributes(otherOriginAttributes); 1661 NS_ENSURE_SUCCESS(rv, rv); 1662 1663 if (ourOriginAttributes != otherOriginAttributes) { 1664 return NS_ERROR_NOT_IMPLEMENTED; 1665 } 1666 1667 if (mInSwap || aOther->mInSwap) { 1668 return NS_ERROR_NOT_IMPLEMENTED; 1669 } 1670 AutoResetInFrameSwap autoFrameSwap(this, aOther, ourDocshell, otherDocshell, 1671 ourEventTarget, otherEventTarget); 1672 1673 nsSubDocumentFrame* ourFrameFrame = do_QueryFrame(ourFrame); 1674 if (!ourFrameFrame) { 1675 return NS_ERROR_NOT_IMPLEMENTED; 1676 } 1677 1678 // OK. First begin to swap the docshells in the two nsIFrames 1679 rv = ourFrameFrame->BeginSwapDocShells(otherFrame); 1680 if (NS_FAILED(rv)) { 1681 return rv; 1682 } 1683 1684 // Now move the docshells to the right docshell trees. Note that this 1685 // resets their treeowners to null. 1686 ourParentItem->RemoveChild(ourDocshell); 1687 otherParentItem->RemoveChild(otherDocshell); 1688 if (ourBc->IsContent()) { 1689 ourOwner->ContentShellRemoved(ourDocshell); 1690 otherOwner->ContentShellRemoved(otherDocshell); 1691 } 1692 1693 ourParentItem->AddChild(otherDocshell); 1694 otherParentItem->AddChild(ourDocshell); 1695 1696 // Restore the correct chrome event handlers. 1697 ourDocshell->SetChromeEventHandler(otherChromeEventHandler); 1698 otherDocshell->SetChromeEventHandler(ourChromeEventHandler); 1699 // Restore the correct treeowners 1700 // (and also chrome event handlers for content frames only). 1701 SetTreeOwnerAndChromeEventHandlerOnDocshellTree( 1702 ourDocshell, otherOwner, 1703 ourBc->IsContent() ? otherChromeEventHandler.get() : nullptr); 1704 SetTreeOwnerAndChromeEventHandlerOnDocshellTree( 1705 otherDocshell, ourOwner, 1706 ourBc->IsContent() ? ourChromeEventHandler.get() : nullptr); 1707 1708 // Switch the owner content before we start calling AddTreeItemToTreeOwner. 1709 // Note that we rely on this to deal with setting mObservingOwnerContent to 1710 // false and calling RemoveMutationObserver as needed. 1711 SetOwnerContent(otherContent); 1712 aOther->SetOwnerContent(ourContent); 1713 1714 AddTreeItemToTreeOwner(ourDocshell, otherOwner); 1715 aOther->AddTreeItemToTreeOwner(otherDocshell, ourOwner); 1716 1717 // SetSubDocumentFor nulls out parent documents on the old child doc if a 1718 // new non-null document is passed in, so just go ahead and remove both 1719 // kids before reinserting in the parent subdoc maps, to avoid 1720 // complications. 1721 ourParentDocument->SetSubDocumentFor(ourContent, nullptr); 1722 otherParentDocument->SetSubDocumentFor(otherContent, nullptr); 1723 ourParentDocument->SetSubDocumentFor(ourContent, otherChildDocument); 1724 otherParentDocument->SetSubDocumentFor(otherContent, ourChildDocument); 1725 1726 ourWindow->SetFrameElementInternal(otherFrameElement); 1727 otherWindow->SetFrameElementInternal(ourFrameElement); 1728 1729 RefPtr<nsFrameMessageManager> ourMessageManager = mMessageManager; 1730 RefPtr<nsFrameMessageManager> otherMessageManager = aOther->mMessageManager; 1731 // Swap pointers in child message managers. 1732 if (mChildMessageManager) { 1733 InProcessBrowserChildMessageManager* browserChild = mChildMessageManager; 1734 browserChild->SetOwner(otherContent); 1735 browserChild->SetChromeMessageManager(otherMessageManager); 1736 } 1737 if (aOther->mChildMessageManager) { 1738 InProcessBrowserChildMessageManager* otherBrowserChild = 1739 aOther->mChildMessageManager; 1740 otherBrowserChild->SetOwner(ourContent); 1741 otherBrowserChild->SetChromeMessageManager(ourMessageManager); 1742 } 1743 // Swap and setup things in parent message managers. 1744 if (mMessageManager) { 1745 mMessageManager->SetCallback(aOther); 1746 } 1747 if (aOther->mMessageManager) { 1748 aOther->mMessageManager->SetCallback(this); 1749 } 1750 mMessageManager.swap(aOther->mMessageManager); 1751 1752 // Perform the actual swap of the internal refptrs. We keep a strong reference 1753 // to ourselves to make sure we don't die while we overwrite our reference to 1754 // ourself. 1755 RefPtr<nsFrameLoader> kungFuDeathGrip(this); 1756 aThisOwner->SetFrameLoader(aOther); 1757 aOtherOwner->SetFrameLoader(kungFuDeathGrip); 1758 1759 // Drop any cached content viewers in the two session histories. 1760 if (ourHistory) { 1761 ourHistory->EvictLocalDocumentViewers(); 1762 } 1763 if (otherHistory) { 1764 otherHistory->EvictLocalDocumentViewers(); 1765 } 1766 1767 NS_ASSERTION(ourFrame == ourContent->GetPrimaryFrame() && 1768 otherFrame == otherContent->GetPrimaryFrame(), 1769 "changed primary frame"); 1770 1771 ourFrameFrame->EndSwapDocShells(otherFrame); 1772 1773 // If the content being swapped came from windows on two screens with 1774 // incompatible backing resolution (e.g. dragging a tab between windows on 1775 // hi-dpi and low-dpi screens), it will have style data that is based on 1776 // the wrong appUnitsPerDevPixel value. So we tell the PresShells that their 1777 // backing scale factor may have changed. (Bug 822266) 1778 ourFrame->PresShell()->BackingScaleFactorChanged(); 1779 otherFrame->PresShell()->BackingScaleFactorChanged(); 1780 1781 return NS_OK; 1782 } 1783 1784 void nsFrameLoader::Destroy(bool aForProcessSwitch) { 1785 StartDestroy(aForProcessSwitch); 1786 } 1787 1788 class nsFrameLoaderDestroyRunnable : public Runnable { 1789 enum DestroyPhase { 1790 // See the implementation of Run for an explanation of these phases. 1791 eDestroyDocShell, 1792 eWaitForUnloadMessage, 1793 eDestroyComplete 1794 }; 1795 1796 RefPtr<nsFrameLoader> mFrameLoader; 1797 DestroyPhase mPhase; 1798 1799 public: 1800 explicit nsFrameLoaderDestroyRunnable(nsFrameLoader* aFrameLoader) 1801 : mozilla::Runnable("nsFrameLoaderDestroyRunnable"), 1802 mFrameLoader(aFrameLoader), 1803 mPhase(eDestroyDocShell) {} 1804 1805 NS_IMETHOD Run() override; 1806 }; 1807 1808 void nsFrameLoader::StartDestroy(bool aForProcessSwitch) { 1809 // nsFrameLoader::StartDestroy is called just before the frameloader is 1810 // detached from the <browser> element. Destruction continues in phases via 1811 // the nsFrameLoaderDestroyRunnable. 1812 1813 if (mDestroyCalled) { 1814 return; 1815 } 1816 mDestroyCalled = true; 1817 1818 // Request a full tab state flush if the tab is closing. 1819 // 1820 // XXX If we find that we need to do Session Store cleanup for the frameloader 1821 // that's going away, we should unconditionally do the flush here, but include 1822 // the |aForProcessSwitch| flag in the completion notification. 1823 if (!aForProcessSwitch) { 1824 RequestFinalTabStateFlush(); 1825 } 1826 1827 // After this point, we return an error when trying to send a message using 1828 // the message manager on the frame. 1829 if (mMessageManager) { 1830 mMessageManager->Close(); 1831 } 1832 1833 // Retain references to the <browser> element and the frameloader in case we 1834 // receive any messages from the message manager on the frame. These 1835 // references are dropped in DestroyComplete. 1836 if (mChildMessageManager || mRemoteBrowser) { 1837 mOwnerContentStrong = mOwnerContent; 1838 if (auto* browserParent = GetBrowserParent()) { 1839 browserParent->CacheFrameLoader(this); 1840 } 1841 if (mChildMessageManager) { 1842 mChildMessageManager->CacheFrameLoader(this); 1843 } 1844 } 1845 1846 // If the BrowserParent has installed any event listeners on the window, this 1847 // is its last chance to remove them while we're still in the document. 1848 if (auto* browserParent = GetBrowserParent()) { 1849 browserParent->RemoveWindowListeners(); 1850 } 1851 1852 // Hide the content viewer before nulling out the embedder element and so. 1853 Hide(); 1854 1855 nsCOMPtr<Document> doc; 1856 bool dynamicSubframeRemoval = false; 1857 if (mOwnerContent) { 1858 doc = mOwnerContent->OwnerDoc(); 1859 dynamicSubframeRemoval = !aForProcessSwitch && 1860 mPendingBrowsingContext->IsSubframe() && 1861 !doc->InUnlinkOrDeletion(); 1862 doc->SetSubDocumentFor(mOwnerContent, nullptr); 1863 MaybeUpdatePrimaryBrowserParent(eBrowserParentRemoved); 1864 1865 nsCOMPtr<nsFrameLoaderOwner> owner = do_QueryInterface(mOwnerContent); 1866 owner->FrameLoaderDestroying(this, !aForProcessSwitch); 1867 SetOwnerContent(nullptr); 1868 } 1869 1870 // Seems like this is a dynamic frame removal. 1871 if (dynamicSubframeRemoval) { 1872 BrowsingContext* browsingContext = GetExtantBrowsingContext(); 1873 if (browsingContext) { 1874 RefPtr<ChildSHistory> childSHistory = 1875 browsingContext->Top()->GetChildSessionHistory(); 1876 if (childSHistory) { 1877 if (mozilla::SessionHistoryInParent()) { 1878 uint32_t addedEntries = 0; 1879 browsingContext->PreOrderWalk([&addedEntries](BrowsingContext* aBC) { 1880 const uint32_t len = aBC->GetHistoryEntryCount(); 1881 // There might not be a SH entry yet, which is fine. 1882 // The first entry doesn't increase history length, as it's added to 1883 // it's parent entry 1884 addedEntries += len > 0 ? len - 1 : 0; 1885 }); 1886 1887 nsID changeID = {}; 1888 if (addedEntries > 0) { 1889 ChildSHistory* shistory = 1890 browsingContext->Top()->GetChildSessionHistory(); 1891 if (shistory) { 1892 changeID = shistory->AddPendingHistoryChange(0, -addedEntries); 1893 } 1894 } 1895 browsingContext->RemoveFromSessionHistory(changeID); 1896 } else { 1897 AutoTArray<nsID, 16> ids({browsingContext->GetHistoryID()}); 1898 childSHistory->LegacySHistory()->RemoveEntries( 1899 ids, childSHistory->Index()); 1900 } 1901 } 1902 } 1903 } 1904 1905 // Let the tree owner know we're gone. 1906 if (nsCOMPtr<nsIDocShell> ds = GetDocShell()) { 1907 if (mIsTopLevelContent) { 1908 nsCOMPtr<nsIDocShellTreeItem> parentItem; 1909 ds->GetInProcessParent(getter_AddRefs(parentItem)); 1910 if (nsCOMPtr<nsIDocShellTreeOwner> owner = do_GetInterface(parentItem)) { 1911 owner->ContentShellRemoved(ds); 1912 } 1913 } 1914 if (nsCOMPtr<nsPIDOMWindowOuter> win = ds->GetWindow()) { 1915 win->SetFrameElementInternal(nullptr); 1916 } 1917 } 1918 1919 nsCOMPtr<nsIRunnable> destroyRunnable = 1920 new nsFrameLoaderDestroyRunnable(this); 1921 if (mNeedsAsyncDestroy || !doc || 1922 NS_FAILED(doc->FinalizeFrameLoader(this, destroyRunnable))) { 1923 NS_DispatchToCurrentThread(destroyRunnable); 1924 } 1925 } 1926 1927 nsresult nsFrameLoaderDestroyRunnable::Run() { 1928 switch (mPhase) { 1929 case eDestroyDocShell: 1930 mFrameLoader->DestroyDocShell(); 1931 1932 // In the out-of-process case, BrowserParent will eventually call 1933 // DestroyComplete once it receives a __delete__ message from the child. 1934 // In the in-process case, or if the BrowserParent was already destroyed, 1935 // we dispatch a series of runnables to ensure that DestroyComplete gets 1936 // called at the right time. The frame loader is kept alive by 1937 // mFrameLoader during this time. 1938 if (!mFrameLoader->GetRemoteBrowser() || 1939 !mFrameLoader->GetRemoteBrowser()->CanRecv()) { 1940 // When the docshell is destroyed, NotifyWindowIDDestroyed is called to 1941 // asynchronously notify {outer,inner}-window-destroyed via a runnable. 1942 // We don't want DestroyComplete to run until after those runnables have 1943 // run. Since we're enqueueing ourselves after the window-destroyed 1944 // runnables are enqueued, we're guaranteed to run after. 1945 mPhase = eWaitForUnloadMessage; 1946 NS_DispatchToCurrentThread(this); 1947 } 1948 break; 1949 1950 case eWaitForUnloadMessage: 1951 // The *-window-destroyed observers have finished running at this 1952 // point. However, it's possible that a *-window-destroyed observer might 1953 // have sent a message using the message manager. These messages might not 1954 // have been processed yet. So we enqueue ourselves again to ensure that 1955 // DestroyComplete runs after all messages sent by *-window-destroyed 1956 // observers have been processed. 1957 mPhase = eDestroyComplete; 1958 NS_DispatchToCurrentThread(this); 1959 break; 1960 1961 case eDestroyComplete: 1962 // Now that all messages sent by unload listeners and window destroyed 1963 // observers have been processed, we disconnect the message manager and 1964 // finish destruction. 1965 mFrameLoader->DestroyComplete(); 1966 break; 1967 } 1968 1969 return NS_OK; 1970 } 1971 1972 void nsFrameLoader::DestroyDocShell() { 1973 // This code runs after the frameloader has been detached from the <browser> 1974 // element. We postpone this work because we may not be allowed to run 1975 // script at that time. 1976 1977 // Ask the BrowserChild to fire the frame script "unload" event, destroy its 1978 // docshell, and finally destroy the PBrowser actor. This eventually leads to 1979 // nsFrameLoader::DestroyComplete being called. 1980 if (mRemoteBrowser) { 1981 mRemoteBrowser->DestroyStart(); 1982 } 1983 1984 // Fire the "unload" event if we're in-process. 1985 if (mChildMessageManager) { 1986 mChildMessageManager->FireUnloadEvent(); 1987 } 1988 1989 if (mSessionStoreChild) { 1990 mSessionStoreChild->Stop(); 1991 mSessionStoreChild = nullptr; 1992 } 1993 1994 // Destroy the docshell. 1995 if (GetDocShell()) { 1996 GetDocShell()->Destroy(); 1997 } 1998 1999 if (!mWillChangeProcess && mPendingBrowsingContext && 2000 mPendingBrowsingContext->EverAttached()) { 2001 mPendingBrowsingContext->Detach(); 2002 } 2003 2004 mPendingBrowsingContext = nullptr; 2005 mDocShell = nullptr; 2006 2007 if (mChildMessageManager) { 2008 // Stop handling events in the in-process frame script. 2009 mChildMessageManager->DisconnectEventListeners(); 2010 } 2011 } 2012 2013 void nsFrameLoader::DestroyComplete() { 2014 // We get here, as part of StartDestroy, after the docshell has been destroyed 2015 // and all message manager messages sent during docshell destruction have been 2016 // dispatched. We also get here if the child process crashes. In the latter 2017 // case, StartDestroy might not have been called. 2018 2019 // Drop the strong references created in StartDestroy. 2020 if (mChildMessageManager || mRemoteBrowser) { 2021 mOwnerContentStrong = nullptr; 2022 if (auto* browserParent = GetBrowserParent()) { 2023 browserParent->CacheFrameLoader(nullptr); 2024 } 2025 if (mChildMessageManager) { 2026 mChildMessageManager->CacheFrameLoader(nullptr); 2027 } 2028 } 2029 2030 // Call BrowserParent::Destroy if we haven't already (in case of a crash). 2031 if (mRemoteBrowser) { 2032 mRemoteBrowser->DestroyComplete(); 2033 mRemoteBrowser = nullptr; 2034 } 2035 2036 if (mMessageManager) { 2037 mMessageManager->Disconnect(); 2038 } 2039 2040 if (mChildMessageManager) { 2041 mChildMessageManager->Disconnect(); 2042 } 2043 2044 mMessageManager = nullptr; 2045 mChildMessageManager = nullptr; 2046 } 2047 2048 void nsFrameLoader::SetOwnerContent(Element* aContent) { 2049 if (mObservingOwnerContent) { 2050 mObservingOwnerContent = false; 2051 mOwnerContent->RemoveMutationObserver(this); 2052 } 2053 2054 // XXXBFCache Need to update also all the non-current FrameLoaders in the 2055 // owner when moving a FrameLoader. 2056 // This temporary setup doesn't crash, but behaves badly with bfcached docs. 2057 if (RefPtr<nsFrameLoaderOwner> owner = do_QueryObject(mOwnerContent)) { 2058 owner->DetachFrameLoader(this); 2059 } 2060 2061 mOwnerContent = aContent; 2062 2063 if (RefPtr<nsFrameLoaderOwner> owner = do_QueryObject(mOwnerContent)) { 2064 owner->AttachFrameLoader(this); 2065 2066 #ifdef NIGHTLY_BUILD 2067 if (mozilla::BFCacheInParent() && XRE_IsParentProcess()) { 2068 if (BrowsingContext* bc = GetMaybePendingBrowsingContext()) { 2069 nsISHistory* shistory = bc->Canonical()->GetSessionHistory(); 2070 if (shistory) { 2071 uint32_t count = shistory->GetCount(); 2072 for (uint32_t i = 0; i < count; ++i) { 2073 nsCOMPtr<nsISHEntry> entry; 2074 shistory->GetEntryAtIndex(i, getter_AddRefs(entry)); 2075 nsCOMPtr<SessionHistoryEntry> she = do_QueryInterface(entry); 2076 MOZ_RELEASE_ASSERT(!she || !she->GetFrameLoader()); 2077 } 2078 } 2079 } 2080 } 2081 #endif 2082 } 2083 2084 if (mSessionStoreChild && mOwnerContent) { 2085 // mOwnerContent will only be null when the frame loader is being destroyed, 2086 // so the session store listener will be destroyed along with it. 2087 // XXX(farre): This probably needs to update the cache. See bug 1698497. 2088 mSessionStoreChild->SetOwnerContent(mOwnerContent); 2089 } 2090 2091 if (RefPtr<BrowsingContext> browsingContext = GetExtantBrowsingContext()) { 2092 browsingContext->SetEmbedderElement(mOwnerContent); 2093 } 2094 2095 if (mSessionStoreChild) { 2096 // UpdateEventTargets will requery its browser contexts for event 2097 // targets, so this call needs to happen after the call to 2098 // SetEmbedderElement above. 2099 mSessionStoreChild->UpdateEventTargets(); 2100 } 2101 2102 AutoJSAPI jsapi; 2103 jsapi.Init(); 2104 2105 JS::Rooted<JSObject*> wrapper(jsapi.cx(), GetWrapper()); 2106 if (wrapper) { 2107 JSAutoRealm ar(jsapi.cx(), wrapper); 2108 IgnoredErrorResult rv; 2109 UpdateReflectorGlobal(jsapi.cx(), wrapper, rv); 2110 (void)NS_WARN_IF(rv.Failed()); 2111 } 2112 } 2113 2114 nsIContent* nsFrameLoader::GetParentObject() const { return mOwnerContent; } 2115 2116 void nsFrameLoader::AssertSafeToInit() { 2117 MOZ_DIAGNOSTIC_ASSERT(nsContentUtils::IsSafeToRunScript() || 2118 mOwnerContent->OwnerDoc()->IsStaticDocument(), 2119 "FrameLoader should never be initialized during " 2120 "document update or reflow!"); 2121 } 2122 2123 nsresult nsFrameLoader::MaybeCreateDocShell() { 2124 if (GetDocShell()) { 2125 return NS_OK; 2126 } 2127 if (IsRemoteFrame()) { 2128 return NS_OK; 2129 } 2130 NS_ENSURE_STATE(!mDestroyCalled); 2131 2132 AssertSafeToInit(); 2133 2134 // Get our parent docshell off the document of mOwnerContent 2135 // XXXbz this is such a total hack.... We really need to have a 2136 // better setup for doing this. 2137 Document* doc = mOwnerContent->OwnerDoc(); 2138 2139 MOZ_RELEASE_ASSERT(!doc->IsResourceDoc(), "We shouldn't even exist"); 2140 2141 // If we've already tried to initialize and failed, don't try again. 2142 if (mInitialized) { 2143 return NS_ERROR_NOT_AVAILABLE; 2144 } 2145 mInitialized = true; 2146 2147 // Check if the document still has a window since it is possible for an 2148 // iframe to be inserted and cause the creation of the docshell in a 2149 // partially unloaded document (see Bug 1305237 comment 127). 2150 if (!doc->IsStaticDocument() && 2151 (!doc->GetWindow() || !mOwnerContent->IsInComposedDoc())) { 2152 return NS_ERROR_UNEXPECTED; 2153 } 2154 2155 if (!doc->IsActive()) { 2156 // Don't allow subframe loads in non-active documents. 2157 // (See bug 610571 comment 5.) 2158 return NS_ERROR_NOT_AVAILABLE; 2159 } 2160 2161 RefPtr<nsDocShell> parentDocShell = nsDocShell::Cast(doc->GetDocShell()); 2162 if (NS_WARN_IF(!parentDocShell)) { 2163 return NS_ERROR_UNEXPECTED; 2164 } 2165 2166 if (doc->GetWindowContext()->IsDiscarded() || 2167 parentDocShell->GetBrowsingContext()->IsDiscarded()) { 2168 // Don't allow subframe loads in discarded contexts. 2169 // (see bug 1652085, bug 1656854) 2170 return NS_ERROR_NOT_AVAILABLE; 2171 } 2172 2173 if (!EnsureBrowsingContextAttached()) { 2174 return NS_ERROR_FAILURE; 2175 } 2176 2177 mPendingBrowsingContext->SetEmbedderElement(mOwnerContent); 2178 2179 // nsDocShell::Create will attach itself to the passed browsing 2180 // context inside of nsDocShell::Create 2181 RefPtr<nsDocShell> docShell = nsDocShell::Create(mPendingBrowsingContext); 2182 NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE); 2183 mDocShell = docShell; 2184 2185 mPendingBrowsingContext->Embed(); 2186 2187 InvokeBrowsingContextReadyCallback(); 2188 2189 mIsTopLevelContent = mPendingBrowsingContext->IsTopContent(); 2190 2191 if (mIsTopLevelContent) { 2192 // Manually add ourselves to our parent's docshell, as BrowsingContext won't 2193 // have done this for us. 2194 // 2195 // XXX(nika): Consider removing the DocShellTree in the future, for 2196 // consistency between local and remote frames.. 2197 parentDocShell->AddChild(docShell); 2198 } 2199 2200 // Now that we are part of the DocShellTree, attach our DocShell to our 2201 // parent's TreeOwner. 2202 nsCOMPtr<nsIDocShellTreeOwner> parentTreeOwner; 2203 parentDocShell->GetTreeOwner(getter_AddRefs(parentTreeOwner)); 2204 AddTreeItemToTreeOwner(docShell, parentTreeOwner); 2205 2206 // Make sure all nsDocShells have links back to the content element in the 2207 // nearest enclosing chrome shell. 2208 RefPtr<EventTarget> chromeEventHandler; 2209 bool parentIsContent = parentDocShell->GetBrowsingContext()->IsContent(); 2210 if (parentIsContent) { 2211 // Our parent shell is a content shell. Get the chrome event handler from it 2212 // and use that for our shell as well. 2213 parentDocShell->GetChromeEventHandler(getter_AddRefs(chromeEventHandler)); 2214 } else { 2215 // Our parent shell is a chrome shell. It is therefore our nearest enclosing 2216 // chrome shell. 2217 chromeEventHandler = mOwnerContent; 2218 } 2219 2220 docShell->SetChromeEventHandler(chromeEventHandler); 2221 2222 // This is nasty, this code (the docShell->GetWindow() below) 2223 // *must* come *after* the above call to 2224 // docShell->SetChromeEventHandler() for the global window to get 2225 // the right chrome event handler. 2226 2227 // Tell the window about the frame that hosts it. 2228 nsCOMPtr<nsPIDOMWindowOuter> newWindow = docShell->GetWindow(); 2229 if (NS_WARN_IF(!newWindow)) { 2230 // Do not call Destroy() here. See bug 472312. 2231 NS_WARNING("Something wrong when creating the docshell for a frameloader!"); 2232 // The docshell isn't completely initialized. Clear it so that it isn't 2233 // reachable. Future calls to MaybeCreateDocShell will fail due to 2234 // mInitialized. 2235 mDocShell = nullptr; 2236 return NS_ERROR_FAILURE; 2237 } 2238 2239 newWindow->SetFrameElementInternal(mOwnerContent); 2240 2241 // Allow scripts to close the docshell if specified. 2242 if (mOwnerContent->IsXULElement(nsGkAtoms::browser) && 2243 mOwnerContent->AttrValueIs(kNameSpaceID_None, 2244 nsGkAtoms::allowscriptstoclose, 2245 nsGkAtoms::_true, eCaseMatters)) { 2246 nsGlobalWindowOuter::Cast(newWindow)->AllowScriptsToClose(); 2247 } 2248 2249 NS_ENSURE_STATE(mOwnerContent); 2250 2251 // If we are an in-process browser, we want to set up our session history. 2252 if (mIsTopLevelContent && mOwnerContent->IsXULElement(nsGkAtoms::browser) && 2253 !mOwnerContent->HasAttr(nsGkAtoms::disablehistory)) { 2254 // XXX(nika): Set this up more explicitly? 2255 mPendingBrowsingContext->InitSessionHistory(); 2256 } 2257 2258 // Apply sandbox flags even if our owner is not an iframe, as this copies 2259 // flags from our owning content's owning document. 2260 // Note: ApplySandboxFlags should be called after docShell->SetIsFrame 2261 // because we need to get the correct presentation URL in ApplySandboxFlags. 2262 uint32_t sandboxFlags = 0; 2263 HTMLIFrameElement* iframe = HTMLIFrameElement::FromNode(mOwnerContent); 2264 if (iframe) { 2265 sandboxFlags = iframe->GetSandboxFlags(); 2266 } 2267 ApplySandboxFlags(sandboxFlags); 2268 MOZ_ALWAYS_SUCCEEDS(mPendingBrowsingContext->SetInitialSandboxFlags( 2269 mPendingBrowsingContext->GetSandboxFlags())); 2270 2271 // Gather things to inherit into the initial about:blank 2272 2273 // For HTML [i]frames and objects, perform the inheritance here. (It would 2274 // probably be more proper to hoist this to each call site of 2275 // nsFrameLoader::Create.) 2276 nsCOMPtr<nsIPrincipal> principal = doc->NodePrincipal(); 2277 nsCOMPtr<nsIPrincipal> partitionedPrincipal = doc->PartitionedPrincipal(); 2278 2279 // We use mOpenWindowInfo so that JS can force a principal onto us 2280 if (mOpenWindowInfo && mOpenWindowInfo->PrincipalToInheritForAboutBlank()) { 2281 principal = mOpenWindowInfo->PrincipalToInheritForAboutBlank(); 2282 partitionedPrincipal = 2283 mOpenWindowInfo->PartitionedPrincipalToInheritForAboutBlank(); 2284 } 2285 2286 if ((mPendingBrowsingContext->IsContent() || XRE_IsContentProcess()) && 2287 (!principal || principal->IsSystemPrincipal())) { 2288 // Never inherit system principal to a content HTML [i]frame. 2289 principal = NullPrincipal::Create( 2290 mPendingBrowsingContext->OriginAttributesRef(), nullptr); 2291 partitionedPrincipal = principal; 2292 } 2293 2294 RefPtr<nsOpenWindowInfo> openWindowInfo = new nsOpenWindowInfo(); 2295 openWindowInfo->mPrincipalToInheritForAboutBlank = principal.forget(); 2296 openWindowInfo->mPartitionedPrincipalToInheritForAboutBlank = 2297 partitionedPrincipal.forget(); 2298 openWindowInfo->mPolicyContainerToInheritForAboutBlank = 2299 doc->GetPolicyContainer(); 2300 openWindowInfo->mCoepToInheritForAboutBlank = doc->GetEmbedderPolicy(); 2301 openWindowInfo->mBaseUriToInheritForAboutBlank = mOwnerContent->GetBaseURI(); 2302 if (!docShell->Initialize(openWindowInfo, nullptr)) { 2303 // Do not call Destroy() here. See bug 472312. 2304 NS_WARNING("Something wrong when creating the docshell for a frameloader!"); 2305 return NS_ERROR_FAILURE; 2306 } 2307 2308 ReallyLoadFrameScripts(); 2309 2310 // Previously, the lazy about:blank creation had the effect of running 2311 // nsGlobalWindowOuter::DispatchDOMWindowCreated, which sets up the message 2312 // manager, after ReallyLoadFrameScripts(). We can't achieve the same by using 2313 // a script blocker while calling `docShell->Initialize()`, because the 2314 // initialization expects to be able to assert that scripts are allowed to 2315 // run. Therefore, let's fix up the message manager setup here. 2316 if (Document* doc = docShell->GetDocument()) { 2317 if (nsPIDOMWindowOuter* window = doc->GetWindow()) { 2318 window->UpdateParentTarget(); 2319 } 2320 } 2321 2322 return NS_OK; 2323 } 2324 2325 void nsFrameLoader::GetURL(nsString& aURL, nsIPrincipal** aTriggeringPrincipal, 2326 nsIPolicyContainer** aPolicyContainer) { 2327 aURL.Truncate(); 2328 // Within this function we default to using the NodePrincipal as the 2329 // triggeringPrincipal and the CSP of the document. 2330 // Expanded Principals however override the CSP of the document, hence 2331 // if frame->GetSrcTriggeringPrincipal() returns a valid principal, we 2332 // have to query the CSP from that Principal. 2333 nsCOMPtr<nsIPrincipal> triggeringPrincipal = mOwnerContent->NodePrincipal(); 2334 nsCOMPtr<nsIPolicyContainer> policyContainer = 2335 mOwnerContent->GetPolicyContainer(); 2336 2337 if (mOwnerContent->IsHTMLElement(nsGkAtoms::object)) { 2338 mOwnerContent->GetAttr(nsGkAtoms::data, aURL); 2339 } else { 2340 mOwnerContent->GetAttr(nsGkAtoms::src, aURL); 2341 if (RefPtr<nsGenericHTMLFrameElement> frame = 2342 do_QueryObject(mOwnerContent)) { 2343 nsCOMPtr<nsIPrincipal> srcPrincipal = frame->GetSrcTriggeringPrincipal(); 2344 if (srcPrincipal) { 2345 triggeringPrincipal = srcPrincipal; 2346 nsCOMPtr<nsIExpandedPrincipal> ep = 2347 do_QueryInterface(triggeringPrincipal); 2348 if (ep) { 2349 // Bug 1548468: Move CSP off ExpandedPrincipal 2350 RefPtr<PolicyContainer> addonPolicyContainer; 2351 if (nsCOMPtr<nsIContentSecurityPolicy> addonCSP = ep->GetCsp()) { 2352 addonPolicyContainer = new PolicyContainer(); 2353 addonPolicyContainer->SetCSP(addonCSP); 2354 } 2355 policyContainer = addonPolicyContainer.forget(); 2356 } 2357 } 2358 } 2359 } 2360 triggeringPrincipal.forget(aTriggeringPrincipal); 2361 policyContainer.forget(aPolicyContainer); 2362 } 2363 2364 nsresult nsFrameLoader::CheckForRecursiveLoad(nsIURI* aURI) { 2365 MOZ_ASSERT(!IsRemoteFrame(), 2366 "Shouldn't call CheckForRecursiveLoad on remote frames."); 2367 2368 mDepthTooGreat = false; 2369 RefPtr<BrowsingContext> parentBC( 2370 mOwnerContent->OwnerDoc()->GetBrowsingContext()); 2371 NS_ENSURE_STATE(parentBC); 2372 2373 if (!parentBC->IsContent()) { 2374 return NS_OK; 2375 } 2376 2377 // Bug 8065: Don't exceed some maximum depth in content frames 2378 // (MAX_DEPTH_CONTENT_FRAMES) 2379 int32_t depth = 0; 2380 for (BrowsingContext* bc = parentBC; bc; bc = bc->GetParent()) { 2381 ++depth; 2382 if (depth >= MAX_DEPTH_CONTENT_FRAMES) { 2383 mDepthTooGreat = true; 2384 NS_WARNING("Too many nested content frames so giving up"); 2385 2386 return NS_ERROR_UNEXPECTED; // Too deep, give up! (silently?) 2387 } 2388 } 2389 2390 return NS_OK; 2391 } 2392 2393 nsresult nsFrameLoader::GetWindowDimensions(LayoutDeviceIntRect& aRect) { 2394 if (!mOwnerContent) { 2395 return NS_ERROR_FAILURE; 2396 } 2397 2398 // Need to get outer window position here 2399 Document* doc = mOwnerContent->GetComposedDoc(); 2400 if (!doc) { 2401 return NS_ERROR_FAILURE; 2402 } 2403 2404 MOZ_RELEASE_ASSERT(!doc->IsResourceDoc(), "We shouldn't even exist"); 2405 2406 nsCOMPtr<nsPIDOMWindowOuter> win = doc->GetWindow(); 2407 if (!win) { 2408 return NS_ERROR_FAILURE; 2409 } 2410 2411 nsCOMPtr<nsIDocShellTreeItem> parentAsItem(win->GetDocShell()); 2412 if (!parentAsItem) { 2413 return NS_ERROR_FAILURE; 2414 } 2415 2416 nsCOMPtr<nsIDocShellTreeOwner> parentOwner; 2417 if (NS_FAILED(parentAsItem->GetTreeOwner(getter_AddRefs(parentOwner))) || 2418 !parentOwner) { 2419 return NS_ERROR_FAILURE; 2420 } 2421 2422 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_GetInterface(parentOwner)); 2423 aRect.MoveTo(treeOwnerAsWin->GetPosition()); 2424 aRect.SizeTo(treeOwnerAsWin->GetSize()); 2425 return NS_OK; 2426 } 2427 2428 nsresult nsFrameLoader::UpdatePositionAndSize(nsSubDocumentFrame* aFrame) { 2429 const auto size = aFrame->GetSubdocumentSize(); 2430 mLazySize = size; 2431 2432 if (IsRemoteFrame()) { 2433 if (mRemoteBrowser) { 2434 // If we were not able to show remote frame before, we should probably 2435 // retry now to send correct showInfo. 2436 if (!mRemoteBrowserShown) { 2437 ShowRemoteFrame(aFrame); 2438 } 2439 LayoutDeviceIntRect dimensions; 2440 MOZ_TRY(GetWindowDimensions(dimensions)); 2441 mRemoteBrowser->UpdateDimensions(dimensions, size); 2442 mRemoteBrowserSized = true; 2443 } 2444 return NS_OK; 2445 } 2446 nsCOMPtr<nsIBaseWindow> baseWindow = GetDocShell(IgnoreErrors()); 2447 if (!baseWindow) { 2448 return NS_OK; 2449 } 2450 int32_t x = 0; 2451 int32_t y = 0; 2452 2453 AutoWeakFrame weakFrame(aFrame); 2454 baseWindow->GetPosition(&x, &y); 2455 2456 if (!weakFrame.IsAlive()) { 2457 // GetPosition() killed us 2458 return NS_OK; 2459 } 2460 baseWindow->SetPositionAndSize(x, y, size.width, size.height, 2461 nsIBaseWindow::eDelayResize); 2462 return NS_OK; 2463 } 2464 2465 void nsFrameLoader::PropagateIsUnderHiddenEmbedderElement( 2466 bool aIsUnderHiddenEmbedderElement) { 2467 bool isUnderHiddenEmbedderElement = true; 2468 if (Document* ownerDoc = GetOwnerDoc()) { 2469 if (PresShell* presShell = ownerDoc->GetPresShell()) { 2470 isUnderHiddenEmbedderElement = presShell->IsUnderHiddenEmbedderElement(); 2471 } 2472 } 2473 2474 isUnderHiddenEmbedderElement |= aIsUnderHiddenEmbedderElement; 2475 2476 BrowsingContext* browsingContext = GetExtantBrowsingContext(); 2477 if (browsingContext && browsingContext->IsUnderHiddenEmbedderElement() != 2478 isUnderHiddenEmbedderElement) { 2479 (void)browsingContext->SetIsUnderHiddenEmbedderElement( 2480 isUnderHiddenEmbedderElement); 2481 } 2482 } 2483 2484 void nsFrameLoader::UpdateRemoteStyle( 2485 mozilla::StyleImageRendering aImageRendering) { 2486 MOZ_DIAGNOSTIC_ASSERT(IsRemoteFrame()); 2487 2488 if (auto* browserBridgeChild = GetBrowserBridgeChild()) { 2489 browserBridgeChild->SendUpdateRemoteStyle(aImageRendering); 2490 } 2491 } 2492 2493 uint32_t nsFrameLoader::LazyWidth() const { 2494 uint32_t lazyWidth = mLazySize.width; 2495 if (nsIFrame* frame = GetPrimaryFrameOfOwningContent()) { 2496 lazyWidth = frame->PresContext()->DevPixelsToIntCSSPixels(lazyWidth); 2497 } 2498 return lazyWidth; 2499 } 2500 2501 uint32_t nsFrameLoader::LazyHeight() const { 2502 uint32_t lazyHeight = mLazySize.height; 2503 if (nsIFrame* frame = GetPrimaryFrameOfOwningContent()) { 2504 lazyHeight = frame->PresContext()->DevPixelsToIntCSSPixels(lazyHeight); 2505 } 2506 return lazyHeight; 2507 } 2508 2509 bool nsFrameLoader::EnsureRemoteBrowser() { 2510 MOZ_ASSERT(IsRemoteFrame()); 2511 return mRemoteBrowser || TryRemoteBrowser(); 2512 } 2513 2514 bool nsFrameLoader::TryRemoteBrowserInternal() { 2515 NS_ASSERTION(!mRemoteBrowser, 2516 "TryRemoteBrowser called with a remote browser already?"); 2517 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess(), 2518 "Remote subframes should only be created using the " 2519 "`CanonicalBrowsingContext::ChangeRemoteness` API"); 2520 2521 AssertSafeToInit(); 2522 2523 if (!mOwnerContent) { 2524 return false; 2525 } 2526 2527 // XXXsmaug Per spec (2014/08/21) frameloader should not work in case the 2528 // element isn't in document, only in shadow dom, but that will change 2529 // https://www.w3.org/Bugs/Public/show_bug.cgi?id=26365#c0 2530 RefPtr<Document> doc = mOwnerContent->GetComposedDoc(); 2531 if (!doc) { 2532 return false; 2533 } 2534 2535 MOZ_RELEASE_ASSERT(!doc->IsResourceDoc(), "We shouldn't even exist"); 2536 2537 // Graphics initialization code relies on having a frame for the 2538 // remote browser case, as we can be inside a popup, which is a different 2539 // widget. 2540 2541 if (!mOwnerContent->GetPrimaryFrame()) { 2542 doc->FlushPendingNotifications(FlushType::Frames); 2543 } 2544 2545 // The flush could have initialized us. 2546 if (mRemoteBrowser) { 2547 return true; 2548 } 2549 2550 // If we've already tried to initialize and failed, don't try again. 2551 if (mInitialized) { 2552 return false; 2553 } 2554 mInitialized = true; 2555 2556 // Ensure the world hasn't changed that much as a result of that. 2557 if (!mOwnerContent || mOwnerContent->OwnerDoc() != doc || 2558 !mOwnerContent->IsInComposedDoc()) { 2559 return false; 2560 } 2561 2562 if (RefPtr<nsFrameLoaderOwner> flo = do_QueryObject(mOwnerContent)) { 2563 RefPtr<nsFrameLoader> fl = flo->GetFrameLoader(); 2564 if (fl != this) { 2565 MOZ_ASSERT_UNREACHABLE( 2566 "Got TryRemoteBrowserInternal but mOwnerContent already has a " 2567 "different frameloader?"); 2568 return false; 2569 } 2570 } 2571 2572 if (!doc->IsActive()) { 2573 // Don't allow subframe loads in non-active documents. 2574 // (See bug 610571 comment 5.) 2575 return false; 2576 } 2577 2578 nsCOMPtr<nsPIDOMWindowOuter> parentWin = doc->GetWindow(); 2579 if (!parentWin) { 2580 return false; 2581 } 2582 2583 nsCOMPtr<nsIDocShell> parentDocShell = parentWin->GetDocShell(); 2584 if (!parentDocShell) { 2585 return false; 2586 } 2587 2588 if (!EnsureBrowsingContextAttached()) { 2589 return false; 2590 } 2591 2592 if (mPendingBrowsingContext->IsTop()) { 2593 mPendingBrowsingContext->InitSessionHistory(); 2594 } 2595 2596 // iframes for JS plugins also get to skip these checks. We control the URL 2597 // that gets loaded, but the load is triggered from the document containing 2598 // the plugin. 2599 // out of process iframes also get to skip this check. 2600 if (!XRE_IsContentProcess()) { 2601 if (parentDocShell->ItemType() != nsIDocShellTreeItem::typeChrome) { 2602 // Allow three exceptions to this rule : 2603 // - about:addons so it can load remote extension options pages 2604 // - about:preferences (in Thunderbird only) so it can load remote 2605 // extension options pages for FileLink providers 2606 // - DevTools webext panels if DevTools is loaded in a content frame 2607 // - DevTools Network Monitor, which uses content frame for HTML request 2608 // previews 2609 // - Chrome mochitests can also do this. 2610 // 2611 // Note that the new frame's message manager will not be a child of the 2612 // chrome window message manager, and, the values of window.top and 2613 // window.parent will be different than they would be for a non-remote 2614 // frame. 2615 nsIURI* parentURI = parentWin->GetDocumentURI(); 2616 if (!parentURI) { 2617 return false; 2618 } 2619 2620 nsAutoCString specIgnoringRef; 2621 if (NS_FAILED(parentURI->GetSpecIgnoringRef(specIgnoringRef))) { 2622 return false; 2623 } 2624 2625 const bool allowed = [&] { 2626 const nsLiteralCString kAllowedURIs[] = { 2627 "about:addons"_ns, 2628 "chrome://mozapps/content/extensions/aboutaddons.html"_ns, 2629 #ifdef MOZ_THUNDERBIRD 2630 "about:3pane"_ns, 2631 "about:message"_ns, 2632 "about:preferences"_ns, 2633 #endif 2634 "chrome://browser/content/webext-panels.xhtml"_ns, 2635 "chrome://devtools/content/netmonitor/index.html"_ns, 2636 "chrome://devtools/content/webconsole/index.html"_ns, 2637 }; 2638 2639 for (const auto& allowedURI : kAllowedURIs) { 2640 if (specIgnoringRef.Equals(allowedURI)) { 2641 return true; 2642 } 2643 } 2644 2645 if (xpc::IsInAutomation() && 2646 StringBeginsWith(specIgnoringRef, 2647 "chrome://mochitests/content/chrome/"_ns)) { 2648 return true; 2649 } 2650 return false; 2651 }(); 2652 2653 if (!allowed) { 2654 NS_WARNING( 2655 nsPrintfCString("Forbidden remote frame from content docshell %s", 2656 specIgnoringRef.get()) 2657 .get()); 2658 return false; 2659 } 2660 } 2661 2662 if (!mOwnerContent->IsXULElement()) { 2663 return false; 2664 } 2665 2666 if (!mOwnerContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type, 2667 nsGkAtoms::content, eIgnoreCase)) { 2668 return false; 2669 } 2670 } 2671 2672 uint32_t chromeFlags = 0; 2673 nsCOMPtr<nsIDocShellTreeOwner> parentOwner; 2674 if (NS_FAILED(parentDocShell->GetTreeOwner(getter_AddRefs(parentOwner))) || 2675 !parentOwner) { 2676 return false; 2677 } 2678 nsCOMPtr<nsIAppWindow> window(do_GetInterface(parentOwner)); 2679 if (window && NS_FAILED(window->GetChromeFlags(&chromeFlags))) { 2680 return false; 2681 } 2682 2683 AUTO_PROFILER_LABEL("nsFrameLoader::TryRemoteBrowser:Create", OTHER); 2684 2685 MutableTabContext context; 2686 nsresult rv = GetNewTabContext(&context); 2687 NS_ENSURE_SUCCESS(rv, false); 2688 2689 RefPtr<Element> ownerElement = mOwnerContent; 2690 2691 RefPtr<BrowserParent> nextRemoteBrowser = 2692 mOpenWindowInfo ? mOpenWindowInfo->GetNextRemoteBrowser() : nullptr; 2693 if (nextRemoteBrowser) { 2694 mRemoteBrowser = new BrowserHost(nextRemoteBrowser); 2695 if (nextRemoteBrowser->GetOwnerElement()) { 2696 MOZ_ASSERT_UNREACHABLE("Shouldn't have an owner element before"); 2697 return false; 2698 } 2699 nextRemoteBrowser->SetOwnerElement(ownerElement); 2700 } else { 2701 RefPtr<ContentParent> contentParent; 2702 if (mChildID != 0) { 2703 ContentProcessManager* cpm = ContentProcessManager::GetSingleton(); 2704 if (!cpm) { 2705 return false; 2706 } 2707 contentParent = cpm->GetContentProcessById(ContentParentId(mChildID)); 2708 } 2709 mRemoteBrowser = 2710 ContentParent::CreateBrowser(context, ownerElement, mRemoteType, 2711 mPendingBrowsingContext, contentParent); 2712 } 2713 if (!mRemoteBrowser) { 2714 return false; 2715 } 2716 2717 MOZ_DIAGNOSTIC_ASSERT(mPendingBrowsingContext == 2718 mRemoteBrowser->GetBrowsingContext()); 2719 2720 mRemoteBrowser->GetBrowsingContext()->Embed(); 2721 InvokeBrowsingContextReadyCallback(); 2722 2723 // Grab the reference to the actor 2724 RefPtr<BrowserParent> browserParent = GetBrowserParent(); 2725 2726 MOZ_ASSERT(browserParent->CanSend(), "BrowserParent cannot send?"); 2727 2728 // We no longer need the remoteType attribute on the frame element. 2729 // The remoteType can be queried by asking the message manager instead. 2730 ownerElement->UnsetAttr(kNameSpaceID_None, nsGkAtoms::RemoteType, false); 2731 2732 // Now that browserParent is set, we can initialize graphics 2733 browserParent->InitRendering(); 2734 2735 MaybeUpdatePrimaryBrowserParent(eBrowserParentChanged); 2736 2737 mChildID = browserParent->Manager()->ChildID(); 2738 2739 nsCOMPtr<nsIDocShellTreeItem> rootItem; 2740 parentDocShell->GetInProcessRootTreeItem(getter_AddRefs(rootItem)); 2741 RefPtr<nsGlobalWindowOuter> rootWin = 2742 nsGlobalWindowOuter::Cast(rootItem->GetWindow()); 2743 2744 if (rootWin && rootWin->IsChromeWindow()) { 2745 browserParent->SetBrowserDOMWindow(rootWin->GetBrowserDOMWindow()); 2746 } 2747 2748 // For xul:browsers, update some settings based on attributes: 2749 if (mOwnerContent->IsXULElement()) { 2750 // Send down the name of the browser through browserParent if it is set. 2751 nsAutoString frameName; 2752 mOwnerContent->GetAttr(nsGkAtoms::name, frameName); 2753 if (nsContentUtils::IsOverridingWindowName(frameName)) { 2754 MOZ_ALWAYS_SUCCEEDS(mPendingBrowsingContext->SetName(frameName)); 2755 } 2756 // Allow scripts to close the window if the browser specified so: 2757 if (mOwnerContent->AttrValueIs(kNameSpaceID_None, 2758 nsGkAtoms::allowscriptstoclose, 2759 nsGkAtoms::_true, eCaseMatters)) { 2760 (void)browserParent->SendAllowScriptsToClose(); 2761 } 2762 } 2763 2764 ReallyLoadFrameScripts(); 2765 2766 return true; 2767 } 2768 2769 bool nsFrameLoader::TryRemoteBrowser() { 2770 // NOTE: Do not add new checks to this function, it exists to ensure we always 2771 // MaybeNotifyCrashed for any errors within TryRemoteBrowserInternal. If new 2772 // checks are added, they should be added into that function, not here. 2773 2774 // Try to create the internal remote browser. 2775 if (TryRemoteBrowserInternal()) { 2776 return true; 2777 } 2778 2779 // We shouldn't TryRemoteBrowser again, even if a check failed before we 2780 // initialize mInitialized within TryRemoteBrowserInternal. 2781 mInitialized = true; 2782 2783 // Check if we should report a browser-crashed error because the browser 2784 // failed to start. 2785 if (XRE_IsParentProcess() && mOwnerContent && mOwnerContent->IsXULElement()) { 2786 MaybeNotifyCrashed(nullptr, ContentParentId(), nullptr); 2787 } 2788 2789 return false; 2790 } 2791 2792 nsIFrame* nsFrameLoader::GetPrimaryFrameOfOwningContent() const { 2793 return mOwnerContent ? mOwnerContent->GetPrimaryFrame() : nullptr; 2794 } 2795 2796 Document* nsFrameLoader::GetOwnerDoc() const { 2797 return mOwnerContent ? mOwnerContent->OwnerDoc() : nullptr; 2798 } 2799 2800 BrowserParent* nsFrameLoader::GetBrowserParent() const { 2801 if (!mRemoteBrowser) { 2802 return nullptr; 2803 } 2804 RefPtr<BrowserHost> browserHost = mRemoteBrowser->AsBrowserHost(); 2805 if (!browserHost) { 2806 return nullptr; 2807 } 2808 return browserHost->GetActor(); 2809 } 2810 2811 BrowserBridgeChild* nsFrameLoader::GetBrowserBridgeChild() const { 2812 if (!mRemoteBrowser) { 2813 return nullptr; 2814 } 2815 RefPtr<BrowserBridgeHost> browserBridgeHost = 2816 mRemoteBrowser->AsBrowserBridgeHost(); 2817 if (!browserBridgeHost) { 2818 return nullptr; 2819 } 2820 return browserBridgeHost->GetActor(); 2821 } 2822 2823 mozilla::layers::LayersId nsFrameLoader::GetLayersId() const { 2824 MOZ_ASSERT(mIsRemoteFrame); 2825 return mRemoteBrowser->GetLayersId(); 2826 } 2827 2828 nsresult nsFrameLoader::DoRemoteStaticClone(nsFrameLoader* aStaticCloneOf, 2829 nsIPrintSettings* aPrintSettings) { 2830 MOZ_ASSERT(aStaticCloneOf->IsRemoteFrame()); 2831 MOZ_ASSERT(aPrintSettings); 2832 auto* cc = ContentChild::GetSingleton(); 2833 if (!cc) { 2834 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess()); 2835 // TODO: Could possibly be implemented without too much effort. 2836 return NS_ERROR_NOT_IMPLEMENTED; 2837 } 2838 BrowsingContext* bcToClone = aStaticCloneOf->GetBrowsingContext(); 2839 if (NS_WARN_IF(!bcToClone)) { 2840 return NS_ERROR_UNEXPECTED; 2841 } 2842 BrowsingContext* bc = GetBrowsingContext(); 2843 MOZ_DIAGNOSTIC_ASSERT(bc); 2844 nsCOMPtr<nsIPrintSettingsService> printSettingsSvc = 2845 do_GetService("@mozilla.org/gfx/printsettings-service;1"); 2846 if (NS_WARN_IF(!printSettingsSvc)) { 2847 return NS_ERROR_UNEXPECTED; 2848 } 2849 embedding::PrintData printData; 2850 nsresult rv = 2851 printSettingsSvc->SerializeToPrintData(aPrintSettings, &printData); 2852 if (NS_WARN_IF(NS_FAILED(rv))) { 2853 return rv; 2854 } 2855 2856 cc->SendCloneDocumentTreeInto(bcToClone, bc, printData); 2857 return NS_OK; 2858 } 2859 2860 nsresult nsFrameLoader::FinishStaticClone( 2861 nsFrameLoader* aStaticCloneOf, nsIPrintSettings* aPrintSettings, 2862 bool* aOutHasInProcessPrintCallbacks) { 2863 MOZ_DIAGNOSTIC_ASSERT( 2864 !nsContentUtils::IsSafeToRunScript(), 2865 "A script blocker should be on the stack while FinishStaticClone is run"); 2866 2867 // NOTE: We don't check `aStaticCloneOf->IsDead()` here, as the nsFrameLoader 2868 // which we're a static clone of may be in the process of being destroyed. It 2869 // won't be fully destroyed when `FinishStaticClone` is called, as a script 2870 // blocker on our caller's stack is preventing it from becoming finalized. 2871 // 2872 // This is quite fragile, but is quite difficult to work around without 2873 // getting print-preview to stop re-cloning and replacing the previewed 2874 // document when changing layout. 2875 if (NS_WARN_IF(IsDead())) { 2876 return NS_ERROR_UNEXPECTED; 2877 } 2878 2879 if (aStaticCloneOf->IsRemoteFrame()) { 2880 return DoRemoteStaticClone(aStaticCloneOf, aPrintSettings); 2881 } 2882 2883 nsIDocShell* origDocShell = aStaticCloneOf->GetDocShell(); 2884 NS_ENSURE_STATE(origDocShell); 2885 2886 nsCOMPtr<Document> doc = origDocShell->GetDocument(); 2887 NS_ENSURE_STATE(doc); 2888 2889 MaybeCreateDocShell(); 2890 RefPtr<nsDocShell> docShell = GetDocShell(); 2891 NS_ENSURE_STATE(docShell); 2892 2893 nsCOMPtr<Document> kungFuDeathGrip = docShell->GetDocument(); 2894 (void)kungFuDeathGrip; 2895 2896 nsCOMPtr<nsIDocumentViewer> viewer; 2897 docShell->GetDocViewer(getter_AddRefs(viewer)); 2898 NS_ENSURE_STATE(viewer); 2899 2900 nsCOMPtr<Document> clonedDoc = doc->CreateStaticClone( 2901 docShell, viewer, aPrintSettings, aOutHasInProcessPrintCallbacks); 2902 2903 return NS_OK; 2904 } 2905 2906 bool nsFrameLoader::DoLoadMessageManagerScript(const nsAString& aURL, 2907 bool aRunInGlobalScope) { 2908 if (auto* browserParent = GetBrowserParent()) { 2909 return browserParent->SendLoadRemoteScript(aURL, aRunInGlobalScope); 2910 } 2911 RefPtr<InProcessBrowserChildMessageManager> browserChild = 2912 GetBrowserChildMessageManager(); 2913 if (browserChild) { 2914 browserChild->LoadFrameScript(aURL, aRunInGlobalScope); 2915 } 2916 return true; 2917 } 2918 2919 class nsAsyncMessageToChild : public nsSameProcessAsyncMessageBase, 2920 public Runnable { 2921 public: 2922 explicit nsAsyncMessageToChild(nsFrameLoader* aFrameLoader) 2923 : mozilla::Runnable("nsAsyncMessageToChild"), 2924 mFrameLoader(aFrameLoader) {} 2925 2926 NS_IMETHOD Run() override { 2927 InProcessBrowserChildMessageManager* browserChild = 2928 mFrameLoader->mChildMessageManager; 2929 // Since bug 1126089, messages can arrive even when the docShell is 2930 // destroyed. Here we make sure that those messages are not delivered. 2931 if (browserChild && browserChild->GetInnerManager() && 2932 mFrameLoader->GetExistingDocShell()) { 2933 JS::Rooted<JSObject*> kungFuDeathGrip(dom::RootingCx(), 2934 browserChild->GetWrapper()); 2935 ReceiveMessage(static_cast<EventTarget*>(browserChild), mFrameLoader, 2936 browserChild->GetInnerManager()); 2937 } 2938 return NS_OK; 2939 } 2940 RefPtr<nsFrameLoader> mFrameLoader; 2941 }; 2942 2943 nsresult nsFrameLoader::DoSendAsyncMessage(const nsAString& aMessage, 2944 StructuredCloneData& aData) { 2945 auto* browserParent = GetBrowserParent(); 2946 if (browserParent) { 2947 ClonedMessageData data; 2948 if (!BuildClonedMessageData(aData, data)) { 2949 MOZ_CRASH(); 2950 return NS_ERROR_DOM_DATA_CLONE_ERR; 2951 } 2952 if (browserParent->SendAsyncMessage(aMessage, data)) { 2953 return NS_OK; 2954 } else { 2955 return NS_ERROR_UNEXPECTED; 2956 } 2957 } 2958 2959 if (mChildMessageManager) { 2960 RefPtr<nsAsyncMessageToChild> ev = new nsAsyncMessageToChild(this); 2961 nsresult rv = ev->Init(aMessage, aData); 2962 if (NS_FAILED(rv)) { 2963 return rv; 2964 } 2965 rv = NS_DispatchToCurrentThread(ev); 2966 if (NS_FAILED(rv)) { 2967 return rv; 2968 } 2969 return rv; 2970 } 2971 2972 // We don't have any targets to send our asynchronous message to. 2973 return NS_ERROR_UNEXPECTED; 2974 } 2975 2976 already_AddRefed<MessageSender> nsFrameLoader::GetMessageManager() { 2977 EnsureMessageManager(); 2978 return do_AddRef(mMessageManager); 2979 } 2980 2981 nsresult nsFrameLoader::EnsureMessageManager() { 2982 NS_ENSURE_STATE(mOwnerContent); 2983 2984 if (mMessageManager) { 2985 return NS_OK; 2986 } 2987 2988 if (!mIsTopLevelContent && !IsRemoteFrame() && 2989 !(mOwnerContent->IsXULElement() && 2990 mOwnerContent->AttrValueIs(kNameSpaceID_None, 2991 nsGkAtoms::forcemessagemanager, 2992 nsGkAtoms::_true, eCaseMatters))) { 2993 return NS_OK; 2994 } 2995 2996 RefPtr<nsGlobalWindowOuter> window = 2997 nsGlobalWindowOuter::Cast(GetOwnerDoc()->GetWindow()); 2998 RefPtr<ChromeMessageBroadcaster> parentManager; 2999 3000 if (window && window->IsChromeWindow()) { 3001 nsAutoString messagemanagergroup; 3002 if (mOwnerContent->IsXULElement() && 3003 mOwnerContent->GetAttr(nsGkAtoms::messagemanagergroup, 3004 messagemanagergroup)) { 3005 parentManager = window->GetGroupMessageManager(messagemanagergroup); 3006 } 3007 3008 if (!parentManager) { 3009 parentManager = window->GetMessageManager(); 3010 } 3011 } else { 3012 parentManager = nsFrameMessageManager::GetGlobalMessageManager(); 3013 } 3014 3015 mMessageManager = new ChromeMessageSender(parentManager); 3016 if (!IsRemoteFrame()) { 3017 nsresult rv = MaybeCreateDocShell(); 3018 if (NS_FAILED(rv)) { 3019 return rv; 3020 } 3021 MOZ_ASSERT(GetDocShell(), 3022 "MaybeCreateDocShell succeeded, but null docShell"); 3023 if (!GetDocShell()) { 3024 return NS_ERROR_FAILURE; 3025 } 3026 mChildMessageManager = InProcessBrowserChildMessageManager::Create( 3027 GetDocShell(), mOwnerContent, mMessageManager); 3028 NS_ENSURE_TRUE(mChildMessageManager, NS_ERROR_UNEXPECTED); 3029 3030 // Set up session store 3031 if (SessionStorePlatformCollection()) { 3032 if (XRE_IsParentProcess() && mIsTopLevelContent) { 3033 mSessionStoreChild = SessionStoreChild::GetOrCreate( 3034 GetExtantBrowsingContext(), mOwnerContent); 3035 } 3036 } 3037 } 3038 return NS_OK; 3039 } 3040 3041 nsresult nsFrameLoader::ReallyLoadFrameScripts() { 3042 nsresult rv = EnsureMessageManager(); 3043 if (NS_WARN_IF(NS_FAILED(rv))) { 3044 return rv; 3045 } 3046 if (mMessageManager) { 3047 mMessageManager->InitWithCallback(this); 3048 } 3049 return NS_OK; 3050 } 3051 3052 already_AddRefed<Element> nsFrameLoader::GetOwnerElement() { 3053 return do_AddRef(mOwnerContent); 3054 } 3055 3056 const LazyLoadFrameResumptionState& 3057 nsFrameLoader::GetLazyLoadFrameResumptionState() { 3058 static const LazyLoadFrameResumptionState sEmpty; 3059 if (auto* iframe = HTMLIFrameElement::FromNode(*mOwnerContent)) { 3060 return iframe->GetLazyLoadFrameResumptionState(); 3061 } 3062 return sEmpty; 3063 } 3064 3065 void nsFrameLoader::SetDetachedSubdocs(WeakPresShellArray&& aDocs) { 3066 mDetachedSubdocs = std::move(aDocs); 3067 } 3068 3069 auto nsFrameLoader::TakeDetachedSubdocs() -> WeakPresShellArray { 3070 return std::move(mDetachedSubdocs); 3071 } 3072 3073 void nsFrameLoader::ApplySandboxFlags(uint32_t sandboxFlags) { 3074 // If our BrowsingContext doesn't exist yet, it means we haven't been 3075 // initialized yet. This method will be called again once we're initialized 3076 // from MaybeCreateDocShell. <iframe> BrowsingContexts are never created as 3077 // initially remote, so we don't need to worry about updating sandbox flags 3078 // for an uninitialized initially-remote iframe. 3079 BrowsingContext* context = GetExtantBrowsingContext(); 3080 if (!context) { 3081 MOZ_ASSERT(!IsRemoteFrame(), 3082 "cannot apply sandbox flags to an uninitialized " 3083 "initially-remote frame"); 3084 return; 3085 } 3086 3087 uint32_t parentSandboxFlags = mOwnerContent->OwnerDoc()->GetSandboxFlags(); 3088 3089 // The child can only add restrictions, never remove them. 3090 sandboxFlags |= parentSandboxFlags; 3091 3092 MOZ_ALWAYS_SUCCEEDS(context->SetSandboxFlags(sandboxFlags)); 3093 } 3094 3095 /* virtual */ 3096 void nsFrameLoader::AttributeChanged(mozilla::dom::Element* aElement, 3097 int32_t aNameSpaceID, nsAtom* aAttribute, 3098 AttrModType, 3099 const nsAttrValue* aOldValue) { 3100 MOZ_ASSERT(mObservingOwnerContent); 3101 3102 if (aElement != mOwnerContent) { 3103 return; 3104 } 3105 3106 if (aNameSpaceID != kNameSpaceID_None || 3107 (aAttribute != TypeAttrName(aElement) && 3108 aAttribute != nsGkAtoms::primary)) { 3109 return; 3110 } 3111 3112 // Note: This logic duplicates a lot of logic in 3113 // MaybeCreateDocshell. We should fix that. 3114 3115 // Notify our enclosing chrome that our type has changed. We only do this 3116 // if our parent is chrome, since in all other cases we're random content 3117 // subframes and the treeowner shouldn't worry about us. 3118 if (!GetDocShell()) { 3119 MaybeUpdatePrimaryBrowserParent(eBrowserParentChanged); 3120 return; 3121 } 3122 3123 nsCOMPtr<nsIDocShellTreeItem> parentItem; 3124 GetDocShell()->GetInProcessParent(getter_AddRefs(parentItem)); 3125 if (!parentItem) { 3126 return; 3127 } 3128 3129 if (parentItem->ItemType() != nsIDocShellTreeItem::typeChrome) { 3130 return; 3131 } 3132 3133 nsCOMPtr<nsIDocShellTreeOwner> parentTreeOwner; 3134 parentItem->GetTreeOwner(getter_AddRefs(parentTreeOwner)); 3135 if (!parentTreeOwner) { 3136 return; 3137 } 3138 3139 bool is_primary = aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::primary, 3140 nsGkAtoms::_true, eIgnoreCase); 3141 3142 // when a content panel is no longer primary, hide any open popups it may have 3143 if (!is_primary) { 3144 nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); 3145 if (pm) { 3146 pm->HidePopupsInDocShell(GetDocShell()); 3147 } 3148 } 3149 3150 parentTreeOwner->ContentShellRemoved(GetDocShell()); 3151 if (aElement->AttrValueIs(kNameSpaceID_None, TypeAttrName(aElement), 3152 nsGkAtoms::content, eIgnoreCase)) { 3153 parentTreeOwner->ContentShellAdded(GetDocShell(), is_primary); 3154 } 3155 } 3156 3157 void nsFrameLoader::RequestUpdatePosition(ErrorResult& aRv) { 3158 if (auto* browserParent = GetBrowserParent()) { 3159 nsresult rv = browserParent->UpdatePosition(); 3160 3161 if (NS_FAILED(rv)) { 3162 aRv.Throw(rv); 3163 } 3164 } 3165 } 3166 3167 SessionStoreParent* nsFrameLoader::GetSessionStoreParent() { 3168 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess()); 3169 if (mSessionStoreChild) { 3170 return static_cast<SessionStoreParent*>( 3171 InProcessChild::ParentActorFor(mSessionStoreChild)); 3172 } 3173 3174 if (BrowserParent* browserParent = GetBrowserParent()) { 3175 return static_cast<SessionStoreParent*>( 3176 SingleManagedOrNull(browserParent->ManagedPSessionStoreParent())); 3177 } 3178 3179 return nullptr; 3180 } 3181 3182 already_AddRefed<Promise> nsFrameLoader::RequestTabStateFlush( 3183 ErrorResult& aRv) { 3184 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess()); 3185 Document* ownerDoc = GetOwnerDoc(); 3186 if (!ownerDoc) { 3187 aRv.ThrowNotSupportedError("No owner document"); 3188 return nullptr; 3189 } 3190 3191 RefPtr<Promise> promise = Promise::Create(ownerDoc->GetOwnerGlobal(), aRv); 3192 if (aRv.Failed()) { 3193 return nullptr; 3194 } 3195 3196 BrowsingContext* browsingContext = GetExtantBrowsingContext(); 3197 if (!browsingContext) { 3198 promise->MaybeResolveWithUndefined(); 3199 return promise.forget(); 3200 } 3201 3202 SessionStoreParent* sessionStoreParent = GetSessionStoreParent(); 3203 if (!sessionStoreParent) { 3204 promise->MaybeResolveWithUndefined(); 3205 return promise.forget(); 3206 } 3207 3208 sessionStoreParent->FlushAllSessionStoreChildren( 3209 [promise]() { promise->MaybeResolveWithUndefined(); }); 3210 3211 return promise.forget(); 3212 } 3213 3214 void nsFrameLoader::RequestFinalTabStateFlush() { 3215 BrowsingContext* context = GetExtantBrowsingContext(); 3216 if (!context || !context->IsTop() || context->Canonical()->IsReplaced()) { 3217 return; 3218 } 3219 3220 RefPtr<CanonicalBrowsingContext> canonical = context->Canonical(); 3221 RefPtr<WindowGlobalParent> wgp = canonical->GetCurrentWindowGlobal(); 3222 RefPtr<Element> embedder = context->GetEmbedderElement(); 3223 3224 RefPtr<SessionStoreParent> sessionStoreParent = GetSessionStoreParent(); 3225 if (!sessionStoreParent) { 3226 canonical->ClearPermanentKey(); 3227 if (wgp) { 3228 wgp->NotifySessionStoreUpdatesComplete(embedder); 3229 } 3230 3231 return; 3232 } 3233 3234 sessionStoreParent->FinalFlushAllSessionStoreChildren( 3235 [canonical, wgp, embedder]() { 3236 if (canonical) { 3237 canonical->ClearPermanentKey(); 3238 } 3239 if (wgp) { 3240 wgp->NotifySessionStoreUpdatesComplete(embedder); 3241 } 3242 }); 3243 } 3244 3245 void nsFrameLoader::RequestEpochUpdate(uint32_t aEpoch) { 3246 BrowsingContext* context = GetExtantBrowsingContext(); 3247 if (context) { 3248 BrowsingContext* top = context->Top(); 3249 (void)top->SetSessionStoreEpoch(aEpoch); 3250 } 3251 } 3252 3253 void nsFrameLoader::RequestSHistoryUpdate() { 3254 if (mSessionStoreChild) { 3255 mSessionStoreChild->UpdateSHistoryChanges(); 3256 return; 3257 } 3258 3259 // If remote browsing (e10s), handle this with the BrowserParent. 3260 if (auto* browserParent = GetBrowserParent()) { 3261 (void)browserParent->SendUpdateSHistory(); 3262 } 3263 } 3264 3265 already_AddRefed<Promise> nsFrameLoader::PrintPreview( 3266 nsIPrintSettings* aPrintSettings, BrowsingContext* aSourceBrowsingContext, 3267 ErrorResult& aRv) { 3268 auto* ownerDoc = GetOwnerDoc(); 3269 if (!ownerDoc) { 3270 aRv.ThrowNotSupportedError("No owner document"); 3271 return nullptr; 3272 } 3273 RefPtr<Promise> promise = Promise::Create(ownerDoc->GetOwnerGlobal(), aRv); 3274 if (!promise) { 3275 return nullptr; 3276 } 3277 3278 #ifndef NS_PRINTING 3279 promise->MaybeRejectWithNotSupportedError("Build does not support printing"); 3280 return promise.forget(); 3281 #else 3282 auto resolve = [promise](PrintPreviewResultInfo aInfo) { 3283 using Orientation = dom::PrintPreviewOrientation; 3284 if (aInfo.sheetCount() > 0) { 3285 PrintPreviewSuccessInfo info; 3286 info.mSheetCount = aInfo.sheetCount(); 3287 info.mTotalPageCount = aInfo.totalPageCount(); 3288 info.mHasSelection = aInfo.hasSelection(); 3289 info.mHasSelfSelection = aInfo.hasSelfSelection(); 3290 info.mIsEmpty = aInfo.isEmpty(); 3291 if (aInfo.printLandscape()) { 3292 info.mOrientation = aInfo.printLandscape().value() 3293 ? Orientation::Landscape 3294 : Orientation::Portrait; 3295 } else { 3296 MOZ_ASSERT(info.mOrientation == Orientation::Unspecified); 3297 } 3298 if (aInfo.pageWidth()) { 3299 info.mPageWidth = aInfo.pageWidth().value(); 3300 } 3301 if (aInfo.pageHeight()) { 3302 info.mPageHeight = aInfo.pageHeight().value(); 3303 } 3304 3305 promise->MaybeResolve(info); 3306 } else { 3307 promise->MaybeRejectWithUnknownError("Print preview failed"); 3308 } 3309 }; 3310 3311 if (auto* browserParent = GetBrowserParent()) { 3312 nsCOMPtr<nsIPrintSettingsService> printSettingsSvc = 3313 do_GetService("@mozilla.org/gfx/printsettings-service;1"); 3314 if (!printSettingsSvc) { 3315 promise->MaybeRejectWithNotSupportedError("No nsIPrintSettingsService"); 3316 return promise.forget(); 3317 } 3318 3319 embedding::PrintData printData; 3320 nsresult rv = 3321 printSettingsSvc->SerializeToPrintData(aPrintSettings, &printData); 3322 if (NS_WARN_IF(NS_FAILED(rv))) { 3323 promise->MaybeReject(ErrorResult(rv)); 3324 return promise.forget(); 3325 } 3326 3327 browserParent->SendPrintPreview(printData, aSourceBrowsingContext) 3328 ->Then( 3329 GetMainThreadSerialEventTarget(), __func__, std::move(resolve), 3330 [promise](const mozilla::ipc::ResponseRejectReason) { 3331 promise->MaybeRejectWithUnknownError("Print preview IPC failed"); 3332 }); 3333 3334 return promise.forget(); 3335 } 3336 3337 RefPtr<nsGlobalWindowOuter> sourceWindow; 3338 if (aSourceBrowsingContext) { 3339 sourceWindow = 3340 nsGlobalWindowOuter::Cast(aSourceBrowsingContext->GetDOMWindow()); 3341 } else { 3342 nsDocShell* ourDocshell = GetExistingDocShell(); 3343 if (NS_WARN_IF(!ourDocshell)) { 3344 promise->MaybeRejectWithNotSupportedError("No print preview docShell"); 3345 return promise.forget(); 3346 } 3347 sourceWindow = nsGlobalWindowOuter::Cast(ourDocshell->GetWindow()); 3348 } 3349 if (NS_WARN_IF(!sourceWindow)) { 3350 promise->MaybeRejectWithNotSupportedError("No print preview source window"); 3351 return promise.forget(); 3352 } 3353 3354 nsCOMPtr<nsIDocShell> docShellToCloneInto = nullptr; 3355 if (aSourceBrowsingContext) { 3356 // We're going to call `Print()` below on a window that is not our own, 3357 // which happens when we are creating a new print preview document instead 3358 // of just applying a settings change to the existing PP document. In this 3359 // case we need to explicitly pass our docShell as the docShell to clone 3360 // into. 3361 docShellToCloneInto = GetExistingDocShell(); 3362 if (NS_WARN_IF(!docShellToCloneInto)) { 3363 promise->MaybeRejectWithNotSupportedError("No print preview docShell"); 3364 return promise.forget(); 3365 } 3366 3367 // We need to make sure we're displayed so that the view tree ends up right. 3368 RefPtr<BrowsingContext> bc = docShellToCloneInto->GetBrowsingContext(); 3369 if (NS_WARN_IF(!bc)) { 3370 promise->MaybeRejectWithNotSupportedError( 3371 "No print preview browsing context"); 3372 return promise.forget(); 3373 } 3374 3375 RefPtr<Element> embedder = bc->GetEmbedderElement(); 3376 if (NS_WARN_IF(!embedder)) { 3377 promise->MaybeRejectWithNotSupportedError( 3378 "Trying to clone into a frameloader with no element?"); 3379 return promise.forget(); 3380 } 3381 3382 nsIFrame* frame = embedder->GetPrimaryFrame(FlushType::Frames); 3383 if (NS_WARN_IF(!frame)) { 3384 promise->MaybeRejectWithNotSupportedError("Frame is not being displayed"); 3385 return promise.forget(); 3386 } 3387 } 3388 3389 // Unfortunately we can't pass `resolve` directly here because IPDL, for now, 3390 // unfortunately generates slightly different parameter types for functions 3391 // taking PrintPreviewResultInfo in PBrowserParent vs. PBrowserChild. 3392 ErrorResult rv; 3393 sourceWindow->Print( 3394 aPrintSettings, 3395 /* aRemotePrintJob = */ nullptr, 3396 /* aListener = */ nullptr, docShellToCloneInto, 3397 nsGlobalWindowOuter::IsPreview::Yes, 3398 nsGlobalWindowOuter::IsForWindowDotPrint::No, 3399 [resolve](const PrintPreviewResultInfo& aInfo) { resolve(aInfo); }, 3400 nullptr, rv); 3401 if (NS_WARN_IF(rv.Failed())) { 3402 promise->MaybeReject(std::move(rv)); 3403 } 3404 3405 return promise.forget(); 3406 #endif 3407 } 3408 3409 void nsFrameLoader::ExitPrintPreview() { 3410 #ifdef NS_PRINTING 3411 if (auto* browserParent = GetBrowserParent()) { 3412 (void)browserParent->SendExitPrintPreview(); 3413 return; 3414 } 3415 if (NS_WARN_IF(!GetExistingDocShell())) { 3416 return; 3417 } 3418 nsCOMPtr<nsIWebBrowserPrint> webBrowserPrint = 3419 do_GetInterface(ToSupports(GetExistingDocShell()->GetWindow())); 3420 if (NS_WARN_IF(!webBrowserPrint)) { 3421 return; 3422 } 3423 webBrowserPrint->ExitPrintPreview(); 3424 #endif 3425 } 3426 3427 already_AddRefed<nsIRemoteTab> nsFrameLoader::GetRemoteTab() { 3428 if (!mRemoteBrowser) { 3429 return nullptr; 3430 } 3431 if (auto* browserHost = mRemoteBrowser->AsBrowserHost()) { 3432 return do_AddRef(browserHost); 3433 } 3434 return nullptr; 3435 } 3436 3437 already_AddRefed<nsILoadContext> nsFrameLoader::GetLoadContext() { 3438 return do_AddRef(GetBrowsingContext()); 3439 } 3440 3441 BrowsingContext* nsFrameLoader::GetBrowsingContext() { 3442 if (!mInitialized) { 3443 if (IsRemoteFrame()) { 3444 (void)EnsureRemoteBrowser(); 3445 } else if (mOwnerContent) { 3446 (void)MaybeCreateDocShell(); 3447 } 3448 } 3449 MOZ_ASSERT(mInitialized || mDestroyCalled); 3450 return GetExtantBrowsingContext(); 3451 } 3452 3453 BrowsingContext* nsFrameLoader::GetExtantBrowsingContext() { 3454 if (!mPendingBrowsingContext) { 3455 // If mPendingBrowsingContext is null then the frame loader is being 3456 // destroyed (nsFrameLoader::DestroyDocShell was called), so return null 3457 // here in that case. 3458 return nullptr; 3459 } 3460 3461 if (!mInitialized || !mPendingBrowsingContext->EverAttached()) { 3462 // Don't return the pending BrowsingContext until this nsFrameLoader has 3463 // been initialized, and the BC was attached. 3464 return nullptr; 3465 } 3466 3467 return mPendingBrowsingContext; 3468 } 3469 3470 void nsFrameLoader::StartPersistence( 3471 BrowsingContext* aContext, nsIWebBrowserPersistDocumentReceiver* aRecv, 3472 ErrorResult& aRv) { 3473 MOZ_ASSERT(aRecv); 3474 RefPtr<BrowsingContext> context = aContext ? aContext : GetBrowsingContext(); 3475 3476 if (!context || !context->IsInSubtreeOf(GetBrowsingContext())) { 3477 aRecv->OnError(NS_ERROR_NO_CONTENT); 3478 return; 3479 } 3480 3481 if (!context->GetDocShell() && XRE_IsParentProcess()) { 3482 CanonicalBrowsingContext* canonical = 3483 CanonicalBrowsingContext::Cast(context); 3484 if (!canonical->GetCurrentWindowGlobal()) { 3485 aRecv->OnError(NS_ERROR_NO_CONTENT); 3486 return; 3487 } 3488 RefPtr<BrowserParent> browserParent = 3489 canonical->GetCurrentWindowGlobal()->GetBrowserParent(); 3490 browserParent->StartPersistence(canonical, aRecv, aRv); 3491 return; 3492 } 3493 3494 nsCOMPtr<Document> foundDoc = context->GetDocument(); 3495 3496 if (!foundDoc) { 3497 aRecv->OnError(NS_ERROR_NO_CONTENT); 3498 } else { 3499 nsCOMPtr<nsIWebBrowserPersistDocument> pdoc = 3500 new mozilla::WebBrowserPersistLocalDocument(foundDoc); 3501 aRecv->OnDocumentReady(pdoc); 3502 } 3503 } 3504 3505 void nsFrameLoader::MaybeUpdatePrimaryBrowserParent( 3506 BrowserParentChange aChange) { 3507 if (!mOwnerContent || !mRemoteBrowser) { 3508 return; 3509 } 3510 3511 RefPtr<BrowserHost> browserHost = mRemoteBrowser->AsBrowserHost(); 3512 if (!browserHost) { 3513 return; 3514 } 3515 3516 nsCOMPtr<nsIDocShell> docShell = mOwnerContent->OwnerDoc()->GetDocShell(); 3517 if (!docShell) { 3518 return; 3519 } 3520 3521 BrowsingContext* browsingContext = docShell->GetBrowsingContext(); 3522 if (!browsingContext->IsChrome()) { 3523 return; 3524 } 3525 3526 nsCOMPtr<nsIDocShellTreeOwner> parentTreeOwner; 3527 docShell->GetTreeOwner(getter_AddRefs(parentTreeOwner)); 3528 if (!parentTreeOwner) { 3529 return; 3530 } 3531 3532 if (!mObservingOwnerContent) { 3533 mOwnerContent->AddMutationObserver(this); 3534 mObservingOwnerContent = true; 3535 } 3536 3537 parentTreeOwner->RemoteTabRemoved(browserHost); 3538 if (aChange == eBrowserParentChanged) { 3539 bool isPrimary = mOwnerContent->AttrValueIs( 3540 kNameSpaceID_None, nsGkAtoms::primary, nsGkAtoms::_true, eIgnoreCase); 3541 parentTreeOwner->RemoteTabAdded(browserHost, isPrimary); 3542 } 3543 } 3544 3545 nsresult nsFrameLoader::GetNewTabContext(MutableTabContext* aTabContext, 3546 nsIURI* aURI) { 3547 nsCOMPtr<nsIDocShell> docShell = mOwnerContent->OwnerDoc()->GetDocShell(); 3548 nsCOMPtr<nsILoadContext> parentContext = do_QueryInterface(docShell); 3549 NS_ENSURE_STATE(parentContext); 3550 3551 MOZ_ASSERT(mPendingBrowsingContext->EverAttached()); 3552 3553 uint64_t chromeOuterWindowID = 0; 3554 3555 nsCOMPtr<nsPIWindowRoot> root = 3556 nsContentUtils::GetWindowRoot(mOwnerContent->OwnerDoc()); 3557 if (root) { 3558 nsPIDOMWindowOuter* outerWin = root->GetWindow(); 3559 if (outerWin) { 3560 chromeOuterWindowID = outerWin->WindowID(); 3561 } 3562 } 3563 3564 uint32_t maxTouchPoints = BrowserParent::GetMaxTouchPoints(mOwnerContent); 3565 3566 bool tabContextUpdated = 3567 aTabContext->SetTabContext(chromeOuterWindowID, maxTouchPoints); 3568 NS_ENSURE_STATE(tabContextUpdated); 3569 3570 return NS_OK; 3571 } 3572 3573 nsresult nsFrameLoader::PopulateOriginContextIdsFromAttributes( 3574 OriginAttributes& aAttr) { 3575 // Only XUL are allowed to set context IDs 3576 uint32_t namespaceID = mOwnerContent->GetNameSpaceID(); 3577 if (namespaceID != kNameSpaceID_XUL) { 3578 return NS_OK; 3579 } 3580 3581 nsAutoString attributeValue; 3582 if (aAttr.mUserContextId == 3583 nsIScriptSecurityManager::DEFAULT_USER_CONTEXT_ID && 3584 mOwnerContent->GetAttr(nsGkAtoms::usercontextid, attributeValue) && 3585 !attributeValue.IsEmpty()) { 3586 nsresult rv; 3587 aAttr.mUserContextId = attributeValue.ToInteger(&rv); 3588 NS_ENSURE_SUCCESS(rv, rv); 3589 } 3590 3591 if (aAttr.mGeckoViewSessionContextId.IsEmpty() && 3592 mOwnerContent->GetAttr(nsGkAtoms::geckoViewSessionContextId, 3593 attributeValue) && 3594 !attributeValue.IsEmpty()) { 3595 // XXX: Should we check the format from `GeckoViewNavigation.sys.mjs` here? 3596 aAttr.mGeckoViewSessionContextId = attributeValue; 3597 } 3598 3599 return NS_OK; 3600 } 3601 3602 ProcessMessageManager* nsFrameLoader::GetProcessMessageManager() const { 3603 if (auto* browserParent = GetBrowserParent()) { 3604 return browserParent->Manager()->GetMessageManager(); 3605 } 3606 return nullptr; 3607 }; 3608 3609 JSObject* nsFrameLoader::WrapObject(JSContext* cx, 3610 JS::Handle<JSObject*> aGivenProto) { 3611 JS::Rooted<JSObject*> result(cx); 3612 FrameLoader_Binding::Wrap(cx, this, this, aGivenProto, &result); 3613 return result; 3614 } 3615 3616 void nsFrameLoader::SetWillChangeProcess() { 3617 mWillChangeProcess = true; 3618 3619 if (IsRemoteFrame()) { 3620 if (auto* browserParent = GetBrowserParent()) { 3621 if (auto* bc = CanonicalBrowsingContext::Cast(mPendingBrowsingContext); 3622 bc && bc->EverAttached()) { 3623 bc->StartUnloadingHost(browserParent->Manager()->ChildID()); 3624 bc->SetCurrentBrowserParent(nullptr); 3625 } 3626 // OOP Browser - Go directly over Browser Parent 3627 (void)browserParent->SendWillChangeProcess(); 3628 } else if (auto* browserBridgeChild = GetBrowserBridgeChild()) { 3629 // OOP IFrame - Through Browser Bridge Parent, set on browser child 3630 (void)browserBridgeChild->SendWillChangeProcess(); 3631 } 3632 return; 3633 } 3634 3635 // In process 3636 RefPtr<nsDocShell> docshell = GetDocShell(); 3637 MOZ_ASSERT(docshell); 3638 docshell->SetWillChangeProcess(); 3639 } 3640 3641 static mozilla::Result<bool, nsresult> BuildIDMismatchMemoryAndDisk() { 3642 if (const char* forceMismatch = PR_GetEnv("MOZ_FORCE_BUILDID_MISMATCH")) { 3643 if (forceMismatch[0] == '1') { 3644 NS_WARNING("Forcing a buildid mismatch"); 3645 return true; 3646 } 3647 } 3648 3649 #if defined(ANDROID) 3650 // Android packages on installation will stop existing instance, so we 3651 // cannot run into this problem. 3652 return false; 3653 #else 3654 3655 # if defined(XP_WIN) 3656 { 3657 // Windows Store packages cannot run into this problem. 3658 nsCOMPtr<nsIPropertyBag2> infoService = 3659 do_GetService("@mozilla.org/system-info;1"); 3660 MOZ_ASSERT(infoService, "Could not find a system info service"); 3661 bool isMSIX; 3662 nsresult rv = infoService->GetPropertyAsBool(u"isPackagedApp"_ns, &isMSIX); 3663 if (NS_SUCCEEDED(rv)) { 3664 if (isMSIX) { 3665 return false; 3666 } 3667 } 3668 } 3669 # endif // XP_WIN 3670 3671 nsresult rv; 3672 nsCOMPtr<nsIFile> file; 3673 3674 rv = NS_GetSpecialDirectory(NS_GRE_BIN_DIR, getter_AddRefs(file)); 3675 MOZ_TRY(rv); 3676 3677 rv = file->Append(XUL_DLL u""_ns); 3678 MOZ_TRY(rv); 3679 3680 nsAutoString xul; 3681 rv = file->GetPath(xul); 3682 MOZ_TRY(rv); 3683 3684 nsCString installedBuildID; 3685 nsCString section_name(MOZ_BUILDID_SECTION_NAME); 3686 rv = read_toolkit_buildid_from_file(&xul, §ion_name, &installedBuildID); 3687 MOZ_TRY(rv); 3688 3689 return (installedBuildID != PlatformBuildID()); 3690 #endif // !ANDROID 3691 } 3692 3693 void nsFrameLoader::MaybeNotifyCrashed(BrowsingContext* aBrowsingContext, 3694 ContentParentId aChildID, 3695 mozilla::ipc::MessageChannel* aChannel) { 3696 if (mTabProcessCrashFired) { 3697 return; 3698 } 3699 3700 if (mPendingBrowsingContext == aBrowsingContext) { 3701 mTabProcessCrashFired = true; 3702 } 3703 3704 // Fire the crashed observer notification. 3705 nsCOMPtr<nsIObserverService> os = services::GetObserverService(); 3706 if (!os) { 3707 return; 3708 } 3709 3710 os->NotifyObservers(ToSupports(this), "oop-frameloader-crashed", nullptr); 3711 3712 // Check our owner element still references us. If it's moved, on, events 3713 // don't need to be fired. 3714 RefPtr<nsFrameLoaderOwner> owner = do_QueryObject(mOwnerContent); 3715 if (!owner) { 3716 return; 3717 } 3718 3719 RefPtr<nsFrameLoader> currentFrameLoader = owner->GetFrameLoader(); 3720 if (currentFrameLoader != this) { 3721 return; 3722 } 3723 3724 #if defined(MOZ_TELEMETRY_REPORTING) 3725 bool sendTelemetryFalsePositive = false, sendTelemetryTrueMismatch = false; 3726 3727 static bool haveSentTelemetryFalsePositive = false, 3728 haveSentTelemetryTrueMismatch = false; 3729 #endif // defined(MOZ_TELEMETRY_REPORTING) 3730 3731 // Fire the actual crashed event. 3732 nsString eventName; 3733 if (aChannel && !aChannel->DoBuildIDsMatch()) { 3734 auto changedOrError = BuildIDMismatchMemoryAndDisk(); 3735 if (changedOrError.isErr()) { 3736 NS_WARNING("Error while checking buildid mismatch"); 3737 eventName = u"oop-browser-crashed"_ns; 3738 } else { 3739 bool aChanged = changedOrError.unwrap(); 3740 if (aChanged) { 3741 NS_WARNING("True build ID mismatch"); 3742 eventName = u"oop-browser-buildid-mismatch"_ns; 3743 #if defined(MOZ_TELEMETRY_REPORTING) 3744 sendTelemetryTrueMismatch = true; 3745 #endif // defined(MOZ_TELEMETRY_REPORTING) 3746 } else { 3747 NS_WARNING("build ID mismatch false alarm"); 3748 eventName = u"oop-browser-crashed"_ns; 3749 #if defined(MOZ_TELEMETRY_REPORTING) 3750 sendTelemetryFalsePositive = true; 3751 #endif // defined(MOZ_TELEMETRY_REPORTING) 3752 } 3753 } 3754 } else { 3755 NS_WARNING("No build ID mismatch"); 3756 eventName = u"oop-browser-crashed"_ns; 3757 } 3758 3759 #if defined(MOZ_TELEMETRY_REPORTING) 3760 if (sendTelemetryFalsePositive && !haveSentTelemetryFalsePositive) { 3761 glean::dom_contentprocess::build_id_mismatch_false_positive.Add(1); 3762 haveSentTelemetryFalsePositive = true; 3763 } 3764 3765 if (sendTelemetryTrueMismatch && !haveSentTelemetryTrueMismatch) { 3766 glean::dom_contentprocess::build_id_mismatch.Add(1); 3767 haveSentTelemetryTrueMismatch = true; 3768 } 3769 #endif // defined(MOZ_TELEMETRY_REPORTING) 3770 3771 FrameCrashedEventInit init; 3772 init.mBubbles = true; 3773 init.mCancelable = true; 3774 if (aBrowsingContext) { 3775 init.mBrowsingContextId = aBrowsingContext->Id(); 3776 init.mIsTopFrame = aBrowsingContext->IsTop(); 3777 init.mChildID = aChildID; 3778 } 3779 3780 RefPtr<FrameCrashedEvent> event = FrameCrashedEvent::Constructor( 3781 mOwnerContent->OwnerDoc(), eventName, init); 3782 event->SetTrusted(true); 3783 3784 RefPtr<Element> ownerContent = mOwnerContent; 3785 EventDispatcher::DispatchDOMEvent(ownerContent, nullptr, event, nullptr, 3786 nullptr); 3787 } 3788 3789 bool nsFrameLoader::EnsureBrowsingContextAttached() { 3790 nsresult rv; 3791 3792 Document* parentDoc = mOwnerContent->OwnerDoc(); 3793 MOZ_ASSERT(parentDoc); 3794 BrowsingContext* parentContext = parentDoc->GetBrowsingContext(); 3795 MOZ_ASSERT(parentContext); 3796 3797 // Inherit the `use` flags from our parent BrowsingContext. 3798 bool usePrivateBrowsing = parentContext->UsePrivateBrowsing(); 3799 bool useRemoteSubframes = parentContext->UseRemoteSubframes(); 3800 bool useRemoteTabs = parentContext->UseRemoteTabs(); 3801 3802 // Determine the exact OriginAttributes which should be used for our 3803 // BrowsingContext. This will be used to initialize OriginAttributes if the 3804 // BrowsingContext has not already been created. 3805 OriginAttributes attrs; 3806 if (mPendingBrowsingContext->IsContent()) { 3807 if (mPendingBrowsingContext->GetParent()) { 3808 MOZ_ASSERT(mPendingBrowsingContext->GetParent() == parentContext); 3809 parentContext->GetOriginAttributes(attrs); 3810 } 3811 3812 // Inherit the `mFirstPartyDomain` flag from our parent document's result 3813 // principal, if it was set. 3814 if (parentContext->IsContent() && 3815 !parentDoc->NodePrincipal()->IsSystemPrincipal()) { 3816 OriginAttributes docAttrs = 3817 parentDoc->NodePrincipal()->OriginAttributesRef(); 3818 // We only want to inherit firstPartyDomain here, other attributes should 3819 // be constant. 3820 MOZ_ASSERT(attrs.EqualsIgnoringFPD(docAttrs)); 3821 attrs.mFirstPartyDomain = docAttrs.mFirstPartyDomain; 3822 } 3823 3824 // Inherit the PrivateBrowsing flag across content/chrome boundaries. 3825 attrs.SyncAttributesWithPrivateBrowsing(usePrivateBrowsing); 3826 3827 // A <browser> element may have overridden userContextId or 3828 // geckoViewUserContextId. 3829 rv = PopulateOriginContextIdsFromAttributes(attrs); 3830 if (NS_WARN_IF(NS_FAILED(rv))) { 3831 return false; 3832 } 3833 } 3834 3835 // If we've already been attached, return. 3836 if (mPendingBrowsingContext->EverAttached()) { 3837 MOZ_DIAGNOSTIC_ASSERT(mPendingBrowsingContext->UsePrivateBrowsing() == 3838 usePrivateBrowsing); 3839 MOZ_DIAGNOSTIC_ASSERT(mPendingBrowsingContext->UseRemoteTabs() == 3840 useRemoteTabs); 3841 MOZ_DIAGNOSTIC_ASSERT(mPendingBrowsingContext->UseRemoteSubframes() == 3842 useRemoteSubframes); 3843 // Don't assert that our OriginAttributes match, as we could have different 3844 // OriginAttributes in the case where we were opened using window.open. 3845 return true; 3846 } 3847 3848 // Initialize non-synced OriginAttributes and related fields. 3849 rv = mPendingBrowsingContext->SetOriginAttributes(attrs); 3850 NS_ENSURE_SUCCESS(rv, false); 3851 rv = mPendingBrowsingContext->SetUsePrivateBrowsing(usePrivateBrowsing); 3852 NS_ENSURE_SUCCESS(rv, false); 3853 rv = mPendingBrowsingContext->SetRemoteTabs(useRemoteTabs); 3854 NS_ENSURE_SUCCESS(rv, false); 3855 rv = mPendingBrowsingContext->SetRemoteSubframes(useRemoteSubframes); 3856 NS_ENSURE_SUCCESS(rv, false); 3857 3858 // Finish attaching. 3859 mPendingBrowsingContext->EnsureAttached(); 3860 return true; 3861 } 3862 3863 void nsFrameLoader::InvokeBrowsingContextReadyCallback() { 3864 if (mOpenWindowInfo) { 3865 if (RefPtr<nsIBrowsingContextReadyCallback> callback = 3866 mOpenWindowInfo->BrowsingContextReadyCallback()) { 3867 callback->BrowsingContextReady(mPendingBrowsingContext); 3868 } 3869 } 3870 }