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/. */
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"
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"
38 #include "nsIScrollableFrame.h"
39 #include "nsSubDocumentFrame.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"
50 #include "nsBaseWidget.h"
51 #include "nsQueryObject.h"
52 #include "ReferrerInfo.h"
53 #include "nsIOpenWindowInfo.h"
54 #include "nsISHistory.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"
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"
136 # include "nsIWebBrowserPrint.h"
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
)
176 nsFrameLoader::nsFrameLoader(Element
* aOwner
, BrowsingContext
* aBrowsingContext
,
177 bool aIsRemoteFrame
, bool aNetworkCreated
)
178 : mPendingBrowsingContext(aBrowsingContext
),
179 mOwnerContent(aOwner
),
180 mDetachedSubdocFrame(nullptr),
183 mRemoteType(NOT_REMOTE_TYPE
),
185 mDepthTooGreat(false),
186 mIsTopLevelContent(false),
187 mDestroyCalled(false),
188 mNeedsAsyncDestroy(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
);
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
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()) {
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()) {
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());
296 aOpenWindowInfo
->GetNextRemoteBrowser()->GetBrowsingContext());
299 RefPtr
<BrowsingContext
> opener
;
300 if (aOpenWindowInfo
&& !aOpenWindowInfo
->GetForceNoOpener()) {
301 opener
= aOpenWindowInfo
->GetParent();
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()) {
314 "Opener was closed from a nested event loop in the parent process. "
321 RefPtr
<nsGlobalWindowInner
> parentInner
=
322 nsGlobalWindowInner::Cast(aOwner
->OwnerDoc()->GetInnerWindow());
323 if (NS_WARN_IF(!parentInner
) || parentInner
->IsDying()) {
327 BrowsingContext
* parentBC
= parentInner
->GetBrowsingContext();
328 if (NS_WARN_IF(!parentBC
) || parentBC
->IsDiscarded()) {
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,
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()) {
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(
383 nsAutoString attrString
;
384 if (aOwner
->GetNameSpaceID() != kNameSpaceID_XUL
||
385 !aOwner
->GetAttr(nsGkAtoms::initialBrowsingContextGroupId
, attrString
)) {
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
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!");
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()),
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.
457 MOZ_ASSERT(XRE_IsParentProcess());
458 nsAutoString remoteType
;
459 if (aOwner
->GetAttr(nsGkAtoms::RemoteType
, remoteType
) &&
460 !remoteType
.IsEmpty()) {
461 CopyUTF16toUTF8(remoteType
, fl
->mRemoteType
);
463 fl
->mRemoteType
= DEFAULT_REMOTE_TYPE
;
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);
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());
486 RefPtr
<BrowsingContext
> context
= aContext
;
487 if (!context
|| !aPreserveContext
) {
488 context
= CreateBrowsingContext(aOwner
, /* openWindowInfo */ nullptr,
492 XRE_IsParentProcess(),
493 "Recreating browing contexts only supported in the parent process");
494 aContext
->Canonical()->SynchronizeLayoutHistoryState();
495 aContext
->Canonical()->ReplacedBy(context
->Canonical(),
499 NS_ENSURE_TRUE(context
, nullptr);
501 RefPtr
<nsFrameLoader
> fl
=
502 new nsFrameLoader(aOwner
, context
, aIsRemote
, aNetworkCreated
);
506 void nsFrameLoader::LoadFrame(bool aOriginalSrc
) {
507 if (NS_WARN_IF(!mOwnerContent
)) {
512 nsCOMPtr
<nsIPrincipal
> principal
;
513 nsCOMPtr
<nsIContentSecurityPolicy
> csp
;
515 bool isSrcdoc
= mOwnerContent
->IsHTMLElement(nsGkAtoms::iframe
) &&
516 mOwnerContent
->HasAttr(nsGkAtoms::srcdoc
);
518 src
.AssignLiteral("about:srcdoc");
519 principal
= mOwnerContent
->NodePrincipal();
520 csp
= mOwnerContent
->GetCsp();
522 GetURL(src
, getter_AddRefs(principal
), getter_AddRefs(csp
));
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
)) {
535 src
.AssignLiteral("about:blank");
536 principal
= mOwnerContent
->NodePrincipal();
537 csp
= mOwnerContent
->GetCsp();
541 Document
* doc
= mOwnerContent
->OwnerDoc();
542 if (doc
->IsStaticDocument()) {
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
);
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
) {
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
,
595 if (!aURI
) return NS_ERROR_INVALID_POINTER
;
596 NS_ENSURE_STATE(!mDestroyCalled
&& mOwnerContent
);
598 aTriggeringPrincipal
,
599 "Must have an explicit triggeringPrincipal to nsFrameLoader::LoadURI.");
601 mLoadingOriginalSrc
= aOriginalSrc
;
603 nsCOMPtr
<Document
> doc
= mOwnerContent
->OwnerDoc();
606 rv
= CheckURILoad(aURI
, aTriggeringPrincipal
);
607 NS_ENSURE_SUCCESS(rv
, rv
);
610 mTriggeringPrincipal
= aTriggeringPrincipal
;
612 rv
= doc
->InitializeFrameLoader(this);
614 mURIToLoad
= nullptr;
615 mTriggeringPrincipal
= nullptr;
621 void nsFrameLoader::ResumeLoad(uint64_t aPendingSwitchID
) {
622 Document
* doc
= mOwnerContent
->OwnerDoc();
623 if (doc
->IsStaticDocument()) {
624 // Static doc shouldn't load sub-documents.
628 if (NS_WARN_IF(mDestroyCalled
|| !mOwnerContent
)) {
633 mLoadingOriginalSrc
= false;
634 mURIToLoad
= nullptr;
635 mPendingSwitchID
= aPendingSwitchID
;
636 mTriggeringPrincipal
= mOwnerContent
->NodePrincipal();
637 mCsp
= mOwnerContent
->GetCsp();
639 nsresult rv
= doc
->InitializeFrameLoader(this);
641 mPendingSwitchID
= 0;
642 mTriggeringPrincipal
= nullptr;
649 nsresult
nsFrameLoader::ReallyStartLoading() {
650 nsresult rv
= ReallyStartLoadingInternal();
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
);
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.
684 loadState
->SetCsp(mCsp
);
685 } else if (!mTriggeringPrincipal
) {
686 nsCOMPtr
<nsIContentSecurityPolicy
> csp
= mOwnerContent
->GetCsp();
687 loadState
->SetCsp(csp
);
691 bool isSrcdoc
= mOwnerContent
->IsHTMLElement(nsGkAtoms::iframe
) &&
692 mOwnerContent
->GetAttr(nsGkAtoms::srcdoc
, srcdoc
);
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();
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;
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));
749 nsresult rv
= MaybeCreateDocShell();
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;
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
);
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();
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());
813 return rv
; // We're not
816 // Bail out if this is an infinite recursion scenario
817 if (IsRemoteFrame()) {
820 return CheckForRecursiveLoad(aURI
);
823 nsDocShell
* nsFrameLoader::GetDocShell(ErrorResult
& aRv
) {
824 if (IsRemoteFrame()) {
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.
832 nsresult rv
= MaybeCreateDocShell();
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
));
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
);
871 nsCOMPtr
<nsIMozBrowserFrame
> mozbrowser
=
872 aOwnerContent
->GetAsMozBrowserFrame();
874 mozbrowser
->GetMozbrowser(&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
);
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
)) {
926 void nsFrameLoader::MaybeShowFrame() {
927 nsIFrame
* frame
= GetPrimaryFrameOfOwningContent();
929 nsSubDocumentFrame
* subDocFrame
= do_QueryFrame(frame
);
931 subDocFrame
->MaybeShowViewer();
936 static ScrollbarPreference
GetScrollbarPreference(const Element
* 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
);
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();
961 bool nsFrameLoader::Show(nsSubDocumentFrame
* frame
) {
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();
980 nsDocShell
* ds
= GetDocShell();
981 MOZ_ASSERT(ds
, "MaybeCreateDocShell succeeded, but null docShell");
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
,
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
,
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;
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());
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();
1045 mHideCalled
= false;
1052 void nsFrameLoader::MarginsChanged() {
1053 // We assume that the margins are always zero for remote frames.
1054 if (IsRemoteFrame()) {
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
1066 if (!docShell
->UpdateFrameMargins(GetMarginAttributes(mOwnerContent
))) {
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.");
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()) {
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()) {
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
)) {
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);
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
);
1145 void nsFrameLoader::Hide() {
1154 if (!GetDocShell()) {
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();
1173 nsPresContext
* presContext
= frame
->PresContext();
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());
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");
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
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
1244 // This is the reason why now we must retrieve the correct value from the
1245 // usercontextid attribute before comparing our originAttributes with the
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;
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();
1326 shistory
->EvictAllContentViewers();
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
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
))) {
1391 MutableTabContext otherContext
;
1392 rv
= aOther
->GetNewTabContext(&otherContext
);
1393 if (NS_WARN_IF(NS_FAILED(rv
))) {
1397 Unused
<< browserParent
->SendSwappedWithOtherRemoteLoader(
1398 ourContext
.AsIPCTabContext());
1399 Unused
<< otherBrowserParent
->SendSwappedWithOtherRemoteLoader(
1400 otherContext
.AsIPCTabContext());
1404 class MOZ_RAII AutoResetInFrameSwap final
{
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
,
1431 nsContentUtils::FirePageHideEventForFrameLoaderSwap(mOtherDocShell
,
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();
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
) {
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");
1475 NS_ENSURE_STATE(!mInShow
&& !aOther
->mInShow
);
1477 if (IsRemoteFrame() != aOther
->IsRemoteFrame()) {
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
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
) {
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
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
1674 // This is the reason why now we must retrieve the correct value from the
1675 // usercontextid attribute before comparing our originAttributes with the
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
)) {
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
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
1785 RefPtr
<nsFrameLoader
> kungFuDeathGrip(this);
1786 aThisOwner
->SetFrameLoader(aOther
);
1787 aOtherOwner
->SetFrameLoader(kungFuDeathGrip
);
1789 // Drop any cached content viewers in the two session histories.
1791 ourHistory
->EvictLocalContentViewers();
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();
1818 void nsFrameLoader::Destroy(bool aForProcessSwitch
) {
1819 StartDestroy(aForProcessSwitch
);
1822 class nsFrameLoaderDestroyRunnable
: public Runnable
{
1824 // See the implementation of Run for an explanation of these phases.
1826 eWaitForUnloadMessage
,
1830 RefPtr
<nsFrameLoader
> mFrameLoader
;
1831 DestroyPhase mPhase
;
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
) {
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;
1916 if (addedEntries
> 0) {
1917 ChildSHistory
* shistory
=
1918 browsingContext
->Top()->GetChildSessionHistory();
1920 changeID
= shistory
->AddPendingHistoryChange(0, -addedEntries
);
1923 browsingContext
->RemoveFromSessionHistory(changeID
);
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
);
1940 owner
->ContentShellRemoved(GetDocShell());
1945 // Let our window know that we are gone
1946 if (GetDocShell()) {
1947 nsCOMPtr
<nsPIDOMWindowOuter
> win_private(GetDocShell()->GetWindow());
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() {
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);
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);
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();
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();
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());
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();
2139 JS::Rooted
<JSObject
*> wrapper(jsapi
.cx(), GetWrapper());
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()) {
2166 if (IsRemoteFrame()) {
2169 NS_ENSURE_STATE(!mDestroyCalled
);
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.
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
));
2254 // Our parent shell is a chrome shell. It is therefore our nearest enclosing
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
);
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.
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
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();
2339 void nsFrameLoader::GetURL(nsString
& aURI
, nsIPrincipal
** aTriggeringPrincipal
,
2340 nsIContentSecurityPolicy
** aCsp
) {
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
);
2353 mOwnerContent
->GetAttr(nsGkAtoms::src
, aURI
);
2354 if (RefPtr
<nsGenericHTMLFrameElement
> frame
=
2355 do_QueryObject(mOwnerContent
)) {
2356 nsCOMPtr
<nsIPrincipal
> srcPrincipal
= frame
->GetSrcTriggeringPrincipal();
2358 triggeringPrincipal
= srcPrincipal
;
2359 nsCOMPtr
<nsIExpandedPrincipal
> ep
=
2360 do_QueryInterface(triggeringPrincipal
);
2367 triggeringPrincipal
.forget(aTriggeringPrincipal
);
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()) {
2384 // Bug 8065: Don't exceed some maximum depth in content frames
2385 // (MAX_DEPTH_CONTENT_FRAMES)
2387 for (BrowsingContext
* bc
= parentBC
; bc
; bc
= bc
->GetParent()) {
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?)
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();
2408 return NS_ERROR_FAILURE
;
2411 MOZ_RELEASE_ASSERT(!doc
->IsResourceDoc(), "We shouldn't even exist");
2413 nsCOMPtr
<nsPIDOMWindowOuter
> win
= doc
->GetWindow();
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
))) ||
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
);
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
);
2447 mRemoteBrowser
->UpdateDimensions(dimensions
, size
);
2451 UpdateBaseWindowPositionAndSize(aIFrame
);
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
2492 AutoWeakFrame
weakFrame(aIFrame
);
2494 baseWindow
->GetPosition(&x
, &y
);
2496 if (!weakFrame
.IsAlive()) {
2497 // GetPosition() killed us
2501 ScreenIntSize size
= aIFrame
->GetSubdocumentSize();
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();
2514 lazyWidth
= frame
->PresContext()->DevPixelsToIntCSSPixels(lazyWidth
);
2520 uint32_t nsFrameLoader::LazyHeight() const {
2521 uint32_t lazyHeight
= mLazySize
.height
;
2523 nsIFrame
* frame
= GetPrimaryFrameOfOwningContent();
2525 lazyHeight
= frame
->PresContext()->DevPixelsToIntCSSPixels(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");
2545 if (!mOwnerContent
) {
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();
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
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
) {
2575 // If we've already tried to initialize and failed, don't try again.
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()) {
2587 if (RefPtr
<nsFrameLoaderOwner
> flo
= do_QueryObject(mOwnerContent
)) {
2588 RefPtr
<nsFrameLoader
> fl
= flo
->GetFrameLoader();
2590 MOZ_ASSERT_UNREACHABLE(
2591 "Got TryRemoteBrowserInternal but mOwnerContent already has a "
2592 "different frameloader?");
2597 if (!doc
->IsActive()) {
2598 // Don't allow subframe loads in non-active documents.
2599 // (See bug 610571 comment 5.)
2603 nsCOMPtr
<nsPIDOMWindowOuter
> parentWin
= doc
->GetWindow();
2608 nsCOMPtr
<nsIDocShell
> parentDocShell
= parentWin
->GetDocShell();
2609 if (!parentDocShell
) {
2613 if (!EnsureBrowsingContextAttached()) {
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
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
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
2641 nsIURI
* parentURI
= parentWin
->GetDocumentURI();
2646 nsAutoCString specIgnoringRef
;
2647 if (NS_FAILED(parentURI
->GetSpecIgnoringRef(specIgnoringRef
))) {
2651 const bool allowed
= [&] {
2652 const nsLiteralCString kAllowedURIs
[] = {
2654 "chrome://mozapps/content/extensions/aboutaddons.html"_ns
,
2655 #ifdef MOZ_THUNDERBIRD
2658 "about:preferences"_ns
,
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
)) {
2671 if (xpc::IsInAutomation() &&
2672 StringBeginsWith(specIgnoringRef
,
2673 "chrome://mochitests/content/chrome/"_ns
)) {
2681 nsPrintfCString("Forbidden remote frame from content docshell %s",
2682 specIgnoringRef
.get())
2688 if (!mOwnerContent
->IsXULElement()) {
2692 if (!mOwnerContent
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::type
,
2693 nsGkAtoms::content
, eIgnoreCase
)) {
2698 uint32_t chromeFlags
= 0;
2699 nsCOMPtr
<nsIDocShellTreeOwner
> parentOwner
;
2700 if (NS_FAILED(parentDocShell
->GetTreeOwner(getter_AddRefs(parentOwner
))) ||
2704 nsCOMPtr
<nsIAppWindow
> window(do_GetInterface(parentOwner
));
2705 if (window
&& NS_FAILED(window
->GetChromeFlags(&chromeFlags
))) {
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");
2725 nextRemoteBrowser
->SetOwnerElement(ownerElement
);
2727 RefPtr
<ContentParent
> contentParent
;
2728 if (mChildID
!= 0) {
2729 ContentProcessManager
* cpm
= ContentProcessManager::GetSingleton();
2733 contentParent
= cpm
->GetContentProcessById(ContentParentId(mChildID
));
2736 ContentParent::CreateBrowser(context
, ownerElement
, mRemoteType
,
2737 mPendingBrowsingContext
, contentParent
);
2739 if (!mRemoteBrowser
) {
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();
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
)) {
2803 // Try to create the internal remote browser.
2804 if (TryRemoteBrowserInternal()) {
2808 // Check if we should report a browser-crashed error because the browser
2810 if (XRE_IsParentProcess() && mOwnerContent
&& mOwnerContent
->IsXULElement()) {
2811 MaybeNotifyCrashed(nullptr, ContentParentId(), nullptr);
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");
2833 RemoteBrowser
* nsFrameLoader::GetRemoteBrowser() const {
2834 return mRemoteBrowser
;
2837 BrowserParent
* nsFrameLoader::GetBrowserParent() const {
2838 if (!mRemoteBrowser
) {
2841 RefPtr
<BrowserHost
> browserHost
= mRemoteBrowser
->AsBrowserHost();
2845 return browserHost
->GetActor();
2848 BrowserBridgeChild
* nsFrameLoader::GetBrowserBridgeChild() const {
2849 if (!mRemoteBrowser
) {
2852 RefPtr
<BrowserBridgeHost
> browserBridgeHost
=
2853 mRemoteBrowser
->AsBrowserBridgeHost();
2854 if (!browserBridgeHost
) {
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();
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
;
2888 printSettingsSvc
->SerializeToPrintData(aPrintSettings
, &printData
);
2889 if (NS_WARN_IF(NS_FAILED(rv
))) {
2893 cc
->SendCloneDocumentTreeInto(bcToClone
, bc
, printData
);
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
);
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();
2951 browserChild
->LoadFrameScript(aURL
, aRunInGlobalScope
);
2956 class nsAsyncMessageToChild
: public nsSameProcessAsyncMessageBase
,
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());
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
)) {
2987 return NS_ERROR_DOM_DATA_CLONE_ERR
;
2989 if (browserParent
->SendAsyncMessage(aMessage
, data
)) {
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
)) {
3002 rv
= NS_DispatchToCurrentThread(ev
);
3003 if (NS_FAILED(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
) {
3025 if (!mIsTopLevelContent
&& !OwnerIsMozBrowserFrame() && !IsRemoteFrame() &&
3026 !(mOwnerContent
->IsXULElement() &&
3027 mOwnerContent
->AttrValueIs(kNameSpaceID_None
,
3028 nsGkAtoms::forcemessagemanager
,
3029 nsGkAtoms::_true
, eCaseMatters
))) {
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();
3049 parentManager
= nsFrameMessageManager::GetGlobalMessageManager();
3052 mMessageManager
= new ChromeMessageSender(parentManager
);
3053 if (!IsRemoteFrame()) {
3054 nsresult rv
= MaybeCreateDocShell();
3055 if (NS_FAILED(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
);
3078 nsresult
nsFrameLoader::ReallyLoadFrameScripts() {
3079 nsresult rv
= EnsureMessageManager();
3080 if (NS_WARN_IF(NS_FAILED(rv
))) {
3083 if (mMessageManager
) {
3084 mMessageManager
->InitWithCallback(this);
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();
3102 void nsFrameLoader::SetDetachedSubdocFrame(nsIFrame
* aDetachedFrame
) {
3103 mDetachedSubdocFrame
= aDetachedFrame
;
3104 mHadDetachedFrame
= !!aDetachedFrame
;
3107 nsIFrame
* nsFrameLoader::GetDetachedSubdocFrame(bool* aOutIsSet
) const {
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();
3122 MOZ_ASSERT(!IsRemoteFrame(),
3123 "cannot apply sandbox flags to an uninitialized "
3124 "initially-remote frame");
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
));
3137 void nsFrameLoader::AttributeChanged(mozilla::dom::Element
* aElement
,
3138 int32_t aNameSpaceID
, nsAtom
* aAttribute
,
3140 const nsAttrValue
* aOldValue
) {
3141 MOZ_ASSERT(mObservingOwnerContent
);
3143 if (aElement
!= mOwnerContent
) {
3147 if (aNameSpaceID
!= kNameSpaceID_None
||
3148 (aAttribute
!= TypeAttrName(aElement
) &&
3149 aAttribute
!= nsGkAtoms::primary
)) {
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
);
3164 nsCOMPtr
<nsIDocShellTreeItem
> parentItem
;
3165 GetDocShell()->GetInProcessParent(getter_AddRefs(parentItem
));
3170 if (parentItem
->ItemType() != nsIDocShellTreeItem::typeChrome
) {
3174 nsCOMPtr
<nsIDocShellTreeOwner
> parentTreeOwner
;
3175 parentItem
->GetTreeOwner(getter_AddRefs(parentTreeOwner
));
3176 if (!parentTreeOwner
) {
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
3185 nsXULPopupManager
* pm
= nsXULPopupManager::GetInstance();
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
)) {
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()));
3223 already_AddRefed
<Promise
> nsFrameLoader::RequestTabStateFlush(
3225 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
3226 Document
* ownerDoc
= GetOwnerDoc();
3228 aRv
.ThrowNotSupportedError("No owner document");
3232 RefPtr
<Promise
> promise
= Promise::Create(ownerDoc
->GetOwnerGlobal(), aRv
);
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()) {
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();
3269 wgp
->NotifySessionStoreUpdatesComplete(embedder
);
3275 sessionStoreParent
->FinalFlushAllSessionStoreChildren(
3276 [canonical
, wgp
, embedder
]() {
3278 canonical
->ClearPermanentKey();
3281 wgp
->NotifySessionStoreUpdatesComplete(embedder
);
3286 void nsFrameLoader::RequestEpochUpdate(uint32_t aEpoch
) {
3287 BrowsingContext
* context
= GetExtantBrowsingContext();
3289 BrowsingContext
* top
= context
->Top();
3290 Unused
<< top
->SetSessionStoreEpoch(aEpoch
);
3294 void nsFrameLoader::RequestSHistoryUpdate() {
3295 if (mSessionStoreChild
) {
3296 mSessionStoreChild
->UpdateSHistoryChanges();
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
,
3309 auto* ownerDoc
= GetOwnerDoc();
3311 aRv
.ThrowNotSupportedError("No owner document");
3314 RefPtr
<Promise
> promise
= Promise::Create(ownerDoc
->GetOwnerGlobal(), aRv
);
3320 promise
->MaybeRejectWithNotSupportedError("Build does not support printing");
3321 return promise
.forget();
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
;
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
);
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
;
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
)
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
) {
3381 nsGlobalWindowOuter::Cast(aSourceBrowsingContext
->GetDOMWindow());
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
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.
3434 sourceWindow
->Print(
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();
3449 void nsFrameLoader::ExitPrintPreview() {
3451 if (auto* browserParent
= GetBrowserParent()) {
3452 Unused
<< browserParent
->SendExitPrintPreview();
3455 if (NS_WARN_IF(!GetExistingDocShell())) {
3458 nsCOMPtr
<nsIWebBrowserPrint
> webBrowserPrint
=
3459 do_GetInterface(ToSupports(GetExistingDocShell()->GetWindow()));
3460 if (NS_WARN_IF(!webBrowserPrint
)) {
3463 webBrowserPrint
->ExitPrintPreview();
3467 already_AddRefed
<nsIRemoteTab
> nsFrameLoader::GetRemoteTab() {
3468 if (!mRemoteBrowser
) {
3471 if (auto* browserHost
= mRemoteBrowser
->AsBrowserHost()) {
3472 return do_AddRef(browserHost
);
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.
3501 if (!mInitialized
|| !mPendingBrowsingContext
->EverAttached()) {
3502 // Don't return the pending BrowsingContext until this nsFrameLoader has
3503 // been initialized, and the BC was attached.
3507 return mPendingBrowsingContext
;
3510 void nsFrameLoader::InitializeBrowserAPI() {
3511 if (!OwnerIsMozBrowserFrame()) {
3515 nsresult rv
= EnsureMessageManager();
3516 if (NS_WARN_IF(NS_FAILED(rv
))) {
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
);
3526 browserFrame
->InitializeBrowserAPI();
3530 void nsFrameLoader::DestroyBrowserFrameScripts() {
3531 if (!OwnerIsMozBrowserFrame()) {
3534 nsCOMPtr
<nsIMozBrowserFrame
> browserFrame
= do_QueryInterface(mOwnerContent
);
3536 browserFrame
->DestroyBrowserFrameScripts();
3540 void nsFrameLoader::StartPersistence(
3541 BrowsingContext
* aContext
, nsIWebBrowserPersistDocumentReceiver
* aRecv
,
3544 RefPtr
<BrowsingContext
> context
= aContext
? aContext
: GetBrowsingContext();
3546 if (!context
|| !context
->IsInSubtreeOf(GetBrowsingContext())) {
3547 aRecv
->OnError(NS_ERROR_NO_CONTENT
);
3551 if (!context
->GetDocShell() && XRE_IsParentProcess()) {
3552 CanonicalBrowsingContext
* canonical
=
3553 CanonicalBrowsingContext::Cast(context
);
3554 if (!canonical
->GetCurrentWindowGlobal()) {
3555 aRecv
->OnError(NS_ERROR_NO_CONTENT
);
3558 RefPtr
<BrowserParent
> browserParent
=
3559 canonical
->GetCurrentWindowGlobal()->GetBrowserParent();
3560 browserParent
->StartPersistence(canonical
, aRecv
, aRv
);
3564 nsCOMPtr
<Document
> foundDoc
= context
->GetDocument();
3567 aRecv
->OnError(NS_ERROR_NO_CONTENT
);
3569 nsCOMPtr
<nsIWebBrowserPersistDocument
> pdoc
=
3570 new mozilla::WebBrowserPersistLocalDocument(foundDoc
);
3571 aRecv
->OnDocumentReady(pdoc
);
3575 void nsFrameLoader::MaybeUpdatePrimaryBrowserParent(
3576 BrowserParentChange aChange
) {
3577 if (!mOwnerContent
|| !mRemoteBrowser
) {
3581 RefPtr
<BrowserHost
> browserHost
= mRemoteBrowser
->AsBrowserHost();
3586 nsCOMPtr
<nsIDocShell
> docShell
= mOwnerContent
->OwnerDoc()->GetDocShell();
3591 BrowsingContext
* browsingContext
= docShell
->GetBrowsingContext();
3592 if (!browsingContext
->IsChrome()) {
3596 nsCOMPtr
<nsIDocShellTreeOwner
> parentTreeOwner
;
3597 docShell
->GetTreeOwner(getter_AddRefs(parentTreeOwner
));
3598 if (!parentTreeOwner
) {
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
,
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());
3628 nsPIDOMWindowOuter
* outerWin
= root
->GetWindow();
3630 chromeOuterWindowID
= outerWin
->WindowID();
3634 uint32_t maxTouchPoints
= BrowserParent::GetMaxTouchPoints(mOwnerContent
);
3636 bool tabContextUpdated
=
3637 aTabContext
->SetTabContext(chromeOuterWindowID
, maxTouchPoints
);
3638 NS_ENSURE_STATE(tabContextUpdated
);
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()) {
3651 nsAutoString attributeValue
;
3652 if (aAttr
.mUserContextId
==
3653 nsIScriptSecurityManager::DEFAULT_USER_CONTEXT_ID
&&
3654 mOwnerContent
->GetAttr(nsGkAtoms::usercontextid
, attributeValue
) &&
3655 !attributeValue
.IsEmpty()) {
3657 aAttr
.mUserContextId
= attributeValue
.ToInteger(&rv
);
3658 NS_ENSURE_SUCCESS(rv
, rv
);
3661 if (aAttr
.mGeckoViewSessionContextId
.IsEmpty() &&
3662 mOwnerContent
->GetAttr(nsGkAtoms::geckoViewSessionContextId
,
3664 !attributeValue
.IsEmpty()) {
3665 // XXX: Should we check the format from `GeckoViewNavigation.jsm` here?
3666 aAttr
.mGeckoViewSessionContextId
= attributeValue
;
3672 ProcessMessageManager
* nsFrameLoader::GetProcessMessageManager() const {
3673 if (auto* browserParent
= GetBrowserParent()) {
3674 return browserParent
->Manager()->GetMessageManager();
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
);
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();
3706 RefPtr
<nsDocShell
> docshell
= GetDocShell();
3707 MOZ_ASSERT(docshell
);
3708 docshell
->SetWillChangeProcess();
3711 static mozilla::Result
<bool, nsresult
> DidBuildIDChange() {
3713 nsCOMPtr
<nsIFile
> file
;
3715 rv
= NS_GetSpecialDirectory(NS_GRE_DIR
, getter_AddRefs(file
));
3718 rv
= file
->AppendNative("platform.ini"_ns
);
3721 nsCOMPtr
<nsIINIParserFactory
> iniFactory
=
3722 do_GetService("@mozilla.org/xpcom/ini-parser-factory;1", &rv
);
3725 nsCOMPtr
<nsIINIParser
> parser
;
3726 rv
= iniFactory
->CreateINIParser(file
, getter_AddRefs(parser
));
3729 nsAutoCString installedBuildID
;
3730 rv
= parser
->GetString("Build"_ns
, "BuildID"_ns
, installedBuildID
);
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
) {
3744 if (mPendingBrowsingContext
== aBrowsingContext
) {
3745 mTabProcessCrashFired
= true;
3748 // Fire the crashed observer notification.
3749 nsCOMPtr
<nsIObserverService
> os
= services::GetObserverService();
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
);
3763 RefPtr
<nsFrameLoader
> currentFrameLoader
= owner
->GetFrameLoader();
3764 if (currentFrameLoader
!= this) {
3768 #if defined(MOZ_TELEMETRY_REPORTING)
3769 bool sendTelemetry
= false;
3770 #endif // defined(MOZ_TELEMETRY_REPORTING)
3772 // Fire the actual crashed event.
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
;
3780 bool aChanged
= changedOrError
.unwrap();
3782 NS_WARNING("True build ID mismatch");
3783 eventName
= u
"oop-browser-buildid-mismatch"_ns
;
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)
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,
3823 bool nsFrameLoader::EnsureBrowsingContextAttached() {
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
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
))) {
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() ==
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.
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();
3907 void nsFrameLoader::InvokeBrowsingContextReadyCallback() {
3908 if (mOpenWindowInfo
) {
3909 if (RefPtr
<nsIBrowsingContextReadyCallback
> callback
=
3910 mOpenWindowInfo
->BrowsingContextReadyCallback()) {
3911 callback
->BrowsingContextReady(mPendingBrowsingContext
);