Backed out changeset ce62de041fca (bug 1860041) for causing failures on test_focus_di...
[gecko.git] / dom / base / nsFrameLoader.cpp
blobf6c7b279638fe7cb82536b08cd34d86394bbc902
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/. */
7 /*
8 * Class for managing loading of a subframe (creation of the docshell,
9 * handling of loads in it, recursion-checking).
12 #include "nsFrameLoader.h"
14 #include "base/basictypes.h"
16 #include "prenv.h"
18 #include "nsDocShell.h"
19 #include "nsIContentInlines.h"
20 #include "nsIContentViewer.h"
21 #include "nsIPrintSettings.h"
22 #include "nsIPrintSettingsService.h"
23 #include "mozilla/dom/Document.h"
24 #include "nsPIDOMWindow.h"
25 #include "nsIWebNavigation.h"
26 #include "nsIWebProgress.h"
27 #include "nsIDocShell.h"
28 #include "nsIDocShellTreeOwner.h"
29 #include "nsDocShellLoadState.h"
30 #include "nsIBaseWindow.h"
31 #include "nsIBrowser.h"
32 #include "nsContentUtils.h"
33 #include "nsUnicharUtils.h"
34 #include "nsIScriptGlobalObject.h"
35 #include "nsIScriptSecurityManager.h"
36 #include "nsFrameLoaderOwner.h"
37 #include "nsIFrame.h"
38 #include "nsIScrollableFrame.h"
39 #include "nsSubDocumentFrame.h"
40 #include "nsError.h"
41 #include "nsIAppWindow.h"
42 #include "nsIMozBrowserFrame.h"
43 #include "nsIScriptError.h"
44 #include "nsGlobalWindowInner.h"
45 #include "nsGlobalWindowOuter.h"
46 #include "nsHTMLDocument.h"
47 #include "nsPIWindowRoot.h"
48 #include "nsLayoutUtils.h"
49 #include "nsView.h"
50 #include "nsBaseWidget.h"
51 #include "nsQueryObject.h"
52 #include "ReferrerInfo.h"
53 #include "nsIOpenWindowInfo.h"
54 #include "nsISHistory.h"
55 #include "nsIURI.h"
56 #include "nsIXULRuntime.h"
57 #include "nsNetUtil.h"
58 #include "nsFocusManager.h"
59 #include "nsIINIParser.h"
60 #include "nsAppRunner.h"
61 #include "nsDirectoryService.h"
62 #include "nsDirectoryServiceDefs.h"
64 #include "nsGkAtoms.h"
65 #include "nsNameSpaceManager.h"
67 #include "nsThreadUtils.h"
69 #include "InProcessBrowserChildMessageManager.h"
71 #include "ContentParent.h"
72 #include "BrowserParent.h"
73 #include "mozilla/AsyncEventDispatcher.h"
74 #include "mozilla/BasePrincipal.h"
75 #include "mozilla/ExpandedPrincipal.h"
76 #include "mozilla/FlushType.h"
77 #include "mozilla/HTMLEditor.h"
78 #include "mozilla/NullPrincipal.h"
79 #include "mozilla/Preferences.h"
80 #include "mozilla/PresShell.h"
81 #include "mozilla/PresShellInlines.h"
82 #include "mozilla/ProcessPriorityManager.h"
83 #include "mozilla/ScopeExit.h"
84 #include "mozilla/StaticPrefs_fission.h"
85 #include "mozilla/Unused.h"
86 #include "mozilla/dom/BrowsingContext.h"
87 #include "mozilla/dom/ChromeMessageSender.h"
88 #include "mozilla/dom/Element.h"
89 #include "mozilla/dom/FrameCrashedEvent.h"
90 #include "mozilla/dom/FrameLoaderBinding.h"
91 #include "mozilla/dom/InProcessChild.h"
92 #include "mozilla/dom/MozFrameLoaderOwnerBinding.h"
93 #include "mozilla/dom/PBrowser.h"
94 #include "mozilla/dom/SessionHistoryEntry.h"
95 #include "mozilla/dom/SessionStoreChild.h"
96 #include "mozilla/dom/SessionStoreParent.h"
97 #include "mozilla/dom/SessionStoreUtils.h"
98 #include "mozilla/dom/WindowGlobalParent.h"
99 #include "mozilla/dom/XULFrameElement.h"
100 #include "mozilla/gfx/CrossProcessPaint.h"
101 #include "mozilla/ProfilerLabels.h"
102 #include "nsGenericHTMLFrameElement.h"
104 #include "jsapi.h"
105 #include "mozilla/dom/HTMLIFrameElement.h"
106 #include "nsSandboxFlags.h"
107 #include "mozilla/layers/CompositorBridgeChild.h"
108 #include "mozilla/dom/CustomEvent.h"
110 #include "mozilla/dom/ipc/StructuredCloneData.h"
111 #include "mozilla/WebBrowserPersistLocalDocument.h"
112 #include "mozilla/dom/Promise.h"
113 #include "mozilla/dom/PromiseNativeHandler.h"
114 #include "mozilla/dom/ChildSHistory.h"
115 #include "mozilla/dom/CanonicalBrowsingContext.h"
116 #include "mozilla/dom/ContentChild.h"
117 #include "mozilla/dom/ContentProcessManager.h"
118 #include "mozilla/dom/BrowserBridgeChild.h"
119 #include "mozilla/dom/BrowserHost.h"
120 #include "mozilla/dom/BrowserBridgeHost.h"
121 #include "mozilla/dom/BrowsingContextGroup.h"
123 #include "mozilla/dom/SessionStorageManager.h"
124 #include "mozilla/ipc/BackgroundChild.h"
125 #include "mozilla/ipc/PBackgroundChild.h"
126 #include "mozilla/dom/PBackgroundSessionStorageCache.h"
127 #include "mozilla/ipc/BackgroundUtils.h"
129 #include "mozilla/dom/HTMLBodyElement.h"
131 #include "mozilla/ContentPrincipal.h"
133 #include "nsXULPopupManager.h"
135 #ifdef NS_PRINTING
136 # include "nsIWebBrowserPrint.h"
137 #endif
139 #if defined(MOZ_TELEMETRY_REPORTING)
140 # include "mozilla/Telemetry.h"
141 #endif // defined(MOZ_TELEMETRY_REPORTING)
143 using namespace mozilla;
144 using namespace mozilla::hal;
145 using namespace mozilla::dom;
146 using namespace mozilla::dom::ipc;
147 using namespace mozilla::ipc;
148 using namespace mozilla::layers;
149 using namespace mozilla::layout;
150 using ViewID = ScrollableLayerGuid::ViewID;
152 using PrintPreviewResolver = std::function<void(const PrintPreviewResultInfo&)>;
154 // Bug 8065: Limit content frame depth to some reasonable level. This
155 // does not count chrome frames when determining depth, nor does it
156 // prevent chrome recursion. Number is fairly arbitrary, but meant to
157 // keep number of shells to a reasonable number on accidental recursion with a
158 // small (but not 1) branching factor. With large branching factors the number
159 // of shells can rapidly become huge and run us out of memory. To solve that,
160 // we'd need to re-institute a fixed version of bug 98158.
161 #define MAX_DEPTH_CONTENT_FRAMES 10
163 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsFrameLoader, mPendingBrowsingContext,
164 mMessageManager, mChildMessageManager,
165 mRemoteBrowser, mSessionStoreChild)
166 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFrameLoader)
167 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFrameLoader)
169 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFrameLoader)
170 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
171 NS_INTERFACE_MAP_ENTRY_CONCRETE(nsFrameLoader)
172 NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
173 NS_INTERFACE_MAP_ENTRY(nsISupports)
174 NS_INTERFACE_MAP_END
176 nsFrameLoader::nsFrameLoader(Element* aOwner, BrowsingContext* aBrowsingContext,
177 bool aIsRemoteFrame, bool aNetworkCreated)
178 : mPendingBrowsingContext(aBrowsingContext),
179 mOwnerContent(aOwner),
180 mDetachedSubdocFrame(nullptr),
181 mPendingSwitchID(0),
182 mChildID(0),
183 mRemoteType(NOT_REMOTE_TYPE),
184 mInitialized(false),
185 mDepthTooGreat(false),
186 mIsTopLevelContent(false),
187 mDestroyCalled(false),
188 mNeedsAsyncDestroy(false),
189 mInSwap(false),
190 mInShow(false),
191 mHideCalled(false),
192 mNetworkCreated(aNetworkCreated),
193 mLoadingOriginalSrc(false),
194 mRemoteBrowserShown(false),
195 mIsRemoteFrame(aIsRemoteFrame),
196 mWillChangeProcess(false),
197 mObservingOwnerContent(false),
198 mHadDetachedFrame(false),
199 mTabProcessCrashFired(false) {
200 nsCOMPtr<nsFrameLoaderOwner> owner = do_QueryInterface(aOwner);
201 owner->AttachFrameLoader(this);
204 nsFrameLoader::~nsFrameLoader() {
205 if (mMessageManager) {
206 mMessageManager->Disconnect();
209 MOZ_ASSERT(!mOwnerContent);
210 MOZ_RELEASE_ASSERT(mDestroyCalled);
213 static nsAtom* TypeAttrName(Element* aOwnerContent) {
214 return aOwnerContent->IsXULElement() ? nsGkAtoms::type
215 : nsGkAtoms::mozframetype;
218 static void GetFrameName(Element* aOwnerContent, nsAString& aFrameName) {
219 int32_t namespaceID = aOwnerContent->GetNameSpaceID();
220 if (namespaceID == kNameSpaceID_XHTML && !aOwnerContent->IsInHTMLDocument()) {
221 aOwnerContent->GetAttr(nsGkAtoms::id, aFrameName);
222 } else {
223 aOwnerContent->GetAttr(nsGkAtoms::name, aFrameName);
224 // XXX if no NAME then use ID, after a transition period this will be
225 // changed so that XUL only uses ID too (bug 254284).
226 if (aFrameName.IsEmpty() && namespaceID == kNameSpaceID_XUL) {
227 aOwnerContent->GetAttr(nsGkAtoms::id, aFrameName);
232 // If this method returns true, the nsFrameLoader will act as a boundary, as is
233 // the case for <iframe mozbrowser> and <browser type="content"> elements.
235 // # Historical Notes (10 April 2019)
237 // In the past, this boundary was defined by the "typeContent" and "typeChrome"
238 // nsIDocShellTreeItem types. There was only ever a single split in the tree,
239 // and it occurred at the boundary between these two types of docshells. When
240 // <iframe mozbrowser> was introduced, it was given special casing to make it
241 // act like a second boundary, without having to change the existing code.
243 // The about:addons page, which is loaded within a content browser, then added a
244 // remote <browser type="content" remote="true"> element. When remote, this
245 // would also act as a mechanism for creating a disjoint tree, due to the
246 // process keeping the embedder and embedee separate.
248 // However, when initial out-of-process iframe support was implemented, this
249 // codepath became a risk, as it could've caused the oop iframe remote
250 // WindowProxy code to be activated for the addons page. This was fixed by
251 // extendng the isolation logic previously reserved to <iframe mozbrowser> to
252 // also cover <browser> elements with the explicit `remote` property loaded in
253 // content.
255 // To keep these boundaries clear, and allow them to work in a cross-process
256 // manner, they are no longer handled by typeContent and typeChrome. Instead,
257 // the actual BrowsingContext tree is broken at these edges.
258 static bool IsTopContent(BrowsingContext* aParent, Element* aOwner) {
259 if (XRE_IsContentProcess()) {
260 return false;
263 // If we have a (deprecated) mozbrowser element, we want to start a new
264 // BrowsingContext tree regardless of whether the parent is chrome or content.
265 nsCOMPtr<nsIMozBrowserFrame> mozbrowser = aOwner->GetAsMozBrowserFrame();
266 if (mozbrowser && mozbrowser->GetReallyIsBrowser()) {
267 return true;
270 if (aParent->IsContent()) {
271 // If we're already in content, we may still want to create a new
272 // BrowsingContext tree if our element is a xul browser element with a
273 // `remote="true"` marker.
274 return aOwner->IsXULElement() &&
275 aOwner->AttrValueIs(kNameSpaceID_None, nsGkAtoms::remote,
276 nsGkAtoms::_true, eCaseMatters);
279 // If we're in a chrome context, we want to start a new tree if we are an
280 // element with a `type="content"` marker.
281 return aOwner->AttrValueIs(kNameSpaceID_None, TypeAttrName(aOwner),
282 nsGkAtoms::content, eIgnoreCase);
285 static already_AddRefed<BrowsingContext> CreateBrowsingContext(
286 Element* aOwner, nsIOpenWindowInfo* aOpenWindowInfo,
287 BrowsingContextGroup* aSpecificGroup, bool aNetworkCreated = false) {
288 MOZ_ASSERT(!aOpenWindowInfo || !aSpecificGroup,
289 "Only one of SpecificGroup and OpenWindowInfo may be provided!");
291 // If we've got a pending BrowserParent from the content process, use the
292 // BrowsingContext which was created for it.
293 if (aOpenWindowInfo && aOpenWindowInfo->GetNextRemoteBrowser()) {
294 MOZ_ASSERT(XRE_IsParentProcess());
295 return do_AddRef(
296 aOpenWindowInfo->GetNextRemoteBrowser()->GetBrowsingContext());
299 RefPtr<BrowsingContext> opener;
300 if (aOpenWindowInfo && !aOpenWindowInfo->GetForceNoOpener()) {
301 opener = aOpenWindowInfo->GetParent();
302 if (opener) {
303 // Must create BrowsingContext with opener in-process.
304 MOZ_ASSERT(opener->IsInProcess());
306 // This can only happen when the opener was closed from a nested event
307 // loop in the window provider code, and only when the open was triggered
308 // by a non-e10s tab, and the new tab is being opened in a new browser
309 // window. Since it is a corner case among corner cases, and the opener
310 // window will appear to be null to consumers after it is discarded
311 // anyway, just drop the opener entirely.
312 if (opener->IsDiscarded()) {
313 NS_WARNING(
314 "Opener was closed from a nested event loop in the parent process. "
315 "Please fix this.");
316 opener = nullptr;
321 RefPtr<nsGlobalWindowInner> parentInner =
322 nsGlobalWindowInner::Cast(aOwner->OwnerDoc()->GetInnerWindow());
323 if (NS_WARN_IF(!parentInner) || parentInner->IsDying()) {
324 return nullptr;
327 BrowsingContext* parentBC = parentInner->GetBrowsingContext();
328 if (NS_WARN_IF(!parentBC) || parentBC->IsDiscarded()) {
329 return nullptr;
332 // Determine the frame name for the new browsing context.
333 nsAutoString frameName;
334 GetFrameName(aOwner, frameName);
336 // Create our BrowsingContext without immediately attaching it. It's possible
337 // that no DocShell or remote browser will ever be created for this
338 // FrameLoader, particularly if the document that we were created for is not
339 // currently active. And in that latter case, if we try to attach our BC now,
340 // it will wind up attached as a child of the currently active inner window
341 // for the BrowsingContext, and cause no end of trouble.
342 if (IsTopContent(parentBC, aOwner)) {
343 // Create toplevel context without a parent & as Type::Content.
344 return BrowsingContext::CreateDetached(
345 nullptr, opener, aSpecificGroup, frameName,
346 BrowsingContext::Type::Content, false);
349 MOZ_ASSERT(!aOpenWindowInfo,
350 "Can't have openWindowInfo for non-toplevel context");
352 MOZ_ASSERT(!aSpecificGroup,
353 "Can't force BrowsingContextGroup for non-toplevel context");
354 return BrowsingContext::CreateDetached(parentInner, nullptr, nullptr,
355 frameName, parentBC->GetType(), false,
356 !aNetworkCreated);
359 static bool InitialLoadIsRemote(Element* aOwner) {
360 // The initial load in an content process iframe should never be made remote.
361 // Content process iframes always become remote due to navigation.
362 if (XRE_IsContentProcess()) {
363 return false;
366 // If we're an <iframe mozbrowser> and we don't have a "remote" attribute,
367 // fall back to the default.
368 nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(aOwner);
369 bool isMozBrowserFrame = browserFrame && browserFrame->GetReallyIsBrowser();
370 if (isMozBrowserFrame && !aOwner->HasAttr(nsGkAtoms::remote)) {
371 return Preferences::GetBool("dom.ipc.browser_frames.oop_by_default", false);
374 // Otherwise, we're remote if we have "remote=true" and we're either a
375 // browser frame or a XUL element.
376 return (isMozBrowserFrame || aOwner->GetNameSpaceID() == kNameSpaceID_XUL) &&
377 aOwner->AttrValueIs(kNameSpaceID_None, nsGkAtoms::remote,
378 nsGkAtoms::_true, eCaseMatters);
381 static already_AddRefed<BrowsingContextGroup> InitialBrowsingContextGroup(
382 Element* aOwner) {
383 nsAutoString attrString;
384 if (aOwner->GetNameSpaceID() != kNameSpaceID_XUL ||
385 !aOwner->GetAttr(nsGkAtoms::initialBrowsingContextGroupId, attrString)) {
386 return nullptr;
389 // It's OK to read the attribute using a signed 64-bit integer parse, as an ID
390 // generated using `nsContentUtils::GenerateProcessSpecificId` (like BCG IDs)
391 // will only ever use 53 bits of precision, so it can be round-tripped through
392 // a JS number.
393 nsresult rv = NS_OK;
394 int64_t signedGroupId = attrString.ToInteger64(&rv, 10);
395 if (NS_FAILED(rv) || signedGroupId <= 0) {
396 MOZ_DIAGNOSTIC_ASSERT(
397 false, "we intended to have a particular id, but failed to parse it!");
398 return nullptr;
401 return BrowsingContextGroup::GetOrCreate(uint64_t(signedGroupId));
404 already_AddRefed<nsFrameLoader> nsFrameLoader::Create(
405 Element* aOwner, bool aNetworkCreated, nsIOpenWindowInfo* aOpenWindowInfo) {
406 NS_ENSURE_TRUE(aOwner, nullptr);
407 Document* doc = aOwner->OwnerDoc();
409 // We never create nsFrameLoaders for elements in resource documents.
411 // We never create nsFrameLoaders for elements in data documents, unless the
412 // document is a static document.
413 // Static documents are an exception because any sub-documents need an
414 // nsFrameLoader to keep the relevant docShell alive, even though the
415 // nsFrameLoader isn't used to load anything (the sub-document is created by
416 // the static clone process).
418 // We never create nsFrameLoaders for elements that are not
419 // in-composed-document, unless the element belongs to a static document.
420 // Static documents are an exception because this method is called at a point
421 // in the static clone process before aOwner has been inserted into its
422 // document. For other types of documents this wouldn't be a problem since
423 // we'd create the nsFrameLoader as necessary after aOwner is inserted into a
424 // document, but the mechanisms that take care of that don't apply for static
425 // documents so we need to create the nsFrameLoader now. (This isn't wasteful
426 // since for a static document we know aOwner will end up in a document and
427 // the nsFrameLoader will be used for its docShell.)
429 NS_ENSURE_TRUE(!doc->IsResourceDoc() &&
430 ((!doc->IsLoadedAsData() && aOwner->IsInComposedDoc()) ||
431 doc->IsStaticDocument()),
432 nullptr);
434 RefPtr<BrowsingContextGroup> group = InitialBrowsingContextGroup(aOwner);
435 RefPtr<BrowsingContext> context =
436 CreateBrowsingContext(aOwner, aOpenWindowInfo, group, aNetworkCreated);
437 NS_ENSURE_TRUE(context, nullptr);
439 if (XRE_IsParentProcess() && aOpenWindowInfo) {
440 MOZ_ASSERT(context->IsTopContent());
441 if (RefPtr<BrowsingContext> crossGroupOpener =
442 aOpenWindowInfo->GetParent()) {
443 context->Canonical()->SetCrossGroupOpenerId(crossGroupOpener->Id());
447 bool isRemoteFrame = InitialLoadIsRemote(aOwner);
448 RefPtr<nsFrameLoader> fl =
449 new nsFrameLoader(aOwner, context, isRemoteFrame, aNetworkCreated);
450 fl->mOpenWindowInfo = aOpenWindowInfo;
452 // If this is a toplevel initial remote frame, we're looking at a browser
453 // loaded in the parent process. Pull the remote type attribute off of the
454 // <browser> element to determine which remote type it should be loaded in, or
455 // use `DEFAULT_REMOTE_TYPE` if we can't tell.
456 if (isRemoteFrame) {
457 MOZ_ASSERT(XRE_IsParentProcess());
458 nsAutoString remoteType;
459 if (aOwner->GetAttr(nsGkAtoms::RemoteType, remoteType) &&
460 !remoteType.IsEmpty()) {
461 CopyUTF16toUTF8(remoteType, fl->mRemoteType);
462 } else {
463 fl->mRemoteType = DEFAULT_REMOTE_TYPE;
466 return fl.forget();
469 /* static */
470 already_AddRefed<nsFrameLoader> nsFrameLoader::Recreate(
471 mozilla::dom::Element* aOwner, BrowsingContext* aContext,
472 BrowsingContextGroup* aSpecificGroup,
473 const NavigationIsolationOptions& aRemotenessOptions, bool aIsRemote,
474 bool aNetworkCreated, bool aPreserveContext) {
475 NS_ENSURE_TRUE(aOwner, nullptr);
477 #ifdef DEBUG
478 // This version of Create is only called for Remoteness updates, so we can
479 // assume we need a FrameLoader here and skip the check in the other Create.
480 Document* doc = aOwner->OwnerDoc();
481 MOZ_ASSERT(!doc->IsResourceDoc());
482 MOZ_ASSERT((!doc->IsLoadedAsData() && aOwner->IsInComposedDoc()) ||
483 doc->IsStaticDocument());
484 #endif
486 RefPtr<BrowsingContext> context = aContext;
487 if (!context || !aPreserveContext) {
488 context = CreateBrowsingContext(aOwner, /* openWindowInfo */ nullptr,
489 aSpecificGroup);
490 if (aContext) {
491 MOZ_ASSERT(
492 XRE_IsParentProcess(),
493 "Recreating browing contexts only supported in the parent process");
494 aContext->Canonical()->SynchronizeLayoutHistoryState();
495 aContext->Canonical()->ReplacedBy(context->Canonical(),
496 aRemotenessOptions);
499 NS_ENSURE_TRUE(context, nullptr);
501 RefPtr<nsFrameLoader> fl =
502 new nsFrameLoader(aOwner, context, aIsRemote, aNetworkCreated);
503 return fl.forget();
506 void nsFrameLoader::LoadFrame(bool aOriginalSrc) {
507 if (NS_WARN_IF(!mOwnerContent)) {
508 return;
511 nsAutoString src;
512 nsCOMPtr<nsIPrincipal> principal;
513 nsCOMPtr<nsIContentSecurityPolicy> csp;
515 bool isSrcdoc = mOwnerContent->IsHTMLElement(nsGkAtoms::iframe) &&
516 mOwnerContent->HasAttr(nsGkAtoms::srcdoc);
517 if (isSrcdoc) {
518 src.AssignLiteral("about:srcdoc");
519 principal = mOwnerContent->NodePrincipal();
520 csp = mOwnerContent->GetCsp();
521 } else {
522 GetURL(src, getter_AddRefs(principal), getter_AddRefs(csp));
524 src.Trim(" \t\n\r");
526 if (src.IsEmpty()) {
527 // If the frame is a XUL element and has the attribute 'nodefaultsrc=true'
528 // then we will not use 'about:blank' as fallback but return early without
529 // starting a load if no 'src' attribute is given (or it's empty).
530 if (mOwnerContent->IsXULElement() &&
531 mOwnerContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::nodefaultsrc,
532 nsGkAtoms::_true, eCaseMatters)) {
533 return;
535 src.AssignLiteral("about:blank");
536 principal = mOwnerContent->NodePrincipal();
537 csp = mOwnerContent->GetCsp();
541 Document* doc = mOwnerContent->OwnerDoc();
542 if (doc->IsStaticDocument()) {
543 return;
546 // If we are being loaded by a lazy loaded iframe, use its base URI first
547 // instead of the current base URI.
548 auto* lazyBaseURI = GetLazyLoadFrameResumptionState().mBaseURI.get();
549 nsIURI* baseURI = lazyBaseURI ? lazyBaseURI : mOwnerContent->GetBaseURI();
551 auto encoding = doc->GetDocumentCharacterSet();
553 nsCOMPtr<nsIURI> uri;
554 nsresult rv = NS_NewURI(getter_AddRefs(uri), src, encoding, baseURI);
556 // If the URI was malformed, try to recover by loading about:blank.
557 if (rv == NS_ERROR_MALFORMED_URI) {
558 rv = NS_NewURI(getter_AddRefs(uri), u"about:blank"_ns, encoding, baseURI);
561 if (NS_SUCCEEDED(rv)) {
562 rv = LoadURI(uri, principal, csp, aOriginalSrc);
565 if (NS_FAILED(rv)) {
566 FireErrorEvent();
570 void nsFrameLoader::ConfigRemoteProcess(const nsACString& aRemoteType,
571 ContentParent* aContentParent) {
572 MOZ_DIAGNOSTIC_ASSERT(IsRemoteFrame(), "Must be a remote frame");
573 MOZ_DIAGNOSTIC_ASSERT(!mRemoteBrowser, "Must not have a browser yet");
574 MOZ_DIAGNOSTIC_ASSERT_IF(aContentParent,
575 aContentParent->GetRemoteType() == aRemoteType);
577 mRemoteType = aRemoteType;
578 mChildID = aContentParent ? aContentParent->ChildID() : 0;
581 void nsFrameLoader::FireErrorEvent() {
582 if (!mOwnerContent) {
583 return;
585 RefPtr<AsyncEventDispatcher> loadBlockingAsyncDispatcher =
586 new LoadBlockingAsyncEventDispatcher(
587 mOwnerContent, u"error"_ns, CanBubble::eNo, ChromeOnlyDispatch::eNo);
588 loadBlockingAsyncDispatcher->PostDOMEvent();
591 nsresult nsFrameLoader::LoadURI(nsIURI* aURI,
592 nsIPrincipal* aTriggeringPrincipal,
593 nsIContentSecurityPolicy* aCsp,
594 bool aOriginalSrc) {
595 if (!aURI) return NS_ERROR_INVALID_POINTER;
596 NS_ENSURE_STATE(!mDestroyCalled && mOwnerContent);
597 MOZ_ASSERT(
598 aTriggeringPrincipal,
599 "Must have an explicit triggeringPrincipal to nsFrameLoader::LoadURI.");
601 mLoadingOriginalSrc = aOriginalSrc;
603 nsCOMPtr<Document> doc = mOwnerContent->OwnerDoc();
605 nsresult rv;
606 rv = CheckURILoad(aURI, aTriggeringPrincipal);
607 NS_ENSURE_SUCCESS(rv, rv);
609 mURIToLoad = aURI;
610 mTriggeringPrincipal = aTriggeringPrincipal;
611 mCsp = aCsp;
612 rv = doc->InitializeFrameLoader(this);
613 if (NS_FAILED(rv)) {
614 mURIToLoad = nullptr;
615 mTriggeringPrincipal = nullptr;
616 mCsp = nullptr;
618 return rv;
621 void nsFrameLoader::ResumeLoad(uint64_t aPendingSwitchID) {
622 Document* doc = mOwnerContent->OwnerDoc();
623 if (doc->IsStaticDocument()) {
624 // Static doc shouldn't load sub-documents.
625 return;
628 if (NS_WARN_IF(mDestroyCalled || !mOwnerContent)) {
629 FireErrorEvent();
630 return;
633 mLoadingOriginalSrc = false;
634 mURIToLoad = nullptr;
635 mPendingSwitchID = aPendingSwitchID;
636 mTriggeringPrincipal = mOwnerContent->NodePrincipal();
637 mCsp = mOwnerContent->GetCsp();
639 nsresult rv = doc->InitializeFrameLoader(this);
640 if (NS_FAILED(rv)) {
641 mPendingSwitchID = 0;
642 mTriggeringPrincipal = nullptr;
643 mCsp = nullptr;
645 FireErrorEvent();
649 nsresult nsFrameLoader::ReallyStartLoading() {
650 nsresult rv = ReallyStartLoadingInternal();
651 if (NS_FAILED(rv)) {
652 FireErrorEvent();
655 return rv;
658 nsresult nsFrameLoader::ReallyStartLoadingInternal() {
659 NS_ENSURE_STATE((mURIToLoad || mPendingSwitchID) && mOwnerContent &&
660 mOwnerContent->IsInComposedDoc());
662 AUTO_PROFILER_LABEL("nsFrameLoader::ReallyStartLoadingInternal", OTHER);
663 RefPtr<nsDocShellLoadState> loadState;
664 if (!mPendingSwitchID) {
665 loadState = new nsDocShellLoadState(mURIToLoad);
666 loadState->SetOriginalFrameSrc(mLoadingOriginalSrc);
668 // The triggering principal could be null if the frame is loaded other
669 // than the src attribute, for example, the frame is sandboxed. In that
670 // case we use the principal of the owner content, which is needed to
671 // prevent XSS attaches on documents loaded in subframes.
672 if (mTriggeringPrincipal) {
673 loadState->SetTriggeringPrincipal(mTriggeringPrincipal);
674 } else {
675 loadState->SetTriggeringPrincipal(mOwnerContent->NodePrincipal());
678 // If we have an explicit CSP, we set it. If not, we only query it from
679 // the document in case there was no explicit triggeringPrincipal.
680 // Otherwise it's possible that the original triggeringPrincipal did not
681 // have a CSP which causes the CSP on the Principal and explicit CSP
682 // to be out of sync.
683 if (mCsp) {
684 loadState->SetCsp(mCsp);
685 } else if (!mTriggeringPrincipal) {
686 nsCOMPtr<nsIContentSecurityPolicy> csp = mOwnerContent->GetCsp();
687 loadState->SetCsp(csp);
690 nsAutoString srcdoc;
691 bool isSrcdoc = mOwnerContent->IsHTMLElement(nsGkAtoms::iframe) &&
692 mOwnerContent->GetAttr(nsGkAtoms::srcdoc, srcdoc);
694 if (isSrcdoc) {
695 loadState->SetSrcdocData(srcdoc);
696 loadState->SetBaseURI(mOwnerContent->GetBaseURI());
699 auto referrerInfo = MakeRefPtr<ReferrerInfo>(
700 *mOwnerContent, GetLazyLoadFrameResumptionState().mReferrerPolicy);
701 loadState->SetReferrerInfo(referrerInfo);
703 loadState->SetIsFromProcessingFrameAttributes();
705 // Default flags:
706 int32_t flags = nsIWebNavigation::LOAD_FLAGS_NONE;
708 // Flags for browser frame:
709 if (OwnerIsMozBrowserFrame()) {
710 flags = nsIWebNavigation::LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP |
711 nsIWebNavigation::LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL;
713 loadState->SetLoadFlags(flags);
715 loadState->SetFirstParty(false);
717 // If we're loading the default about:blank document in a <browser> element,
718 // prevent the load from causing a process switch by explicitly overriding
719 // remote type selection.
720 if (mPendingBrowsingContext->IsTopContent() &&
721 mOwnerContent->IsXULElement(nsGkAtoms::browser) &&
722 NS_IsAboutBlank(mURIToLoad) &&
723 loadState->TriggeringPrincipal()->IsSystemPrincipal()) {
724 loadState->SetRemoteTypeOverride(mRemoteType);
728 if (IsRemoteFrame()) {
729 if (!EnsureRemoteBrowser()) {
730 NS_WARNING("Couldn't create child process for iframe.");
731 return NS_ERROR_FAILURE;
734 if (mPendingSwitchID) {
735 mRemoteBrowser->ResumeLoad(mPendingSwitchID);
736 mPendingSwitchID = 0;
737 } else {
738 mRemoteBrowser->LoadURL(loadState);
741 if (!mRemoteBrowserShown) {
742 // This can fail if it's too early to show the frame, we will retry later.
743 Unused << ShowRemoteFrame(ScreenIntSize(0, 0));
746 return NS_OK;
749 nsresult rv = MaybeCreateDocShell();
750 if (NS_FAILED(rv)) {
751 return rv;
753 MOZ_ASSERT(GetDocShell(),
754 "MaybeCreateDocShell succeeded with a null docShell");
756 // If we have a pending switch, just resume our load.
757 if (mPendingSwitchID) {
758 bool tmpState = mNeedsAsyncDestroy;
759 mNeedsAsyncDestroy = true;
760 rv = GetDocShell()->ResumeRedirectedLoad(mPendingSwitchID, -1);
761 mNeedsAsyncDestroy = tmpState;
762 mPendingSwitchID = 0;
763 return rv;
766 // Just to be safe, recheck uri.
767 rv = CheckURILoad(mURIToLoad, mTriggeringPrincipal);
768 NS_ENSURE_SUCCESS(rv, rv);
770 mLoadingOriginalSrc = false;
772 // Kick off the load...
773 bool tmpState = mNeedsAsyncDestroy;
774 mNeedsAsyncDestroy = true;
776 RefPtr<nsDocShell> docShell = GetDocShell();
777 rv = docShell->LoadURI(loadState, false);
778 mNeedsAsyncDestroy = tmpState;
779 mURIToLoad = nullptr;
780 NS_ENSURE_SUCCESS(rv, rv);
782 return NS_OK;
785 nsresult nsFrameLoader::CheckURILoad(nsIURI* aURI,
786 nsIPrincipal* aTriggeringPrincipal) {
787 // Check for security. The fun part is trying to figure out what principals
788 // to use. The way I figure it, if we're doing a LoadFrame() accidentally
789 // (eg someone created a frame/iframe node, we're being parsed, XUL iframes
790 // are being reframed, etc.) then we definitely want to use the node
791 // principal of mOwnerContent for security checks. If, on the other hand,
792 // someone's setting the src on our owner content, or created it via script,
793 // or whatever, then they can clearly access it... and we should still use
794 // the principal of mOwnerContent. I don't think that leads to privilege
795 // escalation, and it's reasonably guaranteed to not lead to XSS issues
796 // (since caller can already access mOwnerContent in this case). So just use
797 // the principal of mOwnerContent no matter what. If script wants to run
798 // things with its own permissions, which differ from those of mOwnerContent
799 // (which means the script is privileged in some way) it should set
800 // window.location instead.
801 nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
803 // Get our principal
804 nsIPrincipal* principal =
805 (aTriggeringPrincipal ? aTriggeringPrincipal
806 : mOwnerContent->NodePrincipal());
808 // Check if we are allowed to load absURL
809 nsresult rv = secMan->CheckLoadURIWithPrincipal(
810 principal, aURI, nsIScriptSecurityManager::STANDARD,
811 mOwnerContent->OwnerDoc()->InnerWindowID());
812 if (NS_FAILED(rv)) {
813 return rv; // We're not
816 // Bail out if this is an infinite recursion scenario
817 if (IsRemoteFrame()) {
818 return NS_OK;
820 return CheckForRecursiveLoad(aURI);
823 nsDocShell* nsFrameLoader::GetDocShell(ErrorResult& aRv) {
824 if (IsRemoteFrame()) {
825 return nullptr;
828 // If we have an owner, make sure we have a docshell and return
829 // that. If not, we're most likely in the middle of being torn down,
830 // then we just return null.
831 if (mOwnerContent) {
832 nsresult rv = MaybeCreateDocShell();
833 if (NS_FAILED(rv)) {
834 aRv.Throw(rv);
835 return nullptr;
837 MOZ_ASSERT(GetDocShell(),
838 "MaybeCreateDocShell succeeded, but null docShell");
841 return GetDocShell();
844 static void SetTreeOwnerAndChromeEventHandlerOnDocshellTree(
845 nsIDocShellTreeItem* aItem, nsIDocShellTreeOwner* aOwner,
846 EventTarget* aHandler) {
847 MOZ_ASSERT(aItem, "Must have item");
849 aItem->SetTreeOwner(aOwner);
851 int32_t childCount = 0;
852 aItem->GetInProcessChildCount(&childCount);
853 for (int32_t i = 0; i < childCount; ++i) {
854 nsCOMPtr<nsIDocShellTreeItem> item;
855 aItem->GetInProcessChildAt(i, getter_AddRefs(item));
856 if (aHandler) {
857 nsCOMPtr<nsIDocShell> shell(do_QueryInterface(item));
858 shell->SetChromeEventHandler(aHandler);
860 SetTreeOwnerAndChromeEventHandlerOnDocshellTree(item, aOwner, aHandler);
864 #if defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
865 static bool CheckDocShellType(mozilla::dom::Element* aOwnerContent,
866 nsIDocShellTreeItem* aDocShell, nsAtom* aAtom) {
867 bool isContent = aOwnerContent->AttrValueIs(kNameSpaceID_None, aAtom,
868 nsGkAtoms::content, eIgnoreCase);
870 if (!isContent) {
871 nsCOMPtr<nsIMozBrowserFrame> mozbrowser =
872 aOwnerContent->GetAsMozBrowserFrame();
873 if (mozbrowser) {
874 mozbrowser->GetMozbrowser(&isContent);
878 if (isContent) {
879 return aDocShell->ItemType() == nsIDocShellTreeItem::typeContent;
882 nsCOMPtr<nsIDocShellTreeItem> parent;
883 aDocShell->GetInProcessParent(getter_AddRefs(parent));
885 return parent && parent->ItemType() == aDocShell->ItemType();
887 #endif // defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
890 * Hook up a given TreeItem to its tree owner. aItem's type must have already
891 * been set, and it should already be part of the DocShellTree.
892 * @param aItem the treeitem we're working with
893 * @param aTreeOwner the relevant treeowner; might be null
895 void nsFrameLoader::AddTreeItemToTreeOwner(nsIDocShellTreeItem* aItem,
896 nsIDocShellTreeOwner* aOwner) {
897 MOZ_ASSERT(aItem, "Must have docshell treeitem");
898 MOZ_ASSERT(mOwnerContent, "Must have owning content");
900 MOZ_DIAGNOSTIC_ASSERT(
901 CheckDocShellType(mOwnerContent, aItem, TypeAttrName(mOwnerContent)),
902 "Correct ItemType should be set when creating BrowsingContext");
904 if (mIsTopLevelContent) {
905 bool is_primary = mOwnerContent->AttrValueIs(
906 kNameSpaceID_None, nsGkAtoms::primary, nsGkAtoms::_true, eIgnoreCase);
907 if (aOwner) {
908 mOwnerContent->AddMutationObserver(this);
909 mObservingOwnerContent = true;
910 aOwner->ContentShellAdded(aItem, is_primary);
915 static bool AllDescendantsOfType(BrowsingContext* aParent,
916 BrowsingContext::Type aType) {
917 for (auto& child : aParent->Children()) {
918 if (child->GetType() != aType || !AllDescendantsOfType(child, aType)) {
919 return false;
923 return true;
926 void nsFrameLoader::MaybeShowFrame() {
927 nsIFrame* frame = GetPrimaryFrameOfOwningContent();
928 if (frame) {
929 nsSubDocumentFrame* subDocFrame = do_QueryFrame(frame);
930 if (subDocFrame) {
931 subDocFrame->MaybeShowViewer();
936 static ScrollbarPreference GetScrollbarPreference(const Element* aOwner) {
937 if (!aOwner) {
938 return ScrollbarPreference::Auto;
940 const nsAttrValue* attrValue = aOwner->GetParsedAttr(nsGkAtoms::scrolling);
941 return nsGenericHTMLFrameElement::MapScrollingAttribute(attrValue);
944 static CSSIntSize GetMarginAttributes(const Element* aOwner) {
945 CSSIntSize result(-1, -1);
946 auto* content = nsGenericHTMLElement::FromNodeOrNull(aOwner);
947 if (!content) {
948 return result;
950 const nsAttrValue* attr = content->GetParsedAttr(nsGkAtoms::marginwidth);
951 if (attr && attr->Type() == nsAttrValue::eInteger) {
952 result.width = attr->GetIntegerValue();
954 attr = content->GetParsedAttr(nsGkAtoms::marginheight);
955 if (attr && attr->Type() == nsAttrValue::eInteger) {
956 result.height = attr->GetIntegerValue();
958 return result;
961 bool nsFrameLoader::Show(nsSubDocumentFrame* frame) {
962 if (mInShow) {
963 return false;
965 mInShow = true;
967 auto resetInShow = mozilla::MakeScopeExit([&] { mInShow = false; });
969 ScreenIntSize size = frame->GetSubdocumentSize();
970 if (IsRemoteFrame()) {
971 // FIXME(bug 1588791): For fission iframes we need to pass down the
972 // scrollbar preferences.
973 return ShowRemoteFrame(size, frame);
976 nsresult rv = MaybeCreateDocShell();
977 if (NS_FAILED(rv)) {
978 return false;
980 nsDocShell* ds = GetDocShell();
981 MOZ_ASSERT(ds, "MaybeCreateDocShell succeeded, but null docShell");
982 if (!ds) {
983 return false;
986 ds->SetScrollbarPreference(GetScrollbarPreference(mOwnerContent));
987 const bool marginsChanged =
988 ds->UpdateFrameMargins(GetMarginAttributes(mOwnerContent));
989 if (PresShell* presShell = ds->GetPresShell()) {
990 // Ensure root scroll frame is reflowed in case margins have changed
991 if (marginsChanged) {
992 if (nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame()) {
993 presShell->FrameNeedsReflow(rootScrollFrame, IntrinsicDirty::None,
994 NS_FRAME_IS_DIRTY);
997 return true;
1000 nsView* view = frame->EnsureInnerView();
1001 if (!view) return false;
1003 RefPtr<nsDocShell> baseWindow = GetDocShell();
1004 baseWindow->InitWindow(nullptr, view->GetWidget(), 0, 0, size.width,
1005 size.height);
1006 baseWindow->SetVisibility(true);
1007 NS_ENSURE_TRUE(GetDocShell(), false);
1009 // Trigger editor re-initialization if midas is turned on in the
1010 // sub-document. This shouldn't be necessary, but given the way our
1011 // editor works, it is. See
1012 // https://bugzilla.mozilla.org/show_bug.cgi?id=284245
1013 if (RefPtr<PresShell> presShell = GetDocShell()->GetPresShell()) {
1014 Document* doc = presShell->GetDocument();
1015 nsHTMLDocument* htmlDoc =
1016 doc && doc->IsHTMLOrXHTML() ? doc->AsHTMLDocument() : nullptr;
1018 if (htmlDoc) {
1019 nsAutoString designMode;
1020 htmlDoc->GetDesignMode(designMode);
1022 if (designMode.EqualsLiteral("on")) {
1023 // Hold on to the editor object to let the document reattach to the
1024 // same editor object, instead of creating a new one.
1025 RefPtr<HTMLEditor> htmlEditor = GetDocShell()->GetHTMLEditor();
1026 Unused << htmlEditor;
1027 htmlDoc->SetDesignMode(u"off"_ns, Nothing(), IgnoreErrors());
1029 htmlDoc->SetDesignMode(u"on"_ns, Nothing(), IgnoreErrors());
1030 } else {
1031 // Re-initialize the presentation for contenteditable documents
1032 bool editable = false, hasEditingSession = false;
1033 GetDocShell()->GetEditable(&editable);
1034 GetDocShell()->GetHasEditingSession(&hasEditingSession);
1035 RefPtr<HTMLEditor> htmlEditor = GetDocShell()->GetHTMLEditor();
1036 if (editable && hasEditingSession && htmlEditor) {
1037 htmlEditor->PostCreate();
1043 mInShow = false;
1044 if (mHideCalled) {
1045 mHideCalled = false;
1046 Hide();
1047 return false;
1049 return true;
1052 void nsFrameLoader::MarginsChanged() {
1053 // We assume that the margins are always zero for remote frames.
1054 if (IsRemoteFrame()) {
1055 return;
1058 nsDocShell* docShell = GetDocShell();
1059 // If there's no docshell, we're probably not up and running yet.
1060 // nsFrameLoader::Show() will take care of setting the right
1061 // margins.
1062 if (!docShell) {
1063 return;
1066 if (!docShell->UpdateFrameMargins(GetMarginAttributes(mOwnerContent))) {
1067 return;
1070 // There's a cached property declaration block
1071 // that needs to be updated
1072 if (Document* doc = docShell->GetDocument()) {
1073 for (nsINode* cur = doc; cur; cur = cur->GetNextNode()) {
1074 if (auto* body = HTMLBodyElement::FromNode(cur)) {
1075 body->FrameMarginsChanged();
1081 bool nsFrameLoader::ShowRemoteFrame(const ScreenIntSize& size,
1082 nsSubDocumentFrame* aFrame) {
1083 AUTO_PROFILER_LABEL("nsFrameLoader::ShowRemoteFrame", OTHER);
1084 NS_ASSERTION(IsRemoteFrame(),
1085 "ShowRemote only makes sense on remote frames.");
1087 if (!EnsureRemoteBrowser()) {
1088 NS_ERROR("Couldn't create child process.");
1089 return false;
1092 // FIXME/bug 589337: Show()/Hide() is pretty expensive for
1093 // cross-process layers; need to figure out what behavior we really
1094 // want here. For now, hack.
1095 if (!mRemoteBrowserShown) {
1096 if (!mOwnerContent || !mOwnerContent->GetComposedDoc()) {
1097 return false;
1100 // We never want to host remote frameloaders in simple popups, like menus.
1101 nsIWidget* widget = nsContentUtils::WidgetForContent(mOwnerContent);
1102 if (!widget || static_cast<nsBaseWidget*>(widget)->IsSmallPopup()) {
1103 return false;
1106 if (BrowserHost* bh = mRemoteBrowser->AsBrowserHost()) {
1107 RefPtr<BrowsingContext> bc = bh->GetBrowsingContext()->Top();
1109 // Set to the current activation of the window.
1110 bc->SetIsActiveBrowserWindow(bc->GetIsActiveBrowserWindow());
1113 nsCOMPtr<nsISupports> container = mOwnerContent->OwnerDoc()->GetContainer();
1114 nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(container);
1115 nsCOMPtr<nsIWidget> mainWidget;
1116 baseWindow->GetMainWidget(getter_AddRefs(mainWidget));
1117 nsSizeMode sizeMode =
1118 mainWidget ? mainWidget->SizeMode() : nsSizeMode_Normal;
1119 OwnerShowInfo info(size, GetScrollbarPreference(mOwnerContent), sizeMode);
1120 if (!mRemoteBrowser->Show(info)) {
1121 return false;
1123 mRemoteBrowserShown = true;
1125 // This notification doesn't apply to fission, apparently.
1126 if (!GetBrowserBridgeChild()) {
1127 if (nsCOMPtr<nsIObserverService> os = services::GetObserverService()) {
1128 os->NotifyObservers(ToSupports(this), "remote-browser-shown", nullptr);
1130 ProcessPriorityManager::RemoteBrowserFrameShown(this);
1132 } else {
1133 nsIntRect dimensions;
1134 NS_ENSURE_SUCCESS(GetWindowDimensions(dimensions), false);
1136 // Don't show remote iframe if we are waiting for the completion of reflow.
1137 if (!aFrame || !aFrame->HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
1138 mRemoteBrowser->UpdateDimensions(dimensions, size);
1142 return true;
1145 void nsFrameLoader::Hide() {
1146 if (mHideCalled) {
1147 return;
1149 if (mInShow) {
1150 mHideCalled = true;
1151 return;
1154 if (!GetDocShell()) {
1155 return;
1158 nsCOMPtr<nsIContentViewer> contentViewer;
1159 GetDocShell()->GetContentViewer(getter_AddRefs(contentViewer));
1160 if (contentViewer) contentViewer->SetSticky(false);
1162 RefPtr<nsDocShell> baseWin = GetDocShell();
1163 baseWin->SetVisibility(false);
1164 baseWin->SetParentWidget(nullptr);
1167 void nsFrameLoader::ForceLayoutIfNecessary() {
1168 nsIFrame* frame = GetPrimaryFrameOfOwningContent();
1169 if (!frame) {
1170 return;
1173 nsPresContext* presContext = frame->PresContext();
1174 if (!presContext) {
1175 return;
1178 // Only force the layout flush if the frameloader hasn't ever been
1179 // run through layout.
1180 if (frame->HasAnyStateBits(NS_FRAME_FIRST_REFLOW)) {
1181 if (RefPtr<PresShell> presShell = presContext->GetPresShell()) {
1182 presShell->FlushPendingNotifications(FlushType::Layout);
1187 nsresult nsFrameLoader::SwapWithOtherRemoteLoader(
1188 nsFrameLoader* aOther, nsFrameLoaderOwner* aThisOwner,
1189 nsFrameLoaderOwner* aOtherOwner) {
1190 MOZ_ASSERT(NS_IsMainThread());
1192 #ifdef DEBUG
1193 RefPtr<nsFrameLoader> first = aThisOwner->GetFrameLoader();
1194 RefPtr<nsFrameLoader> second = aOtherOwner->GetFrameLoader();
1195 MOZ_ASSERT(first == this, "aThisOwner must own this");
1196 MOZ_ASSERT(second == aOther, "aOtherOwner must own aOther");
1197 #endif
1199 Element* ourContent = mOwnerContent;
1200 Element* otherContent = aOther->mOwnerContent;
1202 if (!ourContent || !otherContent) {
1203 // Can't handle this
1204 return NS_ERROR_NOT_IMPLEMENTED;
1207 // Make sure there are no same-origin issues
1208 bool equal;
1209 nsresult rv = ourContent->NodePrincipal()->Equals(
1210 otherContent->NodePrincipal(), &equal);
1211 if (NS_FAILED(rv) || !equal) {
1212 // Security problems loom. Just bail on it all
1213 return NS_ERROR_DOM_SECURITY_ERR;
1216 Document* ourDoc = ourContent->GetComposedDoc();
1217 Document* otherDoc = otherContent->GetComposedDoc();
1218 if (!ourDoc || !otherDoc) {
1219 // Again, how odd, given that we had docshells
1220 return NS_ERROR_NOT_IMPLEMENTED;
1223 PresShell* ourPresShell = ourDoc->GetPresShell();
1224 PresShell* otherPresShell = otherDoc->GetPresShell();
1225 if (!ourPresShell || !otherPresShell) {
1226 return NS_ERROR_NOT_IMPLEMENTED;
1229 auto* browserParent = GetBrowserParent();
1230 auto* otherBrowserParent = aOther->GetBrowserParent();
1232 if (!browserParent || !otherBrowserParent) {
1233 return NS_ERROR_NOT_IMPLEMENTED;
1236 RefPtr<BrowsingContext> ourBc = browserParent->GetBrowsingContext();
1237 RefPtr<BrowsingContext> otherBc = otherBrowserParent->GetBrowsingContext();
1239 // When we swap docShells, maybe we have to deal with a new page created just
1240 // for this operation. In this case, the browser code should already have set
1241 // the correct userContextId attribute value in the owning element, but our
1242 // docShell, that has been created way before) doesn't know that that
1243 // happened.
1244 // This is the reason why now we must retrieve the correct value from the
1245 // usercontextid attribute before comparing our originAttributes with the
1246 // other one.
1247 OriginAttributes ourOriginAttributes = ourBc->OriginAttributesRef();
1248 rv = PopulateOriginContextIdsFromAttributes(ourOriginAttributes);
1249 NS_ENSURE_SUCCESS(rv, rv);
1251 OriginAttributes otherOriginAttributes = otherBc->OriginAttributesRef();
1252 rv = aOther->PopulateOriginContextIdsFromAttributes(otherOriginAttributes);
1253 NS_ENSURE_SUCCESS(rv, rv);
1255 if (!ourOriginAttributes.EqualsIgnoringFPD(otherOriginAttributes)) {
1256 return NS_ERROR_NOT_IMPLEMENTED;
1259 bool ourHasHistory = mIsTopLevelContent &&
1260 ourContent->IsXULElement(nsGkAtoms::browser) &&
1261 !ourContent->HasAttr(nsGkAtoms::disablehistory);
1262 bool otherHasHistory = aOther->mIsTopLevelContent &&
1263 otherContent->IsXULElement(nsGkAtoms::browser) &&
1264 !otherContent->HasAttr(nsGkAtoms::disablehistory);
1265 if (ourHasHistory != otherHasHistory) {
1266 return NS_ERROR_NOT_IMPLEMENTED;
1269 if (mInSwap || aOther->mInSwap) {
1270 return NS_ERROR_NOT_IMPLEMENTED;
1272 mInSwap = aOther->mInSwap = true;
1274 // NOTE(emilio): This doesn't have to flush because the caller does already.
1275 nsIFrame* ourFrame = ourContent->GetPrimaryFrame();
1276 nsIFrame* otherFrame = otherContent->GetPrimaryFrame();
1277 if (!ourFrame || !otherFrame) {
1278 mInSwap = aOther->mInSwap = false;
1279 return NS_ERROR_NOT_IMPLEMENTED;
1282 nsSubDocumentFrame* ourFrameFrame = do_QueryFrame(ourFrame);
1283 if (!ourFrameFrame) {
1284 mInSwap = aOther->mInSwap = false;
1285 return NS_ERROR_NOT_IMPLEMENTED;
1288 rv = ourFrameFrame->BeginSwapDocShells(otherFrame);
1289 if (NS_FAILED(rv)) {
1290 mInSwap = aOther->mInSwap = false;
1291 return rv;
1294 nsCOMPtr<nsIBrowserDOMWindow> otherBrowserDOMWindow =
1295 otherBrowserParent->GetBrowserDOMWindow();
1296 nsCOMPtr<nsIBrowserDOMWindow> browserDOMWindow =
1297 browserParent->GetBrowserDOMWindow();
1299 if (!!otherBrowserDOMWindow != !!browserDOMWindow) {
1300 return NS_ERROR_NOT_IMPLEMENTED;
1303 // Destroy browser frame scripts for content leaving a frame with browser API
1304 if (OwnerIsMozBrowserFrame() && !aOther->OwnerIsMozBrowserFrame()) {
1305 DestroyBrowserFrameScripts();
1307 if (!OwnerIsMozBrowserFrame() && aOther->OwnerIsMozBrowserFrame()) {
1308 aOther->DestroyBrowserFrameScripts();
1311 otherBrowserParent->SetBrowserDOMWindow(browserDOMWindow);
1312 browserParent->SetBrowserDOMWindow(otherBrowserDOMWindow);
1314 MaybeUpdatePrimaryBrowserParent(eBrowserParentRemoved);
1315 aOther->MaybeUpdatePrimaryBrowserParent(eBrowserParentRemoved);
1317 if (mozilla::BFCacheInParent() && XRE_IsParentProcess()) {
1318 // nsFrameLoaders in session history can't be moved to another owner since
1319 // there are no corresponging message managers on which swap can be done.
1320 // See the line mMessageManager.swap(aOther->mMessageManager); below.
1321 auto evict = [](nsFrameLoader* aFrameLoader) {
1322 if (BrowsingContext* bc =
1323 aFrameLoader->GetMaybePendingBrowsingContext()) {
1324 nsCOMPtr<nsISHistory> shistory = bc->Canonical()->GetSessionHistory();
1325 if (shistory) {
1326 shistory->EvictAllContentViewers();
1330 evict(this);
1331 evict(aOther);
1334 SetOwnerContent(otherContent);
1335 aOther->SetOwnerContent(ourContent);
1337 browserParent->SetOwnerElement(otherContent);
1338 otherBrowserParent->SetOwnerElement(ourContent);
1340 // Update window activation state for the swapped owner content.
1341 bool ourActive = otherBc->GetIsActiveBrowserWindow();
1342 bool otherActive = ourBc->GetIsActiveBrowserWindow();
1343 if (ourBc->IsTop()) {
1344 ourBc->SetIsActiveBrowserWindow(otherActive);
1346 if (otherBc->IsTop()) {
1347 otherBc->SetIsActiveBrowserWindow(ourActive);
1350 MaybeUpdatePrimaryBrowserParent(eBrowserParentChanged);
1351 aOther->MaybeUpdatePrimaryBrowserParent(eBrowserParentChanged);
1353 RefPtr<nsFrameMessageManager> ourMessageManager = mMessageManager;
1354 RefPtr<nsFrameMessageManager> otherMessageManager = aOther->mMessageManager;
1355 // Swap and setup things in parent message managers.
1356 if (ourMessageManager) {
1357 ourMessageManager->SetCallback(aOther);
1359 if (otherMessageManager) {
1360 otherMessageManager->SetCallback(this);
1362 mMessageManager.swap(aOther->mMessageManager);
1364 // XXXsmaug what should be done to JSWindowActorParent objects when swapping
1365 // frameloaders? Currently they leak very easily, bug 1697918.
1367 // Perform the actual swap of the internal refptrs. We keep a strong reference
1368 // to ourselves to make sure we don't die while we overwrite our reference to
1369 // ourself.
1370 RefPtr<nsFrameLoader> kungFuDeathGrip(this);
1371 aThisOwner->SetFrameLoader(aOther);
1372 aOtherOwner->SetFrameLoader(kungFuDeathGrip);
1374 ourFrameFrame->EndSwapDocShells(otherFrame);
1376 ourPresShell->BackingScaleFactorChanged();
1377 otherPresShell->BackingScaleFactorChanged();
1379 // Initialize browser API if needed now that owner content has changed.
1380 InitializeBrowserAPI();
1381 aOther->InitializeBrowserAPI();
1383 mInSwap = aOther->mInSwap = false;
1385 // Send an updated tab context since owner content type may have changed.
1386 MutableTabContext ourContext;
1387 rv = GetNewTabContext(&ourContext);
1388 if (NS_WARN_IF(NS_FAILED(rv))) {
1389 return rv;
1391 MutableTabContext otherContext;
1392 rv = aOther->GetNewTabContext(&otherContext);
1393 if (NS_WARN_IF(NS_FAILED(rv))) {
1394 return rv;
1397 Unused << browserParent->SendSwappedWithOtherRemoteLoader(
1398 ourContext.AsIPCTabContext());
1399 Unused << otherBrowserParent->SendSwappedWithOtherRemoteLoader(
1400 otherContext.AsIPCTabContext());
1401 return NS_OK;
1404 class MOZ_RAII AutoResetInFrameSwap final {
1405 public:
1406 AutoResetInFrameSwap(nsFrameLoader* aThisFrameLoader,
1407 nsFrameLoader* aOtherFrameLoader,
1408 nsDocShell* aThisDocShell, nsDocShell* aOtherDocShell,
1409 EventTarget* aThisEventTarget,
1410 EventTarget* aOtherEventTarget)
1411 : mThisFrameLoader(aThisFrameLoader),
1412 mOtherFrameLoader(aOtherFrameLoader),
1413 mThisDocShell(aThisDocShell),
1414 mOtherDocShell(aOtherDocShell),
1415 mThisEventTarget(aThisEventTarget),
1416 mOtherEventTarget(aOtherEventTarget) {
1417 mThisFrameLoader->mInSwap = true;
1418 mOtherFrameLoader->mInSwap = true;
1419 mThisDocShell->SetInFrameSwap(true);
1420 mOtherDocShell->SetInFrameSwap(true);
1422 // Fire pageshow events on still-loading pages, and then fire pagehide
1423 // events. Note that we do NOT fire these in the normal way, but just fire
1424 // them on the chrome event handlers.
1425 nsContentUtils::FirePageShowEventForFrameLoaderSwap(
1426 mThisDocShell, mThisEventTarget, false);
1427 nsContentUtils::FirePageShowEventForFrameLoaderSwap(
1428 mOtherDocShell, mOtherEventTarget, false);
1429 nsContentUtils::FirePageHideEventForFrameLoaderSwap(mThisDocShell,
1430 mThisEventTarget);
1431 nsContentUtils::FirePageHideEventForFrameLoaderSwap(mOtherDocShell,
1432 mOtherEventTarget);
1435 ~AutoResetInFrameSwap() {
1436 nsContentUtils::FirePageShowEventForFrameLoaderSwap(mThisDocShell,
1437 mThisEventTarget, true);
1438 nsContentUtils::FirePageShowEventForFrameLoaderSwap(
1439 mOtherDocShell, mOtherEventTarget, true);
1441 mThisFrameLoader->mInSwap = false;
1442 mOtherFrameLoader->mInSwap = false;
1443 mThisDocShell->SetInFrameSwap(false);
1444 mOtherDocShell->SetInFrameSwap(false);
1446 // This is needed to get visibility state right in cases when we swapped a
1447 // visible tab (foreground in visible window) with a non-visible tab.
1448 if (RefPtr<Document> doc = mThisDocShell->GetDocument()) {
1449 doc->UpdateVisibilityState();
1451 if (RefPtr<Document> doc = mOtherDocShell->GetDocument()) {
1452 doc->UpdateVisibilityState();
1456 private:
1457 RefPtr<nsFrameLoader> mThisFrameLoader;
1458 RefPtr<nsFrameLoader> mOtherFrameLoader;
1459 RefPtr<nsDocShell> mThisDocShell;
1460 RefPtr<nsDocShell> mOtherDocShell;
1461 nsCOMPtr<EventTarget> mThisEventTarget;
1462 nsCOMPtr<EventTarget> mOtherEventTarget;
1465 nsresult nsFrameLoader::SwapWithOtherLoader(nsFrameLoader* aOther,
1466 nsFrameLoaderOwner* aThisOwner,
1467 nsFrameLoaderOwner* aOtherOwner) {
1468 #ifdef DEBUG
1469 RefPtr<nsFrameLoader> first = aThisOwner->GetFrameLoader();
1470 RefPtr<nsFrameLoader> second = aOtherOwner->GetFrameLoader();
1471 MOZ_ASSERT(first == this, "aThisOwner must own this");
1472 MOZ_ASSERT(second == aOther, "aOtherOwner must own aOther");
1473 #endif
1475 NS_ENSURE_STATE(!mInShow && !aOther->mInShow);
1477 if (IsRemoteFrame() != aOther->IsRemoteFrame()) {
1478 NS_WARNING(
1479 "Swapping remote and non-remote frames is not currently supported");
1480 return NS_ERROR_NOT_IMPLEMENTED;
1483 RefPtr<Element> ourContent = mOwnerContent;
1484 RefPtr<Element> otherContent = aOther->mOwnerContent;
1485 if (!ourContent || !otherContent) {
1486 // Can't handle this
1487 return NS_ERROR_NOT_IMPLEMENTED;
1490 nsIFrame* ourFrame = ourContent->GetPrimaryFrame(FlushType::Frames);
1491 nsIFrame* otherFrame = otherContent->GetPrimaryFrame(FlushType::Frames);
1492 if (!ourFrame || !otherFrame) {
1493 return NS_ERROR_NOT_IMPLEMENTED;
1496 // Ensure the flushes above haven't changed all the world.
1497 if (ourContent != mOwnerContent || otherContent != aOther->mOwnerContent) {
1498 return NS_ERROR_NOT_IMPLEMENTED;
1501 bool ourHasSrcdoc = ourContent->IsHTMLElement(nsGkAtoms::iframe) &&
1502 ourContent->HasAttr(nsGkAtoms::srcdoc);
1503 bool otherHasSrcdoc = otherContent->IsHTMLElement(nsGkAtoms::iframe) &&
1504 otherContent->HasAttr(nsGkAtoms::srcdoc);
1505 if (ourHasSrcdoc || otherHasSrcdoc) {
1506 // Ignore this case entirely for now, since we support XUL <-> HTML swapping
1507 return NS_ERROR_NOT_IMPLEMENTED;
1510 bool ourFullscreenAllowed = ourContent->IsXULElement() ||
1511 (OwnerIsMozBrowserFrame() &&
1512 ourContent->HasAttr(nsGkAtoms::allowfullscreen));
1513 bool otherFullscreenAllowed =
1514 otherContent->IsXULElement() ||
1515 (aOther->OwnerIsMozBrowserFrame() &&
1516 otherContent->HasAttr(nsGkAtoms::allowfullscreen));
1517 if (ourFullscreenAllowed != otherFullscreenAllowed) {
1518 return NS_ERROR_NOT_IMPLEMENTED;
1521 nsILoadContext* ourLoadContext = ourContent->OwnerDoc()->GetLoadContext();
1522 nsILoadContext* otherLoadContext = otherContent->OwnerDoc()->GetLoadContext();
1523 MOZ_ASSERT(ourLoadContext && otherLoadContext,
1524 "Swapping frames within dead documents?");
1525 if (ourLoadContext->UseRemoteTabs() != otherLoadContext->UseRemoteTabs()) {
1526 NS_WARNING("Can't swap between e10s and non-e10s windows");
1527 return NS_ERROR_NOT_IMPLEMENTED;
1529 if (ourLoadContext->UseRemoteSubframes() !=
1530 otherLoadContext->UseRemoteSubframes()) {
1531 NS_WARNING("Can't swap between fission and non-fission windows");
1532 return NS_ERROR_NOT_IMPLEMENTED;
1535 // Divert to a separate path for the remaining steps in the remote case
1536 if (IsRemoteFrame()) {
1537 MOZ_ASSERT(aOther->IsRemoteFrame());
1538 return SwapWithOtherRemoteLoader(aOther, aThisOwner, aOtherOwner);
1541 // Make sure there are no same-origin issues
1542 bool equal;
1543 nsresult rv = ourContent->NodePrincipal()->Equals(
1544 otherContent->NodePrincipal(), &equal);
1545 if (NS_FAILED(rv) || !equal) {
1546 // Security problems loom. Just bail on it all
1547 return NS_ERROR_DOM_SECURITY_ERR;
1550 RefPtr<nsDocShell> ourDocshell = GetExistingDocShell();
1551 RefPtr<nsDocShell> otherDocshell = aOther->GetExistingDocShell();
1552 if (!ourDocshell || !otherDocshell) {
1553 // How odd
1554 return NS_ERROR_NOT_IMPLEMENTED;
1557 // To avoid having to mess with session history, avoid swapping
1558 // frameloaders that don't correspond to root same-type docshells,
1559 // unless both roots have session history disabled.
1560 nsCOMPtr<nsIDocShellTreeItem> ourRootTreeItem, otherRootTreeItem;
1561 ourDocshell->GetInProcessSameTypeRootTreeItem(
1562 getter_AddRefs(ourRootTreeItem));
1563 otherDocshell->GetInProcessSameTypeRootTreeItem(
1564 getter_AddRefs(otherRootTreeItem));
1565 nsCOMPtr<nsIWebNavigation> ourRootWebnav = do_QueryInterface(ourRootTreeItem);
1566 nsCOMPtr<nsIWebNavigation> otherRootWebnav =
1567 do_QueryInterface(otherRootTreeItem);
1569 if (!ourRootWebnav || !otherRootWebnav) {
1570 return NS_ERROR_NOT_IMPLEMENTED;
1573 RefPtr<ChildSHistory> ourHistory = ourRootWebnav->GetSessionHistory();
1574 RefPtr<ChildSHistory> otherHistory = otherRootWebnav->GetSessionHistory();
1576 if ((ourRootTreeItem != ourDocshell || otherRootTreeItem != otherDocshell) &&
1577 (ourHistory || otherHistory)) {
1578 return NS_ERROR_NOT_IMPLEMENTED;
1581 RefPtr<BrowsingContext> ourBc = ourDocshell->GetBrowsingContext();
1582 RefPtr<BrowsingContext> otherBc = otherDocshell->GetBrowsingContext();
1584 // Also make sure that the two BrowsingContexts are the same type. Otherwise
1585 // swapping is certainly not safe. If this needs to be changed then
1586 // the code below needs to be audited as it assumes identical types.
1587 if (ourBc->GetType() != otherBc->GetType()) {
1588 return NS_ERROR_NOT_IMPLEMENTED;
1591 // We ensure that BCs are either both top frames or both subframes.
1592 if (ourBc->IsTop() != otherBc->IsTop()) {
1593 return NS_ERROR_NOT_IMPLEMENTED;
1596 // One more twist here. Setting up the right treeowners in a heterogeneous
1597 // tree is a bit of a pain. So make sure that if `ourBc->GetType()` is not
1598 // nsIDocShellTreeItem::typeContent then all of our descendants are the same
1599 // type as us.
1600 if (!ourBc->IsContent() &&
1601 (!AllDescendantsOfType(ourBc, ourBc->GetType()) ||
1602 !AllDescendantsOfType(otherBc, otherBc->GetType()))) {
1603 return NS_ERROR_NOT_IMPLEMENTED;
1606 // Save off the tree owners, frame elements, chrome event handlers, and
1607 // docshell and document parents before doing anything else.
1608 nsCOMPtr<nsIDocShellTreeOwner> ourOwner, otherOwner;
1609 ourDocshell->GetTreeOwner(getter_AddRefs(ourOwner));
1610 otherDocshell->GetTreeOwner(getter_AddRefs(otherOwner));
1611 // Note: it's OK to have null treeowners.
1613 nsCOMPtr<nsIDocShellTreeItem> ourParentItem, otherParentItem;
1614 ourDocshell->GetInProcessParent(getter_AddRefs(ourParentItem));
1615 otherDocshell->GetInProcessParent(getter_AddRefs(otherParentItem));
1616 if (!ourParentItem || !otherParentItem) {
1617 return NS_ERROR_NOT_IMPLEMENTED;
1620 nsCOMPtr<nsPIDOMWindowOuter> ourWindow = ourDocshell->GetWindow();
1621 nsCOMPtr<nsPIDOMWindowOuter> otherWindow = otherDocshell->GetWindow();
1623 nsCOMPtr<Element> ourFrameElement = ourWindow->GetFrameElementInternal();
1624 nsCOMPtr<Element> otherFrameElement = otherWindow->GetFrameElementInternal();
1626 nsCOMPtr<EventTarget> ourChromeEventHandler =
1627 ourWindow->GetChromeEventHandler();
1628 nsCOMPtr<EventTarget> otherChromeEventHandler =
1629 otherWindow->GetChromeEventHandler();
1631 nsCOMPtr<EventTarget> ourEventTarget = ourWindow->GetParentTarget();
1632 nsCOMPtr<EventTarget> otherEventTarget = otherWindow->GetParentTarget();
1634 NS_ASSERTION(SameCOMIdentity(ourFrameElement, ourContent) &&
1635 SameCOMIdentity(otherFrameElement, otherContent) &&
1636 SameCOMIdentity(ourChromeEventHandler, ourContent) &&
1637 SameCOMIdentity(otherChromeEventHandler, otherContent),
1638 "How did that happen, exactly?");
1640 nsCOMPtr<Document> ourChildDocument = ourWindow->GetExtantDoc();
1641 nsCOMPtr<Document> otherChildDocument = otherWindow->GetExtantDoc();
1642 if (!ourChildDocument || !otherChildDocument) {
1643 // This shouldn't be happening
1644 return NS_ERROR_NOT_IMPLEMENTED;
1647 nsCOMPtr<Document> ourParentDocument =
1648 ourChildDocument->GetInProcessParentDocument();
1649 nsCOMPtr<Document> otherParentDocument =
1650 otherChildDocument->GetInProcessParentDocument();
1652 // Make sure to swap docshells between the two frames.
1653 Document* ourDoc = ourContent->GetComposedDoc();
1654 Document* otherDoc = otherContent->GetComposedDoc();
1655 if (!ourDoc || !otherDoc) {
1656 // Again, how odd, given that we had docshells
1657 return NS_ERROR_NOT_IMPLEMENTED;
1660 NS_ASSERTION(ourDoc == ourParentDocument, "Unexpected parent document");
1661 NS_ASSERTION(otherDoc == otherParentDocument, "Unexpected parent document");
1663 PresShell* ourPresShell = ourDoc->GetPresShell();
1664 PresShell* otherPresShell = otherDoc->GetPresShell();
1665 if (!ourPresShell || !otherPresShell) {
1666 return NS_ERROR_NOT_IMPLEMENTED;
1669 // When we swap docShells, maybe we have to deal with a new page created just
1670 // for this operation. In this case, the browser code should already have set
1671 // the correct userContextId attribute value in the owning element, but our
1672 // docShell, that has been created way before) doesn't know that that
1673 // happened.
1674 // This is the reason why now we must retrieve the correct value from the
1675 // usercontextid attribute before comparing our originAttributes with the
1676 // other one.
1677 OriginAttributes ourOriginAttributes = ourDocshell->GetOriginAttributes();
1678 rv = PopulateOriginContextIdsFromAttributes(ourOriginAttributes);
1679 NS_ENSURE_SUCCESS(rv, rv);
1681 OriginAttributes otherOriginAttributes = otherDocshell->GetOriginAttributes();
1682 rv = aOther->PopulateOriginContextIdsFromAttributes(otherOriginAttributes);
1683 NS_ENSURE_SUCCESS(rv, rv);
1685 if (ourOriginAttributes != otherOriginAttributes) {
1686 return NS_ERROR_NOT_IMPLEMENTED;
1689 if (mInSwap || aOther->mInSwap) {
1690 return NS_ERROR_NOT_IMPLEMENTED;
1692 AutoResetInFrameSwap autoFrameSwap(this, aOther, ourDocshell, otherDocshell,
1693 ourEventTarget, otherEventTarget);
1695 nsSubDocumentFrame* ourFrameFrame = do_QueryFrame(ourFrame);
1696 if (!ourFrameFrame) {
1697 return NS_ERROR_NOT_IMPLEMENTED;
1700 // OK. First begin to swap the docshells in the two nsIFrames
1701 rv = ourFrameFrame->BeginSwapDocShells(otherFrame);
1702 if (NS_FAILED(rv)) {
1703 return rv;
1706 // Destroy browser frame scripts for content leaving a frame with browser API
1707 if (OwnerIsMozBrowserFrame() && !aOther->OwnerIsMozBrowserFrame()) {
1708 DestroyBrowserFrameScripts();
1710 if (!OwnerIsMozBrowserFrame() && aOther->OwnerIsMozBrowserFrame()) {
1711 aOther->DestroyBrowserFrameScripts();
1714 // Now move the docshells to the right docshell trees. Note that this
1715 // resets their treeowners to null.
1716 ourParentItem->RemoveChild(ourDocshell);
1717 otherParentItem->RemoveChild(otherDocshell);
1718 if (ourBc->IsContent()) {
1719 ourOwner->ContentShellRemoved(ourDocshell);
1720 otherOwner->ContentShellRemoved(otherDocshell);
1723 ourParentItem->AddChild(otherDocshell);
1724 otherParentItem->AddChild(ourDocshell);
1726 // Restore the correct chrome event handlers.
1727 ourDocshell->SetChromeEventHandler(otherChromeEventHandler);
1728 otherDocshell->SetChromeEventHandler(ourChromeEventHandler);
1729 // Restore the correct treeowners
1730 // (and also chrome event handlers for content frames only).
1731 SetTreeOwnerAndChromeEventHandlerOnDocshellTree(
1732 ourDocshell, otherOwner,
1733 ourBc->IsContent() ? otherChromeEventHandler.get() : nullptr);
1734 SetTreeOwnerAndChromeEventHandlerOnDocshellTree(
1735 otherDocshell, ourOwner,
1736 ourBc->IsContent() ? ourChromeEventHandler.get() : nullptr);
1738 // Switch the owner content before we start calling AddTreeItemToTreeOwner.
1739 // Note that we rely on this to deal with setting mObservingOwnerContent to
1740 // false and calling RemoveMutationObserver as needed.
1741 SetOwnerContent(otherContent);
1742 aOther->SetOwnerContent(ourContent);
1744 AddTreeItemToTreeOwner(ourDocshell, otherOwner);
1745 aOther->AddTreeItemToTreeOwner(otherDocshell, ourOwner);
1747 // SetSubDocumentFor nulls out parent documents on the old child doc if a
1748 // new non-null document is passed in, so just go ahead and remove both
1749 // kids before reinserting in the parent subdoc maps, to avoid
1750 // complications.
1751 ourParentDocument->SetSubDocumentFor(ourContent, nullptr);
1752 otherParentDocument->SetSubDocumentFor(otherContent, nullptr);
1753 ourParentDocument->SetSubDocumentFor(ourContent, otherChildDocument);
1754 otherParentDocument->SetSubDocumentFor(otherContent, ourChildDocument);
1756 ourWindow->SetFrameElementInternal(otherFrameElement);
1757 otherWindow->SetFrameElementInternal(ourFrameElement);
1759 RefPtr<nsFrameMessageManager> ourMessageManager = mMessageManager;
1760 RefPtr<nsFrameMessageManager> otherMessageManager = aOther->mMessageManager;
1761 // Swap pointers in child message managers.
1762 if (mChildMessageManager) {
1763 InProcessBrowserChildMessageManager* browserChild = mChildMessageManager;
1764 browserChild->SetOwner(otherContent);
1765 browserChild->SetChromeMessageManager(otherMessageManager);
1767 if (aOther->mChildMessageManager) {
1768 InProcessBrowserChildMessageManager* otherBrowserChild =
1769 aOther->mChildMessageManager;
1770 otherBrowserChild->SetOwner(ourContent);
1771 otherBrowserChild->SetChromeMessageManager(ourMessageManager);
1773 // Swap and setup things in parent message managers.
1774 if (mMessageManager) {
1775 mMessageManager->SetCallback(aOther);
1777 if (aOther->mMessageManager) {
1778 aOther->mMessageManager->SetCallback(this);
1780 mMessageManager.swap(aOther->mMessageManager);
1782 // Perform the actual swap of the internal refptrs. We keep a strong reference
1783 // to ourselves to make sure we don't die while we overwrite our reference to
1784 // ourself.
1785 RefPtr<nsFrameLoader> kungFuDeathGrip(this);
1786 aThisOwner->SetFrameLoader(aOther);
1787 aOtherOwner->SetFrameLoader(kungFuDeathGrip);
1789 // Drop any cached content viewers in the two session histories.
1790 if (ourHistory) {
1791 ourHistory->EvictLocalContentViewers();
1793 if (otherHistory) {
1794 otherHistory->EvictLocalContentViewers();
1797 NS_ASSERTION(ourFrame == ourContent->GetPrimaryFrame() &&
1798 otherFrame == otherContent->GetPrimaryFrame(),
1799 "changed primary frame");
1801 ourFrameFrame->EndSwapDocShells(otherFrame);
1803 // If the content being swapped came from windows on two screens with
1804 // incompatible backing resolution (e.g. dragging a tab between windows on
1805 // hi-dpi and low-dpi screens), it will have style data that is based on
1806 // the wrong appUnitsPerDevPixel value. So we tell the PresShells that their
1807 // backing scale factor may have changed. (Bug 822266)
1808 ourFrame->PresShell()->BackingScaleFactorChanged();
1809 otherFrame->PresShell()->BackingScaleFactorChanged();
1811 // Initialize browser API if needed now that owner content has changed
1812 InitializeBrowserAPI();
1813 aOther->InitializeBrowserAPI();
1815 return NS_OK;
1818 void nsFrameLoader::Destroy(bool aForProcessSwitch) {
1819 StartDestroy(aForProcessSwitch);
1822 class nsFrameLoaderDestroyRunnable : public Runnable {
1823 enum DestroyPhase {
1824 // See the implementation of Run for an explanation of these phases.
1825 eDestroyDocShell,
1826 eWaitForUnloadMessage,
1827 eDestroyComplete
1830 RefPtr<nsFrameLoader> mFrameLoader;
1831 DestroyPhase mPhase;
1833 public:
1834 explicit nsFrameLoaderDestroyRunnable(nsFrameLoader* aFrameLoader)
1835 : mozilla::Runnable("nsFrameLoaderDestroyRunnable"),
1836 mFrameLoader(aFrameLoader),
1837 mPhase(eDestroyDocShell) {}
1839 NS_IMETHOD Run() override;
1842 void nsFrameLoader::StartDestroy(bool aForProcessSwitch) {
1843 // nsFrameLoader::StartDestroy is called just before the frameloader is
1844 // detached from the <browser> element. Destruction continues in phases via
1845 // the nsFrameLoaderDestroyRunnable.
1847 if (mDestroyCalled) {
1848 return;
1850 mDestroyCalled = true;
1852 // Request a full tab state flush if the tab is closing.
1854 // XXX If we find that we need to do Session Store cleanup for the frameloader
1855 // that's going away, we should unconditionally do the flush here, but include
1856 // the |aForProcessSwitch| flag in the completion notification.
1857 if (!aForProcessSwitch) {
1858 RequestFinalTabStateFlush();
1861 // After this point, we return an error when trying to send a message using
1862 // the message manager on the frame.
1863 if (mMessageManager) {
1864 mMessageManager->Close();
1867 // Retain references to the <browser> element and the frameloader in case we
1868 // receive any messages from the message manager on the frame. These
1869 // references are dropped in DestroyComplete.
1870 if (mChildMessageManager || mRemoteBrowser) {
1871 mOwnerContentStrong = mOwnerContent;
1872 if (auto* browserParent = GetBrowserParent()) {
1873 browserParent->CacheFrameLoader(this);
1875 if (mChildMessageManager) {
1876 mChildMessageManager->CacheFrameLoader(this);
1880 // If the BrowserParent has installed any event listeners on the window, this
1881 // is its last chance to remove them while we're still in the document.
1882 if (auto* browserParent = GetBrowserParent()) {
1883 browserParent->RemoveWindowListeners();
1886 nsCOMPtr<Document> doc;
1887 bool dynamicSubframeRemoval = false;
1888 if (mOwnerContent) {
1889 doc = mOwnerContent->OwnerDoc();
1890 dynamicSubframeRemoval = !aForProcessSwitch &&
1891 mPendingBrowsingContext->IsSubframe() &&
1892 !doc->InUnlinkOrDeletion();
1893 doc->SetSubDocumentFor(mOwnerContent, nullptr);
1894 MaybeUpdatePrimaryBrowserParent(eBrowserParentRemoved);
1896 nsCOMPtr<nsFrameLoaderOwner> owner = do_QueryInterface(mOwnerContent);
1897 owner->FrameLoaderDestroying(this, !aForProcessSwitch);
1898 SetOwnerContent(nullptr);
1901 // Seems like this is a dynamic frame removal.
1902 if (dynamicSubframeRemoval) {
1903 BrowsingContext* browsingContext = GetExtantBrowsingContext();
1904 if (browsingContext) {
1905 RefPtr<ChildSHistory> childSHistory =
1906 browsingContext->Top()->GetChildSessionHistory();
1907 if (childSHistory) {
1908 if (mozilla::SessionHistoryInParent()) {
1909 uint32_t addedEntries = 0;
1910 browsingContext->PreOrderWalk([&addedEntries](BrowsingContext* aBC) {
1911 // The initial load doesn't increase history length.
1912 addedEntries += aBC->GetHistoryEntryCount() - 1;
1915 nsID changeID = {};
1916 if (addedEntries > 0) {
1917 ChildSHistory* shistory =
1918 browsingContext->Top()->GetChildSessionHistory();
1919 if (shistory) {
1920 changeID = shistory->AddPendingHistoryChange(0, -addedEntries);
1923 browsingContext->RemoveFromSessionHistory(changeID);
1924 } else {
1925 AutoTArray<nsID, 16> ids({browsingContext->GetHistoryID()});
1926 childSHistory->LegacySHistory()->RemoveEntries(
1927 ids, childSHistory->Index());
1933 // Let the tree owner know we're gone.
1934 if (mIsTopLevelContent) {
1935 if (GetDocShell()) {
1936 nsCOMPtr<nsIDocShellTreeItem> parentItem;
1937 GetDocShell()->GetInProcessParent(getter_AddRefs(parentItem));
1938 nsCOMPtr<nsIDocShellTreeOwner> owner = do_GetInterface(parentItem);
1939 if (owner) {
1940 owner->ContentShellRemoved(GetDocShell());
1945 // Let our window know that we are gone
1946 if (GetDocShell()) {
1947 nsCOMPtr<nsPIDOMWindowOuter> win_private(GetDocShell()->GetWindow());
1948 if (win_private) {
1949 win_private->SetFrameElementInternal(nullptr);
1953 nsCOMPtr<nsIRunnable> destroyRunnable =
1954 new nsFrameLoaderDestroyRunnable(this);
1955 if (mNeedsAsyncDestroy || !doc ||
1956 NS_FAILED(doc->FinalizeFrameLoader(this, destroyRunnable))) {
1957 NS_DispatchToCurrentThread(destroyRunnable);
1961 nsresult nsFrameLoaderDestroyRunnable::Run() {
1962 switch (mPhase) {
1963 case eDestroyDocShell:
1964 mFrameLoader->DestroyDocShell();
1966 // In the out-of-process case, BrowserParent will eventually call
1967 // DestroyComplete once it receives a __delete__ message from the child.
1968 // In the in-process case, or if the BrowserParent was already destroyed,
1969 // we dispatch a series of runnables to ensure that DestroyComplete gets
1970 // called at the right time. The frame loader is kept alive by
1971 // mFrameLoader during this time.
1972 if (!mFrameLoader->GetRemoteBrowser() ||
1973 !mFrameLoader->GetRemoteBrowser()->CanRecv()) {
1974 // When the docshell is destroyed, NotifyWindowIDDestroyed is called to
1975 // asynchronously notify {outer,inner}-window-destroyed via a runnable.
1976 // We don't want DestroyComplete to run until after those runnables have
1977 // run. Since we're enqueueing ourselves after the window-destroyed
1978 // runnables are enqueued, we're guaranteed to run after.
1979 mPhase = eWaitForUnloadMessage;
1980 NS_DispatchToCurrentThread(this);
1982 break;
1984 case eWaitForUnloadMessage:
1985 // The *-window-destroyed observers have finished running at this
1986 // point. However, it's possible that a *-window-destroyed observer might
1987 // have sent a message using the message manager. These messages might not
1988 // have been processed yet. So we enqueue ourselves again to ensure that
1989 // DestroyComplete runs after all messages sent by *-window-destroyed
1990 // observers have been processed.
1991 mPhase = eDestroyComplete;
1992 NS_DispatchToCurrentThread(this);
1993 break;
1995 case eDestroyComplete:
1996 // Now that all messages sent by unload listeners and window destroyed
1997 // observers have been processed, we disconnect the message manager and
1998 // finish destruction.
1999 mFrameLoader->DestroyComplete();
2000 break;
2003 return NS_OK;
2006 void nsFrameLoader::DestroyDocShell() {
2007 // This code runs after the frameloader has been detached from the <browser>
2008 // element. We postpone this work because we may not be allowed to run
2009 // script at that time.
2011 // Ask the BrowserChild to fire the frame script "unload" event, destroy its
2012 // docshell, and finally destroy the PBrowser actor. This eventually leads to
2013 // nsFrameLoader::DestroyComplete being called.
2014 if (mRemoteBrowser) {
2015 mRemoteBrowser->DestroyStart();
2018 // Fire the "unload" event if we're in-process.
2019 if (mChildMessageManager) {
2020 mChildMessageManager->FireUnloadEvent();
2023 if (mSessionStoreChild) {
2024 mSessionStoreChild->Stop();
2025 mSessionStoreChild = nullptr;
2028 // Destroy the docshell.
2029 if (GetDocShell()) {
2030 GetDocShell()->Destroy();
2033 if (!mWillChangeProcess && mPendingBrowsingContext &&
2034 mPendingBrowsingContext->EverAttached()) {
2035 mPendingBrowsingContext->Detach();
2038 mPendingBrowsingContext = nullptr;
2039 mDocShell = nullptr;
2041 if (mChildMessageManager) {
2042 // Stop handling events in the in-process frame script.
2043 mChildMessageManager->DisconnectEventListeners();
2047 void nsFrameLoader::DestroyComplete() {
2048 // We get here, as part of StartDestroy, after the docshell has been destroyed
2049 // and all message manager messages sent during docshell destruction have been
2050 // dispatched. We also get here if the child process crashes. In the latter
2051 // case, StartDestroy might not have been called.
2053 // Drop the strong references created in StartDestroy.
2054 if (mChildMessageManager || mRemoteBrowser) {
2055 mOwnerContentStrong = nullptr;
2056 if (auto* browserParent = GetBrowserParent()) {
2057 browserParent->CacheFrameLoader(nullptr);
2059 if (mChildMessageManager) {
2060 mChildMessageManager->CacheFrameLoader(nullptr);
2064 // Call BrowserParent::Destroy if we haven't already (in case of a crash).
2065 if (mRemoteBrowser) {
2066 mRemoteBrowser->DestroyComplete();
2067 mRemoteBrowser = nullptr;
2070 if (mMessageManager) {
2071 mMessageManager->Disconnect();
2074 if (mChildMessageManager) {
2075 mChildMessageManager->Disconnect();
2078 mMessageManager = nullptr;
2079 mChildMessageManager = nullptr;
2082 void nsFrameLoader::SetOwnerContent(Element* aContent) {
2083 if (mObservingOwnerContent) {
2084 mObservingOwnerContent = false;
2085 mOwnerContent->RemoveMutationObserver(this);
2088 // XXXBFCache Need to update also all the non-current FrameLoaders in the
2089 // owner when moving a FrameLoader.
2090 // This temporary setup doesn't crash, but behaves badly with bfcached docs.
2091 if (RefPtr<nsFrameLoaderOwner> owner = do_QueryObject(mOwnerContent)) {
2092 owner->DetachFrameLoader(this);
2095 mOwnerContent = aContent;
2097 if (RefPtr<nsFrameLoaderOwner> owner = do_QueryObject(mOwnerContent)) {
2098 owner->AttachFrameLoader(this);
2100 #ifdef NIGHTLY_BUILD
2101 if (mozilla::BFCacheInParent() && XRE_IsParentProcess()) {
2102 if (BrowsingContext* bc = GetMaybePendingBrowsingContext()) {
2103 nsISHistory* shistory = bc->Canonical()->GetSessionHistory();
2104 if (shistory) {
2105 uint32_t count = shistory->GetCount();
2106 for (uint32_t i = 0; i < count; ++i) {
2107 nsCOMPtr<nsISHEntry> entry;
2108 shistory->GetEntryAtIndex(i, getter_AddRefs(entry));
2109 nsCOMPtr<SessionHistoryEntry> she = do_QueryInterface(entry);
2110 MOZ_RELEASE_ASSERT(!she || !she->GetFrameLoader());
2115 #endif
2118 if (mSessionStoreChild && mOwnerContent) {
2119 // mOwnerContent will only be null when the frame loader is being destroyed,
2120 // so the session store listener will be destroyed along with it.
2121 // XXX(farre): This probably needs to update the cache. See bug 1698497.
2122 mSessionStoreChild->SetOwnerContent(mOwnerContent);
2125 if (RefPtr<BrowsingContext> browsingContext = GetExtantBrowsingContext()) {
2126 browsingContext->SetEmbedderElement(mOwnerContent);
2129 if (mSessionStoreChild) {
2130 // UpdateEventTargets will requery its browser contexts for event
2131 // targets, so this call needs to happen after the call to
2132 // SetEmbedderElement above.
2133 mSessionStoreChild->UpdateEventTargets();
2136 AutoJSAPI jsapi;
2137 jsapi.Init();
2139 JS::Rooted<JSObject*> wrapper(jsapi.cx(), GetWrapper());
2140 if (wrapper) {
2141 JSAutoRealm ar(jsapi.cx(), wrapper);
2142 IgnoredErrorResult rv;
2143 UpdateReflectorGlobal(jsapi.cx(), wrapper, rv);
2144 Unused << NS_WARN_IF(rv.Failed());
2148 bool nsFrameLoader::OwnerIsMozBrowserFrame() {
2149 nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwnerContent);
2150 return browserFrame ? browserFrame->GetReallyIsBrowser() : false;
2153 nsIContent* nsFrameLoader::GetParentObject() const { return mOwnerContent; }
2155 void nsFrameLoader::AssertSafeToInit() {
2156 MOZ_DIAGNOSTIC_ASSERT(nsContentUtils::IsSafeToRunScript() ||
2157 mOwnerContent->OwnerDoc()->IsStaticDocument(),
2158 "FrameLoader should never be initialized during "
2159 "document update or reflow!");
2162 nsresult nsFrameLoader::MaybeCreateDocShell() {
2163 if (GetDocShell()) {
2164 return NS_OK;
2166 if (IsRemoteFrame()) {
2167 return NS_OK;
2169 NS_ENSURE_STATE(!mDestroyCalled);
2171 AssertSafeToInit();
2173 // Get our parent docshell off the document of mOwnerContent
2174 // XXXbz this is such a total hack.... We really need to have a
2175 // better setup for doing this.
2176 Document* doc = mOwnerContent->OwnerDoc();
2178 MOZ_RELEASE_ASSERT(!doc->IsResourceDoc(), "We shouldn't even exist");
2180 // If we've already tried to initialize and failed, don't try again.
2181 if (mInitialized) {
2182 return NS_ERROR_NOT_AVAILABLE;
2184 mInitialized = true;
2186 // Check if the document still has a window since it is possible for an
2187 // iframe to be inserted and cause the creation of the docshell in a
2188 // partially unloaded document (see Bug 1305237 comment 127).
2189 if (!doc->IsStaticDocument() &&
2190 (!doc->GetWindow() || !mOwnerContent->IsInComposedDoc())) {
2191 return NS_ERROR_UNEXPECTED;
2194 if (!doc->IsActive()) {
2195 // Don't allow subframe loads in non-active documents.
2196 // (See bug 610571 comment 5.)
2197 return NS_ERROR_NOT_AVAILABLE;
2200 RefPtr<nsDocShell> parentDocShell = nsDocShell::Cast(doc->GetDocShell());
2201 if (NS_WARN_IF(!parentDocShell)) {
2202 return NS_ERROR_UNEXPECTED;
2205 if (doc->GetWindowContext()->IsDiscarded() ||
2206 parentDocShell->GetBrowsingContext()->IsDiscarded()) {
2207 // Don't allow subframe loads in discarded contexts.
2208 // (see bug 1652085, bug 1656854)
2209 return NS_ERROR_NOT_AVAILABLE;
2212 if (!EnsureBrowsingContextAttached()) {
2213 return NS_ERROR_FAILURE;
2216 mPendingBrowsingContext->SetEmbedderElement(mOwnerContent);
2218 // nsDocShell::Create will attach itself to the passed browsing
2219 // context inside of nsDocShell::Create
2220 RefPtr<nsDocShell> docShell = nsDocShell::Create(mPendingBrowsingContext);
2221 NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
2222 mDocShell = docShell;
2224 mPendingBrowsingContext->Embed();
2226 InvokeBrowsingContextReadyCallback();
2228 mIsTopLevelContent = mPendingBrowsingContext->IsTopContent();
2230 if (mIsTopLevelContent) {
2231 // Manually add ourselves to our parent's docshell, as BrowsingContext won't
2232 // have done this for us.
2234 // XXX(nika): Consider removing the DocShellTree in the future, for
2235 // consistency between local and remote frames..
2236 parentDocShell->AddChild(docShell);
2239 // Now that we are part of the DocShellTree, attach our DocShell to our
2240 // parent's TreeOwner.
2241 nsCOMPtr<nsIDocShellTreeOwner> parentTreeOwner;
2242 parentDocShell->GetTreeOwner(getter_AddRefs(parentTreeOwner));
2243 AddTreeItemToTreeOwner(docShell, parentTreeOwner);
2245 // Make sure all nsDocShells have links back to the content element in the
2246 // nearest enclosing chrome shell.
2247 RefPtr<EventTarget> chromeEventHandler;
2248 bool parentIsContent = parentDocShell->GetBrowsingContext()->IsContent();
2249 if (parentIsContent) {
2250 // Our parent shell is a content shell. Get the chrome event handler from it
2251 // and use that for our shell as well.
2252 parentDocShell->GetChromeEventHandler(getter_AddRefs(chromeEventHandler));
2253 } else {
2254 // Our parent shell is a chrome shell. It is therefore our nearest enclosing
2255 // chrome shell.
2256 chromeEventHandler = mOwnerContent;
2259 docShell->SetChromeEventHandler(chromeEventHandler);
2261 // This is nasty, this code (the docShell->GetWindow() below)
2262 // *must* come *after* the above call to
2263 // docShell->SetChromeEventHandler() for the global window to get
2264 // the right chrome event handler.
2266 // Tell the window about the frame that hosts it.
2267 nsCOMPtr<nsPIDOMWindowOuter> newWindow = docShell->GetWindow();
2268 if (NS_WARN_IF(!newWindow)) {
2269 // Do not call Destroy() here. See bug 472312.
2270 NS_WARNING("Something wrong when creating the docshell for a frameloader!");
2271 return NS_ERROR_FAILURE;
2274 newWindow->SetFrameElementInternal(mOwnerContent);
2276 // Allow scripts to close the docshell if specified.
2277 if (mOwnerContent->IsXULElement(nsGkAtoms::browser) &&
2278 mOwnerContent->AttrValueIs(kNameSpaceID_None,
2279 nsGkAtoms::allowscriptstoclose,
2280 nsGkAtoms::_true, eCaseMatters)) {
2281 nsGlobalWindowOuter::Cast(newWindow)->AllowScriptsToClose();
2284 if (!docShell->Initialize()) {
2285 // Do not call Destroy() here. See bug 472312.
2286 NS_WARNING("Something wrong when creating the docshell for a frameloader!");
2287 return NS_ERROR_FAILURE;
2290 NS_ENSURE_STATE(mOwnerContent);
2292 // If we are an in-process browser, we want to set up our session history.
2293 if (mIsTopLevelContent && mOwnerContent->IsXULElement(nsGkAtoms::browser) &&
2294 !mOwnerContent->HasAttr(nsGkAtoms::disablehistory)) {
2295 // XXX(nika): Set this up more explicitly?
2296 mPendingBrowsingContext->InitSessionHistory();
2299 // Apply sandbox flags even if our owner is not an iframe, as this copies
2300 // flags from our owning content's owning document.
2301 // Note: ApplySandboxFlags should be called after docShell->SetIsFrame
2302 // because we need to get the correct presentation URL in ApplySandboxFlags.
2303 uint32_t sandboxFlags = 0;
2304 HTMLIFrameElement* iframe = HTMLIFrameElement::FromNode(mOwnerContent);
2305 if (iframe) {
2306 sandboxFlags = iframe->GetSandboxFlags();
2308 ApplySandboxFlags(sandboxFlags);
2309 MOZ_ALWAYS_SUCCEEDS(mPendingBrowsingContext->SetInitialSandboxFlags(
2310 mPendingBrowsingContext->GetSandboxFlags()));
2312 if (OwnerIsMozBrowserFrame()) {
2313 // For inproc frames, set the docshell properties.
2314 nsAutoString name;
2315 if (mOwnerContent->GetAttr(nsGkAtoms::name, name)) {
2316 docShell->SetName(name);
2320 ReallyLoadFrameScripts();
2321 InitializeBrowserAPI();
2323 // Previously we would forcibly create the initial about:blank document for
2324 // in-process content frames from a frame script which eagerly loaded in
2325 // every tab. This lead to other frontend components growing dependencies on
2326 // the initial about:blank document being created eagerly. See bug 1471327
2327 // for details.
2329 // We also eagerly create the initial about:blank document for remote loads
2330 // separately when initializing BrowserChild.
2331 if (mIsTopLevelContent &&
2332 mPendingBrowsingContext->GetMessageManagerGroup() == u"browsers"_ns) {
2333 Unused << mDocShell->GetDocument();
2336 return NS_OK;
2339 void nsFrameLoader::GetURL(nsString& aURI, nsIPrincipal** aTriggeringPrincipal,
2340 nsIContentSecurityPolicy** aCsp) {
2341 aURI.Truncate();
2342 // Within this function we default to using the NodePrincipal as the
2343 // triggeringPrincipal and the CSP of the document.
2344 // Expanded Principals however override the CSP of the document, hence
2345 // if frame->GetSrcTriggeringPrincipal() returns a valid principal, we
2346 // have to query the CSP from that Principal.
2347 nsCOMPtr<nsIPrincipal> triggeringPrincipal = mOwnerContent->NodePrincipal();
2348 nsCOMPtr<nsIContentSecurityPolicy> csp = mOwnerContent->GetCsp();
2350 if (mOwnerContent->IsHTMLElement(nsGkAtoms::object)) {
2351 mOwnerContent->GetAttr(nsGkAtoms::data, aURI);
2352 } else {
2353 mOwnerContent->GetAttr(nsGkAtoms::src, aURI);
2354 if (RefPtr<nsGenericHTMLFrameElement> frame =
2355 do_QueryObject(mOwnerContent)) {
2356 nsCOMPtr<nsIPrincipal> srcPrincipal = frame->GetSrcTriggeringPrincipal();
2357 if (srcPrincipal) {
2358 triggeringPrincipal = srcPrincipal;
2359 nsCOMPtr<nsIExpandedPrincipal> ep =
2360 do_QueryInterface(triggeringPrincipal);
2361 if (ep) {
2362 csp = ep->GetCsp();
2367 triggeringPrincipal.forget(aTriggeringPrincipal);
2368 csp.forget(aCsp);
2371 nsresult nsFrameLoader::CheckForRecursiveLoad(nsIURI* aURI) {
2372 MOZ_ASSERT(!IsRemoteFrame(),
2373 "Shouldn't call CheckForRecursiveLoad on remote frames.");
2375 mDepthTooGreat = false;
2376 RefPtr<BrowsingContext> parentBC(
2377 mOwnerContent->OwnerDoc()->GetBrowsingContext());
2378 NS_ENSURE_STATE(parentBC);
2380 if (!parentBC->IsContent()) {
2381 return NS_OK;
2384 // Bug 8065: Don't exceed some maximum depth in content frames
2385 // (MAX_DEPTH_CONTENT_FRAMES)
2386 int32_t depth = 0;
2387 for (BrowsingContext* bc = parentBC; bc; bc = bc->GetParent()) {
2388 ++depth;
2389 if (depth >= MAX_DEPTH_CONTENT_FRAMES) {
2390 mDepthTooGreat = true;
2391 NS_WARNING("Too many nested content frames so giving up");
2393 return NS_ERROR_UNEXPECTED; // Too deep, give up! (silently?)
2397 return NS_OK;
2400 nsresult nsFrameLoader::GetWindowDimensions(nsIntRect& aRect) {
2401 if (!mOwnerContent) {
2402 return NS_ERROR_FAILURE;
2405 // Need to get outer window position here
2406 Document* doc = mOwnerContent->GetComposedDoc();
2407 if (!doc) {
2408 return NS_ERROR_FAILURE;
2411 MOZ_RELEASE_ASSERT(!doc->IsResourceDoc(), "We shouldn't even exist");
2413 nsCOMPtr<nsPIDOMWindowOuter> win = doc->GetWindow();
2414 if (!win) {
2415 return NS_ERROR_FAILURE;
2418 nsCOMPtr<nsIDocShellTreeItem> parentAsItem(win->GetDocShell());
2419 if (!parentAsItem) {
2420 return NS_ERROR_FAILURE;
2423 nsCOMPtr<nsIDocShellTreeOwner> parentOwner;
2424 if (NS_FAILED(parentAsItem->GetTreeOwner(getter_AddRefs(parentOwner))) ||
2425 !parentOwner) {
2426 return NS_ERROR_FAILURE;
2429 nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_GetInterface(parentOwner));
2430 treeOwnerAsWin->GetPosition(&aRect.x, &aRect.y);
2431 treeOwnerAsWin->GetSize(&aRect.width, &aRect.height);
2432 return NS_OK;
2435 nsresult nsFrameLoader::UpdatePositionAndSize(nsSubDocumentFrame* aIFrame) {
2436 if (IsRemoteFrame()) {
2437 if (mRemoteBrowser) {
2438 ScreenIntSize size = aIFrame->GetSubdocumentSize();
2439 // If we were not able to show remote frame before, we should probably
2440 // retry now to send correct showInfo.
2441 if (!mRemoteBrowserShown) {
2442 ShowRemoteFrame(size, aIFrame);
2444 nsIntRect dimensions;
2445 NS_ENSURE_SUCCESS(GetWindowDimensions(dimensions), NS_ERROR_FAILURE);
2446 mLazySize = size;
2447 mRemoteBrowser->UpdateDimensions(dimensions, size);
2449 return NS_OK;
2451 UpdateBaseWindowPositionAndSize(aIFrame);
2452 return NS_OK;
2455 void nsFrameLoader::PropagateIsUnderHiddenEmbedderElement(
2456 bool aIsUnderHiddenEmbedderElement) {
2457 bool isUnderHiddenEmbedderElement = true;
2458 if (Document* ownerDoc = GetOwnerDoc()) {
2459 if (PresShell* presShell = ownerDoc->GetPresShell()) {
2460 isUnderHiddenEmbedderElement = presShell->IsUnderHiddenEmbedderElement();
2464 isUnderHiddenEmbedderElement |= aIsUnderHiddenEmbedderElement;
2466 BrowsingContext* browsingContext = GetExtantBrowsingContext();
2467 if (browsingContext && browsingContext->IsUnderHiddenEmbedderElement() !=
2468 isUnderHiddenEmbedderElement) {
2469 Unused << browsingContext->SetIsUnderHiddenEmbedderElement(
2470 isUnderHiddenEmbedderElement);
2474 void nsFrameLoader::UpdateRemoteStyle(
2475 mozilla::StyleImageRendering aImageRendering) {
2476 MOZ_DIAGNOSTIC_ASSERT(IsRemoteFrame());
2478 if (auto* browserBridgeChild = GetBrowserBridgeChild()) {
2479 browserBridgeChild->SendUpdateRemoteStyle(aImageRendering);
2483 void nsFrameLoader::UpdateBaseWindowPositionAndSize(
2484 nsSubDocumentFrame* aIFrame) {
2485 nsCOMPtr<nsIBaseWindow> baseWindow = GetDocShell(IgnoreErrors());
2487 // resize the sub document
2488 if (baseWindow) {
2489 int32_t x = 0;
2490 int32_t y = 0;
2492 AutoWeakFrame weakFrame(aIFrame);
2494 baseWindow->GetPosition(&x, &y);
2496 if (!weakFrame.IsAlive()) {
2497 // GetPosition() killed us
2498 return;
2501 ScreenIntSize size = aIFrame->GetSubdocumentSize();
2502 mLazySize = size;
2504 baseWindow->SetPositionAndSize(x, y, size.width, size.height,
2505 nsIBaseWindow::eDelayResize);
2509 uint32_t nsFrameLoader::LazyWidth() const {
2510 uint32_t lazyWidth = mLazySize.width;
2512 nsIFrame* frame = GetPrimaryFrameOfOwningContent();
2513 if (frame) {
2514 lazyWidth = frame->PresContext()->DevPixelsToIntCSSPixels(lazyWidth);
2517 return lazyWidth;
2520 uint32_t nsFrameLoader::LazyHeight() const {
2521 uint32_t lazyHeight = mLazySize.height;
2523 nsIFrame* frame = GetPrimaryFrameOfOwningContent();
2524 if (frame) {
2525 lazyHeight = frame->PresContext()->DevPixelsToIntCSSPixels(lazyHeight);
2528 return lazyHeight;
2531 bool nsFrameLoader::EnsureRemoteBrowser() {
2532 MOZ_ASSERT(IsRemoteFrame());
2533 return mRemoteBrowser || TryRemoteBrowser();
2536 bool nsFrameLoader::TryRemoteBrowserInternal() {
2537 NS_ASSERTION(!mRemoteBrowser,
2538 "TryRemoteBrowser called with a remote browser already?");
2539 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess(),
2540 "Remote subframes should only be created using the "
2541 "`CanonicalBrowsingContext::ChangeRemoteness` API");
2543 AssertSafeToInit();
2545 if (!mOwnerContent) {
2546 return false;
2549 // XXXsmaug Per spec (2014/08/21) frameloader should not work in case the
2550 // element isn't in document, only in shadow dom, but that will change
2551 // https://www.w3.org/Bugs/Public/show_bug.cgi?id=26365#c0
2552 RefPtr<Document> doc = mOwnerContent->GetComposedDoc();
2553 if (!doc) {
2554 return false;
2557 MOZ_RELEASE_ASSERT(!doc->IsResourceDoc(), "We shouldn't even exist");
2559 // Graphics initialization code relies on having a frame for the
2560 // remote browser case, as we can be inside a popup, which is a different
2561 // widget.
2563 // FIXME: Ideally this should be unconditional, but we skip if for <iframe
2564 // mozbrowser> because the old RDM ui depends on current behavior, and the
2565 // mozbrowser frame code is scheduled for deletion, see bug 1574886.
2566 if (!OwnerIsMozBrowserFrame() && !mOwnerContent->GetPrimaryFrame()) {
2567 doc->FlushPendingNotifications(FlushType::Frames);
2570 // The flush could have initialized us.
2571 if (mRemoteBrowser) {
2572 return true;
2575 // If we've already tried to initialize and failed, don't try again.
2576 if (mInitialized) {
2577 return false;
2579 mInitialized = true;
2581 // Ensure the world hasn't changed that much as a result of that.
2582 if (!mOwnerContent || mOwnerContent->OwnerDoc() != doc ||
2583 !mOwnerContent->IsInComposedDoc()) {
2584 return false;
2587 if (RefPtr<nsFrameLoaderOwner> flo = do_QueryObject(mOwnerContent)) {
2588 RefPtr<nsFrameLoader> fl = flo->GetFrameLoader();
2589 if (fl != this) {
2590 MOZ_ASSERT_UNREACHABLE(
2591 "Got TryRemoteBrowserInternal but mOwnerContent already has a "
2592 "different frameloader?");
2593 return false;
2597 if (!doc->IsActive()) {
2598 // Don't allow subframe loads in non-active documents.
2599 // (See bug 610571 comment 5.)
2600 return false;
2603 nsCOMPtr<nsPIDOMWindowOuter> parentWin = doc->GetWindow();
2604 if (!parentWin) {
2605 return false;
2608 nsCOMPtr<nsIDocShell> parentDocShell = parentWin->GetDocShell();
2609 if (!parentDocShell) {
2610 return false;
2613 if (!EnsureBrowsingContextAttached()) {
2614 return false;
2617 if (mPendingBrowsingContext->IsTop()) {
2618 mPendingBrowsingContext->InitSessionHistory();
2621 // <iframe mozbrowser> gets to skip these checks.
2622 // iframes for JS plugins also get to skip these checks. We control the URL
2623 // that gets loaded, but the load is triggered from the document containing
2624 // the plugin.
2625 // out of process iframes also get to skip this check.
2626 if (!OwnerIsMozBrowserFrame() && !XRE_IsContentProcess()) {
2627 if (parentDocShell->ItemType() != nsIDocShellTreeItem::typeChrome) {
2628 // Allow three exceptions to this rule :
2629 // - about:addons so it can load remote extension options pages
2630 // - about:preferences (in Thunderbird only) so it can load remote
2631 // extension options pages for FileLink providers
2632 // - DevTools webext panels if DevTools is loaded in a content frame
2633 // - DevTools Network Monitor, which uses content frame for HTML request
2634 // previews
2635 // - Chrome mochitests can also do this.
2637 // Note that the new frame's message manager will not be a child of the
2638 // chrome window message manager, and, the values of window.top and
2639 // window.parent will be different than they would be for a non-remote
2640 // frame.
2641 nsIURI* parentURI = parentWin->GetDocumentURI();
2642 if (!parentURI) {
2643 return false;
2646 nsAutoCString specIgnoringRef;
2647 if (NS_FAILED(parentURI->GetSpecIgnoringRef(specIgnoringRef))) {
2648 return false;
2651 const bool allowed = [&] {
2652 const nsLiteralCString kAllowedURIs[] = {
2653 "about:addons"_ns,
2654 "chrome://mozapps/content/extensions/aboutaddons.html"_ns,
2655 #ifdef MOZ_THUNDERBIRD
2656 "about:3pane"_ns,
2657 "about:message"_ns,
2658 "about:preferences"_ns,
2659 #endif
2660 "chrome://browser/content/webext-panels.xhtml"_ns,
2661 "chrome://devtools/content/netmonitor/index.html"_ns,
2662 "chrome://devtools/content/webconsole/index.html"_ns,
2665 for (const auto& allowedURI : kAllowedURIs) {
2666 if (specIgnoringRef.Equals(allowedURI)) {
2667 return true;
2671 if (xpc::IsInAutomation() &&
2672 StringBeginsWith(specIgnoringRef,
2673 "chrome://mochitests/content/chrome/"_ns)) {
2674 return true;
2676 return false;
2677 }();
2679 if (!allowed) {
2680 NS_WARNING(
2681 nsPrintfCString("Forbidden remote frame from content docshell %s",
2682 specIgnoringRef.get())
2683 .get());
2684 return false;
2688 if (!mOwnerContent->IsXULElement()) {
2689 return false;
2692 if (!mOwnerContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
2693 nsGkAtoms::content, eIgnoreCase)) {
2694 return false;
2698 uint32_t chromeFlags = 0;
2699 nsCOMPtr<nsIDocShellTreeOwner> parentOwner;
2700 if (NS_FAILED(parentDocShell->GetTreeOwner(getter_AddRefs(parentOwner))) ||
2701 !parentOwner) {
2702 return false;
2704 nsCOMPtr<nsIAppWindow> window(do_GetInterface(parentOwner));
2705 if (window && NS_FAILED(window->GetChromeFlags(&chromeFlags))) {
2706 return false;
2709 AUTO_PROFILER_LABEL("nsFrameLoader::TryRemoteBrowser:Create", OTHER);
2711 MutableTabContext context;
2712 nsresult rv = GetNewTabContext(&context);
2713 NS_ENSURE_SUCCESS(rv, false);
2715 RefPtr<Element> ownerElement = mOwnerContent;
2717 RefPtr<BrowserParent> nextRemoteBrowser =
2718 mOpenWindowInfo ? mOpenWindowInfo->GetNextRemoteBrowser() : nullptr;
2719 if (nextRemoteBrowser) {
2720 mRemoteBrowser = new BrowserHost(nextRemoteBrowser);
2721 if (nextRemoteBrowser->GetOwnerElement()) {
2722 MOZ_ASSERT_UNREACHABLE("Shouldn't have an owner element before");
2723 return false;
2725 nextRemoteBrowser->SetOwnerElement(ownerElement);
2726 } else {
2727 RefPtr<ContentParent> contentParent;
2728 if (mChildID != 0) {
2729 ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
2730 if (!cpm) {
2731 return false;
2733 contentParent = cpm->GetContentProcessById(ContentParentId(mChildID));
2735 mRemoteBrowser =
2736 ContentParent::CreateBrowser(context, ownerElement, mRemoteType,
2737 mPendingBrowsingContext, contentParent);
2739 if (!mRemoteBrowser) {
2740 return false;
2743 MOZ_DIAGNOSTIC_ASSERT(mPendingBrowsingContext ==
2744 mRemoteBrowser->GetBrowsingContext());
2746 mRemoteBrowser->GetBrowsingContext()->Embed();
2747 InvokeBrowsingContextReadyCallback();
2749 // Grab the reference to the actor
2750 RefPtr<BrowserParent> browserParent = GetBrowserParent();
2752 MOZ_ASSERT(browserParent->CanSend(), "BrowserParent cannot send?");
2754 // We no longer need the remoteType attribute on the frame element.
2755 // The remoteType can be queried by asking the message manager instead.
2756 ownerElement->UnsetAttr(kNameSpaceID_None, nsGkAtoms::RemoteType, false);
2758 // Now that browserParent is set, we can initialize graphics
2759 browserParent->InitRendering();
2761 MaybeUpdatePrimaryBrowserParent(eBrowserParentChanged);
2763 mChildID = browserParent->Manager()->ChildID();
2765 nsCOMPtr<nsIDocShellTreeItem> rootItem;
2766 parentDocShell->GetInProcessRootTreeItem(getter_AddRefs(rootItem));
2767 RefPtr<nsGlobalWindowOuter> rootWin =
2768 nsGlobalWindowOuter::Cast(rootItem->GetWindow());
2770 if (rootWin && rootWin->IsChromeWindow()) {
2771 browserParent->SetBrowserDOMWindow(rootWin->GetBrowserDOMWindow());
2774 // For xul:browsers, update some settings based on attributes:
2775 if (mOwnerContent->IsXULElement()) {
2776 // Send down the name of the browser through browserParent if it is set.
2777 nsAutoString frameName;
2778 mOwnerContent->GetAttr(nsGkAtoms::name, frameName);
2779 if (nsContentUtils::IsOverridingWindowName(frameName)) {
2780 MOZ_ALWAYS_SUCCEEDS(mPendingBrowsingContext->SetName(frameName));
2782 // Allow scripts to close the window if the browser specified so:
2783 if (mOwnerContent->AttrValueIs(kNameSpaceID_None,
2784 nsGkAtoms::allowscriptstoclose,
2785 nsGkAtoms::_true, eCaseMatters)) {
2786 Unused << browserParent->SendAllowScriptsToClose();
2790 ReallyLoadFrameScripts();
2791 InitializeBrowserAPI();
2793 return true;
2796 bool nsFrameLoader::TryRemoteBrowser() {
2797 // Creating remote browsers may result in creating new processes, but during
2798 // parent shutdown that would add just noise, so better bail out.
2799 if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
2800 return false;
2803 // Try to create the internal remote browser.
2804 if (TryRemoteBrowserInternal()) {
2805 return true;
2808 // Check if we should report a browser-crashed error because the browser
2809 // failed to start.
2810 if (XRE_IsParentProcess() && mOwnerContent && mOwnerContent->IsXULElement()) {
2811 MaybeNotifyCrashed(nullptr, ContentParentId(), nullptr);
2814 return false;
2817 nsIFrame* nsFrameLoader::GetPrimaryFrameOfOwningContent() const {
2818 return mOwnerContent ? mOwnerContent->GetPrimaryFrame() : nullptr;
2821 Document* nsFrameLoader::GetOwnerDoc() const {
2822 return mOwnerContent ? mOwnerContent->OwnerDoc() : nullptr;
2825 bool nsFrameLoader::IsRemoteFrame() {
2826 if (mIsRemoteFrame) {
2827 MOZ_ASSERT(!GetDocShell(), "Found a remote frame with a DocShell");
2828 return true;
2830 return false;
2833 RemoteBrowser* nsFrameLoader::GetRemoteBrowser() const {
2834 return mRemoteBrowser;
2837 BrowserParent* nsFrameLoader::GetBrowserParent() const {
2838 if (!mRemoteBrowser) {
2839 return nullptr;
2841 RefPtr<BrowserHost> browserHost = mRemoteBrowser->AsBrowserHost();
2842 if (!browserHost) {
2843 return nullptr;
2845 return browserHost->GetActor();
2848 BrowserBridgeChild* nsFrameLoader::GetBrowserBridgeChild() const {
2849 if (!mRemoteBrowser) {
2850 return nullptr;
2852 RefPtr<BrowserBridgeHost> browserBridgeHost =
2853 mRemoteBrowser->AsBrowserBridgeHost();
2854 if (!browserBridgeHost) {
2855 return nullptr;
2857 return browserBridgeHost->GetActor();
2860 mozilla::layers::LayersId nsFrameLoader::GetLayersId() const {
2861 MOZ_ASSERT(mIsRemoteFrame);
2862 return mRemoteBrowser->GetLayersId();
2865 nsresult nsFrameLoader::DoRemoteStaticClone(nsFrameLoader* aStaticCloneOf,
2866 nsIPrintSettings* aPrintSettings) {
2867 MOZ_ASSERT(aStaticCloneOf->IsRemoteFrame());
2868 MOZ_ASSERT(aPrintSettings);
2869 auto* cc = ContentChild::GetSingleton();
2870 if (!cc) {
2871 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
2872 // TODO: Could possibly be implemented without too much effort.
2873 return NS_ERROR_NOT_IMPLEMENTED;
2875 BrowsingContext* bcToClone = aStaticCloneOf->GetBrowsingContext();
2876 if (NS_WARN_IF(!bcToClone)) {
2877 return NS_ERROR_UNEXPECTED;
2879 BrowsingContext* bc = GetBrowsingContext();
2880 MOZ_DIAGNOSTIC_ASSERT(bc);
2881 nsCOMPtr<nsIPrintSettingsService> printSettingsSvc =
2882 do_GetService("@mozilla.org/gfx/printsettings-service;1");
2883 if (NS_WARN_IF(!printSettingsSvc)) {
2884 return NS_ERROR_UNEXPECTED;
2886 embedding::PrintData printData;
2887 nsresult rv =
2888 printSettingsSvc->SerializeToPrintData(aPrintSettings, &printData);
2889 if (NS_WARN_IF(NS_FAILED(rv))) {
2890 return rv;
2893 cc->SendCloneDocumentTreeInto(bcToClone, bc, printData);
2894 return NS_OK;
2897 nsresult nsFrameLoader::FinishStaticClone(
2898 nsFrameLoader* aStaticCloneOf, nsIPrintSettings* aPrintSettings,
2899 bool* aOutHasInProcessPrintCallbacks) {
2900 MOZ_DIAGNOSTIC_ASSERT(
2901 !nsContentUtils::IsSafeToRunScript(),
2902 "A script blocker should be on the stack while FinishStaticClone is run");
2904 // NOTE: We don't check `aStaticCloneOf->IsDead()` here, as the nsFrameLoader
2905 // which we're a static clone of may be in the process of being destroyed. It
2906 // won't be fully destroyed when `FinishStaticClone` is called, as a script
2907 // blocker on our caller's stack is preventing it from becoming finalized.
2909 // This is quite fragile, but is quite difficult to work around without
2910 // getting print-preview to stop re-cloning and replacing the previewed
2911 // document when changing layout.
2912 if (NS_WARN_IF(IsDead())) {
2913 return NS_ERROR_UNEXPECTED;
2916 if (aStaticCloneOf->IsRemoteFrame()) {
2917 return DoRemoteStaticClone(aStaticCloneOf, aPrintSettings);
2920 nsIDocShell* origDocShell = aStaticCloneOf->GetDocShell();
2921 NS_ENSURE_STATE(origDocShell);
2923 nsCOMPtr<Document> doc = origDocShell->GetDocument();
2924 NS_ENSURE_STATE(doc);
2926 MaybeCreateDocShell();
2927 RefPtr<nsDocShell> docShell = GetDocShell();
2928 NS_ENSURE_STATE(docShell);
2930 nsCOMPtr<Document> kungFuDeathGrip = docShell->GetDocument();
2931 Unused << kungFuDeathGrip;
2933 nsCOMPtr<nsIContentViewer> viewer;
2934 docShell->GetContentViewer(getter_AddRefs(viewer));
2935 NS_ENSURE_STATE(viewer);
2937 nsCOMPtr<Document> clonedDoc = doc->CreateStaticClone(
2938 docShell, viewer, aPrintSettings, aOutHasInProcessPrintCallbacks);
2940 return NS_OK;
2943 bool nsFrameLoader::DoLoadMessageManagerScript(const nsAString& aURL,
2944 bool aRunInGlobalScope) {
2945 if (auto* browserParent = GetBrowserParent()) {
2946 return browserParent->SendLoadRemoteScript(aURL, aRunInGlobalScope);
2948 RefPtr<InProcessBrowserChildMessageManager> browserChild =
2949 GetBrowserChildMessageManager();
2950 if (browserChild) {
2951 browserChild->LoadFrameScript(aURL, aRunInGlobalScope);
2953 return true;
2956 class nsAsyncMessageToChild : public nsSameProcessAsyncMessageBase,
2957 public Runnable {
2958 public:
2959 explicit nsAsyncMessageToChild(nsFrameLoader* aFrameLoader)
2960 : mozilla::Runnable("nsAsyncMessageToChild"),
2961 mFrameLoader(aFrameLoader) {}
2963 NS_IMETHOD Run() override {
2964 InProcessBrowserChildMessageManager* browserChild =
2965 mFrameLoader->mChildMessageManager;
2966 // Since bug 1126089, messages can arrive even when the docShell is
2967 // destroyed. Here we make sure that those messages are not delivered.
2968 if (browserChild && browserChild->GetInnerManager() &&
2969 mFrameLoader->GetExistingDocShell()) {
2970 JS::Rooted<JSObject*> kungFuDeathGrip(dom::RootingCx(),
2971 browserChild->GetWrapper());
2972 ReceiveMessage(static_cast<EventTarget*>(browserChild), mFrameLoader,
2973 browserChild->GetInnerManager());
2975 return NS_OK;
2977 RefPtr<nsFrameLoader> mFrameLoader;
2980 nsresult nsFrameLoader::DoSendAsyncMessage(const nsAString& aMessage,
2981 StructuredCloneData& aData) {
2982 auto* browserParent = GetBrowserParent();
2983 if (browserParent) {
2984 ClonedMessageData data;
2985 if (!BuildClonedMessageData(aData, data)) {
2986 MOZ_CRASH();
2987 return NS_ERROR_DOM_DATA_CLONE_ERR;
2989 if (browserParent->SendAsyncMessage(aMessage, data)) {
2990 return NS_OK;
2991 } else {
2992 return NS_ERROR_UNEXPECTED;
2996 if (mChildMessageManager) {
2997 RefPtr<nsAsyncMessageToChild> ev = new nsAsyncMessageToChild(this);
2998 nsresult rv = ev->Init(aMessage, aData);
2999 if (NS_FAILED(rv)) {
3000 return rv;
3002 rv = NS_DispatchToCurrentThread(ev);
3003 if (NS_FAILED(rv)) {
3004 return rv;
3006 return rv;
3009 // We don't have any targets to send our asynchronous message to.
3010 return NS_ERROR_UNEXPECTED;
3013 already_AddRefed<MessageSender> nsFrameLoader::GetMessageManager() {
3014 EnsureMessageManager();
3015 return do_AddRef(mMessageManager);
3018 nsresult nsFrameLoader::EnsureMessageManager() {
3019 NS_ENSURE_STATE(mOwnerContent);
3021 if (mMessageManager) {
3022 return NS_OK;
3025 if (!mIsTopLevelContent && !OwnerIsMozBrowserFrame() && !IsRemoteFrame() &&
3026 !(mOwnerContent->IsXULElement() &&
3027 mOwnerContent->AttrValueIs(kNameSpaceID_None,
3028 nsGkAtoms::forcemessagemanager,
3029 nsGkAtoms::_true, eCaseMatters))) {
3030 return NS_OK;
3033 RefPtr<nsGlobalWindowOuter> window =
3034 nsGlobalWindowOuter::Cast(GetOwnerDoc()->GetWindow());
3035 RefPtr<ChromeMessageBroadcaster> parentManager;
3037 if (window && window->IsChromeWindow()) {
3038 nsAutoString messagemanagergroup;
3039 if (mOwnerContent->IsXULElement() &&
3040 mOwnerContent->GetAttr(nsGkAtoms::messagemanagergroup,
3041 messagemanagergroup)) {
3042 parentManager = window->GetGroupMessageManager(messagemanagergroup);
3045 if (!parentManager) {
3046 parentManager = window->GetMessageManager();
3048 } else {
3049 parentManager = nsFrameMessageManager::GetGlobalMessageManager();
3052 mMessageManager = new ChromeMessageSender(parentManager);
3053 if (!IsRemoteFrame()) {
3054 nsresult rv = MaybeCreateDocShell();
3055 if (NS_FAILED(rv)) {
3056 return rv;
3058 MOZ_ASSERT(GetDocShell(),
3059 "MaybeCreateDocShell succeeded, but null docShell");
3060 if (!GetDocShell()) {
3061 return NS_ERROR_FAILURE;
3063 mChildMessageManager = InProcessBrowserChildMessageManager::Create(
3064 GetDocShell(), mOwnerContent, mMessageManager);
3065 NS_ENSURE_TRUE(mChildMessageManager, NS_ERROR_UNEXPECTED);
3067 // Set up session store
3068 if (StaticPrefs::browser_sessionstore_platform_collection_AtStartup()) {
3069 if (XRE_IsParentProcess() && mIsTopLevelContent) {
3070 mSessionStoreChild = SessionStoreChild::GetOrCreate(
3071 GetExtantBrowsingContext(), mOwnerContent);
3075 return NS_OK;
3078 nsresult nsFrameLoader::ReallyLoadFrameScripts() {
3079 nsresult rv = EnsureMessageManager();
3080 if (NS_WARN_IF(NS_FAILED(rv))) {
3081 return rv;
3083 if (mMessageManager) {
3084 mMessageManager->InitWithCallback(this);
3086 return NS_OK;
3089 already_AddRefed<Element> nsFrameLoader::GetOwnerElement() {
3090 return do_AddRef(mOwnerContent);
3093 const LazyLoadFrameResumptionState&
3094 nsFrameLoader::GetLazyLoadFrameResumptionState() {
3095 static const LazyLoadFrameResumptionState sEmpty;
3096 if (auto* iframe = HTMLIFrameElement::FromNode(*mOwnerContent)) {
3097 return iframe->GetLazyLoadFrameResumptionState();
3099 return sEmpty;
3102 void nsFrameLoader::SetDetachedSubdocFrame(nsIFrame* aDetachedFrame) {
3103 mDetachedSubdocFrame = aDetachedFrame;
3104 mHadDetachedFrame = !!aDetachedFrame;
3107 nsIFrame* nsFrameLoader::GetDetachedSubdocFrame(bool* aOutIsSet) const {
3108 if (aOutIsSet) {
3109 *aOutIsSet = mHadDetachedFrame;
3111 return mDetachedSubdocFrame.GetFrame();
3114 void nsFrameLoader::ApplySandboxFlags(uint32_t sandboxFlags) {
3115 // If our BrowsingContext doesn't exist yet, it means we haven't been
3116 // initialized yet. This method will be called again once we're initialized
3117 // from MaybeCreateDocShell. <iframe> BrowsingContexts are never created as
3118 // initially remote, so we don't need to worry about updating sandbox flags
3119 // for an uninitialized initially-remote iframe.
3120 BrowsingContext* context = GetExtantBrowsingContext();
3121 if (!context) {
3122 MOZ_ASSERT(!IsRemoteFrame(),
3123 "cannot apply sandbox flags to an uninitialized "
3124 "initially-remote frame");
3125 return;
3128 uint32_t parentSandboxFlags = mOwnerContent->OwnerDoc()->GetSandboxFlags();
3130 // The child can only add restrictions, never remove them.
3131 sandboxFlags |= parentSandboxFlags;
3133 MOZ_ALWAYS_SUCCEEDS(context->SetSandboxFlags(sandboxFlags));
3136 /* virtual */
3137 void nsFrameLoader::AttributeChanged(mozilla::dom::Element* aElement,
3138 int32_t aNameSpaceID, nsAtom* aAttribute,
3139 int32_t aModType,
3140 const nsAttrValue* aOldValue) {
3141 MOZ_ASSERT(mObservingOwnerContent);
3143 if (aElement != mOwnerContent) {
3144 return;
3147 if (aNameSpaceID != kNameSpaceID_None ||
3148 (aAttribute != TypeAttrName(aElement) &&
3149 aAttribute != nsGkAtoms::primary)) {
3150 return;
3153 // Note: This logic duplicates a lot of logic in
3154 // MaybeCreateDocshell. We should fix that.
3156 // Notify our enclosing chrome that our type has changed. We only do this
3157 // if our parent is chrome, since in all other cases we're random content
3158 // subframes and the treeowner shouldn't worry about us.
3159 if (!GetDocShell()) {
3160 MaybeUpdatePrimaryBrowserParent(eBrowserParentChanged);
3161 return;
3164 nsCOMPtr<nsIDocShellTreeItem> parentItem;
3165 GetDocShell()->GetInProcessParent(getter_AddRefs(parentItem));
3166 if (!parentItem) {
3167 return;
3170 if (parentItem->ItemType() != nsIDocShellTreeItem::typeChrome) {
3171 return;
3174 nsCOMPtr<nsIDocShellTreeOwner> parentTreeOwner;
3175 parentItem->GetTreeOwner(getter_AddRefs(parentTreeOwner));
3176 if (!parentTreeOwner) {
3177 return;
3180 bool is_primary = aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::primary,
3181 nsGkAtoms::_true, eIgnoreCase);
3183 // when a content panel is no longer primary, hide any open popups it may have
3184 if (!is_primary) {
3185 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
3186 if (pm) {
3187 pm->HidePopupsInDocShell(GetDocShell());
3191 parentTreeOwner->ContentShellRemoved(GetDocShell());
3192 if (aElement->AttrValueIs(kNameSpaceID_None, TypeAttrName(aElement),
3193 nsGkAtoms::content, eIgnoreCase)) {
3194 parentTreeOwner->ContentShellAdded(GetDocShell(), is_primary);
3198 void nsFrameLoader::RequestUpdatePosition(ErrorResult& aRv) {
3199 if (auto* browserParent = GetBrowserParent()) {
3200 nsresult rv = browserParent->UpdatePosition();
3202 if (NS_FAILED(rv)) {
3203 aRv.Throw(rv);
3208 SessionStoreParent* nsFrameLoader::GetSessionStoreParent() {
3209 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
3210 if (mSessionStoreChild) {
3211 return static_cast<SessionStoreParent*>(
3212 InProcessChild::ParentActorFor(mSessionStoreChild));
3215 if (BrowserParent* browserParent = GetBrowserParent()) {
3216 return static_cast<SessionStoreParent*>(
3217 SingleManagedOrNull(browserParent->ManagedPSessionStoreParent()));
3220 return nullptr;
3223 already_AddRefed<Promise> nsFrameLoader::RequestTabStateFlush(
3224 ErrorResult& aRv) {
3225 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
3226 Document* ownerDoc = GetOwnerDoc();
3227 if (!ownerDoc) {
3228 aRv.ThrowNotSupportedError("No owner document");
3229 return nullptr;
3232 RefPtr<Promise> promise = Promise::Create(ownerDoc->GetOwnerGlobal(), aRv);
3233 if (aRv.Failed()) {
3234 return nullptr;
3237 BrowsingContext* browsingContext = GetExtantBrowsingContext();
3238 if (!browsingContext) {
3239 promise->MaybeResolveWithUndefined();
3240 return promise.forget();
3243 SessionStoreParent* sessionStoreParent = GetSessionStoreParent();
3244 if (!sessionStoreParent) {
3245 promise->MaybeResolveWithUndefined();
3246 return promise.forget();
3249 sessionStoreParent->FlushAllSessionStoreChildren(
3250 [promise]() { promise->MaybeResolveWithUndefined(); });
3252 return promise.forget();
3255 void nsFrameLoader::RequestFinalTabStateFlush() {
3256 BrowsingContext* context = GetExtantBrowsingContext();
3257 if (!context || !context->IsTop() || context->Canonical()->IsReplaced()) {
3258 return;
3261 RefPtr<CanonicalBrowsingContext> canonical = context->Canonical();
3262 RefPtr<WindowGlobalParent> wgp = canonical->GetCurrentWindowGlobal();
3263 RefPtr<Element> embedder = context->GetEmbedderElement();
3265 RefPtr<SessionStoreParent> sessionStoreParent = GetSessionStoreParent();
3266 if (!sessionStoreParent) {
3267 canonical->ClearPermanentKey();
3268 if (wgp) {
3269 wgp->NotifySessionStoreUpdatesComplete(embedder);
3272 return;
3275 sessionStoreParent->FinalFlushAllSessionStoreChildren(
3276 [canonical, wgp, embedder]() {
3277 if (canonical) {
3278 canonical->ClearPermanentKey();
3280 if (wgp) {
3281 wgp->NotifySessionStoreUpdatesComplete(embedder);
3286 void nsFrameLoader::RequestEpochUpdate(uint32_t aEpoch) {
3287 BrowsingContext* context = GetExtantBrowsingContext();
3288 if (context) {
3289 BrowsingContext* top = context->Top();
3290 Unused << top->SetSessionStoreEpoch(aEpoch);
3294 void nsFrameLoader::RequestSHistoryUpdate() {
3295 if (mSessionStoreChild) {
3296 mSessionStoreChild->UpdateSHistoryChanges();
3297 return;
3300 // If remote browsing (e10s), handle this with the BrowserParent.
3301 if (auto* browserParent = GetBrowserParent()) {
3302 Unused << browserParent->SendUpdateSHistory();
3306 already_AddRefed<Promise> nsFrameLoader::PrintPreview(
3307 nsIPrintSettings* aPrintSettings, BrowsingContext* aSourceBrowsingContext,
3308 ErrorResult& aRv) {
3309 auto* ownerDoc = GetOwnerDoc();
3310 if (!ownerDoc) {
3311 aRv.ThrowNotSupportedError("No owner document");
3312 return nullptr;
3314 RefPtr<Promise> promise = Promise::Create(ownerDoc->GetOwnerGlobal(), aRv);
3315 if (!promise) {
3316 return nullptr;
3319 #ifndef NS_PRINTING
3320 promise->MaybeRejectWithNotSupportedError("Build does not support printing");
3321 return promise.forget();
3322 #else
3323 auto resolve = [promise](PrintPreviewResultInfo aInfo) {
3324 using Orientation = dom::PrintPreviewOrientation;
3325 if (aInfo.sheetCount() > 0) {
3326 PrintPreviewSuccessInfo info;
3327 info.mSheetCount = aInfo.sheetCount();
3328 info.mTotalPageCount = aInfo.totalPageCount();
3329 info.mHasSelection = aInfo.hasSelection();
3330 info.mHasSelfSelection = aInfo.hasSelfSelection();
3331 info.mIsEmpty = aInfo.isEmpty();
3332 if (aInfo.printLandscape()) {
3333 info.mOrientation = aInfo.printLandscape().value()
3334 ? Orientation::Landscape
3335 : Orientation::Portrait;
3336 } else {
3337 MOZ_ASSERT(info.mOrientation == Orientation::Unspecified);
3339 if (aInfo.pageWidth()) {
3340 info.mPageWidth = aInfo.pageWidth().value();
3342 if (aInfo.pageHeight()) {
3343 info.mPageHeight = aInfo.pageHeight().value();
3346 promise->MaybeResolve(info);
3347 } else {
3348 promise->MaybeRejectWithUnknownError("Print preview failed");
3352 if (auto* browserParent = GetBrowserParent()) {
3353 nsCOMPtr<nsIPrintSettingsService> printSettingsSvc =
3354 do_GetService("@mozilla.org/gfx/printsettings-service;1");
3355 if (!printSettingsSvc) {
3356 promise->MaybeRejectWithNotSupportedError("No nsIPrintSettingsService");
3357 return promise.forget();
3360 embedding::PrintData printData;
3361 nsresult rv =
3362 printSettingsSvc->SerializeToPrintData(aPrintSettings, &printData);
3363 if (NS_WARN_IF(NS_FAILED(rv))) {
3364 promise->MaybeReject(ErrorResult(rv));
3365 return promise.forget();
3368 browserParent->SendPrintPreview(printData, aSourceBrowsingContext)
3369 ->Then(
3370 GetMainThreadSerialEventTarget(), __func__, std::move(resolve),
3371 [promise](const mozilla::ipc::ResponseRejectReason) {
3372 promise->MaybeRejectWithUnknownError("Print preview IPC failed");
3375 return promise.forget();
3378 RefPtr<nsGlobalWindowOuter> sourceWindow;
3379 if (aSourceBrowsingContext) {
3380 sourceWindow =
3381 nsGlobalWindowOuter::Cast(aSourceBrowsingContext->GetDOMWindow());
3382 } else {
3383 nsDocShell* ourDocshell = GetExistingDocShell();
3384 if (NS_WARN_IF(!ourDocshell)) {
3385 promise->MaybeRejectWithNotSupportedError("No print preview docShell");
3386 return promise.forget();
3388 sourceWindow = nsGlobalWindowOuter::Cast(ourDocshell->GetWindow());
3390 if (NS_WARN_IF(!sourceWindow)) {
3391 promise->MaybeRejectWithNotSupportedError("No print preview source window");
3392 return promise.forget();
3395 nsCOMPtr<nsIDocShell> docShellToCloneInto = nullptr;
3396 if (aSourceBrowsingContext) {
3397 // We're going to call `Print()` below on a window that is not our own,
3398 // which happens when we are creating a new print preview document instead
3399 // of just applying a settings change to the existing PP document. In this
3400 // case we need to explicitly pass our docShell as the docShell to clone
3401 // into.
3402 docShellToCloneInto = GetExistingDocShell();
3403 if (NS_WARN_IF(!docShellToCloneInto)) {
3404 promise->MaybeRejectWithNotSupportedError("No print preview docShell");
3405 return promise.forget();
3408 // We need to make sure we're displayed so that the view tree ends up right.
3409 RefPtr<BrowsingContext> bc = docShellToCloneInto->GetBrowsingContext();
3410 if (NS_WARN_IF(!bc)) {
3411 promise->MaybeRejectWithNotSupportedError(
3412 "No print preview browsing context");
3413 return promise.forget();
3416 RefPtr<Element> embedder = bc->GetEmbedderElement();
3417 if (NS_WARN_IF(!embedder)) {
3418 promise->MaybeRejectWithNotSupportedError(
3419 "Trying to clone into a frameloader with no element?");
3420 return promise.forget();
3423 nsIFrame* frame = embedder->GetPrimaryFrame(FlushType::Frames);
3424 if (NS_WARN_IF(!frame)) {
3425 promise->MaybeRejectWithNotSupportedError("Frame is not being displayed");
3426 return promise.forget();
3430 // Unfortunately we can't pass `resolve` directly here because IPDL, for now,
3431 // unfortunately generates slightly different parameter types for functions
3432 // taking PrintPreviewResultInfo in PBrowserParent vs. PBrowserChild.
3433 ErrorResult rv;
3434 sourceWindow->Print(
3435 aPrintSettings,
3436 /* aRemotePrintJob = */ nullptr,
3437 /* aListener = */ nullptr, docShellToCloneInto,
3438 nsGlobalWindowOuter::IsPreview::Yes,
3439 nsGlobalWindowOuter::IsForWindowDotPrint::No,
3440 [resolve](const PrintPreviewResultInfo& aInfo) { resolve(aInfo); }, rv);
3441 if (NS_WARN_IF(rv.Failed())) {
3442 promise->MaybeReject(std::move(rv));
3445 return promise.forget();
3446 #endif
3449 void nsFrameLoader::ExitPrintPreview() {
3450 #ifdef NS_PRINTING
3451 if (auto* browserParent = GetBrowserParent()) {
3452 Unused << browserParent->SendExitPrintPreview();
3453 return;
3455 if (NS_WARN_IF(!GetExistingDocShell())) {
3456 return;
3458 nsCOMPtr<nsIWebBrowserPrint> webBrowserPrint =
3459 do_GetInterface(ToSupports(GetExistingDocShell()->GetWindow()));
3460 if (NS_WARN_IF(!webBrowserPrint)) {
3461 return;
3463 webBrowserPrint->ExitPrintPreview();
3464 #endif
3467 already_AddRefed<nsIRemoteTab> nsFrameLoader::GetRemoteTab() {
3468 if (!mRemoteBrowser) {
3469 return nullptr;
3471 if (auto* browserHost = mRemoteBrowser->AsBrowserHost()) {
3472 return do_AddRef(browserHost);
3474 return nullptr;
3477 already_AddRefed<nsILoadContext> nsFrameLoader::GetLoadContext() {
3478 return do_AddRef(GetBrowsingContext());
3481 BrowsingContext* nsFrameLoader::GetBrowsingContext() {
3482 if (!mInitialized) {
3483 if (IsRemoteFrame()) {
3484 Unused << EnsureRemoteBrowser();
3485 } else if (mOwnerContent) {
3486 Unused << MaybeCreateDocShell();
3489 MOZ_ASSERT(mInitialized);
3490 return GetExtantBrowsingContext();
3493 BrowsingContext* nsFrameLoader::GetExtantBrowsingContext() {
3494 if (!mPendingBrowsingContext) {
3495 // If mPendingBrowsingContext is null then the frame loader is being
3496 // destroyed (nsFrameLoader::DestroyDocShell was called), so return null
3497 // here in that case.
3498 return nullptr;
3501 if (!mInitialized || !mPendingBrowsingContext->EverAttached()) {
3502 // Don't return the pending BrowsingContext until this nsFrameLoader has
3503 // been initialized, and the BC was attached.
3504 return nullptr;
3507 return mPendingBrowsingContext;
3510 void nsFrameLoader::InitializeBrowserAPI() {
3511 if (!OwnerIsMozBrowserFrame()) {
3512 return;
3515 nsresult rv = EnsureMessageManager();
3516 if (NS_WARN_IF(NS_FAILED(rv))) {
3517 return;
3519 mMessageManager->LoadFrameScript(
3520 u"chrome://global/content/BrowserElementChild.js"_ns,
3521 /* allowDelayedLoad = */ true,
3522 /* aRunInGlobalScope */ true, IgnoreErrors());
3524 nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwnerContent);
3525 if (browserFrame) {
3526 browserFrame->InitializeBrowserAPI();
3530 void nsFrameLoader::DestroyBrowserFrameScripts() {
3531 if (!OwnerIsMozBrowserFrame()) {
3532 return;
3534 nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwnerContent);
3535 if (browserFrame) {
3536 browserFrame->DestroyBrowserFrameScripts();
3540 void nsFrameLoader::StartPersistence(
3541 BrowsingContext* aContext, nsIWebBrowserPersistDocumentReceiver* aRecv,
3542 ErrorResult& aRv) {
3543 MOZ_ASSERT(aRecv);
3544 RefPtr<BrowsingContext> context = aContext ? aContext : GetBrowsingContext();
3546 if (!context || !context->IsInSubtreeOf(GetBrowsingContext())) {
3547 aRecv->OnError(NS_ERROR_NO_CONTENT);
3548 return;
3551 if (!context->GetDocShell() && XRE_IsParentProcess()) {
3552 CanonicalBrowsingContext* canonical =
3553 CanonicalBrowsingContext::Cast(context);
3554 if (!canonical->GetCurrentWindowGlobal()) {
3555 aRecv->OnError(NS_ERROR_NO_CONTENT);
3556 return;
3558 RefPtr<BrowserParent> browserParent =
3559 canonical->GetCurrentWindowGlobal()->GetBrowserParent();
3560 browserParent->StartPersistence(canonical, aRecv, aRv);
3561 return;
3564 nsCOMPtr<Document> foundDoc = context->GetDocument();
3566 if (!foundDoc) {
3567 aRecv->OnError(NS_ERROR_NO_CONTENT);
3568 } else {
3569 nsCOMPtr<nsIWebBrowserPersistDocument> pdoc =
3570 new mozilla::WebBrowserPersistLocalDocument(foundDoc);
3571 aRecv->OnDocumentReady(pdoc);
3575 void nsFrameLoader::MaybeUpdatePrimaryBrowserParent(
3576 BrowserParentChange aChange) {
3577 if (!mOwnerContent || !mRemoteBrowser) {
3578 return;
3581 RefPtr<BrowserHost> browserHost = mRemoteBrowser->AsBrowserHost();
3582 if (!browserHost) {
3583 return;
3586 nsCOMPtr<nsIDocShell> docShell = mOwnerContent->OwnerDoc()->GetDocShell();
3587 if (!docShell) {
3588 return;
3591 BrowsingContext* browsingContext = docShell->GetBrowsingContext();
3592 if (!browsingContext->IsChrome()) {
3593 return;
3596 nsCOMPtr<nsIDocShellTreeOwner> parentTreeOwner;
3597 docShell->GetTreeOwner(getter_AddRefs(parentTreeOwner));
3598 if (!parentTreeOwner) {
3599 return;
3602 if (!mObservingOwnerContent) {
3603 mOwnerContent->AddMutationObserver(this);
3604 mObservingOwnerContent = true;
3607 parentTreeOwner->RemoteTabRemoved(browserHost);
3608 if (aChange == eBrowserParentChanged) {
3609 bool isPrimary = mOwnerContent->AttrValueIs(
3610 kNameSpaceID_None, nsGkAtoms::primary, nsGkAtoms::_true, eIgnoreCase);
3611 parentTreeOwner->RemoteTabAdded(browserHost, isPrimary);
3615 nsresult nsFrameLoader::GetNewTabContext(MutableTabContext* aTabContext,
3616 nsIURI* aURI) {
3617 nsCOMPtr<nsIDocShell> docShell = mOwnerContent->OwnerDoc()->GetDocShell();
3618 nsCOMPtr<nsILoadContext> parentContext = do_QueryInterface(docShell);
3619 NS_ENSURE_STATE(parentContext);
3621 MOZ_ASSERT(mPendingBrowsingContext->EverAttached());
3623 uint64_t chromeOuterWindowID = 0;
3625 nsCOMPtr<nsPIWindowRoot> root =
3626 nsContentUtils::GetWindowRoot(mOwnerContent->OwnerDoc());
3627 if (root) {
3628 nsPIDOMWindowOuter* outerWin = root->GetWindow();
3629 if (outerWin) {
3630 chromeOuterWindowID = outerWin->WindowID();
3634 uint32_t maxTouchPoints = BrowserParent::GetMaxTouchPoints(mOwnerContent);
3636 bool tabContextUpdated =
3637 aTabContext->SetTabContext(chromeOuterWindowID, maxTouchPoints);
3638 NS_ENSURE_STATE(tabContextUpdated);
3640 return NS_OK;
3643 nsresult nsFrameLoader::PopulateOriginContextIdsFromAttributes(
3644 OriginAttributes& aAttr) {
3645 // Only XUL or mozbrowser frames are allowed to set context IDs
3646 uint32_t namespaceID = mOwnerContent->GetNameSpaceID();
3647 if (namespaceID != kNameSpaceID_XUL && !OwnerIsMozBrowserFrame()) {
3648 return NS_OK;
3651 nsAutoString attributeValue;
3652 if (aAttr.mUserContextId ==
3653 nsIScriptSecurityManager::DEFAULT_USER_CONTEXT_ID &&
3654 mOwnerContent->GetAttr(nsGkAtoms::usercontextid, attributeValue) &&
3655 !attributeValue.IsEmpty()) {
3656 nsresult rv;
3657 aAttr.mUserContextId = attributeValue.ToInteger(&rv);
3658 NS_ENSURE_SUCCESS(rv, rv);
3661 if (aAttr.mGeckoViewSessionContextId.IsEmpty() &&
3662 mOwnerContent->GetAttr(nsGkAtoms::geckoViewSessionContextId,
3663 attributeValue) &&
3664 !attributeValue.IsEmpty()) {
3665 // XXX: Should we check the format from `GeckoViewNavigation.jsm` here?
3666 aAttr.mGeckoViewSessionContextId = attributeValue;
3669 return NS_OK;
3672 ProcessMessageManager* nsFrameLoader::GetProcessMessageManager() const {
3673 if (auto* browserParent = GetBrowserParent()) {
3674 return browserParent->Manager()->GetMessageManager();
3676 return nullptr;
3679 JSObject* nsFrameLoader::WrapObject(JSContext* cx,
3680 JS::Handle<JSObject*> aGivenProto) {
3681 JS::Rooted<JSObject*> result(cx);
3682 FrameLoader_Binding::Wrap(cx, this, this, aGivenProto, &result);
3683 return result;
3686 void nsFrameLoader::SetWillChangeProcess() {
3687 mWillChangeProcess = true;
3689 if (IsRemoteFrame()) {
3690 if (auto* browserParent = GetBrowserParent()) {
3691 if (auto* bc = CanonicalBrowsingContext::Cast(mPendingBrowsingContext);
3692 bc && bc->EverAttached()) {
3693 bc->StartUnloadingHost(browserParent->Manager()->ChildID());
3694 bc->SetCurrentBrowserParent(nullptr);
3696 // OOP Browser - Go directly over Browser Parent
3697 Unused << browserParent->SendWillChangeProcess();
3698 } else if (auto* browserBridgeChild = GetBrowserBridgeChild()) {
3699 // OOP IFrame - Through Browser Bridge Parent, set on browser child
3700 Unused << browserBridgeChild->SendWillChangeProcess();
3702 return;
3705 // In process
3706 RefPtr<nsDocShell> docshell = GetDocShell();
3707 MOZ_ASSERT(docshell);
3708 docshell->SetWillChangeProcess();
3711 static mozilla::Result<bool, nsresult> DidBuildIDChange() {
3712 nsresult rv;
3713 nsCOMPtr<nsIFile> file;
3715 rv = NS_GetSpecialDirectory(NS_GRE_DIR, getter_AddRefs(file));
3716 MOZ_TRY(rv);
3718 rv = file->AppendNative("platform.ini"_ns);
3719 MOZ_TRY(rv);
3721 nsCOMPtr<nsIINIParserFactory> iniFactory =
3722 do_GetService("@mozilla.org/xpcom/ini-parser-factory;1", &rv);
3723 MOZ_TRY(rv);
3725 nsCOMPtr<nsIINIParser> parser;
3726 rv = iniFactory->CreateINIParser(file, getter_AddRefs(parser));
3727 MOZ_TRY(rv);
3729 nsAutoCString installedBuildID;
3730 rv = parser->GetString("Build"_ns, "BuildID"_ns, installedBuildID);
3731 MOZ_TRY(rv);
3733 nsDependentCString runningBuildID(PlatformBuildID());
3734 return (installedBuildID != runningBuildID);
3737 void nsFrameLoader::MaybeNotifyCrashed(BrowsingContext* aBrowsingContext,
3738 ContentParentId aChildID,
3739 mozilla::ipc::MessageChannel* aChannel) {
3740 if (mTabProcessCrashFired) {
3741 return;
3744 if (mPendingBrowsingContext == aBrowsingContext) {
3745 mTabProcessCrashFired = true;
3748 // Fire the crashed observer notification.
3749 nsCOMPtr<nsIObserverService> os = services::GetObserverService();
3750 if (!os) {
3751 return;
3754 os->NotifyObservers(ToSupports(this), "oop-frameloader-crashed", nullptr);
3756 // Check our owner element still references us. If it's moved, on, events
3757 // don't need to be fired.
3758 RefPtr<nsFrameLoaderOwner> owner = do_QueryObject(mOwnerContent);
3759 if (!owner) {
3760 return;
3763 RefPtr<nsFrameLoader> currentFrameLoader = owner->GetFrameLoader();
3764 if (currentFrameLoader != this) {
3765 return;
3768 #if defined(MOZ_TELEMETRY_REPORTING)
3769 bool sendTelemetry = false;
3770 #endif // defined(MOZ_TELEMETRY_REPORTING)
3772 // Fire the actual crashed event.
3773 nsString eventName;
3774 if (aChannel && !aChannel->DoBuildIDsMatch()) {
3775 auto changedOrError = DidBuildIDChange();
3776 if (changedOrError.isErr()) {
3777 NS_WARNING("Error while checking buildid mismatch");
3778 eventName = u"oop-browser-buildid-mismatch"_ns;
3779 } else {
3780 bool aChanged = changedOrError.unwrap();
3781 if (aChanged) {
3782 NS_WARNING("True build ID mismatch");
3783 eventName = u"oop-browser-buildid-mismatch"_ns;
3784 } else {
3785 NS_WARNING("build ID mismatch false alarm");
3786 eventName = u"oop-browser-crashed"_ns;
3787 #if defined(MOZ_TELEMETRY_REPORTING)
3788 sendTelemetry = true;
3789 #endif // defined(MOZ_TELEMETRY_REPORTING)
3792 } else {
3793 NS_WARNING("No build ID mismatch");
3794 eventName = u"oop-browser-crashed"_ns;
3797 #if defined(MOZ_TELEMETRY_REPORTING)
3798 if (sendTelemetry) {
3799 Telemetry::ScalarAdd(
3800 Telemetry::ScalarID::DOM_CONTENTPROCESS_BUILDID_MISMATCH_FALSE_POSITIVE,
3803 #endif // defined(MOZ_TELEMETRY_REPORTING)
3805 FrameCrashedEventInit init;
3806 init.mBubbles = true;
3807 init.mCancelable = true;
3808 if (aBrowsingContext) {
3809 init.mBrowsingContextId = aBrowsingContext->Id();
3810 init.mIsTopFrame = aBrowsingContext->IsTop();
3811 init.mChildID = aChildID;
3814 RefPtr<FrameCrashedEvent> event = FrameCrashedEvent::Constructor(
3815 mOwnerContent->OwnerDoc(), eventName, init);
3816 event->SetTrusted(true);
3818 RefPtr<Element> ownerContent = mOwnerContent;
3819 EventDispatcher::DispatchDOMEvent(ownerContent, nullptr, event, nullptr,
3820 nullptr);
3823 bool nsFrameLoader::EnsureBrowsingContextAttached() {
3824 nsresult rv;
3826 Document* parentDoc = mOwnerContent->OwnerDoc();
3827 MOZ_ASSERT(parentDoc);
3828 BrowsingContext* parentContext = parentDoc->GetBrowsingContext();
3829 MOZ_ASSERT(parentContext);
3831 // Inherit the `use` flags from our parent BrowsingContext.
3832 bool usePrivateBrowsing = parentContext->UsePrivateBrowsing();
3833 bool useRemoteSubframes = parentContext->UseRemoteSubframes();
3834 bool useRemoteTabs = parentContext->UseRemoteTabs();
3836 // Determine the exact OriginAttributes which should be used for our
3837 // BrowsingContext. This will be used to initialize OriginAttributes if the
3838 // BrowsingContext has not already been created.
3839 OriginAttributes attrs;
3840 if (mPendingBrowsingContext->IsContent()) {
3841 if (mPendingBrowsingContext->GetParent()) {
3842 MOZ_ASSERT(mPendingBrowsingContext->GetParent() == parentContext);
3843 parentContext->GetOriginAttributes(attrs);
3846 // Inherit the `mFirstPartyDomain` flag from our parent document's result
3847 // principal, if it was set.
3848 if (parentContext->IsContent() &&
3849 !parentDoc->NodePrincipal()->IsSystemPrincipal() &&
3850 !OwnerIsMozBrowserFrame()) {
3851 OriginAttributes docAttrs =
3852 parentDoc->NodePrincipal()->OriginAttributesRef();
3853 // We only want to inherit firstPartyDomain here, other attributes should
3854 // be constant.
3855 MOZ_ASSERT(attrs.EqualsIgnoringFPD(docAttrs));
3856 attrs.mFirstPartyDomain = docAttrs.mFirstPartyDomain;
3859 // Inherit the PrivateBrowsing flag across content/chrome boundaries.
3860 attrs.SyncAttributesWithPrivateBrowsing(usePrivateBrowsing);
3862 // A <browser> element may have overridden userContextId or
3863 // geckoViewUserContextId.
3864 rv = PopulateOriginContextIdsFromAttributes(attrs);
3865 if (NS_WARN_IF(NS_FAILED(rv))) {
3866 return false;
3869 // <iframe mozbrowser> is allowed to set `mozprivatebrowsing` to
3870 // force-enable private browsing.
3871 if (OwnerIsMozBrowserFrame()) {
3872 if (mOwnerContent->HasAttr(nsGkAtoms::mozprivatebrowsing)) {
3873 attrs.SyncAttributesWithPrivateBrowsing(true);
3874 usePrivateBrowsing = true;
3879 // If we've already been attached, return.
3880 if (mPendingBrowsingContext->EverAttached()) {
3881 MOZ_DIAGNOSTIC_ASSERT(mPendingBrowsingContext->UsePrivateBrowsing() ==
3882 usePrivateBrowsing);
3883 MOZ_DIAGNOSTIC_ASSERT(mPendingBrowsingContext->UseRemoteTabs() ==
3884 useRemoteTabs);
3885 MOZ_DIAGNOSTIC_ASSERT(mPendingBrowsingContext->UseRemoteSubframes() ==
3886 useRemoteSubframes);
3887 // Don't assert that our OriginAttributes match, as we could have different
3888 // OriginAttributes in the case where we were opened using window.open.
3889 return true;
3892 // Initialize non-synced OriginAttributes and related fields.
3893 rv = mPendingBrowsingContext->SetOriginAttributes(attrs);
3894 NS_ENSURE_SUCCESS(rv, false);
3895 rv = mPendingBrowsingContext->SetUsePrivateBrowsing(usePrivateBrowsing);
3896 NS_ENSURE_SUCCESS(rv, false);
3897 rv = mPendingBrowsingContext->SetRemoteTabs(useRemoteTabs);
3898 NS_ENSURE_SUCCESS(rv, false);
3899 rv = mPendingBrowsingContext->SetRemoteSubframes(useRemoteSubframes);
3900 NS_ENSURE_SUCCESS(rv, false);
3902 // Finish attaching.
3903 mPendingBrowsingContext->EnsureAttached();
3904 return true;
3907 void nsFrameLoader::InvokeBrowsingContextReadyCallback() {
3908 if (mOpenWindowInfo) {
3909 if (RefPtr<nsIBrowsingContextReadyCallback> callback =
3910 mOpenWindowInfo->BrowsingContextReadyCallback()) {
3911 callback->BrowsingContextReady(mPendingBrowsingContext);