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 "base/basictypes.h"
16 #include "nsDocShell.h"
17 #include "nsIContentInlines.h"
18 #include "nsIContentViewer.h"
19 #include "nsIPrintSettingsService.h"
20 #include "mozilla/dom/Document.h"
21 #include "nsPIDOMWindow.h"
22 #include "nsIWebNavigation.h"
23 #include "nsIWebProgress.h"
24 #include "nsIDocShell.h"
25 #include "nsIDocShellTreeOwner.h"
26 #include "nsDocShellLoadState.h"
27 #include "nsIBaseWindow.h"
28 #include "nsIBrowser.h"
29 #include "nsContentUtils.h"
30 #include "nsUnicharUtils.h"
31 #include "nsIScriptGlobalObject.h"
32 #include "nsIScriptSecurityManager.h"
33 #include "nsFrameLoader.h"
34 #include "nsFrameLoaderOwner.h"
36 #include "nsIScrollableFrame.h"
37 #include "nsSubDocumentFrame.h"
39 #include "nsIAppWindow.h"
40 #include "nsIMozBrowserFrame.h"
41 #include "nsIScriptError.h"
42 #include "nsGlobalWindow.h"
43 #include "nsHTMLDocument.h"
44 #include "nsPIWindowRoot.h"
45 #include "nsLayoutUtils.h"
46 #include "nsMappedAttributes.h"
48 #include "nsBaseWidget.h"
49 #include "nsQueryObject.h"
50 #include "ReferrerInfo.h"
51 #include "nsIOpenWindowInfo.h"
52 #include "nsISHistory.h"
54 #include "nsIXULRuntime.h"
55 #include "nsNetUtil.h"
57 #include "nsGkAtoms.h"
58 #include "nsNameSpaceManager.h"
60 #include "nsThreadUtils.h"
62 #include "nsIDOMChromeWindow.h"
63 #include "InProcessBrowserChildMessageManager.h"
66 #include "ClientLayerManager.h"
68 #include "ContentParent.h"
69 #include "BrowserParent.h"
70 #include "mozilla/AsyncEventDispatcher.h"
71 #include "mozilla/BasePrincipal.h"
72 #include "mozilla/ExpandedPrincipal.h"
73 #include "mozilla/FlushType.h"
74 #include "mozilla/HTMLEditor.h"
75 #include "mozilla/NullPrincipal.h"
76 #include "mozilla/Preferences.h"
77 #include "mozilla/PresShell.h"
78 #include "mozilla/PresShellInlines.h"
79 #include "mozilla/StaticPrefs_fission.h"
80 #include "mozilla/Unused.h"
81 #include "mozilla/dom/ChromeMessageSender.h"
82 #include "mozilla/dom/Element.h"
83 #include "mozilla/dom/FrameCrashedEvent.h"
84 #include "mozilla/dom/FrameLoaderBinding.h"
85 #include "mozilla/dom/MozFrameLoaderOwnerBinding.h"
86 #include "mozilla/dom/PBrowser.h"
87 #include "mozilla/dom/SessionStoreListener.h"
88 #include "mozilla/dom/WindowGlobalParent.h"
89 #include "mozilla/dom/XULFrameElement.h"
90 #include "mozilla/gfx/CrossProcessPaint.h"
91 #include "nsGenericHTMLFrameElement.h"
92 #include "GeckoProfiler.h"
95 #include "mozilla/dom/HTMLIFrameElement.h"
96 #include "nsSandboxFlags.h"
97 #include "mozilla/layers/CompositorBridgeChild.h"
98 #include "mozilla/dom/CustomEvent.h"
100 #include "mozilla/dom/ipc/StructuredCloneData.h"
101 #include "mozilla/WebBrowserPersistLocalDocument.h"
102 #include "mozilla/dom/Promise.h"
103 #include "mozilla/dom/PromiseNativeHandler.h"
104 #include "mozilla/dom/ChildSHistory.h"
105 #include "mozilla/dom/CanonicalBrowsingContext.h"
106 #include "mozilla/dom/ContentChild.h"
107 #include "mozilla/dom/ContentProcessManager.h"
108 #include "mozilla/dom/BrowserBridgeChild.h"
109 #include "mozilla/dom/BrowserHost.h"
110 #include "mozilla/dom/BrowserBridgeHost.h"
111 #include "mozilla/dom/BrowsingContextGroup.h"
113 #include "mozilla/dom/HTMLBodyElement.h"
115 #include "mozilla/ContentPrincipal.h"
118 # include "mozilla/plugins/PPluginWidgetParent.h"
119 # include "../plugins/ipc/PluginWidgetParent.h"
123 # include "nsXULPopupManager.h"
127 # include "mozilla/embedding/printingui/PrintingParent.h"
128 # include "nsIWebBrowserPrint.h"
131 using namespace mozilla
;
132 using namespace mozilla::hal
;
133 using namespace mozilla::dom
;
134 using namespace mozilla::dom::ipc
;
135 using namespace mozilla::layers
;
136 using namespace mozilla::layout
;
137 typedef ScrollableLayerGuid::ViewID ViewID
;
139 using PrintPreviewResolver
= std::function
<void(const PrintPreviewResultInfo
&)>;
141 // Bug 136580: Limit to the number of nested content frames that can have the
142 // same URL. This is to stop content that is recursively loading
143 // itself. Note that "#foo" on the end of URL doesn't affect
144 // whether it's considered identical, but "?foo" or ";foo" are
145 // considered and compared.
146 // Limit this to 2, like chromium does.
147 #define MAX_SAME_URL_CONTENT_FRAMES 2
149 // Bug 8065: Limit content frame depth to some reasonable level. This
150 // does not count chrome frames when determining depth, nor does it
151 // prevent chrome recursion. Number is fairly arbitrary, but meant to
152 // keep number of shells to a reasonable number on accidental recursion with a
153 // small (but not 1) branching factor. With large branching factors the number
154 // of shells can rapidly become huge and run us out of memory. To solve that,
155 // we'd need to re-institute a fixed version of bug 98158.
156 #define MAX_DEPTH_CONTENT_FRAMES 10
158 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsFrameLoader
, mPendingBrowsingContext
,
159 mMessageManager
, mChildMessageManager
,
161 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFrameLoader
)
162 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFrameLoader
)
164 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFrameLoader
)
165 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
166 NS_INTERFACE_MAP_ENTRY_CONCRETE(nsFrameLoader
)
167 NS_INTERFACE_MAP_ENTRY(nsIMutationObserver
)
168 NS_INTERFACE_MAP_ENTRY(nsISupports
)
171 nsFrameLoader::nsFrameLoader(Element
* aOwner
, BrowsingContext
* aBrowsingContext
,
172 bool aIsRemoteFrame
, bool aNetworkCreated
)
173 : mPendingBrowsingContext(aBrowsingContext
),
174 mOwnerContent(aOwner
),
175 mDetachedSubdocFrame(nullptr),
178 mRemoteType(NOT_REMOTE_TYPE
),
179 mDepthTooGreat(false),
180 mIsTopLevelContent(false),
181 mDestroyCalled(false),
182 mNeedsAsyncDestroy(false),
186 mNetworkCreated(aNetworkCreated
),
187 mLoadingOriginalSrc(false),
188 mRemoteBrowserShown(false),
189 mIsRemoteFrame(aIsRemoteFrame
),
190 mWillChangeProcess(false),
191 mObservingOwnerContent(false),
192 mTabProcessCrashFired(false),
193 mNotifyingCrash(false) {}
195 nsFrameLoader::~nsFrameLoader() {
196 if (mMessageManager
) {
197 mMessageManager
->Disconnect();
199 MOZ_RELEASE_ASSERT(mDestroyCalled
);
202 static nsAtom
* TypeAttrName(Element
* aOwnerContent
) {
203 return aOwnerContent
->IsXULElement() ? nsGkAtoms::type
204 : nsGkAtoms::mozframetype
;
207 static void GetFrameName(Element
* aOwnerContent
, nsAString
& aFrameName
) {
208 int32_t namespaceID
= aOwnerContent
->GetNameSpaceID();
209 if (namespaceID
== kNameSpaceID_XHTML
&& !aOwnerContent
->IsInHTMLDocument()) {
210 aOwnerContent
->GetAttr(kNameSpaceID_None
, nsGkAtoms::id
, aFrameName
);
212 aOwnerContent
->GetAttr(kNameSpaceID_None
, nsGkAtoms::name
, aFrameName
);
213 // XXX if no NAME then use ID, after a transition period this will be
214 // changed so that XUL only uses ID too (bug 254284).
215 if (aFrameName
.IsEmpty() && namespaceID
== kNameSpaceID_XUL
) {
216 aOwnerContent
->GetAttr(kNameSpaceID_None
, nsGkAtoms::id
, aFrameName
);
221 // If this method returns true, the nsFrameLoader will act as a boundary, as is
222 // the case for <iframe mozbrowser> and <browser type="content"> elements.
224 // # Historical Notes (10 April 2019)
226 // In the past, this boundary was defined by the "typeContent" and "typeChrome"
227 // nsIDocShellTreeItem types. There was only ever a single split in the tree,
228 // and it occurred at the boundary between these two types of docshells. When
229 // <iframe mozbrowser> was introduced, it was given special casing to make it
230 // act like a second boundary, without having to change the existing code.
232 // The about:addons page, which is loaded within a content browser, then added a
233 // remote <browser type="content" remote="true"> element. When remote, this
234 // would also act as a mechanism for creating a disjoint tree, due to the
235 // process keeping the embedder and embedee separate.
237 // However, when initial out-of-process iframe support was implemented, this
238 // codepath became a risk, as it could've caused the oop iframe remote
239 // WindowProxy code to be activated for the addons page. This was fixed by
240 // extendng the isolation logic previously reserved to <iframe mozbrowser> to
241 // also cover <browser> elements with the explicit `remote` property loaded in
244 // To keep these boundaries clear, and allow them to work in a cross-process
245 // manner, they are no longer handled by typeContent and typeChrome. Instead,
246 // the actual BrowsingContext tree is broken at these edges.
247 static bool IsTopContent(BrowsingContext
* aParent
, Element
* aOwner
) {
248 if (XRE_IsContentProcess()) {
252 // If we have a (deprecated) mozbrowser element, we want to start a new
253 // BrowsingContext tree regardless of whether the parent is chrome or content.
254 nsCOMPtr
<nsIMozBrowserFrame
> mozbrowser
= aOwner
->GetAsMozBrowserFrame();
255 if (mozbrowser
&& mozbrowser
->GetReallyIsBrowser()) {
259 if (aParent
->IsContent()) {
260 // If we're already in content, we may still want to create a new
261 // BrowsingContext tree if our element is a xul browser element with a
262 // `remote="true"` marker.
263 return aOwner
->IsXULElement() &&
264 aOwner
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::remote
,
265 nsGkAtoms::_true
, eCaseMatters
);
268 // If we're in a chrome context, we want to start a new tree if we are an
269 // element with a `type="content"` marker.
270 return aOwner
->AttrValueIs(kNameSpaceID_None
, TypeAttrName(aOwner
),
271 nsGkAtoms::content
, eIgnoreCase
);
274 static already_AddRefed
<BrowsingContext
> CreateBrowsingContext(
275 Element
* aOwner
, nsIOpenWindowInfo
* aOpenWindowInfo
,
276 BrowsingContextGroup
* aSpecificGroup
, bool aNetworkCreated
= false) {
277 MOZ_ASSERT(!aOpenWindowInfo
|| !aSpecificGroup
,
278 "Only one of SpecificGroup and OpenWindowInfo may be provided!");
280 // If we've got a pending BrowserParent from the content process, use the
281 // BrowsingContext which was created for it.
282 if (aOpenWindowInfo
&& aOpenWindowInfo
->GetNextRemoteBrowser()) {
283 MOZ_ASSERT(XRE_IsParentProcess());
285 aOpenWindowInfo
->GetNextRemoteBrowser()->GetBrowsingContext());
288 RefPtr
<BrowsingContext
> opener
;
289 if (aOpenWindowInfo
&& !aOpenWindowInfo
->GetForceNoOpener()) {
290 opener
= aOpenWindowInfo
->GetParent();
291 // Must create BrowsingContext with opener in-process.
292 MOZ_ASSERT_IF(opener
, opener
->IsInProcess());
295 RefPtr
<nsGlobalWindowInner
> parentInner
=
296 nsGlobalWindowInner::Cast(aOwner
->OwnerDoc()->GetInnerWindow());
297 if (NS_WARN_IF(!parentInner
) || parentInner
->IsDying()) {
301 BrowsingContext
* parentBC
= parentInner
->GetBrowsingContext();
302 if (NS_WARN_IF(!parentBC
) || parentBC
->IsDiscarded()) {
306 // Determine the frame name for the new browsing context.
307 nsAutoString frameName
;
308 GetFrameName(aOwner
, frameName
);
310 // Create our BrowsingContext without immediately attaching it. It's possible
311 // that no DocShell or remote browser will ever be created for this
312 // FrameLoader, particularly if the document that we were created for is not
313 // currently active. And in that latter case, if we try to attach our BC now,
314 // it will wind up attached as a child of the currently active inner window
315 // for the BrowsingContext, and cause no end of trouble.
316 if (IsTopContent(parentBC
, aOwner
)) {
317 // Create toplevel context without a parent & as Type::Content.
318 return BrowsingContext::CreateDetached(nullptr, opener
, aSpecificGroup
,
320 BrowsingContext::Type::Content
);
323 MOZ_ASSERT(!aOpenWindowInfo
,
324 "Can't have openWindowInfo for non-toplevel context");
326 MOZ_ASSERT(!aSpecificGroup
,
327 "Can't force BrowsingContextGroup for non-toplevel context");
328 return BrowsingContext::CreateDetached(parentInner
, nullptr, nullptr,
329 frameName
, parentBC
->GetType(),
333 static bool InitialLoadIsRemote(Element
* aOwner
) {
334 if (PR_GetEnv("MOZ_DISABLE_OOP_TABS") ||
335 Preferences::GetBool("dom.ipc.tabs.disabled", false)) {
339 // The initial load in an content process iframe should never be made remote.
340 // Content process iframes always become remote due to navigation.
341 if (XRE_IsContentProcess()) {
345 // If we're an <iframe mozbrowser> and we don't have a "remote" attribute,
346 // fall back to the default.
347 nsCOMPtr
<nsIMozBrowserFrame
> browserFrame
= do_QueryInterface(aOwner
);
348 bool isMozBrowserFrame
= browserFrame
&& browserFrame
->GetReallyIsBrowser();
349 if (isMozBrowserFrame
&&
350 !aOwner
->HasAttr(kNameSpaceID_None
, nsGkAtoms::remote
)) {
351 return Preferences::GetBool("dom.ipc.browser_frames.oop_by_default", false);
354 // Otherwise, we're remote if we have "remote=true" and we're either a
355 // browser frame or a XUL element.
356 return (isMozBrowserFrame
|| aOwner
->GetNameSpaceID() == kNameSpaceID_XUL
) &&
357 aOwner
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::remote
,
358 nsGkAtoms::_true
, eCaseMatters
);
361 static already_AddRefed
<BrowsingContextGroup
> InitialBrowsingContextGroup(
363 nsAutoString attrString
;
364 if (aOwner
->GetNameSpaceID() != kNameSpaceID_XUL
||
365 !aOwner
->GetAttr(nsGkAtoms::initialBrowsingContextGroupId
, attrString
)) {
369 // It's OK to read the attribute using a signed 64-bit integer parse, as an ID
370 // generated using `nsContentUtils::GenerateProcessSpecificId` (like BCG IDs)
371 // will only ever use 53 bits of precision, so it can be round-tripped through
374 int64_t signedGroupId
{attrString
.ToInteger(&rv
, 10)};
375 if (NS_FAILED(rv
) || signedGroupId
<= 0) {
379 return BrowsingContextGroup::GetOrCreate(uint64_t(signedGroupId
));
382 already_AddRefed
<nsFrameLoader
> nsFrameLoader::Create(
383 Element
* aOwner
, bool aNetworkCreated
, nsIOpenWindowInfo
* aOpenWindowInfo
) {
384 NS_ENSURE_TRUE(aOwner
, nullptr);
385 Document
* doc
= aOwner
->OwnerDoc();
387 // We never create nsFrameLoaders for elements in resource documents.
389 // We never create nsFrameLoaders for elements in data documents, unless the
390 // document is a static document.
391 // Static documents are an exception because any sub-documents need an
392 // nsFrameLoader to keep the relevant docShell alive, even though the
393 // nsFrameLoader isn't used to load anything (the sub-document is created by
394 // the static clone process).
396 // We never create nsFrameLoaders for elements that are not
397 // in-composed-document, unless the element belongs to a static document.
398 // Static documents are an exception because this method is called at a point
399 // in the static clone process before aOwner has been inserted into its
400 // document. For other types of documents this wouldn't be a problem since
401 // we'd create the nsFrameLoader as necessary after aOwner is inserted into a
402 // document, but the mechanisms that take care of that don't apply for static
403 // documents so we need to create the nsFrameLoader now. (This isn't wasteful
404 // since for a static document we know aOwner will end up in a document and
405 // the nsFrameLoader will be used for its docShell.)
407 NS_ENSURE_TRUE(!doc
->IsResourceDoc() &&
408 ((!doc
->IsLoadedAsData() && aOwner
->IsInComposedDoc()) ||
409 doc
->IsStaticDocument()),
412 RefPtr
<BrowsingContextGroup
> group
= InitialBrowsingContextGroup(aOwner
);
413 RefPtr
<BrowsingContext
> context
=
414 CreateBrowsingContext(aOwner
, aOpenWindowInfo
, group
, aNetworkCreated
);
415 NS_ENSURE_TRUE(context
, nullptr);
417 if (XRE_IsParentProcess() && aOpenWindowInfo
) {
418 MOZ_ASSERT(context
->IsTopContent());
419 if (RefPtr
<BrowsingContext
> crossGroupOpener
=
420 aOpenWindowInfo
->GetParent()) {
421 context
->Canonical()->SetCrossGroupOpenerId(crossGroupOpener
->Id());
425 bool isRemoteFrame
= InitialLoadIsRemote(aOwner
);
426 RefPtr
<nsFrameLoader
> fl
=
427 new nsFrameLoader(aOwner
, context
, isRemoteFrame
, aNetworkCreated
);
428 fl
->mOpenWindowInfo
= aOpenWindowInfo
;
430 // If this is a toplevel initial remote frame, we're looking at a browser
431 // loaded in the parent process. Pull the remote type attribute off of the
432 // <browser> element to determine which remote type it should be loaded in, or
433 // use `DEFAULT_REMOTE_TYPE` if we can't tell.
435 MOZ_ASSERT(XRE_IsParentProcess());
436 nsAutoString remoteType
;
437 if (aOwner
->GetAttr(kNameSpaceID_None
, nsGkAtoms::RemoteType
, remoteType
) &&
438 !remoteType
.IsEmpty()) {
439 CopyUTF16toUTF8(remoteType
, fl
->mRemoteType
);
441 fl
->mRemoteType
= DEFAULT_REMOTE_TYPE
;
448 already_AddRefed
<nsFrameLoader
> nsFrameLoader::Recreate(
449 mozilla::dom::Element
* aOwner
, BrowsingContext
* aContext
,
450 BrowsingContextGroup
* aSpecificGroup
, bool aIsRemote
, bool aNetworkCreated
,
451 bool aPreserveContext
) {
452 NS_ENSURE_TRUE(aOwner
, nullptr);
455 // This version of Create is only called for Remoteness updates, so we can
456 // assume we need a FrameLoader here and skip the check in the other Create.
457 Document
* doc
= aOwner
->OwnerDoc();
458 MOZ_ASSERT(!doc
->IsResourceDoc());
459 MOZ_ASSERT((!doc
->IsLoadedAsData() && aOwner
->IsInComposedDoc()) ||
460 doc
->IsStaticDocument());
463 RefPtr
<BrowsingContext
> context
= aContext
;
464 if (!context
|| !aPreserveContext
) {
465 context
= CreateBrowsingContext(aOwner
, /* openWindowInfo */ nullptr,
469 XRE_IsParentProcess(),
470 "Recreating browing contexts only supported in the parent process");
471 aContext
->Canonical()->ReplacedBy(context
->Canonical());
474 NS_ENSURE_TRUE(context
, nullptr);
476 RefPtr
<nsFrameLoader
> fl
=
477 new nsFrameLoader(aOwner
, context
, aIsRemote
, aNetworkCreated
);
481 void nsFrameLoader::LoadFrame(bool aOriginalSrc
) {
482 if (NS_WARN_IF(!mOwnerContent
)) {
487 nsCOMPtr
<nsIPrincipal
> principal
;
488 nsCOMPtr
<nsIContentSecurityPolicy
> csp
;
490 bool isSrcdoc
= mOwnerContent
->IsHTMLElement(nsGkAtoms::iframe
) &&
491 mOwnerContent
->HasAttr(kNameSpaceID_None
, nsGkAtoms::srcdoc
);
493 src
.AssignLiteral("about:srcdoc");
494 principal
= mOwnerContent
->NodePrincipal();
495 csp
= mOwnerContent
->GetCsp();
497 GetURL(src
, getter_AddRefs(principal
), getter_AddRefs(csp
));
502 // If the frame is a XUL element and has the attribute 'nodefaultsrc=true'
503 // then we will not use 'about:blank' as fallback but return early without
504 // starting a load if no 'src' attribute is given (or it's empty).
505 if (mOwnerContent
->IsXULElement() &&
506 mOwnerContent
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::nodefaultsrc
,
507 nsGkAtoms::_true
, eCaseMatters
)) {
510 src
.AssignLiteral("about:blank");
511 principal
= mOwnerContent
->NodePrincipal();
512 csp
= mOwnerContent
->GetCsp();
516 Document
* doc
= mOwnerContent
->OwnerDoc();
517 if (doc
->IsStaticDocument()) {
521 nsIURI
* base_uri
= mOwnerContent
->GetBaseURI();
522 auto encoding
= doc
->GetDocumentCharacterSet();
524 nsCOMPtr
<nsIURI
> uri
;
525 nsresult rv
= NS_NewURI(getter_AddRefs(uri
), src
, encoding
, base_uri
);
527 // If the URI was malformed, try to recover by loading about:blank.
528 if (rv
== NS_ERROR_MALFORMED_URI
) {
529 rv
= NS_NewURI(getter_AddRefs(uri
), u
"about:blank"_ns
, encoding
, base_uri
);
532 if (NS_SUCCEEDED(rv
)) {
533 rv
= LoadURI(uri
, principal
, csp
, aOriginalSrc
);
541 void nsFrameLoader::ConfigRemoteProcess(const nsACString
& aRemoteType
,
542 ContentParent
* aContentParent
) {
543 MOZ_DIAGNOSTIC_ASSERT(IsRemoteFrame(), "Must be a remote frame");
544 MOZ_DIAGNOSTIC_ASSERT(!mRemoteBrowser
, "Must not have a browser yet");
545 MOZ_DIAGNOSTIC_ASSERT_IF(aContentParent
,
546 aContentParent
->GetRemoteType() == aRemoteType
);
548 mRemoteType
= aRemoteType
;
549 mChildID
= aContentParent
? aContentParent
->ChildID() : 0;
552 void nsFrameLoader::FireErrorEvent() {
553 if (!mOwnerContent
) {
556 RefPtr
<AsyncEventDispatcher
> loadBlockingAsyncDispatcher
=
557 new LoadBlockingAsyncEventDispatcher(
558 mOwnerContent
, u
"error"_ns
, CanBubble::eNo
, ChromeOnlyDispatch::eNo
);
559 loadBlockingAsyncDispatcher
->PostDOMEvent();
562 nsresult
nsFrameLoader::LoadURI(nsIURI
* aURI
,
563 nsIPrincipal
* aTriggeringPrincipal
,
564 nsIContentSecurityPolicy
* aCsp
,
566 if (!aURI
) return NS_ERROR_INVALID_POINTER
;
567 NS_ENSURE_STATE(!mDestroyCalled
&& mOwnerContent
);
569 aTriggeringPrincipal
,
570 "Must have an explicit triggeringPrincipal to nsFrameLoader::LoadURI.");
572 mLoadingOriginalSrc
= aOriginalSrc
;
574 nsCOMPtr
<Document
> doc
= mOwnerContent
->OwnerDoc();
577 rv
= CheckURILoad(aURI
, aTriggeringPrincipal
);
578 NS_ENSURE_SUCCESS(rv
, rv
);
581 mTriggeringPrincipal
= aTriggeringPrincipal
;
583 rv
= doc
->InitializeFrameLoader(this);
585 mURIToLoad
= nullptr;
586 mTriggeringPrincipal
= nullptr;
592 void nsFrameLoader::ResumeLoad(uint64_t aPendingSwitchID
) {
593 Document
* doc
= mOwnerContent
->OwnerDoc();
594 if (doc
->IsStaticDocument()) {
595 // Static doc shouldn't load sub-documents.
599 if (NS_WARN_IF(mDestroyCalled
|| !mOwnerContent
)) {
604 mLoadingOriginalSrc
= false;
605 mURIToLoad
= nullptr;
606 mPendingSwitchID
= aPendingSwitchID
;
607 mTriggeringPrincipal
= mOwnerContent
->NodePrincipal();
608 mCsp
= mOwnerContent
->GetCsp();
610 nsresult rv
= doc
->InitializeFrameLoader(this);
612 mPendingSwitchID
= 0;
613 mTriggeringPrincipal
= nullptr;
620 nsresult
nsFrameLoader::ReallyStartLoading() {
621 nsresult rv
= ReallyStartLoadingInternal();
629 nsresult
nsFrameLoader::ReallyStartLoadingInternal() {
630 NS_ENSURE_STATE((mURIToLoad
|| mPendingSwitchID
) && mOwnerContent
&&
631 mOwnerContent
->IsInComposedDoc());
633 AUTO_PROFILER_LABEL("nsFrameLoader::ReallyStartLoadingInternal", OTHER
);
634 RefPtr
<nsDocShellLoadState
> loadState
;
635 if (!mPendingSwitchID
) {
636 loadState
= new nsDocShellLoadState(mURIToLoad
);
637 loadState
->SetOriginalFrameSrc(mLoadingOriginalSrc
);
639 // The triggering principal could be null if the frame is loaded other
640 // than the src attribute, for example, the frame is sandboxed. In that
641 // case we use the principal of the owner content, which is needed to
642 // prevent XSS attaches on documents loaded in subframes.
643 if (mTriggeringPrincipal
) {
644 loadState
->SetTriggeringPrincipal(mTriggeringPrincipal
);
646 loadState
->SetTriggeringPrincipal(mOwnerContent
->NodePrincipal());
649 // If we have an explicit CSP, we set it. If not, we only query it from
650 // the document in case there was no explicit triggeringPrincipal.
651 // Otherwise it's possible that the original triggeringPrincipal did not
652 // have a CSP which causes the CSP on the Principal and explicit CSP
653 // to be out of sync.
655 loadState
->SetCsp(mCsp
);
656 } else if (!mTriggeringPrincipal
) {
657 nsCOMPtr
<nsIContentSecurityPolicy
> csp
= mOwnerContent
->GetCsp();
658 loadState
->SetCsp(csp
);
663 mOwnerContent
->IsHTMLElement(nsGkAtoms::iframe
) &&
664 mOwnerContent
->GetAttr(kNameSpaceID_None
, nsGkAtoms::srcdoc
, srcdoc
);
667 loadState
->SetSrcdocData(srcdoc
);
668 loadState
->SetBaseURI(mOwnerContent
->GetBaseURI());
671 auto referrerInfo
= MakeRefPtr
<ReferrerInfo
>(*mOwnerContent
);
672 loadState
->SetReferrerInfo(referrerInfo
);
674 loadState
->SetIsFromProcessingFrameAttributes();
677 int32_t flags
= nsIWebNavigation::LOAD_FLAGS_NONE
;
679 // Flags for browser frame:
680 if (OwnerIsMozBrowserFrame()) {
681 flags
= nsIWebNavigation::LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP
|
682 nsIWebNavigation::LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL
;
684 loadState
->SetLoadFlags(flags
);
686 loadState
->SetFirstParty(false);
689 if (IsRemoteFrame()) {
690 if (!EnsureRemoteBrowser()) {
691 NS_WARNING("Couldn't create child process for iframe.");
692 return NS_ERROR_FAILURE
;
695 if (mPendingSwitchID
) {
696 mRemoteBrowser
->ResumeLoad(mPendingSwitchID
);
697 mPendingSwitchID
= 0;
699 mRemoteBrowser
->LoadURL(loadState
);
702 if (!mRemoteBrowserShown
) {
703 // This can fail if it's too early to show the frame, we will retry later.
704 Unused
<< ShowRemoteFrame(ScreenIntSize(0, 0));
711 // If we already have a docshell, ensure that the docshell's storage access
713 GetDocShell()->MaybeClearStorageAccessFlag();
716 nsresult rv
= MaybeCreateDocShell();
720 MOZ_ASSERT(GetDocShell(),
721 "MaybeCreateDocShell succeeded with a null docShell");
723 // If we have a pending switch, just resume our load.
724 if (mPendingSwitchID
) {
725 bool tmpState
= mNeedsAsyncDestroy
;
726 mNeedsAsyncDestroy
= true;
727 rv
= GetDocShell()->ResumeRedirectedLoad(mPendingSwitchID
, -1);
728 mNeedsAsyncDestroy
= tmpState
;
729 mPendingSwitchID
= 0;
733 // Just to be safe, recheck uri.
734 rv
= CheckURILoad(mURIToLoad
, mTriggeringPrincipal
);
735 NS_ENSURE_SUCCESS(rv
, rv
);
737 mLoadingOriginalSrc
= false;
739 // Kick off the load...
740 bool tmpState
= mNeedsAsyncDestroy
;
741 mNeedsAsyncDestroy
= true;
743 rv
= GetDocShell()->LoadURI(loadState
, false);
744 mNeedsAsyncDestroy
= tmpState
;
745 mURIToLoad
= nullptr;
746 NS_ENSURE_SUCCESS(rv
, rv
);
751 nsresult
nsFrameLoader::CheckURILoad(nsIURI
* aURI
,
752 nsIPrincipal
* aTriggeringPrincipal
) {
753 // Check for security. The fun part is trying to figure out what principals
754 // to use. The way I figure it, if we're doing a LoadFrame() accidentally
755 // (eg someone created a frame/iframe node, we're being parsed, XUL iframes
756 // are being reframed, etc.) then we definitely want to use the node
757 // principal of mOwnerContent for security checks. If, on the other hand,
758 // someone's setting the src on our owner content, or created it via script,
759 // or whatever, then they can clearly access it... and we should still use
760 // the principal of mOwnerContent. I don't think that leads to privilege
761 // escalation, and it's reasonably guaranteed to not lead to XSS issues
762 // (since caller can already access mOwnerContent in this case). So just use
763 // the principal of mOwnerContent no matter what. If script wants to run
764 // things with its own permissions, which differ from those of mOwnerContent
765 // (which means the script is privileged in some way) it should set
766 // window.location instead.
767 nsIScriptSecurityManager
* secMan
= nsContentUtils::GetSecurityManager();
770 nsIPrincipal
* principal
=
771 (aTriggeringPrincipal
? aTriggeringPrincipal
772 : mOwnerContent
->NodePrincipal());
774 // Check if we are allowed to load absURL
775 nsresult rv
= secMan
->CheckLoadURIWithPrincipal(
776 principal
, aURI
, nsIScriptSecurityManager::STANDARD
,
777 mOwnerContent
->OwnerDoc()->InnerWindowID());
779 return rv
; // We're not
782 // Bail out if this is an infinite recursion scenario
783 if (IsRemoteFrame()) {
786 return CheckForRecursiveLoad(aURI
);
789 nsDocShell
* nsFrameLoader::GetDocShell(ErrorResult
& aRv
) {
790 if (IsRemoteFrame()) {
794 // If we have an owner, make sure we have a docshell and return
795 // that. If not, we're most likely in the middle of being torn down,
796 // then we just return null.
798 nsresult rv
= MaybeCreateDocShell();
803 MOZ_ASSERT(GetDocShell(),
804 "MaybeCreateDocShell succeeded, but null docShell");
807 return GetDocShell();
810 static void SetTreeOwnerAndChromeEventHandlerOnDocshellTree(
811 nsIDocShellTreeItem
* aItem
, nsIDocShellTreeOwner
* aOwner
,
812 EventTarget
* aHandler
) {
813 MOZ_ASSERT(aItem
, "Must have item");
815 aItem
->SetTreeOwner(aOwner
);
817 int32_t childCount
= 0;
818 aItem
->GetInProcessChildCount(&childCount
);
819 for (int32_t i
= 0; i
< childCount
; ++i
) {
820 nsCOMPtr
<nsIDocShellTreeItem
> item
;
821 aItem
->GetInProcessChildAt(i
, getter_AddRefs(item
));
823 nsCOMPtr
<nsIDocShell
> shell(do_QueryInterface(item
));
824 shell
->SetChromeEventHandler(aHandler
);
826 SetTreeOwnerAndChromeEventHandlerOnDocshellTree(item
, aOwner
, aHandler
);
830 #if defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
831 static bool CheckDocShellType(mozilla::dom::Element
* aOwnerContent
,
832 nsIDocShellTreeItem
* aDocShell
, nsAtom
* aAtom
) {
833 bool isContent
= aOwnerContent
->AttrValueIs(kNameSpaceID_None
, aAtom
,
834 nsGkAtoms::content
, eIgnoreCase
);
837 nsCOMPtr
<nsIMozBrowserFrame
> mozbrowser
=
838 aOwnerContent
->GetAsMozBrowserFrame();
840 mozbrowser
->GetMozbrowser(&isContent
);
845 return aDocShell
->ItemType() == nsIDocShellTreeItem::typeContent
;
848 nsCOMPtr
<nsIDocShellTreeItem
> parent
;
849 aDocShell
->GetInProcessParent(getter_AddRefs(parent
));
851 return parent
&& parent
->ItemType() == aDocShell
->ItemType();
853 #endif // defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
856 * Hook up a given TreeItem to its tree owner. aItem's type must have already
857 * been set, and it should already be part of the DocShellTree.
858 * @param aItem the treeitem we're working with
859 * @param aTreeOwner the relevant treeowner; might be null
861 void nsFrameLoader::AddTreeItemToTreeOwner(nsIDocShellTreeItem
* aItem
,
862 nsIDocShellTreeOwner
* aOwner
) {
863 MOZ_ASSERT(aItem
, "Must have docshell treeitem");
864 MOZ_ASSERT(mOwnerContent
, "Must have owning content");
866 MOZ_DIAGNOSTIC_ASSERT(
867 CheckDocShellType(mOwnerContent
, aItem
, TypeAttrName(mOwnerContent
)),
868 "Correct ItemType should be set when creating BrowsingContext");
870 if (mIsTopLevelContent
) {
871 bool is_primary
= mOwnerContent
->AttrValueIs(
872 kNameSpaceID_None
, nsGkAtoms::primary
, nsGkAtoms::_true
, eIgnoreCase
);
874 mOwnerContent
->AddMutationObserver(this);
875 mObservingOwnerContent
= true;
876 aOwner
->ContentShellAdded(aItem
, is_primary
);
881 static bool AllDescendantsOfType(BrowsingContext
* aParent
,
882 BrowsingContext::Type aType
) {
883 for (auto& child
: aParent
->Children()) {
884 if (child
->GetType() != aType
|| !AllDescendantsOfType(child
, aType
)) {
892 static bool ParentWindowIsActive(Document
* aDoc
) {
893 nsCOMPtr
<nsPIWindowRoot
> root
= nsContentUtils::GetWindowRoot(aDoc
);
895 nsPIDOMWindowOuter
* rootWin
= root
->GetWindow();
896 return rootWin
&& rootWin
->IsActive();
901 void nsFrameLoader::MaybeShowFrame() {
902 nsIFrame
* frame
= GetPrimaryFrameOfOwningContent();
904 nsSubDocumentFrame
* subDocFrame
= do_QueryFrame(frame
);
906 subDocFrame
->MaybeShowViewer();
911 static ScrollbarPreference
GetScrollbarPreference(const Element
* aOwner
) {
913 return ScrollbarPreference::Auto
;
915 const nsAttrValue
* attrValue
= aOwner
->GetParsedAttr(nsGkAtoms::scrolling
);
916 return nsGenericHTMLFrameElement::MapScrollingAttribute(attrValue
);
919 static CSSIntSize
GetMarginAttributes(const Element
* aOwner
) {
920 CSSIntSize
result(-1, -1);
921 auto* content
= nsGenericHTMLElement::FromNodeOrNull(aOwner
);
925 const nsAttrValue
* attr
= content
->GetParsedAttr(nsGkAtoms::marginwidth
);
926 if (attr
&& attr
->Type() == nsAttrValue::eInteger
) {
927 result
.width
= attr
->GetIntegerValue();
929 attr
= content
->GetParsedAttr(nsGkAtoms::marginheight
);
930 if (attr
&& attr
->Type() == nsAttrValue::eInteger
) {
931 result
.height
= attr
->GetIntegerValue();
936 bool nsFrameLoader::Show(nsSubDocumentFrame
* frame
) {
942 auto resetInShow
= mozilla::MakeScopeExit([&] { mInShow
= false; });
944 ScreenIntSize size
= frame
->GetSubdocumentSize();
945 if (IsRemoteFrame()) {
946 // FIXME(bug 1588791): For fission iframes we need to pass down the
947 // scrollbar preferences.
948 return ShowRemoteFrame(size
, frame
);
951 nsresult rv
= MaybeCreateDocShell();
955 nsDocShell
* ds
= GetDocShell();
956 MOZ_ASSERT(ds
, "MaybeCreateDocShell succeeded, but null docShell");
961 ds
->SetScrollbarPreference(GetScrollbarPreference(mOwnerContent
));
962 const bool marginsChanged
=
963 ds
->UpdateFrameMargins(GetMarginAttributes(mOwnerContent
));
964 if (PresShell
* presShell
= ds
->GetPresShell()) {
965 // Ensure root scroll frame is reflowed in case margins have changed
966 if (marginsChanged
) {
967 if (nsIFrame
* rootScrollFrame
= presShell
->GetRootScrollFrame()) {
968 presShell
->FrameNeedsReflow(rootScrollFrame
, IntrinsicDirty::Resize
,
975 nsView
* view
= frame
->EnsureInnerView();
976 if (!view
) return false;
978 RefPtr
<nsDocShell
> baseWindow
= GetDocShell();
979 baseWindow
->InitWindow(nullptr, view
->GetWidget(), 0, 0, size
.width
,
981 baseWindow
->SetVisibility(true);
982 NS_ENSURE_TRUE(GetDocShell(), false);
984 // Trigger editor re-initialization if midas is turned on in the
985 // sub-document. This shouldn't be necessary, but given the way our
986 // editor works, it is. See
987 // https://bugzilla.mozilla.org/show_bug.cgi?id=284245
988 if (RefPtr
<PresShell
> presShell
= GetDocShell()->GetPresShell()) {
989 Document
* doc
= presShell
->GetDocument();
990 nsHTMLDocument
* htmlDoc
=
991 doc
&& doc
->IsHTMLOrXHTML() ? doc
->AsHTMLDocument() : nullptr;
994 nsAutoString designMode
;
995 htmlDoc
->GetDesignMode(designMode
);
997 if (designMode
.EqualsLiteral("on")) {
998 // Hold on to the editor object to let the document reattach to the
999 // same editor object, instead of creating a new one.
1000 RefPtr
<HTMLEditor
> htmlEditor
= GetDocShell()->GetHTMLEditor();
1001 Unused
<< htmlEditor
;
1002 htmlDoc
->SetDesignMode(u
"off"_ns
, Nothing(), IgnoreErrors());
1004 htmlDoc
->SetDesignMode(u
"on"_ns
, Nothing(), IgnoreErrors());
1006 // Re-initialize the presentation for contenteditable documents
1007 bool editable
= false, hasEditingSession
= false;
1008 GetDocShell()->GetEditable(&editable
);
1009 GetDocShell()->GetHasEditingSession(&hasEditingSession
);
1010 RefPtr
<HTMLEditor
> htmlEditor
= GetDocShell()->GetHTMLEditor();
1011 if (editable
&& hasEditingSession
&& htmlEditor
) {
1012 htmlEditor
->PostCreate();
1020 mHideCalled
= false;
1027 void nsFrameLoader::MarginsChanged() {
1028 // We assume that the margins are always zero for remote frames.
1029 if (IsRemoteFrame()) {
1033 nsDocShell
* docShell
= GetDocShell();
1034 // If there's no docshell, we're probably not up and running yet.
1035 // nsFrameLoader::Show() will take care of setting the right
1041 if (!docShell
->UpdateFrameMargins(GetMarginAttributes(mOwnerContent
))) {
1045 // There's a cached property declaration block
1046 // that needs to be updated
1047 if (Document
* doc
= docShell
->GetDocument()) {
1048 for (nsINode
* cur
= doc
; cur
; cur
= cur
->GetNextNode()) {
1049 if (cur
->IsHTMLElement(nsGkAtoms::body
)) {
1050 static_cast<HTMLBodyElement
*>(cur
)->ClearMappedServoStyle();
1055 // Trigger a restyle if there's a prescontext
1056 // FIXME: This could do something much less expensive.
1057 if (nsPresContext
* presContext
= docShell
->GetPresContext()) {
1058 // rebuild, because now the same nsMappedAttributes* will produce
1059 // a different style
1060 presContext
->RebuildAllStyleData(nsChangeHint(0),
1061 RestyleHint::RestyleSubtree());
1065 bool nsFrameLoader::ShowRemoteFrame(const ScreenIntSize
& size
,
1066 nsSubDocumentFrame
* aFrame
) {
1067 AUTO_PROFILER_LABEL("nsFrameLoader::ShowRemoteFrame", OTHER
);
1068 NS_ASSERTION(IsRemoteFrame(),
1069 "ShowRemote only makes sense on remote frames.");
1071 if (!EnsureRemoteBrowser()) {
1072 NS_ERROR("Couldn't create child process.");
1076 // FIXME/bug 589337: Show()/Hide() is pretty expensive for
1077 // cross-process layers; need to figure out what behavior we really
1078 // want here. For now, hack.
1079 if (!mRemoteBrowserShown
) {
1080 if (!mOwnerContent
|| !mOwnerContent
->GetComposedDoc()) {
1084 // We never want to host remote frameloaders in simple popups, like menus.
1085 nsIWidget
* widget
= nsContentUtils::WidgetForContent(mOwnerContent
);
1086 if (!widget
|| static_cast<nsBaseWidget
*>(widget
)->IsSmallPopup()) {
1090 nsCOMPtr
<nsISupports
> container
= mOwnerContent
->OwnerDoc()->GetContainer();
1091 nsCOMPtr
<nsIBaseWindow
> baseWindow
= do_QueryInterface(container
);
1092 nsCOMPtr
<nsIWidget
> mainWidget
;
1093 baseWindow
->GetMainWidget(getter_AddRefs(mainWidget
));
1094 nsSizeMode sizeMode
=
1095 mainWidget
? mainWidget
->SizeMode() : nsSizeMode_Normal
;
1096 OwnerShowInfo
info(size
, GetScrollbarPreference(mOwnerContent
),
1097 ParentWindowIsActive(mOwnerContent
->OwnerDoc()),
1099 if (!mRemoteBrowser
->Show(info
)) {
1102 mRemoteBrowserShown
= true;
1104 // This notification doesn't apply to fission, apparently.
1105 if (!GetBrowserBridgeChild()) {
1106 if (nsCOMPtr
<nsIObserverService
> os
= services::GetObserverService()) {
1107 os
->NotifyObservers(ToSupports(this), "remote-browser-shown", nullptr);
1111 nsIntRect dimensions
;
1112 NS_ENSURE_SUCCESS(GetWindowDimensions(dimensions
), false);
1114 // Don't show remote iframe if we are waiting for the completion of reflow.
1115 if (!aFrame
|| !(aFrame
->GetStateBits() & NS_FRAME_FIRST_REFLOW
)) {
1116 mRemoteBrowser
->UpdateDimensions(dimensions
, size
);
1123 void nsFrameLoader::Hide() {
1132 if (!GetDocShell()) {
1136 GetDocShell()->MaybeClearStorageAccessFlag();
1138 nsCOMPtr
<nsIContentViewer
> contentViewer
;
1139 GetDocShell()->GetContentViewer(getter_AddRefs(contentViewer
));
1140 if (contentViewer
) contentViewer
->SetSticky(false);
1142 RefPtr
<nsDocShell
> baseWin
= GetDocShell();
1143 baseWin
->SetVisibility(false);
1144 baseWin
->SetParentWidget(nullptr);
1147 void nsFrameLoader::ForceLayoutIfNecessary() {
1148 nsIFrame
* frame
= GetPrimaryFrameOfOwningContent();
1153 nsPresContext
* presContext
= frame
->PresContext();
1158 // Only force the layout flush if the frameloader hasn't ever been
1159 // run through layout.
1160 if (frame
->GetStateBits() & NS_FRAME_FIRST_REFLOW
) {
1161 if (RefPtr
<PresShell
> presShell
= presContext
->GetPresShell()) {
1162 presShell
->FlushPendingNotifications(FlushType::Layout
);
1167 nsresult
nsFrameLoader::SwapWithOtherRemoteLoader(
1168 nsFrameLoader
* aOther
, nsFrameLoaderOwner
* aThisOwner
,
1169 nsFrameLoaderOwner
* aOtherOwner
) {
1170 MOZ_ASSERT(NS_IsMainThread());
1173 RefPtr
<nsFrameLoader
> first
= aThisOwner
->GetFrameLoader();
1174 RefPtr
<nsFrameLoader
> second
= aOtherOwner
->GetFrameLoader();
1175 MOZ_ASSERT(first
== this, "aThisOwner must own this");
1176 MOZ_ASSERT(second
== aOther
, "aOtherOwner must own aOther");
1179 Element
* ourContent
= mOwnerContent
;
1180 Element
* otherContent
= aOther
->mOwnerContent
;
1182 if (!ourContent
|| !otherContent
) {
1183 // Can't handle this
1184 return NS_ERROR_NOT_IMPLEMENTED
;
1187 // Make sure there are no same-origin issues
1189 nsresult rv
= ourContent
->NodePrincipal()->Equals(
1190 otherContent
->NodePrincipal(), &equal
);
1191 if (NS_FAILED(rv
) || !equal
) {
1192 // Security problems loom. Just bail on it all
1193 return NS_ERROR_DOM_SECURITY_ERR
;
1196 Document
* ourDoc
= ourContent
->GetComposedDoc();
1197 Document
* otherDoc
= otherContent
->GetComposedDoc();
1198 if (!ourDoc
|| !otherDoc
) {
1199 // Again, how odd, given that we had docshells
1200 return NS_ERROR_NOT_IMPLEMENTED
;
1203 PresShell
* ourPresShell
= ourDoc
->GetPresShell();
1204 PresShell
* otherPresShell
= otherDoc
->GetPresShell();
1205 if (!ourPresShell
|| !otherPresShell
) {
1206 return NS_ERROR_NOT_IMPLEMENTED
;
1209 auto* browserParent
= GetBrowserParent();
1210 auto* otherBrowserParent
= aOther
->GetBrowserParent();
1212 if (!browserParent
|| !otherBrowserParent
) {
1213 return NS_ERROR_NOT_IMPLEMENTED
;
1216 RefPtr
<BrowsingContext
> ourBc
= browserParent
->GetBrowsingContext();
1217 RefPtr
<BrowsingContext
> otherBc
= otherBrowserParent
->GetBrowsingContext();
1219 // When we swap docShells, maybe we have to deal with a new page created just
1220 // for this operation. In this case, the browser code should already have set
1221 // the correct userContextId attribute value in the owning element, but our
1222 // docShell, that has been created way before) doesn't know that that
1224 // This is the reason why now we must retrieve the correct value from the
1225 // usercontextid attribute before comparing our originAttributes with the
1227 OriginAttributes ourOriginAttributes
= ourBc
->OriginAttributesRef();
1228 rv
= PopulateOriginContextIdsFromAttributes(ourOriginAttributes
);
1229 NS_ENSURE_SUCCESS(rv
, rv
);
1231 OriginAttributes otherOriginAttributes
= otherBc
->OriginAttributesRef();
1232 rv
= aOther
->PopulateOriginContextIdsFromAttributes(otherOriginAttributes
);
1233 NS_ENSURE_SUCCESS(rv
, rv
);
1235 if (!ourOriginAttributes
.EqualsIgnoringFPD(otherOriginAttributes
)) {
1236 return NS_ERROR_NOT_IMPLEMENTED
;
1239 bool ourHasHistory
=
1240 mIsTopLevelContent
&& ourContent
->IsXULElement(nsGkAtoms::browser
) &&
1241 !ourContent
->HasAttr(kNameSpaceID_None
, nsGkAtoms::disablehistory
);
1242 bool otherHasHistory
=
1243 aOther
->mIsTopLevelContent
&&
1244 otherContent
->IsXULElement(nsGkAtoms::browser
) &&
1245 !otherContent
->HasAttr(kNameSpaceID_None
, nsGkAtoms::disablehistory
);
1246 if (ourHasHistory
!= otherHasHistory
) {
1247 return NS_ERROR_NOT_IMPLEMENTED
;
1250 if (mInSwap
|| aOther
->mInSwap
) {
1251 return NS_ERROR_NOT_IMPLEMENTED
;
1253 mInSwap
= aOther
->mInSwap
= true;
1255 // NOTE(emilio): This doesn't have to flush because the caller does already.
1256 nsIFrame
* ourFrame
= ourContent
->GetPrimaryFrame();
1257 nsIFrame
* otherFrame
= otherContent
->GetPrimaryFrame();
1258 if (!ourFrame
|| !otherFrame
) {
1259 mInSwap
= aOther
->mInSwap
= false;
1260 return NS_ERROR_NOT_IMPLEMENTED
;
1263 nsSubDocumentFrame
* ourFrameFrame
= do_QueryFrame(ourFrame
);
1264 if (!ourFrameFrame
) {
1265 mInSwap
= aOther
->mInSwap
= false;
1266 return NS_ERROR_NOT_IMPLEMENTED
;
1269 rv
= ourFrameFrame
->BeginSwapDocShells(otherFrame
);
1270 if (NS_FAILED(rv
)) {
1271 mInSwap
= aOther
->mInSwap
= false;
1275 nsCOMPtr
<nsIBrowserDOMWindow
> otherBrowserDOMWindow
=
1276 otherBrowserParent
->GetBrowserDOMWindow();
1277 nsCOMPtr
<nsIBrowserDOMWindow
> browserDOMWindow
=
1278 browserParent
->GetBrowserDOMWindow();
1280 if (!!otherBrowserDOMWindow
!= !!browserDOMWindow
) {
1281 return NS_ERROR_NOT_IMPLEMENTED
;
1284 // Destroy browser frame scripts for content leaving a frame with browser API
1285 if (OwnerIsMozBrowserFrame() && !aOther
->OwnerIsMozBrowserFrame()) {
1286 DestroyBrowserFrameScripts();
1288 if (!OwnerIsMozBrowserFrame() && aOther
->OwnerIsMozBrowserFrame()) {
1289 aOther
->DestroyBrowserFrameScripts();
1292 otherBrowserParent
->SetBrowserDOMWindow(browserDOMWindow
);
1293 browserParent
->SetBrowserDOMWindow(otherBrowserDOMWindow
);
1296 // Native plugin windows used by this remote content need to be reparented.
1297 if (nsPIDOMWindowOuter
* newWin
= ourDoc
->GetWindow()) {
1298 RefPtr
<nsIWidget
> newParent
=
1299 nsGlobalWindowOuter::Cast(newWin
)->GetMainWidget();
1300 const ManagedContainer
<mozilla::plugins::PPluginWidgetParent
>& plugins
=
1301 otherBrowserParent
->ManagedPPluginWidgetParent();
1302 for (auto iter
= plugins
.ConstIter(); !iter
.Done(); iter
.Next()) {
1303 static_cast<mozilla::plugins::PluginWidgetParent
*>(iter
.Get()->GetKey())
1304 ->SetParent(newParent
);
1309 MaybeUpdatePrimaryBrowserParent(eBrowserParentRemoved
);
1310 aOther
->MaybeUpdatePrimaryBrowserParent(eBrowserParentRemoved
);
1312 SetOwnerContent(otherContent
);
1313 aOther
->SetOwnerContent(ourContent
);
1315 browserParent
->SetOwnerElement(otherContent
);
1316 otherBrowserParent
->SetOwnerElement(ourContent
);
1318 // Update window activation state for the swapped owner content.
1319 Unused
<< browserParent
->SendParentActivated(
1320 ParentWindowIsActive(otherContent
->OwnerDoc()));
1321 Unused
<< otherBrowserParent
->SendParentActivated(
1322 ParentWindowIsActive(ourContent
->OwnerDoc()));
1324 MaybeUpdatePrimaryBrowserParent(eBrowserParentChanged
);
1325 aOther
->MaybeUpdatePrimaryBrowserParent(eBrowserParentChanged
);
1327 RefPtr
<nsFrameMessageManager
> ourMessageManager
= mMessageManager
;
1328 RefPtr
<nsFrameMessageManager
> otherMessageManager
= aOther
->mMessageManager
;
1329 // Swap and setup things in parent message managers.
1330 if (ourMessageManager
) {
1331 ourMessageManager
->SetCallback(aOther
);
1333 if (otherMessageManager
) {
1334 otherMessageManager
->SetCallback(this);
1336 mMessageManager
.swap(aOther
->mMessageManager
);
1338 // Perform the actual swap of the internal refptrs. We keep a strong reference
1339 // to ourselves to make sure we don't die while we overwrite our reference to
1341 RefPtr
<nsFrameLoader
> kungFuDeathGrip(this);
1342 aThisOwner
->SetFrameLoader(aOther
);
1343 aOtherOwner
->SetFrameLoader(kungFuDeathGrip
);
1345 ourFrameFrame
->EndSwapDocShells(otherFrame
);
1347 ourPresShell
->BackingScaleFactorChanged();
1348 otherPresShell
->BackingScaleFactorChanged();
1350 // Initialize browser API if needed now that owner content has changed.
1351 InitializeBrowserAPI();
1352 aOther
->InitializeBrowserAPI();
1354 mInSwap
= aOther
->mInSwap
= false;
1356 // Send an updated tab context since owner content type may have changed.
1357 MutableTabContext ourContext
;
1358 rv
= GetNewTabContext(&ourContext
);
1359 if (NS_WARN_IF(NS_FAILED(rv
))) {
1362 MutableTabContext otherContext
;
1363 rv
= aOther
->GetNewTabContext(&otherContext
);
1364 if (NS_WARN_IF(NS_FAILED(rv
))) {
1368 Unused
<< browserParent
->SendSwappedWithOtherRemoteLoader(
1369 ourContext
.AsIPCTabContext());
1370 Unused
<< otherBrowserParent
->SendSwappedWithOtherRemoteLoader(
1371 otherContext
.AsIPCTabContext());
1375 class MOZ_RAII AutoResetInFrameSwap final
{
1377 AutoResetInFrameSwap(nsFrameLoader
* aThisFrameLoader
,
1378 nsFrameLoader
* aOtherFrameLoader
,
1379 nsDocShell
* aThisDocShell
, nsDocShell
* aOtherDocShell
,
1380 EventTarget
* aThisEventTarget
,
1381 EventTarget
* aOtherEventTarget
)
1382 : mThisFrameLoader(aThisFrameLoader
),
1383 mOtherFrameLoader(aOtherFrameLoader
),
1384 mThisDocShell(aThisDocShell
),
1385 mOtherDocShell(aOtherDocShell
),
1386 mThisEventTarget(aThisEventTarget
),
1387 mOtherEventTarget(aOtherEventTarget
) {
1388 mThisFrameLoader
->mInSwap
= true;
1389 mOtherFrameLoader
->mInSwap
= true;
1390 mThisDocShell
->SetInFrameSwap(true);
1391 mOtherDocShell
->SetInFrameSwap(true);
1393 // Fire pageshow events on still-loading pages, and then fire pagehide
1394 // events. Note that we do NOT fire these in the normal way, but just fire
1395 // them on the chrome event handlers.
1396 nsContentUtils::FirePageShowEventForFrameLoaderSwap(
1397 mThisDocShell
, mThisEventTarget
, false);
1398 nsContentUtils::FirePageShowEventForFrameLoaderSwap(
1399 mOtherDocShell
, mOtherEventTarget
, false);
1400 nsContentUtils::FirePageHideEventForFrameLoaderSwap(mThisDocShell
,
1402 nsContentUtils::FirePageHideEventForFrameLoaderSwap(mOtherDocShell
,
1406 ~AutoResetInFrameSwap() {
1407 nsContentUtils::FirePageShowEventForFrameLoaderSwap(mThisDocShell
,
1408 mThisEventTarget
, true);
1409 nsContentUtils::FirePageShowEventForFrameLoaderSwap(
1410 mOtherDocShell
, mOtherEventTarget
, true);
1412 mThisFrameLoader
->mInSwap
= false;
1413 mOtherFrameLoader
->mInSwap
= false;
1414 mThisDocShell
->SetInFrameSwap(false);
1415 mOtherDocShell
->SetInFrameSwap(false);
1417 // This is needed to get visibility state right in cases when we swapped a
1418 // visible tab (foreground in visible window) with a non-visible tab.
1419 if (RefPtr
<Document
> doc
= mThisDocShell
->GetDocument()) {
1420 doc
->UpdateVisibilityState();
1422 if (RefPtr
<Document
> doc
= mOtherDocShell
->GetDocument()) {
1423 doc
->UpdateVisibilityState();
1428 RefPtr
<nsFrameLoader
> mThisFrameLoader
;
1429 RefPtr
<nsFrameLoader
> mOtherFrameLoader
;
1430 RefPtr
<nsDocShell
> mThisDocShell
;
1431 RefPtr
<nsDocShell
> mOtherDocShell
;
1432 nsCOMPtr
<EventTarget
> mThisEventTarget
;
1433 nsCOMPtr
<EventTarget
> mOtherEventTarget
;
1436 nsresult
nsFrameLoader::SwapWithOtherLoader(nsFrameLoader
* aOther
,
1437 nsFrameLoaderOwner
* aThisOwner
,
1438 nsFrameLoaderOwner
* aOtherOwner
) {
1440 RefPtr
<nsFrameLoader
> first
= aThisOwner
->GetFrameLoader();
1441 RefPtr
<nsFrameLoader
> second
= aOtherOwner
->GetFrameLoader();
1442 MOZ_ASSERT(first
== this, "aThisOwner must own this");
1443 MOZ_ASSERT(second
== aOther
, "aOtherOwner must own aOther");
1446 NS_ENSURE_STATE(!mInShow
&& !aOther
->mInShow
);
1448 if (IsRemoteFrame() != aOther
->IsRemoteFrame()) {
1450 "Swapping remote and non-remote frames is not currently supported");
1451 return NS_ERROR_NOT_IMPLEMENTED
;
1454 RefPtr
<Element
> ourContent
= mOwnerContent
;
1455 RefPtr
<Element
> otherContent
= aOther
->mOwnerContent
;
1456 if (!ourContent
|| !otherContent
) {
1457 // Can't handle this
1458 return NS_ERROR_NOT_IMPLEMENTED
;
1461 nsIFrame
* ourFrame
= ourContent
->GetPrimaryFrame(FlushType::Frames
);
1462 nsIFrame
* otherFrame
= otherContent
->GetPrimaryFrame(FlushType::Frames
);
1463 if (!ourFrame
|| !otherFrame
) {
1464 return NS_ERROR_NOT_IMPLEMENTED
;
1467 // Ensure the flushes above haven't changed all the world.
1468 if (ourContent
!= mOwnerContent
|| otherContent
!= aOther
->mOwnerContent
) {
1469 return NS_ERROR_NOT_IMPLEMENTED
;
1472 bool ourHasSrcdoc
= ourContent
->IsHTMLElement(nsGkAtoms::iframe
) &&
1473 ourContent
->HasAttr(kNameSpaceID_None
, nsGkAtoms::srcdoc
);
1474 bool otherHasSrcdoc
=
1475 otherContent
->IsHTMLElement(nsGkAtoms::iframe
) &&
1476 otherContent
->HasAttr(kNameSpaceID_None
, nsGkAtoms::srcdoc
);
1477 if (ourHasSrcdoc
|| otherHasSrcdoc
) {
1478 // Ignore this case entirely for now, since we support XUL <-> HTML swapping
1479 return NS_ERROR_NOT_IMPLEMENTED
;
1482 bool ourFullscreenAllowed
= ourContent
->IsXULElement() ||
1483 (OwnerIsMozBrowserFrame() &&
1484 ourContent
->HasAttr(nsGkAtoms::allowfullscreen
));
1485 bool otherFullscreenAllowed
=
1486 otherContent
->IsXULElement() ||
1487 (aOther
->OwnerIsMozBrowserFrame() &&
1488 otherContent
->HasAttr(nsGkAtoms::allowfullscreen
));
1489 if (ourFullscreenAllowed
!= otherFullscreenAllowed
) {
1490 return NS_ERROR_NOT_IMPLEMENTED
;
1493 nsILoadContext
* ourLoadContext
= ourContent
->OwnerDoc()->GetLoadContext();
1494 nsILoadContext
* otherLoadContext
= otherContent
->OwnerDoc()->GetLoadContext();
1495 MOZ_ASSERT(ourLoadContext
&& otherLoadContext
,
1496 "Swapping frames within dead documents?");
1497 if (ourLoadContext
->UseRemoteTabs() != otherLoadContext
->UseRemoteTabs()) {
1498 NS_WARNING("Can't swap between e10s and non-e10s windows");
1499 return NS_ERROR_NOT_IMPLEMENTED
;
1501 if (ourLoadContext
->UseRemoteSubframes() !=
1502 otherLoadContext
->UseRemoteSubframes()) {
1503 NS_WARNING("Can't swap between fission and non-fission windows");
1504 return NS_ERROR_NOT_IMPLEMENTED
;
1507 // Divert to a separate path for the remaining steps in the remote case
1508 if (IsRemoteFrame()) {
1509 MOZ_ASSERT(aOther
->IsRemoteFrame());
1510 return SwapWithOtherRemoteLoader(aOther
, aThisOwner
, aOtherOwner
);
1513 // Make sure there are no same-origin issues
1515 nsresult rv
= ourContent
->NodePrincipal()->Equals(
1516 otherContent
->NodePrincipal(), &equal
);
1517 if (NS_FAILED(rv
) || !equal
) {
1518 // Security problems loom. Just bail on it all
1519 return NS_ERROR_DOM_SECURITY_ERR
;
1522 RefPtr
<nsDocShell
> ourDocshell
=
1523 static_cast<nsDocShell
*>(GetExistingDocShell());
1524 RefPtr
<nsDocShell
> otherDocshell
=
1525 static_cast<nsDocShell
*>(aOther
->GetExistingDocShell());
1526 if (!ourDocshell
|| !otherDocshell
) {
1528 return NS_ERROR_NOT_IMPLEMENTED
;
1531 // To avoid having to mess with session history, avoid swapping
1532 // frameloaders that don't correspond to root same-type docshells,
1533 // unless both roots have session history disabled.
1534 nsCOMPtr
<nsIDocShellTreeItem
> ourRootTreeItem
, otherRootTreeItem
;
1535 ourDocshell
->GetInProcessSameTypeRootTreeItem(
1536 getter_AddRefs(ourRootTreeItem
));
1537 otherDocshell
->GetInProcessSameTypeRootTreeItem(
1538 getter_AddRefs(otherRootTreeItem
));
1539 nsCOMPtr
<nsIWebNavigation
> ourRootWebnav
= do_QueryInterface(ourRootTreeItem
);
1540 nsCOMPtr
<nsIWebNavigation
> otherRootWebnav
=
1541 do_QueryInterface(otherRootTreeItem
);
1543 if (!ourRootWebnav
|| !otherRootWebnav
) {
1544 return NS_ERROR_NOT_IMPLEMENTED
;
1547 RefPtr
<ChildSHistory
> ourHistory
= ourRootWebnav
->GetSessionHistory();
1548 RefPtr
<ChildSHistory
> otherHistory
= otherRootWebnav
->GetSessionHistory();
1550 if ((ourRootTreeItem
!= ourDocshell
|| otherRootTreeItem
!= otherDocshell
) &&
1551 (ourHistory
|| otherHistory
)) {
1552 return NS_ERROR_NOT_IMPLEMENTED
;
1555 RefPtr
<BrowsingContext
> ourBc
= ourDocshell
->GetBrowsingContext();
1556 RefPtr
<BrowsingContext
> otherBc
= otherDocshell
->GetBrowsingContext();
1558 // Also make sure that the two BrowsingContexts are the same type. Otherwise
1559 // swapping is certainly not safe. If this needs to be changed then
1560 // the code below needs to be audited as it assumes identical types.
1561 if (ourBc
->GetType() != otherBc
->GetType()) {
1562 return NS_ERROR_NOT_IMPLEMENTED
;
1565 // We ensure that BCs are either both top frames or both subframes.
1566 if (ourBc
->IsTop() != otherBc
->IsTop()) {
1567 return NS_ERROR_NOT_IMPLEMENTED
;
1570 // One more twist here. Setting up the right treeowners in a heterogeneous
1571 // tree is a bit of a pain. So make sure that if `ourBc->GetType()` is not
1572 // nsIDocShellTreeItem::typeContent then all of our descendants are the same
1574 if (!ourBc
->IsContent() &&
1575 (!AllDescendantsOfType(ourBc
, ourBc
->GetType()) ||
1576 !AllDescendantsOfType(otherBc
, otherBc
->GetType()))) {
1577 return NS_ERROR_NOT_IMPLEMENTED
;
1580 // Save off the tree owners, frame elements, chrome event handlers, and
1581 // docshell and document parents before doing anything else.
1582 nsCOMPtr
<nsIDocShellTreeOwner
> ourOwner
, otherOwner
;
1583 ourDocshell
->GetTreeOwner(getter_AddRefs(ourOwner
));
1584 otherDocshell
->GetTreeOwner(getter_AddRefs(otherOwner
));
1585 // Note: it's OK to have null treeowners.
1587 nsCOMPtr
<nsIDocShellTreeItem
> ourParentItem
, otherParentItem
;
1588 ourDocshell
->GetInProcessParent(getter_AddRefs(ourParentItem
));
1589 otherDocshell
->GetInProcessParent(getter_AddRefs(otherParentItem
));
1590 if (!ourParentItem
|| !otherParentItem
) {
1591 return NS_ERROR_NOT_IMPLEMENTED
;
1594 nsCOMPtr
<nsPIDOMWindowOuter
> ourWindow
= ourDocshell
->GetWindow();
1595 nsCOMPtr
<nsPIDOMWindowOuter
> otherWindow
= otherDocshell
->GetWindow();
1597 nsCOMPtr
<Element
> ourFrameElement
= ourWindow
->GetFrameElementInternal();
1598 nsCOMPtr
<Element
> otherFrameElement
= otherWindow
->GetFrameElementInternal();
1600 nsCOMPtr
<EventTarget
> ourChromeEventHandler
=
1601 ourWindow
->GetChromeEventHandler();
1602 nsCOMPtr
<EventTarget
> otherChromeEventHandler
=
1603 otherWindow
->GetChromeEventHandler();
1605 nsCOMPtr
<EventTarget
> ourEventTarget
= ourWindow
->GetParentTarget();
1606 nsCOMPtr
<EventTarget
> otherEventTarget
= otherWindow
->GetParentTarget();
1608 NS_ASSERTION(SameCOMIdentity(ourFrameElement
, ourContent
) &&
1609 SameCOMIdentity(otherFrameElement
, otherContent
) &&
1610 SameCOMIdentity(ourChromeEventHandler
, ourContent
) &&
1611 SameCOMIdentity(otherChromeEventHandler
, otherContent
),
1612 "How did that happen, exactly?");
1614 nsCOMPtr
<Document
> ourChildDocument
= ourWindow
->GetExtantDoc();
1615 nsCOMPtr
<Document
> otherChildDocument
= otherWindow
->GetExtantDoc();
1616 if (!ourChildDocument
|| !otherChildDocument
) {
1617 // This shouldn't be happening
1618 return NS_ERROR_NOT_IMPLEMENTED
;
1621 nsCOMPtr
<Document
> ourParentDocument
=
1622 ourChildDocument
->GetInProcessParentDocument();
1623 nsCOMPtr
<Document
> otherParentDocument
=
1624 otherChildDocument
->GetInProcessParentDocument();
1626 // Make sure to swap docshells between the two frames.
1627 Document
* ourDoc
= ourContent
->GetComposedDoc();
1628 Document
* otherDoc
= otherContent
->GetComposedDoc();
1629 if (!ourDoc
|| !otherDoc
) {
1630 // Again, how odd, given that we had docshells
1631 return NS_ERROR_NOT_IMPLEMENTED
;
1634 NS_ASSERTION(ourDoc
== ourParentDocument
, "Unexpected parent document");
1635 NS_ASSERTION(otherDoc
== otherParentDocument
, "Unexpected parent document");
1637 PresShell
* ourPresShell
= ourDoc
->GetPresShell();
1638 PresShell
* otherPresShell
= otherDoc
->GetPresShell();
1639 if (!ourPresShell
|| !otherPresShell
) {
1640 return NS_ERROR_NOT_IMPLEMENTED
;
1643 // When we swap docShells, maybe we have to deal with a new page created just
1644 // for this operation. In this case, the browser code should already have set
1645 // the correct userContextId attribute value in the owning element, but our
1646 // docShell, that has been created way before) doesn't know that that
1648 // This is the reason why now we must retrieve the correct value from the
1649 // usercontextid attribute before comparing our originAttributes with the
1651 OriginAttributes ourOriginAttributes
= ourDocshell
->GetOriginAttributes();
1652 rv
= PopulateOriginContextIdsFromAttributes(ourOriginAttributes
);
1653 NS_ENSURE_SUCCESS(rv
, rv
);
1655 OriginAttributes otherOriginAttributes
= otherDocshell
->GetOriginAttributes();
1656 rv
= aOther
->PopulateOriginContextIdsFromAttributes(otherOriginAttributes
);
1657 NS_ENSURE_SUCCESS(rv
, rv
);
1659 if (ourOriginAttributes
!= otherOriginAttributes
) {
1660 return NS_ERROR_NOT_IMPLEMENTED
;
1663 if (mInSwap
|| aOther
->mInSwap
) {
1664 return NS_ERROR_NOT_IMPLEMENTED
;
1666 AutoResetInFrameSwap
autoFrameSwap(this, aOther
, ourDocshell
, otherDocshell
,
1667 ourEventTarget
, otherEventTarget
);
1669 nsSubDocumentFrame
* ourFrameFrame
= do_QueryFrame(ourFrame
);
1670 if (!ourFrameFrame
) {
1671 return NS_ERROR_NOT_IMPLEMENTED
;
1674 // OK. First begin to swap the docshells in the two nsIFrames
1675 rv
= ourFrameFrame
->BeginSwapDocShells(otherFrame
);
1676 if (NS_FAILED(rv
)) {
1680 // Destroy browser frame scripts for content leaving a frame with browser API
1681 if (OwnerIsMozBrowserFrame() && !aOther
->OwnerIsMozBrowserFrame()) {
1682 DestroyBrowserFrameScripts();
1684 if (!OwnerIsMozBrowserFrame() && aOther
->OwnerIsMozBrowserFrame()) {
1685 aOther
->DestroyBrowserFrameScripts();
1688 // Now move the docshells to the right docshell trees. Note that this
1689 // resets their treeowners to null.
1690 ourParentItem
->RemoveChild(ourDocshell
);
1691 otherParentItem
->RemoveChild(otherDocshell
);
1692 if (ourBc
->IsContent()) {
1693 ourOwner
->ContentShellRemoved(ourDocshell
);
1694 otherOwner
->ContentShellRemoved(otherDocshell
);
1697 ourParentItem
->AddChild(otherDocshell
);
1698 otherParentItem
->AddChild(ourDocshell
);
1700 // Restore the correct chrome event handlers.
1701 ourDocshell
->SetChromeEventHandler(otherChromeEventHandler
);
1702 otherDocshell
->SetChromeEventHandler(ourChromeEventHandler
);
1703 // Restore the correct treeowners
1704 // (and also chrome event handlers for content frames only).
1705 SetTreeOwnerAndChromeEventHandlerOnDocshellTree(
1706 ourDocshell
, otherOwner
,
1707 ourBc
->IsContent() ? otherChromeEventHandler
.get() : nullptr);
1708 SetTreeOwnerAndChromeEventHandlerOnDocshellTree(
1709 otherDocshell
, ourOwner
,
1710 ourBc
->IsContent() ? ourChromeEventHandler
.get() : nullptr);
1712 // Switch the owner content before we start calling AddTreeItemToTreeOwner.
1713 // Note that we rely on this to deal with setting mObservingOwnerContent to
1714 // false and calling RemoveMutationObserver as needed.
1715 SetOwnerContent(otherContent
);
1716 aOther
->SetOwnerContent(ourContent
);
1718 AddTreeItemToTreeOwner(ourDocshell
, otherOwner
);
1719 aOther
->AddTreeItemToTreeOwner(otherDocshell
, ourOwner
);
1721 // SetSubDocumentFor nulls out parent documents on the old child doc if a
1722 // new non-null document is passed in, so just go ahead and remove both
1723 // kids before reinserting in the parent subdoc maps, to avoid
1725 ourParentDocument
->SetSubDocumentFor(ourContent
, nullptr);
1726 otherParentDocument
->SetSubDocumentFor(otherContent
, nullptr);
1727 ourParentDocument
->SetSubDocumentFor(ourContent
, otherChildDocument
);
1728 otherParentDocument
->SetSubDocumentFor(otherContent
, ourChildDocument
);
1730 ourWindow
->SetFrameElementInternal(otherFrameElement
);
1731 otherWindow
->SetFrameElementInternal(ourFrameElement
);
1733 RefPtr
<nsFrameMessageManager
> ourMessageManager
= mMessageManager
;
1734 RefPtr
<nsFrameMessageManager
> otherMessageManager
= aOther
->mMessageManager
;
1735 // Swap pointers in child message managers.
1736 if (mChildMessageManager
) {
1737 InProcessBrowserChildMessageManager
* browserChild
= mChildMessageManager
;
1738 browserChild
->SetOwner(otherContent
);
1739 browserChild
->SetChromeMessageManager(otherMessageManager
);
1741 if (aOther
->mChildMessageManager
) {
1742 InProcessBrowserChildMessageManager
* otherBrowserChild
=
1743 aOther
->mChildMessageManager
;
1744 otherBrowserChild
->SetOwner(ourContent
);
1745 otherBrowserChild
->SetChromeMessageManager(ourMessageManager
);
1747 // Swap and setup things in parent message managers.
1748 if (mMessageManager
) {
1749 mMessageManager
->SetCallback(aOther
);
1751 if (aOther
->mMessageManager
) {
1752 aOther
->mMessageManager
->SetCallback(this);
1754 mMessageManager
.swap(aOther
->mMessageManager
);
1756 // Perform the actual swap of the internal refptrs. We keep a strong reference
1757 // to ourselves to make sure we don't die while we overwrite our reference to
1759 RefPtr
<nsFrameLoader
> kungFuDeathGrip(this);
1760 aThisOwner
->SetFrameLoader(aOther
);
1761 aOtherOwner
->SetFrameLoader(kungFuDeathGrip
);
1763 // Drop any cached content viewers in the two session histories.
1765 ourHistory
->EvictLocalContentViewers();
1768 otherHistory
->EvictLocalContentViewers();
1771 NS_ASSERTION(ourFrame
== ourContent
->GetPrimaryFrame() &&
1772 otherFrame
== otherContent
->GetPrimaryFrame(),
1773 "changed primary frame");
1775 ourFrameFrame
->EndSwapDocShells(otherFrame
);
1777 // If the content being swapped came from windows on two screens with
1778 // incompatible backing resolution (e.g. dragging a tab between windows on
1779 // hi-dpi and low-dpi screens), it will have style data that is based on
1780 // the wrong appUnitsPerDevPixel value. So we tell the PresShells that their
1781 // backing scale factor may have changed. (Bug 822266)
1782 ourFrame
->PresShell()->BackingScaleFactorChanged();
1783 otherFrame
->PresShell()->BackingScaleFactorChanged();
1785 // Initialize browser API if needed now that owner content has changed
1786 InitializeBrowserAPI();
1787 aOther
->InitializeBrowserAPI();
1792 void nsFrameLoader::Destroy(bool aForProcessSwitch
) {
1793 StartDestroy(aForProcessSwitch
);
1796 class nsFrameLoaderDestroyRunnable
: public Runnable
{
1798 // See the implementation of Run for an explanation of these phases.
1800 eWaitForUnloadMessage
,
1804 RefPtr
<nsFrameLoader
> mFrameLoader
;
1805 DestroyPhase mPhase
;
1808 explicit nsFrameLoaderDestroyRunnable(nsFrameLoader
* aFrameLoader
)
1809 : mozilla::Runnable("nsFrameLoaderDestroyRunnable"),
1810 mFrameLoader(aFrameLoader
),
1811 mPhase(eDestroyDocShell
) {}
1813 NS_IMETHOD
Run() override
;
1816 void nsFrameLoader::StartDestroy(bool aForProcessSwitch
) {
1817 // nsFrameLoader::StartDestroy is called just before the frameloader is
1818 // detached from the <browser> element. Destruction continues in phases via
1819 // the nsFrameLoaderDestroyRunnable.
1821 if (mDestroyCalled
) {
1824 mDestroyCalled
= true;
1826 // request a tabStateFlush before tab is closed
1827 RequestTabStateFlush(/*flushId*/ 0, /*isFinal*/ true);
1829 // After this point, we return an error when trying to send a message using
1830 // the message manager on the frame.
1831 if (mMessageManager
) {
1832 mMessageManager
->Close();
1835 // Retain references to the <browser> element and the frameloader in case we
1836 // receive any messages from the message manager on the frame. These
1837 // references are dropped in DestroyComplete.
1838 if (mChildMessageManager
|| mRemoteBrowser
) {
1839 mOwnerContentStrong
= mOwnerContent
;
1840 if (auto* browserParent
= GetBrowserParent()) {
1841 browserParent
->CacheFrameLoader(this);
1843 if (mChildMessageManager
) {
1844 mChildMessageManager
->CacheFrameLoader(this);
1848 // If the BrowserParent has installed any event listeners on the window, this
1849 // is its last chance to remove them while we're still in the document.
1850 if (auto* browserParent
= GetBrowserParent()) {
1851 browserParent
->RemoveWindowListeners();
1852 if (aForProcessSwitch
) {
1853 // This should suspend all future progress events from this BrowserParent,
1854 // since we're going to tear it down after stopping the docshell in it.
1855 browserParent
->SuspendProgressEventsUntilAfterNextLoadStarts();
1859 nsCOMPtr
<Document
> doc
;
1860 bool dynamicSubframeRemoval
= false;
1861 if (mOwnerContent
) {
1862 doc
= mOwnerContent
->OwnerDoc();
1863 dynamicSubframeRemoval
= !mIsTopLevelContent
&& !doc
->InUnlinkOrDeletion();
1864 doc
->SetSubDocumentFor(mOwnerContent
, nullptr);
1865 MaybeUpdatePrimaryBrowserParent(eBrowserParentRemoved
);
1866 SetOwnerContent(nullptr);
1869 // Seems like this is a dynamic frame removal.
1870 if (dynamicSubframeRemoval
) {
1871 BrowsingContext
* browsingContext
= GetExtantBrowsingContext();
1872 if (browsingContext
) {
1873 RefPtr
<ChildSHistory
> childSHistory
=
1874 browsingContext
->Top()->GetChildSessionHistory();
1875 if (childSHistory
) {
1876 if (mozilla::SessionHistoryInParent()) {
1877 browsingContext
->RemoveFromSessionHistory();
1879 AutoTArray
<nsID
, 16> ids({browsingContext
->GetHistoryID()});
1880 childSHistory
->LegacySHistory()->RemoveEntries(
1881 ids
, childSHistory
->Index());
1887 // Let the tree owner know we're gone.
1888 if (mIsTopLevelContent
) {
1889 if (GetDocShell()) {
1890 nsCOMPtr
<nsIDocShellTreeItem
> parentItem
;
1891 GetDocShell()->GetInProcessParent(getter_AddRefs(parentItem
));
1892 nsCOMPtr
<nsIDocShellTreeOwner
> owner
= do_GetInterface(parentItem
);
1894 owner
->ContentShellRemoved(GetDocShell());
1899 // Let our window know that we are gone
1900 if (GetDocShell()) {
1901 nsCOMPtr
<nsPIDOMWindowOuter
> win_private(GetDocShell()->GetWindow());
1903 win_private
->SetFrameElementInternal(nullptr);
1907 nsCOMPtr
<nsIRunnable
> destroyRunnable
=
1908 new nsFrameLoaderDestroyRunnable(this);
1909 if (mNeedsAsyncDestroy
|| !doc
||
1910 NS_FAILED(doc
->FinalizeFrameLoader(this, destroyRunnable
))) {
1911 NS_DispatchToCurrentThread(destroyRunnable
);
1915 nsresult
nsFrameLoaderDestroyRunnable::Run() {
1917 case eDestroyDocShell
:
1918 mFrameLoader
->DestroyDocShell();
1920 // In the out-of-process case, BrowserParent will eventually call
1921 // DestroyComplete once it receives a __delete__ message from the child.
1922 // In the in-process case, we dispatch a series of runnables to ensure
1923 // that DestroyComplete gets called at the right time. The frame loader is
1924 // kept alive by mFrameLoader during this time.
1925 if (mFrameLoader
->mChildMessageManager
) {
1926 // When the docshell is destroyed, NotifyWindowIDDestroyed is called to
1927 // asynchronously notify {outer,inner}-window-destroyed via a runnable.
1928 // We don't want DestroyComplete to run until after those runnables have
1929 // run. Since we're enqueueing ourselves after the window-destroyed
1930 // runnables are enqueued, we're guaranteed to run after.
1931 mPhase
= eWaitForUnloadMessage
;
1932 NS_DispatchToCurrentThread(this);
1936 case eWaitForUnloadMessage
:
1937 // The *-window-destroyed observers have finished running at this
1938 // point. However, it's possible that a *-window-destroyed observer might
1939 // have sent a message using the message manager. These messages might not
1940 // have been processed yet. So we enqueue ourselves again to ensure that
1941 // DestroyComplete runs after all messages sent by *-window-destroyed
1942 // observers have been processed.
1943 mPhase
= eDestroyComplete
;
1944 NS_DispatchToCurrentThread(this);
1947 case eDestroyComplete
:
1948 // Now that all messages sent by unload listeners and window destroyed
1949 // observers have been processed, we disconnect the message manager and
1950 // finish destruction.
1951 mFrameLoader
->DestroyComplete();
1958 void nsFrameLoader::DestroyDocShell() {
1959 // This code runs after the frameloader has been detached from the <browser>
1960 // element. We postpone this work because we may not be allowed to run
1961 // script at that time.
1963 // Ask the BrowserChild to fire the frame script "unload" event, destroy its
1964 // docshell, and finally destroy the PBrowser actor. This eventually leads to
1965 // nsFrameLoader::DestroyComplete being called.
1966 if (mRemoteBrowser
) {
1967 mRemoteBrowser
->DestroyStart();
1970 // Fire the "unload" event if we're in-process.
1971 if (mChildMessageManager
) {
1972 mChildMessageManager
->FireUnloadEvent();
1975 if (mSessionStoreListener
) {
1976 mSessionStoreListener
->RemoveListeners();
1977 mSessionStoreListener
= nullptr;
1980 // Destroy the docshell.
1981 if (GetDocShell()) {
1982 GetDocShell()->Destroy();
1985 if (!mWillChangeProcess
&& mPendingBrowsingContext
&&
1986 mPendingBrowsingContext
->EverAttached()) {
1987 mPendingBrowsingContext
->Detach();
1990 mPendingBrowsingContext
= nullptr;
1991 mDocShell
= nullptr;
1993 if (mChildMessageManager
) {
1994 // Stop handling events in the in-process frame script.
1995 mChildMessageManager
->DisconnectEventListeners();
1999 void nsFrameLoader::DestroyComplete() {
2000 // We get here, as part of StartDestroy, after the docshell has been destroyed
2001 // and all message manager messages sent during docshell destruction have been
2002 // dispatched. We also get here if the child process crashes. In the latter
2003 // case, StartDestroy might not have been called.
2005 // Drop the strong references created in StartDestroy.
2006 if (mChildMessageManager
|| mRemoteBrowser
) {
2007 mOwnerContentStrong
= nullptr;
2008 if (auto* browserParent
= GetBrowserParent()) {
2009 browserParent
->CacheFrameLoader(nullptr);
2011 if (mChildMessageManager
) {
2012 mChildMessageManager
->CacheFrameLoader(nullptr);
2016 // Call BrowserParent::Destroy if we haven't already (in case of a crash).
2017 if (mRemoteBrowser
) {
2018 mRemoteBrowser
->DestroyComplete();
2019 mRemoteBrowser
= nullptr;
2022 if (mMessageManager
) {
2023 mMessageManager
->Disconnect();
2026 if (mChildMessageManager
) {
2027 mChildMessageManager
->Disconnect();
2030 mMessageManager
= nullptr;
2031 mChildMessageManager
= nullptr;
2034 void nsFrameLoader::SetOwnerContent(Element
* aContent
) {
2035 if (mObservingOwnerContent
) {
2036 mObservingOwnerContent
= false;
2037 mOwnerContent
->RemoveMutationObserver(this);
2039 mOwnerContent
= aContent
;
2041 if (RefPtr
<BrowsingContext
> browsingContext
= GetExtantBrowsingContext()) {
2042 browsingContext
->SetEmbedderElement(mOwnerContent
);
2048 JS::RootedObject
wrapper(jsapi
.cx(), GetWrapper());
2050 JSAutoRealm
ar(jsapi
.cx(), wrapper
);
2051 IgnoredErrorResult rv
;
2052 UpdateReflectorGlobal(jsapi
.cx(), wrapper
, rv
);
2053 Unused
<< NS_WARN_IF(rv
.Failed());
2057 bool nsFrameLoader::OwnerIsMozBrowserFrame() {
2058 nsCOMPtr
<nsIMozBrowserFrame
> browserFrame
= do_QueryInterface(mOwnerContent
);
2059 return browserFrame
? browserFrame
->GetReallyIsBrowser() : false;
2062 void nsFrameLoader::AssertSafeToInit() {
2063 MOZ_DIAGNOSTIC_ASSERT(nsContentUtils::IsSafeToRunScript() ||
2064 mOwnerContent
->OwnerDoc()->IsStaticDocument(),
2065 "FrameLoader should never be initialized during "
2066 "document update or reflow!");
2069 nsresult
nsFrameLoader::MaybeCreateDocShell() {
2070 if (GetDocShell()) {
2073 if (IsRemoteFrame()) {
2076 NS_ENSURE_STATE(!mDestroyCalled
);
2080 // Get our parent docshell off the document of mOwnerContent
2081 // XXXbz this is such a total hack.... We really need to have a
2082 // better setup for doing this.
2083 Document
* doc
= mOwnerContent
->OwnerDoc();
2085 MOZ_RELEASE_ASSERT(!doc
->IsResourceDoc(), "We shouldn't even exist");
2087 // Check if the document still has a window since it is possible for an
2088 // iframe to be inserted and cause the creation of the docshell in a
2089 // partially unloaded document (see Bug 1305237 comment 127).
2090 if (!doc
->IsStaticDocument() &&
2091 (!doc
->GetWindow() || !mOwnerContent
->IsInComposedDoc())) {
2092 return NS_ERROR_UNEXPECTED
;
2095 if (!doc
->IsActive()) {
2096 // Don't allow subframe loads in non-active documents.
2097 // (See bug 610571 comment 5.)
2098 return NS_ERROR_NOT_AVAILABLE
;
2101 RefPtr
<nsDocShell
> parentDocShell
= nsDocShell::Cast(doc
->GetDocShell());
2102 if (NS_WARN_IF(!parentDocShell
)) {
2103 return NS_ERROR_UNEXPECTED
;
2106 if (doc
->GetWindowContext()->IsDiscarded() ||
2107 parentDocShell
->GetBrowsingContext()->IsDiscarded()) {
2108 // Don't allow subframe loads in discarded contexts.
2109 // (see bug 1652085, bug 1656854)
2110 return NS_ERROR_NOT_AVAILABLE
;
2113 if (!EnsureBrowsingContextAttached()) {
2114 return NS_ERROR_FAILURE
;
2117 mPendingBrowsingContext
->SetEmbedderElement(mOwnerContent
);
2119 // nsDocShell::Create will attach itself to the passed browsing
2120 // context inside of nsDocShell::Create
2121 RefPtr
<nsDocShell
> docShell
= nsDocShell::Create(mPendingBrowsingContext
);
2122 NS_ENSURE_TRUE(docShell
, NS_ERROR_FAILURE
);
2123 mDocShell
= docShell
;
2125 mPendingBrowsingContext
->Embed();
2127 InvokeBrowsingContextReadyCallback();
2129 mIsTopLevelContent
= mPendingBrowsingContext
->IsTopContent();
2131 if (mIsTopLevelContent
) {
2132 // Manually add ourselves to our parent's docshell, as BrowsingContext won't
2133 // have done this for us.
2135 // XXX(nika): Consider removing the DocShellTree in the future, for
2136 // consistency between local and remote frames..
2137 parentDocShell
->AddChild(docShell
);
2140 // Now that we are part of the DocShellTree, attach our DocShell to our
2141 // parent's TreeOwner.
2142 nsCOMPtr
<nsIDocShellTreeOwner
> parentTreeOwner
;
2143 parentDocShell
->GetTreeOwner(getter_AddRefs(parentTreeOwner
));
2144 AddTreeItemToTreeOwner(docShell
, parentTreeOwner
);
2146 // Make sure all nsDocShells have links back to the content element in the
2147 // nearest enclosing chrome shell.
2148 RefPtr
<EventTarget
> chromeEventHandler
;
2149 bool parentIsContent
= parentDocShell
->GetBrowsingContext()->IsContent();
2150 if (parentIsContent
) {
2151 // Our parent shell is a content shell. Get the chrome event handler from it
2152 // and use that for our shell as well.
2153 parentDocShell
->GetChromeEventHandler(getter_AddRefs(chromeEventHandler
));
2155 // Our parent shell is a chrome shell. It is therefore our nearest enclosing
2157 chromeEventHandler
= mOwnerContent
;
2160 docShell
->SetChromeEventHandler(chromeEventHandler
);
2162 // This is nasty, this code (the docShell->GetWindow() below)
2163 // *must* come *after* the above call to
2164 // docShell->SetChromeEventHandler() for the global window to get
2165 // the right chrome event handler.
2167 // Tell the window about the frame that hosts it.
2168 nsCOMPtr
<nsPIDOMWindowOuter
> newWindow
= docShell
->GetWindow();
2169 if (NS_WARN_IF(!newWindow
)) {
2170 // Do not call Destroy() here. See bug 472312.
2171 NS_WARNING("Something wrong when creating the docshell for a frameloader!");
2172 return NS_ERROR_FAILURE
;
2175 newWindow
->SetFrameElementInternal(mOwnerContent
);
2177 // Allow scripts to close the docshell if specified.
2178 if (mOwnerContent
->IsXULElement(nsGkAtoms::browser
) &&
2179 mOwnerContent
->AttrValueIs(kNameSpaceID_None
,
2180 nsGkAtoms::allowscriptstoclose
,
2181 nsGkAtoms::_true
, eCaseMatters
)) {
2182 nsGlobalWindowOuter::Cast(newWindow
)->AllowScriptsToClose();
2185 if (!docShell
->Initialize()) {
2186 // Do not call Destroy() here. See bug 472312.
2187 NS_WARNING("Something wrong when creating the docshell for a frameloader!");
2188 return NS_ERROR_FAILURE
;
2191 NS_ENSURE_STATE(mOwnerContent
);
2193 // If we are an in-process browser, we want to set up our session history.
2194 if (mIsTopLevelContent
&& mOwnerContent
->IsXULElement(nsGkAtoms::browser
) &&
2195 !mOwnerContent
->HasAttr(kNameSpaceID_None
, nsGkAtoms::disablehistory
)) {
2196 // XXX(nika): Set this up more explicitly?
2197 mPendingBrowsingContext
->InitSessionHistory();
2200 // Apply sandbox flags even if our owner is not an iframe, as this copies
2201 // flags from our owning content's owning document.
2202 // Note: ApplySandboxFlags should be called after docShell->SetIsFrame
2203 // because we need to get the correct presentation URL in ApplySandboxFlags.
2204 uint32_t sandboxFlags
= 0;
2205 HTMLIFrameElement
* iframe
= HTMLIFrameElement::FromNode(mOwnerContent
);
2207 sandboxFlags
= iframe
->GetSandboxFlags();
2209 ApplySandboxFlags(sandboxFlags
);
2211 if (OwnerIsMozBrowserFrame()) {
2212 // For inproc frames, set the docshell properties.
2214 if (mOwnerContent
->GetAttr(kNameSpaceID_None
, nsGkAtoms::name
, name
)) {
2215 docShell
->SetName(name
);
2219 ReallyLoadFrameScripts();
2220 InitializeBrowserAPI();
2225 void nsFrameLoader::GetURL(nsString
& aURI
, nsIPrincipal
** aTriggeringPrincipal
,
2226 nsIContentSecurityPolicy
** aCsp
) {
2228 // Within this function we default to using the NodePrincipal as the
2229 // triggeringPrincipal and the CSP of the document.
2230 // Expanded Principals however override the CSP of the document, hence
2231 // if frame->GetSrcTriggeringPrincipal() returns a valid principal, we
2232 // have to query the CSP from that Principal.
2233 nsCOMPtr
<nsIPrincipal
> triggeringPrincipal
= mOwnerContent
->NodePrincipal();
2234 nsCOMPtr
<nsIContentSecurityPolicy
> csp
= mOwnerContent
->GetCsp();
2236 if (mOwnerContent
->IsHTMLElement(nsGkAtoms::object
)) {
2237 mOwnerContent
->GetAttr(kNameSpaceID_None
, nsGkAtoms::data
, aURI
);
2239 mOwnerContent
->GetAttr(kNameSpaceID_None
, nsGkAtoms::src
, aURI
);
2240 if (RefPtr
<nsGenericHTMLFrameElement
> frame
=
2241 do_QueryObject(mOwnerContent
)) {
2242 nsCOMPtr
<nsIPrincipal
> srcPrincipal
= frame
->GetSrcTriggeringPrincipal();
2244 triggeringPrincipal
= srcPrincipal
;
2245 nsCOMPtr
<nsIExpandedPrincipal
> ep
=
2246 do_QueryInterface(triggeringPrincipal
);
2253 triggeringPrincipal
.forget(aTriggeringPrincipal
);
2257 nsresult
nsFrameLoader::CheckForRecursiveLoad(nsIURI
* aURI
) {
2260 MOZ_ASSERT(!IsRemoteFrame(),
2261 "Shouldn't call CheckForRecursiveLoad on remote frames.");
2263 mDepthTooGreat
= false;
2264 RefPtr
<BrowsingContext
> parentBC(
2265 mOwnerContent
->OwnerDoc()->GetBrowsingContext());
2266 MOZ_ASSERT(parentBC
, "How can we not have a parent here?");
2268 if (!parentBC
->IsContent()) {
2272 // Bug 8065: Don't exceed some maximum depth in content frames
2273 // (MAX_DEPTH_CONTENT_FRAMES)
2275 for (BrowsingContext
* bc
= parentBC
; bc
; bc
= bc
->GetParent()) {
2277 if (depth
>= MAX_DEPTH_CONTENT_FRAMES
) {
2278 mDepthTooGreat
= true;
2279 NS_WARNING("Too many nested content frames so giving up");
2281 return NS_ERROR_UNEXPECTED
; // Too deep, give up! (silently?)
2285 // Bug 136580: Check for recursive frame loading excluding about:srcdoc URIs.
2286 // srcdoc URIs require their contents to be specified inline, so it isn't
2287 // possible for undesirable recursion to occur without the aid of a
2288 // non-srcdoc URI, which this method will block normally.
2289 // Besides, URI is not enough to guarantee uniqueness of srcdoc documents.
2290 nsAutoCString buffer
;
2291 rv
= aURI
->GetScheme(buffer
);
2292 if (NS_SUCCEEDED(rv
) && buffer
.EqualsLiteral("about")) {
2293 rv
= aURI
->GetPathQueryRef(buffer
);
2294 if (NS_SUCCEEDED(rv
) && buffer
.EqualsLiteral("srcdoc")) {
2295 // Duplicates allowed up to depth limits
2299 int32_t matchCount
= 0;
2300 for (BrowsingContext
* bc
= parentBC
; bc
; bc
= bc
->GetParent()) {
2301 // Check the parent URI with the URI we're loading
2302 if (auto* docShell
= nsDocShell::Cast(bc
->GetDocShell())) {
2303 // Does the URI match the one we're about to load?
2304 nsCOMPtr
<nsIURI
> parentURI
;
2305 docShell
->GetCurrentURI(getter_AddRefs(parentURI
));
2307 // Bug 98158/193011: We need to ignore data after the #
2309 rv
= aURI
->EqualsExceptRef(parentURI
, &equal
);
2310 NS_ENSURE_SUCCESS(rv
, rv
);
2314 if (matchCount
>= MAX_SAME_URL_CONTENT_FRAMES
) {
2316 "Too many nested content frames have the same url (recursion?) "
2318 return NS_ERROR_UNEXPECTED
;
2328 nsresult
nsFrameLoader::GetWindowDimensions(nsIntRect
& aRect
) {
2329 if (!mOwnerContent
) {
2330 return NS_ERROR_FAILURE
;
2333 // Need to get outer window position here
2334 Document
* doc
= mOwnerContent
->GetComposedDoc();
2336 return NS_ERROR_FAILURE
;
2339 MOZ_RELEASE_ASSERT(!doc
->IsResourceDoc(), "We shouldn't even exist");
2341 nsCOMPtr
<nsPIDOMWindowOuter
> win
= doc
->GetWindow();
2343 return NS_ERROR_FAILURE
;
2346 nsCOMPtr
<nsIDocShellTreeItem
> parentAsItem(win
->GetDocShell());
2347 if (!parentAsItem
) {
2348 return NS_ERROR_FAILURE
;
2351 nsCOMPtr
<nsIDocShellTreeOwner
> parentOwner
;
2352 if (NS_FAILED(parentAsItem
->GetTreeOwner(getter_AddRefs(parentOwner
))) ||
2354 return NS_ERROR_FAILURE
;
2357 nsCOMPtr
<nsIBaseWindow
> treeOwnerAsWin(do_GetInterface(parentOwner
));
2358 treeOwnerAsWin
->GetPosition(&aRect
.x
, &aRect
.y
);
2359 treeOwnerAsWin
->GetSize(&aRect
.width
, &aRect
.height
);
2363 nsresult
nsFrameLoader::UpdatePositionAndSize(nsSubDocumentFrame
* aIFrame
) {
2364 if (IsRemoteFrame()) {
2365 if (mRemoteBrowser
) {
2366 ScreenIntSize size
= aIFrame
->GetSubdocumentSize();
2367 // If we were not able to show remote frame before, we should probably
2368 // retry now to send correct showInfo.
2369 if (!mRemoteBrowserShown
) {
2370 ShowRemoteFrame(size
, aIFrame
);
2372 nsIntRect dimensions
;
2373 NS_ENSURE_SUCCESS(GetWindowDimensions(dimensions
), NS_ERROR_FAILURE
);
2375 mRemoteBrowser
->UpdateDimensions(dimensions
, size
);
2379 UpdateBaseWindowPositionAndSize(aIFrame
);
2383 void nsFrameLoader::SendIsUnderHiddenEmbedderElement(
2384 bool aIsUnderHiddenEmbedderElement
) {
2385 MOZ_ASSERT(IsRemoteFrame());
2387 if (auto* browserBridgeChild
= GetBrowserBridgeChild()) {
2388 browserBridgeChild
->SetIsUnderHiddenEmbedderElement(
2389 aIsUnderHiddenEmbedderElement
);
2393 void nsFrameLoader::UpdateBaseWindowPositionAndSize(
2394 nsSubDocumentFrame
* aIFrame
) {
2395 nsCOMPtr
<nsIBaseWindow
> baseWindow
= GetDocShell(IgnoreErrors());
2397 // resize the sub document
2402 AutoWeakFrame
weakFrame(aIFrame
);
2404 baseWindow
->GetPosition(&x
, &y
);
2406 if (!weakFrame
.IsAlive()) {
2407 // GetPosition() killed us
2411 ScreenIntSize size
= aIFrame
->GetSubdocumentSize();
2414 baseWindow
->SetPositionAndSize(x
, y
, size
.width
, size
.height
,
2415 nsIBaseWindow::eDelayResize
);
2419 uint32_t nsFrameLoader::LazyWidth() const {
2420 uint32_t lazyWidth
= mLazySize
.width
;
2422 nsIFrame
* frame
= GetPrimaryFrameOfOwningContent();
2424 lazyWidth
= frame
->PresContext()->DevPixelsToIntCSSPixels(lazyWidth
);
2430 uint32_t nsFrameLoader::LazyHeight() const {
2431 uint32_t lazyHeight
= mLazySize
.height
;
2433 nsIFrame
* frame
= GetPrimaryFrameOfOwningContent();
2435 lazyHeight
= frame
->PresContext()->DevPixelsToIntCSSPixels(lazyHeight
);
2441 bool nsFrameLoader::EnsureRemoteBrowser() {
2442 MOZ_ASSERT(IsRemoteFrame());
2443 return mRemoteBrowser
|| TryRemoteBrowser();
2446 bool nsFrameLoader::TryRemoteBrowserInternal() {
2447 NS_ASSERTION(!mRemoteBrowser
,
2448 "TryRemoteBrowser called with a remote browser already?");
2449 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess(),
2450 "Remote subframes should only be created using the "
2451 "`CanonicalBrowsingContext::ChangeRemoteness` API");
2455 if (!mOwnerContent
) {
2459 // XXXsmaug Per spec (2014/08/21) frameloader should not work in case the
2460 // element isn't in document, only in shadow dom, but that will change
2461 // https://www.w3.org/Bugs/Public/show_bug.cgi?id=26365#c0
2462 RefPtr
<Document
> doc
= mOwnerContent
->GetComposedDoc();
2467 MOZ_RELEASE_ASSERT(!doc
->IsResourceDoc(), "We shouldn't even exist");
2469 // Graphics initialization code relies on having frames up-to-date for the
2470 // remote browser case, as we can be inside a popup, which is a different
2473 // FIXME: Ideally this should be unconditional, but we skip if for <iframe
2474 // mozbrowser> because the old RDM ui depends on current behavior, and the
2475 // mozbrowser frame code is scheduled for deletion, see bug 1574886.
2476 if (!OwnerIsMozBrowserFrame()) {
2477 doc
->FlushPendingNotifications(FlushType::Frames
);
2480 // The flush could have initialized us.
2481 if (mRemoteBrowser
) {
2485 // Ensure the world hasn't changed that much as a result of that.
2486 if (!mOwnerContent
|| mOwnerContent
->OwnerDoc() != doc
||
2487 !mOwnerContent
->IsInComposedDoc()) {
2491 if (!doc
->IsActive()) {
2492 // Don't allow subframe loads in non-active documents.
2493 // (See bug 610571 comment 5.)
2497 nsCOMPtr
<nsPIDOMWindowOuter
> parentWin
= doc
->GetWindow();
2502 nsCOMPtr
<nsIDocShell
> parentDocShell
= parentWin
->GetDocShell();
2503 if (!parentDocShell
) {
2507 if (!EnsureBrowsingContextAttached()) {
2511 // <iframe mozbrowser> gets to skip these checks.
2512 // iframes for JS plugins also get to skip these checks. We control the URL
2513 // that gets loaded, but the load is triggered from the document containing
2515 // out of process iframes also get to skip this check.
2516 if (!OwnerIsMozBrowserFrame() && !XRE_IsContentProcess()) {
2517 if (parentDocShell
->ItemType() != nsIDocShellTreeItem::typeChrome
) {
2518 // Allow two exceptions to this rule :
2519 // - about:addon so it can load remote extension options pages
2520 // - DevTools webext panels if DevTools is loaded in a content frame
2522 // Note that the new frame's message manager will not be a child of the
2523 // chrome window message manager, and, the values of window.top and
2524 // window.parent will be different than they would be for a non-remote
2526 nsIURI
* parentURI
= parentWin
->GetDocumentURI();
2531 nsAutoCString specIgnoringRef
;
2532 if (NS_FAILED(parentURI
->GetSpecIgnoringRef(specIgnoringRef
))) {
2536 if (!(specIgnoringRef
.EqualsLiteral("about:addons") ||
2537 specIgnoringRef
.EqualsLiteral(
2538 "chrome://mozapps/content/extensions/aboutaddons.html") ||
2539 specIgnoringRef
.EqualsLiteral(
2540 "chrome://browser/content/webext-panels.xhtml"))) {
2545 if (!mOwnerContent
->IsXULElement()) {
2549 if (!mOwnerContent
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::type
,
2550 nsGkAtoms::content
, eIgnoreCase
)) {
2555 uint32_t chromeFlags
= 0;
2556 nsCOMPtr
<nsIDocShellTreeOwner
> parentOwner
;
2557 if (NS_FAILED(parentDocShell
->GetTreeOwner(getter_AddRefs(parentOwner
))) ||
2561 nsCOMPtr
<nsIAppWindow
> window(do_GetInterface(parentOwner
));
2562 if (window
&& NS_FAILED(window
->GetChromeFlags(&chromeFlags
))) {
2566 AUTO_PROFILER_LABEL("nsFrameLoader::TryRemoteBrowser:Create", OTHER
);
2568 MutableTabContext context
;
2569 nsresult rv
= GetNewTabContext(&context
);
2570 NS_ENSURE_SUCCESS(rv
, false);
2572 RefPtr
<Element
> ownerElement
= mOwnerContent
;
2574 RefPtr
<BrowserParent
> nextRemoteBrowser
=
2575 mOpenWindowInfo
? mOpenWindowInfo
->GetNextRemoteBrowser() : nullptr;
2576 if (nextRemoteBrowser
) {
2577 mRemoteBrowser
= new BrowserHost(nextRemoteBrowser
);
2578 if (nextRemoteBrowser
->GetOwnerElement()) {
2579 MOZ_ASSERT_UNREACHABLE("Shouldn't have an owner element before");
2582 nextRemoteBrowser
->SetOwnerElement(ownerElement
);
2584 RefPtr
<ContentParent
> contentParent
;
2585 if (mChildID
!= 0) {
2586 ContentProcessManager
* cpm
= ContentProcessManager::GetSingleton();
2587 contentParent
= cpm
->GetContentProcessById(ContentParentId(mChildID
));
2590 ContentParent::CreateBrowser(context
, ownerElement
, mRemoteType
,
2591 mPendingBrowsingContext
, contentParent
);
2593 if (!mRemoteBrowser
) {
2597 MOZ_DIAGNOSTIC_ASSERT(mPendingBrowsingContext
==
2598 mRemoteBrowser
->GetBrowsingContext());
2600 mRemoteBrowser
->GetBrowsingContext()->Embed();
2601 InvokeBrowsingContextReadyCallback();
2603 // Grab the reference to the actor
2604 RefPtr
<BrowserParent
> browserParent
= GetBrowserParent();
2606 // We no longer need the remoteType attribute on the frame element.
2607 // The remoteType can be queried by asking the message manager instead.
2608 ownerElement
->UnsetAttr(kNameSpaceID_None
, nsGkAtoms::RemoteType
, false);
2610 // Now that browserParent is set, we can initialize graphics
2611 browserParent
->InitRendering();
2613 MaybeUpdatePrimaryBrowserParent(eBrowserParentChanged
);
2615 mChildID
= browserParent
->Manager()->ChildID();
2617 nsCOMPtr
<nsIDocShellTreeItem
> rootItem
;
2618 parentDocShell
->GetInProcessRootTreeItem(getter_AddRefs(rootItem
));
2619 nsCOMPtr
<nsPIDOMWindowOuter
> rootWin
= rootItem
->GetWindow();
2620 nsCOMPtr
<nsIDOMChromeWindow
> rootChromeWin
= do_QueryInterface(rootWin
);
2622 if (rootChromeWin
) {
2623 nsCOMPtr
<nsIBrowserDOMWindow
> browserDOMWin
;
2624 rootChromeWin
->GetBrowserDOMWindow(getter_AddRefs(browserDOMWin
));
2625 browserParent
->SetBrowserDOMWindow(browserDOMWin
);
2628 // For xul:browsers, update some settings based on attributes:
2629 if (mOwnerContent
->IsXULElement()) {
2630 // Send down the name of the browser through browserParent if it is set.
2631 nsAutoString frameName
;
2632 mOwnerContent
->GetAttr(kNameSpaceID_None
, nsGkAtoms::name
, frameName
);
2633 if (nsContentUtils::IsOverridingWindowName(frameName
)) {
2634 MOZ_ALWAYS_SUCCEEDS(mPendingBrowsingContext
->SetName(frameName
));
2636 // Allow scripts to close the window if the browser specified so:
2637 if (mOwnerContent
->AttrValueIs(kNameSpaceID_None
,
2638 nsGkAtoms::allowscriptstoclose
,
2639 nsGkAtoms::_true
, eCaseMatters
)) {
2640 Unused
<< browserParent
->SendAllowScriptsToClose();
2644 ReallyLoadFrameScripts();
2645 InitializeBrowserAPI();
2650 bool nsFrameLoader::TryRemoteBrowser() {
2651 // Try to create the internal remote browser.
2652 if (TryRemoteBrowserInternal()) {
2656 // Check if we should report a browser-crashed error because the browser
2658 if (XRE_IsParentProcess() && mOwnerContent
&& mOwnerContent
->IsXULElement()) {
2659 MaybeNotifyCrashed(nullptr, nullptr);
2665 bool nsFrameLoader::IsRemoteFrame() {
2666 if (mIsRemoteFrame
) {
2667 MOZ_ASSERT(!GetDocShell(), "Found a remote frame with a DocShell");
2673 RemoteBrowser
* nsFrameLoader::GetRemoteBrowser() const {
2674 return mRemoteBrowser
;
2677 BrowserParent
* nsFrameLoader::GetBrowserParent() const {
2678 if (!mRemoteBrowser
) {
2681 RefPtr
<BrowserHost
> browserHost
= mRemoteBrowser
->AsBrowserHost();
2685 return browserHost
->GetActor();
2688 BrowserBridgeChild
* nsFrameLoader::GetBrowserBridgeChild() const {
2689 if (!mRemoteBrowser
) {
2692 RefPtr
<BrowserBridgeHost
> browserBridgeHost
=
2693 mRemoteBrowser
->AsBrowserBridgeHost();
2694 if (!browserBridgeHost
) {
2697 return browserBridgeHost
->GetActor();
2700 mozilla::layers::LayersId
nsFrameLoader::GetLayersId() const {
2701 MOZ_ASSERT(mIsRemoteFrame
);
2702 return mRemoteBrowser
->GetLayersId();
2705 void nsFrameLoader::ActivateRemoteFrame(ErrorResult
& aRv
) {
2706 auto* browserParent
= GetBrowserParent();
2707 if (!browserParent
) {
2708 aRv
.Throw(NS_ERROR_UNEXPECTED
);
2712 browserParent
->Activate();
2715 void nsFrameLoader::DeactivateRemoteFrame(ErrorResult
& aRv
) {
2716 auto* browserParent
= GetBrowserParent();
2717 if (!browserParent
) {
2718 aRv
.Throw(NS_ERROR_UNEXPECTED
);
2722 browserParent
->Deactivate(false);
2725 void nsFrameLoader::SendCrossProcessMouseEvent(const nsAString
& aType
, float aX
,
2726 float aY
, int32_t aButton
,
2727 int32_t aClickCount
,
2730 auto* browserParent
= GetBrowserParent();
2731 if (!browserParent
) {
2732 aRv
.Throw(NS_ERROR_FAILURE
);
2736 browserParent
->SendMouseEvent(aType
, aX
, aY
, aButton
, aClickCount
,
2740 void nsFrameLoader::ActivateFrameEvent(const nsAString
& aType
, bool aCapture
,
2742 auto* browserParent
= GetBrowserParent();
2743 if (!browserParent
) {
2744 aRv
.Throw(NS_ERROR_FAILURE
);
2748 bool ok
= browserParent
->SendActivateFrameEvent(nsString(aType
), aCapture
);
2750 aRv
.Throw(NS_ERROR_NOT_AVAILABLE
);
2754 nsresult
nsFrameLoader::DoRemoteStaticClone(nsFrameLoader
* aStaticCloneOf
) {
2755 MOZ_ASSERT(aStaticCloneOf
->IsRemoteFrame());
2756 MOZ_DIAGNOSTIC_ASSERT(GetBrowsingContext());
2757 auto* cc
= ContentChild::GetSingleton();
2759 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
2760 // TODO: Could possibly be implemented without too much effort.
2761 return NS_ERROR_NOT_IMPLEMENTED
;
2763 BrowsingContext
* bcToClone
= aStaticCloneOf
->GetBrowsingContext();
2764 if (NS_WARN_IF(!bcToClone
)) {
2765 return NS_ERROR_UNEXPECTED
;
2767 cc
->SendCloneDocumentTreeInto(bcToClone
, GetBrowsingContext());
2771 nsresult
nsFrameLoader::FinishStaticClone(
2772 nsFrameLoader
* aStaticCloneOf
, bool* aOutHasInProcessPrintCallbacks
) {
2773 MOZ_DIAGNOSTIC_ASSERT(
2774 !nsContentUtils::IsSafeToRunScript(),
2775 "A script blocker should be on the stack while FinishStaticClone is run");
2777 // NOTE: We don't check `aStaticCloneOf->IsDead()` here, as the nsFrameLoader
2778 // which we're a static clone of may be in the process of being destroyed. It
2779 // won't be fully destroyed when `FinishStaticClone` is called, as a script
2780 // blocker on our caller's stack is preventing it from becoming finalized.
2782 // This is quite fragile, but is quite difficult to work around without
2783 // getting print-preview to stop re-cloning and replacing the previewed
2784 // document when changing layout.
2785 if (NS_WARN_IF(IsDead())) {
2786 return NS_ERROR_UNEXPECTED
;
2789 MaybeCreateDocShell();
2790 RefPtr
<nsDocShell
> docShell
= GetDocShell();
2791 NS_ENSURE_STATE(docShell
);
2793 nsCOMPtr
<Document
> kungFuDeathGrip
= docShell
->GetDocument();
2794 Unused
<< kungFuDeathGrip
;
2796 if (aStaticCloneOf
->IsRemoteFrame()) {
2797 return DoRemoteStaticClone(aStaticCloneOf
);
2800 nsCOMPtr
<nsIContentViewer
> viewer
;
2801 docShell
->GetContentViewer(getter_AddRefs(viewer
));
2802 NS_ENSURE_STATE(viewer
);
2804 nsIDocShell
* origDocShell
= aStaticCloneOf
->GetDocShell();
2805 NS_ENSURE_STATE(origDocShell
);
2807 nsCOMPtr
<Document
> doc
= origDocShell
->GetDocument();
2808 NS_ENSURE_STATE(doc
);
2810 nsCOMPtr
<Document
> clonedDoc
=
2811 doc
->CreateStaticClone(docShell
, viewer
, aOutHasInProcessPrintCallbacks
);
2816 bool nsFrameLoader::DoLoadMessageManagerScript(const nsAString
& aURL
,
2817 bool aRunInGlobalScope
) {
2818 if (auto* browserParent
= GetBrowserParent()) {
2819 return browserParent
->SendLoadRemoteScript(nsString(aURL
),
2822 RefPtr
<InProcessBrowserChildMessageManager
> browserChild
=
2823 GetBrowserChildMessageManager();
2825 browserChild
->LoadFrameScript(aURL
, aRunInGlobalScope
);
2830 class nsAsyncMessageToChild
: public nsSameProcessAsyncMessageBase
,
2833 explicit nsAsyncMessageToChild(nsFrameLoader
* aFrameLoader
)
2834 : nsSameProcessAsyncMessageBase(),
2835 mozilla::Runnable("nsAsyncMessageToChild"),
2836 mFrameLoader(aFrameLoader
) {}
2838 NS_IMETHOD
Run() override
{
2839 InProcessBrowserChildMessageManager
* browserChild
=
2840 mFrameLoader
->mChildMessageManager
;
2841 // Since bug 1126089, messages can arrive even when the docShell is
2842 // destroyed. Here we make sure that those messages are not delivered.
2843 if (browserChild
&& browserChild
->GetInnerManager() &&
2844 mFrameLoader
->GetExistingDocShell()) {
2845 JS::Rooted
<JSObject
*> kungFuDeathGrip(dom::RootingCx(),
2846 browserChild
->GetWrapper());
2847 ReceiveMessage(static_cast<EventTarget
*>(browserChild
), mFrameLoader
,
2848 browserChild
->GetInnerManager());
2852 RefPtr
<nsFrameLoader
> mFrameLoader
;
2855 nsresult
nsFrameLoader::DoSendAsyncMessage(const nsAString
& aMessage
,
2856 StructuredCloneData
& aData
) {
2857 auto* browserParent
= GetBrowserParent();
2858 if (browserParent
) {
2859 ClonedMessageData data
;
2860 ContentParent
* cp
= browserParent
->Manager();
2861 if (!BuildClonedMessageDataForParent(cp
, aData
, data
)) {
2863 return NS_ERROR_DOM_DATA_CLONE_ERR
;
2865 if (browserParent
->SendAsyncMessage(nsString(aMessage
), data
)) {
2868 return NS_ERROR_UNEXPECTED
;
2872 if (mChildMessageManager
) {
2873 RefPtr
<nsAsyncMessageToChild
> ev
= new nsAsyncMessageToChild(this);
2874 nsresult rv
= ev
->Init(aMessage
, aData
);
2875 if (NS_FAILED(rv
)) {
2878 rv
= NS_DispatchToCurrentThread(ev
);
2879 if (NS_FAILED(rv
)) {
2885 // We don't have any targets to send our asynchronous message to.
2886 return NS_ERROR_UNEXPECTED
;
2889 already_AddRefed
<MessageSender
> nsFrameLoader::GetMessageManager() {
2890 EnsureMessageManager();
2891 return do_AddRef(mMessageManager
);
2894 nsresult
nsFrameLoader::EnsureMessageManager() {
2895 NS_ENSURE_STATE(mOwnerContent
);
2897 if (mMessageManager
) {
2901 if (!mIsTopLevelContent
&& !OwnerIsMozBrowserFrame() && !IsRemoteFrame() &&
2902 !(mOwnerContent
->IsXULElement() &&
2903 mOwnerContent
->AttrValueIs(kNameSpaceID_None
,
2904 nsGkAtoms::forcemessagemanager
,
2905 nsGkAtoms::_true
, eCaseMatters
))) {
2909 RefPtr
<nsGlobalWindowOuter
> window
=
2910 nsGlobalWindowOuter::Cast(GetOwnerDoc()->GetWindow());
2911 RefPtr
<ChromeMessageBroadcaster
> parentManager
;
2913 if (window
&& window
->IsChromeWindow()) {
2914 nsAutoString messagemanagergroup
;
2915 if (mOwnerContent
->IsXULElement() &&
2916 mOwnerContent
->GetAttr(kNameSpaceID_None
,
2917 nsGkAtoms::messagemanagergroup
,
2918 messagemanagergroup
)) {
2919 parentManager
= window
->GetGroupMessageManager(messagemanagergroup
);
2922 if (!parentManager
) {
2923 parentManager
= window
->GetMessageManager();
2926 parentManager
= nsFrameMessageManager::GetGlobalMessageManager();
2929 mMessageManager
= new ChromeMessageSender(parentManager
);
2930 if (!IsRemoteFrame()) {
2931 nsresult rv
= MaybeCreateDocShell();
2932 if (NS_FAILED(rv
)) {
2935 MOZ_ASSERT(GetDocShell(),
2936 "MaybeCreateDocShell succeeded, but null docShell");
2937 if (!GetDocShell()) {
2938 return NS_ERROR_FAILURE
;
2940 mChildMessageManager
= InProcessBrowserChildMessageManager::Create(
2941 GetDocShell(), mOwnerContent
, mMessageManager
);
2942 NS_ENSURE_TRUE(mChildMessageManager
, NS_ERROR_UNEXPECTED
);
2944 #if !defined(MOZ_WIDGET_ANDROID) && !defined(MOZ_THUNDERBIRD) && \
2946 // Set up a TabListener for sessionStore
2947 if (XRE_IsParentProcess()) {
2948 mSessionStoreListener
= new TabListener(GetDocShell(), mOwnerContent
);
2949 rv
= mSessionStoreListener
->Init();
2950 NS_ENSURE_SUCCESS(rv
, rv
);
2957 nsresult
nsFrameLoader::ReallyLoadFrameScripts() {
2958 nsresult rv
= EnsureMessageManager();
2959 if (NS_WARN_IF(NS_FAILED(rv
))) {
2962 if (mMessageManager
) {
2963 mMessageManager
->InitWithCallback(this);
2968 already_AddRefed
<Element
> nsFrameLoader::GetOwnerElement() {
2969 return do_AddRef(mOwnerContent
);
2972 void nsFrameLoader::SetDetachedSubdocFrame(nsIFrame
* aDetachedFrame
,
2973 Document
* aContainerDoc
) {
2974 mDetachedSubdocFrame
= aDetachedFrame
;
2975 mContainerDocWhileDetached
= aContainerDoc
;
2978 nsIFrame
* nsFrameLoader::GetDetachedSubdocFrame(
2979 Document
** aContainerDoc
) const {
2980 NS_IF_ADDREF(*aContainerDoc
= mContainerDocWhileDetached
);
2981 return mDetachedSubdocFrame
.GetFrame();
2984 void nsFrameLoader::ApplySandboxFlags(uint32_t sandboxFlags
) {
2985 // If our BrowsingContext doesn't exist yet, it means we haven't been
2986 // initialized yet. This method will be called again once we're initialized
2987 // from MaybeCreateDocShell. <iframe> BrowsingContexts are never created as
2988 // initially remote, so we don't need to worry about updating sandbox flags
2989 // for an uninitialized initially-remote iframe.
2990 BrowsingContext
* context
= GetExtantBrowsingContext();
2992 MOZ_ASSERT(!IsRemoteFrame(),
2993 "cannot apply sandbox flags to an uninitialized "
2994 "initially-remote frame");
2998 uint32_t parentSandboxFlags
= mOwnerContent
->OwnerDoc()->GetSandboxFlags();
3000 // The child can only add restrictions, never remove them.
3001 sandboxFlags
|= parentSandboxFlags
;
3003 // XXX this probably isn't fission compatible.
3004 if (GetDocShell()) {
3005 // If this frame is a receiving browsing context, we should add
3006 // sandboxed auxiliary navigation flag to sandboxFlags. See
3007 // https://w3c.github.io/presentation-api/#creating-a-receiving-browsing-context
3008 nsAutoString presentationURL
;
3009 nsContentUtils::GetPresentationURL(GetDocShell(), presentationURL
);
3010 if (!presentationURL
.IsEmpty()) {
3011 sandboxFlags
|= SANDBOXED_AUXILIARY_NAVIGATION
;
3015 MOZ_ALWAYS_SUCCEEDS(context
->SetSandboxFlags(sandboxFlags
));
3019 void nsFrameLoader::AttributeChanged(mozilla::dom::Element
* aElement
,
3020 int32_t aNameSpaceID
, nsAtom
* aAttribute
,
3022 const nsAttrValue
* aOldValue
) {
3023 MOZ_ASSERT(mObservingOwnerContent
);
3025 if (aElement
!= mOwnerContent
) {
3029 if (aNameSpaceID
!= kNameSpaceID_None
||
3030 (aAttribute
!= TypeAttrName(aElement
) &&
3031 aAttribute
!= nsGkAtoms::primary
)) {
3035 // Note: This logic duplicates a lot of logic in
3036 // MaybeCreateDocshell. We should fix that.
3038 // Notify our enclosing chrome that our type has changed. We only do this
3039 // if our parent is chrome, since in all other cases we're random content
3040 // subframes and the treeowner shouldn't worry about us.
3041 if (!GetDocShell()) {
3042 MaybeUpdatePrimaryBrowserParent(eBrowserParentChanged
);
3046 nsCOMPtr
<nsIDocShellTreeItem
> parentItem
;
3047 GetDocShell()->GetInProcessParent(getter_AddRefs(parentItem
));
3052 if (parentItem
->ItemType() != nsIDocShellTreeItem::typeChrome
) {
3056 nsCOMPtr
<nsIDocShellTreeOwner
> parentTreeOwner
;
3057 parentItem
->GetTreeOwner(getter_AddRefs(parentTreeOwner
));
3058 if (!parentTreeOwner
) {
3062 bool is_primary
= aElement
->AttrValueIs(kNameSpaceID_None
, nsGkAtoms::primary
,
3063 nsGkAtoms::_true
, eIgnoreCase
);
3066 // when a content panel is no longer primary, hide any open popups it may have
3068 nsXULPopupManager
* pm
= nsXULPopupManager::GetInstance();
3070 pm
->HidePopupsInDocShell(GetDocShell());
3075 parentTreeOwner
->ContentShellRemoved(GetDocShell());
3076 if (aElement
->AttrValueIs(kNameSpaceID_None
, TypeAttrName(aElement
),
3077 nsGkAtoms::content
, eIgnoreCase
)) {
3078 parentTreeOwner
->ContentShellAdded(GetDocShell(), is_primary
);
3083 * Send the RequestNotifyAfterRemotePaint message to the current Tab.
3085 void nsFrameLoader::RequestNotifyAfterRemotePaint() {
3086 // If remote browsing (e10s), handle this with the BrowserParent.
3087 if (auto* browserParent
= GetBrowserParent()) {
3088 Unused
<< browserParent
->SendRequestNotifyAfterRemotePaint();
3092 void nsFrameLoader::RequestUpdatePosition(ErrorResult
& aRv
) {
3093 if (auto* browserParent
= GetBrowserParent()) {
3094 nsresult rv
= browserParent
->UpdatePosition();
3096 if (NS_FAILED(rv
)) {
3102 bool nsFrameLoader::RequestTabStateFlush(uint32_t aFlushId
, bool aIsFinal
) {
3103 if (mSessionStoreListener
) {
3104 mSessionStoreListener
->ForceFlushFromParent(aFlushId
, aIsFinal
);
3105 // No async ipc call is involved in parent only case
3109 // If remote browsing (e10s), handle this with the BrowserParent.
3110 if (auto* browserParent
= GetBrowserParent()) {
3111 Unused
<< browserParent
->SendFlushTabState(aFlushId
, aIsFinal
);
3118 void nsFrameLoader::RequestEpochUpdate(uint32_t aEpoch
) {
3119 if (mSessionStoreListener
) {
3120 mSessionStoreListener
->SetEpoch(aEpoch
);
3124 // If remote browsing (e10s), handle this with the BrowserParent.
3125 if (auto* browserParent
= GetBrowserParent()) {
3126 Unused
<< browserParent
->SendUpdateEpoch(aEpoch
);
3130 void nsFrameLoader::RequestSHistoryUpdate(bool aImmediately
) {
3131 if (mSessionStoreListener
) {
3132 mSessionStoreListener
->UpdateSHistoryChanges(aImmediately
);
3136 // If remote browsing (e10s), handle this with the BrowserParent.
3137 if (auto* browserParent
= GetBrowserParent()) {
3138 Unused
<< browserParent
->SendUpdateSHistory(aImmediately
);
3143 class WebProgressListenerToPromise final
: public nsIWebProgressListener
{
3145 explicit WebProgressListenerToPromise(Promise
* aPromise
)
3146 : mPromise(aPromise
) {}
3150 // NS_DECL_NSIWEBPROGRESSLISTENER
3151 NS_IMETHOD
OnStateChange(nsIWebProgress
* aWebProgress
, nsIRequest
* aRequest
,
3152 uint32_t aStateFlags
, nsresult aStatus
) override
{
3153 if (aStateFlags
& nsIWebProgressListener::STATE_STOP
&&
3154 aStateFlags
& nsIWebProgressListener::STATE_IS_DOCUMENT
&&
3156 mPromise
->MaybeResolveWithUndefined();
3161 NS_IMETHOD
OnStatusChange(nsIWebProgress
* aWebProgress
, nsIRequest
* aRequest
,
3163 const char16_t
* aMessage
) override
{
3164 if (aStatus
!= NS_OK
&& mPromise
) {
3165 mPromise
->MaybeReject(ErrorResult(aStatus
));
3170 NS_IMETHOD
OnProgressChange(nsIWebProgress
* aWebProgress
,
3171 nsIRequest
* aRequest
, int32_t aCurSelfProgress
,
3172 int32_t aMaxSelfProgress
,
3173 int32_t aCurTotalProgress
,
3174 int32_t aMaxTotalProgress
) override
{
3177 NS_IMETHOD
OnLocationChange(nsIWebProgress
* aWebProgress
,
3178 nsIRequest
* aRequest
, nsIURI
* aLocation
,
3179 uint32_t aFlags
) override
{
3182 NS_IMETHOD
OnSecurityChange(nsIWebProgress
* aWebProgress
,
3183 nsIRequest
* aRequest
, uint32_t aState
) override
{
3186 NS_IMETHOD
OnContentBlockingEvent(nsIWebProgress
* aWebProgress
,
3187 nsIRequest
* aRequest
,
3188 uint32_t aEvent
) override
{
3193 ~WebProgressListenerToPromise() = default;
3195 RefPtr
<Promise
> mPromise
;
3198 NS_IMPL_ISUPPORTS(WebProgressListenerToPromise
, nsIWebProgressListener
)
3201 already_AddRefed
<Promise
> nsFrameLoader::PrintPreview(
3202 nsIPrintSettings
* aPrintSettings
,
3203 const Optional
<uint64_t>& aSourceOuterWindowID
, ErrorResult
& aRv
) {
3204 auto* ownerDoc
= GetOwnerDoc();
3206 aRv
.ThrowNotSupportedError("No owner document");
3209 RefPtr
<Promise
> promise
= Promise::Create(ownerDoc
->GetOwnerGlobal(), aRv
);
3215 promise
->MaybeRejectWithNotSupportedError("Build does not support printing");
3216 return promise
.forget();
3218 auto resolve
= [promise
](PrintPreviewResultInfo aInfo
) {
3219 if (aInfo
.sheetCount() > 0) {
3220 PrintPreviewSuccessInfo info
;
3221 info
.mSheetCount
= aInfo
.sheetCount();
3222 info
.mTotalPageCount
= aInfo
.totalPageCount();
3223 info
.mHasSelection
= aInfo
.hasSelection();
3224 promise
->MaybeResolve(info
);
3226 promise
->MaybeRejectWithUnknownError("Print preview failed");
3230 if (auto* browserParent
= GetBrowserParent()) {
3231 nsCOMPtr
<nsIPrintSettingsService
> printSettingsSvc
=
3232 do_GetService("@mozilla.org/gfx/printsettings-service;1");
3233 if (!printSettingsSvc
) {
3234 promise
->MaybeRejectWithNotSupportedError("No nsIPrintSettingsService");
3235 return promise
.forget();
3238 embedding::PrintData printData
;
3240 printSettingsSvc
->SerializeToPrintData(aPrintSettings
, &printData
);
3241 if (NS_WARN_IF(NS_FAILED(rv
))) {
3242 promise
->MaybeReject(ErrorResult(rv
));
3243 return promise
.forget();
3246 auto winID(aSourceOuterWindowID
.WasPassed()
3247 ? Some(aSourceOuterWindowID
.Value())
3250 browserParent
->SendPrintPreview(printData
, winID
)
3252 GetMainThreadSerialEventTarget(), __func__
, std::move(resolve
),
3253 [promise
](const mozilla::ipc::ResponseRejectReason
) {
3254 promise
->MaybeRejectWithUnknownError("Print preview IPC failed");
3257 return promise
.forget();
3260 RefPtr
<nsGlobalWindowOuter
> sourceWindow
;
3261 if (aSourceOuterWindowID
.WasPassed()) {
3263 nsGlobalWindowOuter::GetOuterWindowWithId(aSourceOuterWindowID
.Value());
3265 auto* ourDocshell
= static_cast<nsDocShell
*>(GetExistingDocShell());
3266 if (NS_WARN_IF(!ourDocshell
)) {
3267 promise
->MaybeRejectWithNotSupportedError("No print preview docShell");
3268 return promise
.forget();
3270 sourceWindow
= nsGlobalWindowOuter::Cast(ourDocshell
->GetWindow());
3272 if (NS_WARN_IF(!sourceWindow
)) {
3273 promise
->MaybeRejectWithNotSupportedError("No print preview source window");
3274 return promise
.forget();
3277 nsIDocShell
* docShellToCloneInto
= nullptr;
3278 if (aSourceOuterWindowID
.WasPassed()) {
3279 // We're going to call `Print()` below on a window that is not our own,
3280 // which happens when we are creating a new print preview document instead
3281 // of just applying a settings change to the existing PP document. In this
3282 // case we need to explicity pass our docShell as the docShell to clone
3284 docShellToCloneInto
= GetExistingDocShell();
3285 if (NS_WARN_IF(!docShellToCloneInto
)) {
3286 promise
->MaybeRejectWithNotSupportedError("No print preview docShell");
3287 return promise
.forget();
3291 // Unfortunately we can't pass `resolve` directly here because IPDL, for now,
3292 // unfortunately generates slightly different parameter types for functions
3293 // taking PrintPreviewResultInfo in PBrowserParent vs. PBrowserChild.
3295 sourceWindow
->Print(
3297 /* aListener = */ nullptr, docShellToCloneInto
,
3298 nsGlobalWindowOuter::IsPreview::Yes
,
3299 nsGlobalWindowOuter::BlockUntilDone::No
,
3300 [resolve
](const PrintPreviewResultInfo
& aInfo
) { resolve(aInfo
); }, rv
);
3301 if (NS_WARN_IF(rv
.Failed())) {
3302 promise
->MaybeReject(std::move(rv
));
3305 return promise
.forget();
3309 void nsFrameLoader::ExitPrintPreview() {
3311 if (auto* browserParent
= GetBrowserParent()) {
3312 Unused
<< browserParent
->SendExitPrintPreview();
3315 if (NS_WARN_IF(!GetExistingDocShell())) {
3318 nsCOMPtr
<nsIWebBrowserPrint
> webBrowserPrint
=
3319 do_GetInterface(ToSupports(GetExistingDocShell()->GetWindow()));
3320 if (NS_WARN_IF(!webBrowserPrint
)) {
3323 webBrowserPrint
->ExitPrintPreview();
3327 already_AddRefed
<Promise
> nsFrameLoader::Print(uint64_t aOuterWindowID
,
3328 nsIPrintSettings
* aPrintSettings
,
3330 RefPtr
<Promise
> promise
=
3331 Promise::Create(GetOwnerDoc()->GetOwnerGlobal(), aRv
);
3334 promise
->MaybeReject(ErrorResult(NS_ERROR_NOT_AVAILABLE
));
3335 return promise
.forget();
3338 RefPtr
<WebProgressListenerToPromise
> listener(
3339 new WebProgressListenerToPromise(promise
));
3341 if (auto* browserParent
= GetBrowserParent()) {
3342 RefPtr
<embedding::PrintingParent
> printingParent
=
3343 browserParent
->Manager()->GetPrintingParent();
3345 embedding::PrintData printData
;
3346 nsresult rv
= printingParent
->SerializeAndEnsureRemotePrintJob(
3347 aPrintSettings
, listener
, nullptr, &printData
);
3348 if (NS_WARN_IF(NS_FAILED(rv
))) {
3349 promise
->MaybeReject(ErrorResult(rv
));
3350 return promise
.forget();
3353 bool success
= browserParent
->SendPrint(aOuterWindowID
, printData
);
3355 promise
->MaybeReject(ErrorResult(NS_ERROR_FAILURE
));
3357 return promise
.forget();
3360 RefPtr
<nsGlobalWindowOuter
> outerWindow
=
3361 nsGlobalWindowOuter::GetOuterWindowWithId(aOuterWindowID
);
3362 if (NS_WARN_IF(!outerWindow
)) {
3363 promise
->MaybeReject(ErrorResult(NS_ERROR_FAILURE
));
3364 return promise
.forget();
3368 outerWindow
->Print(aPrintSettings
, listener
,
3369 /* aDocShellToCloneInto = */ nullptr,
3370 nsGlobalWindowOuter::IsPreview::No
,
3371 nsGlobalWindowOuter::BlockUntilDone::No
,
3372 /* aPrintPreviewCallback = */ nullptr, rv
);
3374 promise
->MaybeReject(std::move(rv
));
3377 return promise
.forget();
3381 already_AddRefed
<nsIRemoteTab
> nsFrameLoader::GetRemoteTab() {
3382 if (!mRemoteBrowser
) {
3385 if (auto* browserHost
= mRemoteBrowser
->AsBrowserHost()) {
3386 return do_AddRef(browserHost
);
3391 already_AddRefed
<nsILoadContext
> nsFrameLoader::LoadContext() {
3392 return do_AddRef(GetBrowsingContext());
3395 BrowsingContext
* nsFrameLoader::GetBrowsingContext() {
3396 if (mNotifyingCrash
) {
3397 if (mPendingBrowsingContext
&& mPendingBrowsingContext
->EverAttached()) {
3398 return mPendingBrowsingContext
;
3402 if (IsRemoteFrame()) {
3403 Unused
<< EnsureRemoteBrowser();
3404 } else if (mOwnerContent
) {
3405 Unused
<< MaybeCreateDocShell();
3407 return GetExtantBrowsingContext();
3410 BrowsingContext
* nsFrameLoader::GetExtantBrowsingContext() {
3411 if (!mPendingBrowsingContext
) {
3412 // If mPendingBrowsingContext is null then the frame loader is being
3413 // destroyed (nsFrameLoader::DestroyDocShell was called), so return null
3414 // here in that case.
3418 BrowsingContext
* browsingContext
= nullptr;
3419 if (mRemoteBrowser
) {
3420 browsingContext
= mRemoteBrowser
->GetBrowsingContext();
3421 } else if (mDocShell
) {
3422 browsingContext
= mDocShell
->GetBrowsingContext();
3425 MOZ_ASSERT_IF(browsingContext
, browsingContext
== mPendingBrowsingContext
);
3426 return browsingContext
;
3429 void nsFrameLoader::InitializeBrowserAPI() {
3430 if (!OwnerIsMozBrowserFrame()) {
3434 nsresult rv
= EnsureMessageManager();
3435 if (NS_WARN_IF(NS_FAILED(rv
))) {
3438 mMessageManager
->LoadFrameScript(
3439 u
"chrome://global/content/BrowserElementChild.js"_ns
,
3440 /* allowDelayedLoad = */ true,
3441 /* aRunInGlobalScope */ true, IgnoreErrors());
3443 nsCOMPtr
<nsIMozBrowserFrame
> browserFrame
= do_QueryInterface(mOwnerContent
);
3445 browserFrame
->InitializeBrowserAPI();
3449 void nsFrameLoader::DestroyBrowserFrameScripts() {
3450 if (!OwnerIsMozBrowserFrame()) {
3453 nsCOMPtr
<nsIMozBrowserFrame
> browserFrame
= do_QueryInterface(mOwnerContent
);
3455 browserFrame
->DestroyBrowserFrameScripts();
3459 void nsFrameLoader::StartPersistence(
3460 BrowsingContext
* aContext
, nsIWebBrowserPersistDocumentReceiver
* aRecv
,
3463 RefPtr
<BrowsingContext
> context
= aContext
? aContext
: GetBrowsingContext();
3465 if (!context
|| !context
->IsInSubtreeOf(GetBrowsingContext())) {
3466 aRecv
->OnError(NS_ERROR_NO_CONTENT
);
3470 if (!context
->GetDocShell() && XRE_IsParentProcess()) {
3471 CanonicalBrowsingContext
* canonical
=
3472 CanonicalBrowsingContext::Cast(context
);
3473 RefPtr
<BrowserParent
> browserParent
=
3474 canonical
->GetCurrentWindowGlobal()->GetBrowserParent();
3475 browserParent
->StartPersistence(canonical
, aRecv
, aRv
);
3479 nsCOMPtr
<Document
> foundDoc
= context
->GetDocument();
3482 aRecv
->OnError(NS_ERROR_NO_CONTENT
);
3484 nsCOMPtr
<nsIWebBrowserPersistDocument
> pdoc
=
3485 new mozilla::WebBrowserPersistLocalDocument(foundDoc
);
3486 aRecv
->OnDocumentReady(pdoc
);
3490 void nsFrameLoader::MaybeUpdatePrimaryBrowserParent(
3491 BrowserParentChange aChange
) {
3492 if (!mOwnerContent
|| !mRemoteBrowser
) {
3496 RefPtr
<BrowserHost
> browserHost
= mRemoteBrowser
->AsBrowserHost();
3501 nsCOMPtr
<nsIDocShell
> docShell
= mOwnerContent
->OwnerDoc()->GetDocShell();
3506 BrowsingContext
* browsingContext
= docShell
->GetBrowsingContext();
3507 if (!browsingContext
->IsChrome()) {
3511 nsCOMPtr
<nsIDocShellTreeOwner
> parentTreeOwner
;
3512 docShell
->GetTreeOwner(getter_AddRefs(parentTreeOwner
));
3513 if (!parentTreeOwner
) {
3517 if (!mObservingOwnerContent
) {
3518 mOwnerContent
->AddMutationObserver(this);
3519 mObservingOwnerContent
= true;
3522 parentTreeOwner
->RemoteTabRemoved(browserHost
);
3523 if (aChange
== eBrowserParentChanged
) {
3524 bool isPrimary
= mOwnerContent
->AttrValueIs(
3525 kNameSpaceID_None
, nsGkAtoms::primary
, nsGkAtoms::_true
, eIgnoreCase
);
3526 parentTreeOwner
->RemoteTabAdded(browserHost
, isPrimary
);
3530 nsresult
nsFrameLoader::GetNewTabContext(MutableTabContext
* aTabContext
,
3532 nsAutoString presentationURLStr
;
3533 mOwnerContent
->GetAttr(kNameSpaceID_None
, nsGkAtoms::mozpresentation
,
3534 presentationURLStr
);
3536 nsCOMPtr
<nsIDocShell
> docShell
= mOwnerContent
->OwnerDoc()->GetDocShell();
3537 nsCOMPtr
<nsILoadContext
> parentContext
= do_QueryInterface(docShell
);
3538 NS_ENSURE_STATE(parentContext
);
3540 MOZ_ASSERT(mPendingBrowsingContext
->EverAttached());
3542 UIStateChangeType showFocusRings
= UIStateChangeType_NoChange
;
3543 uint64_t chromeOuterWindowID
= 0;
3545 Document
* doc
= mOwnerContent
->OwnerDoc();
3547 nsCOMPtr
<nsPIWindowRoot
> root
= nsContentUtils::GetWindowRoot(doc
);
3549 showFocusRings
= root
->ShowFocusRings() ? UIStateChangeType_Set
3550 : UIStateChangeType_Clear
;
3552 nsPIDOMWindowOuter
* outerWin
= root
->GetWindow();
3554 chromeOuterWindowID
= outerWin
->WindowID();
3559 uint32_t maxTouchPoints
= BrowserParent::GetMaxTouchPoints(mOwnerContent
);
3561 bool tabContextUpdated
= aTabContext
->SetTabContext(
3562 chromeOuterWindowID
, showFocusRings
, presentationURLStr
, maxTouchPoints
);
3563 NS_ENSURE_STATE(tabContextUpdated
);
3568 nsresult
nsFrameLoader::PopulateOriginContextIdsFromAttributes(
3569 OriginAttributes
& aAttr
) {
3570 // Only XUL or mozbrowser frames are allowed to set context IDs
3571 uint32_t namespaceID
= mOwnerContent
->GetNameSpaceID();
3572 if (namespaceID
!= kNameSpaceID_XUL
&& !OwnerIsMozBrowserFrame()) {
3576 nsAutoString attributeValue
;
3577 if (aAttr
.mUserContextId
==
3578 nsIScriptSecurityManager::DEFAULT_USER_CONTEXT_ID
&&
3579 mOwnerContent
->GetAttr(kNameSpaceID_None
, nsGkAtoms::usercontextid
,
3581 !attributeValue
.IsEmpty()) {
3583 aAttr
.mUserContextId
= attributeValue
.ToInteger(&rv
);
3584 NS_ENSURE_SUCCESS(rv
, rv
);
3587 if (aAttr
.mGeckoViewSessionContextId
.IsEmpty() &&
3588 mOwnerContent
->GetAttr(kNameSpaceID_None
,
3589 nsGkAtoms::geckoViewSessionContextId
,
3591 !attributeValue
.IsEmpty()) {
3592 // XXX: Should we check the format from `GeckoViewNavigation.jsm` here?
3593 aAttr
.mGeckoViewSessionContextId
= attributeValue
;
3599 ProcessMessageManager
* nsFrameLoader::GetProcessMessageManager() const {
3600 if (auto* browserParent
= GetBrowserParent()) {
3601 return browserParent
->Manager()->GetMessageManager();
3606 JSObject
* nsFrameLoader::WrapObject(JSContext
* cx
,
3607 JS::Handle
<JSObject
*> aGivenProto
) {
3608 JS::RootedObject
result(cx
);
3609 FrameLoader_Binding::Wrap(cx
, this, this, aGivenProto
, &result
);
3613 void nsFrameLoader::SetWillChangeProcess() {
3614 mWillChangeProcess
= true;
3616 if (IsRemoteFrame()) {
3617 // OOP Browser - Go directly over Browser Parent
3618 if (auto* browserParent
= GetBrowserParent()) {
3619 // We're going to be synchronously changing the owner of the
3620 // BrowsingContext in the parent process while the current owner may still
3621 // have in-flight requests which only the owner is allowed to make. Those
3622 // requests will typically trigger assertions if they come from a child
3623 // other than the owner.
3625 // To work around this, we record the previous owner at the start of the
3626 // process switch, and clear it when we've received a reply from the
3627 // child, treating ownership mismatches as warnings in the interim.
3629 // In the future, this sort of issue will probably need to be handled
3630 // using ownership epochs, which should be more both flexible and
3631 // resilient. For the moment, though, the surrounding process switch code
3632 // is enough in flux that we're better off with a workable interim
3634 MOZ_DIAGNOSTIC_ASSERT(mPendingBrowsingContext
== GetBrowsingContext());
3635 RefPtr
<CanonicalBrowsingContext
> bc(mPendingBrowsingContext
->Canonical());
3636 uint64_t targetProcessId
= browserParent
->Manager()->ChildID();
3637 bc
->SetInFlightProcessId(targetProcessId
);
3638 auto callback
= [bc
, targetProcessId
](auto) {
3639 bc
->ClearInFlightProcessId(targetProcessId
);
3641 browserParent
->SendWillChangeProcess(callback
, callback
);
3643 // OOP IFrame - Through Browser Bridge Parent, set on browser child
3644 else if (auto* browserBridgeChild
= GetBrowserBridgeChild()) {
3645 Unused
<< browserBridgeChild
->SendWillChangeProcess();
3651 RefPtr
<nsDocShell
> docshell
= GetDocShell();
3652 MOZ_ASSERT(docshell
);
3653 docshell
->SetWillChangeProcess();
3656 void nsFrameLoader::MaybeNotifyCrashed(BrowsingContext
* aBrowsingContext
,
3657 mozilla::ipc::MessageChannel
* aChannel
) {
3658 if (mTabProcessCrashFired
) {
3662 if (mPendingBrowsingContext
== aBrowsingContext
) {
3663 mTabProcessCrashFired
= true;
3666 // Fire the crashed observer notification.
3667 nsCOMPtr
<nsIObserverService
> os
= services::GetObserverService();
3672 mNotifyingCrash
= true;
3673 auto resetNotifyCrash
=
3674 mozilla::MakeScopeExit([&] { mNotifyingCrash
= false; });
3676 os
->NotifyObservers(ToSupports(this), "oop-frameloader-crashed", nullptr);
3678 // Check our owner element still references us. If it's moved, on, events
3679 // don't need to be fired.
3680 RefPtr
<nsFrameLoaderOwner
> owner
= do_QueryObject(mOwnerContent
);
3685 RefPtr
<nsFrameLoader
> currentFrameLoader
= owner
->GetFrameLoader();
3686 if (currentFrameLoader
!= this) {
3690 // Fire the actual crashed event.
3692 if (aChannel
&& !aChannel
->DoBuildIDsMatch()) {
3693 eventName
= u
"oop-browser-buildid-mismatch"_ns
;
3695 eventName
= u
"oop-browser-crashed"_ns
;
3698 FrameCrashedEventInit init
;
3699 init
.mBubbles
= true;
3700 init
.mCancelable
= true;
3701 if (aBrowsingContext
) {
3702 init
.mBrowsingContextId
= aBrowsingContext
->Id();
3703 init
.mIsTopFrame
= aBrowsingContext
->IsTop();
3706 RefPtr
<FrameCrashedEvent
> event
= FrameCrashedEvent::Constructor(
3707 mOwnerContent
->OwnerDoc(), eventName
, init
);
3708 event
->SetTrusted(true);
3709 EventDispatcher::DispatchDOMEvent(mOwnerContent
, nullptr, event
, nullptr,
3713 bool nsFrameLoader::EnsureBrowsingContextAttached() {
3716 Document
* parentDoc
= mOwnerContent
->OwnerDoc();
3717 MOZ_ASSERT(parentDoc
);
3718 BrowsingContext
* parentContext
= parentDoc
->GetBrowsingContext();
3719 MOZ_ASSERT(parentContext
);
3721 // Inherit the `use` flags from our parent BrowsingContext.
3722 bool usePrivateBrowsing
= parentContext
->UsePrivateBrowsing();
3723 bool useRemoteSubframes
= parentContext
->UseRemoteSubframes();
3724 bool useRemoteTabs
= parentContext
->UseRemoteTabs();
3726 // Determine the exact OriginAttributes which should be used for our
3727 // BrowsingContext. This will be used to initialize OriginAttributes if the
3728 // BrowsingContext has not already been created.
3729 OriginAttributes attrs
;
3730 if (mPendingBrowsingContext
->IsContent()) {
3731 if (mPendingBrowsingContext
->GetParent()) {
3732 MOZ_ASSERT(mPendingBrowsingContext
->GetParent() == parentContext
);
3733 parentContext
->GetOriginAttributes(attrs
);
3736 // Inherit the `mFirstPartyDomain` flag from our parent document's result
3737 // principal, if it was set.
3738 if (parentContext
->IsContent() &&
3739 !parentDoc
->NodePrincipal()->IsSystemPrincipal() &&
3740 !OwnerIsMozBrowserFrame()) {
3741 OriginAttributes docAttrs
=
3742 parentDoc
->NodePrincipal()->OriginAttributesRef();
3743 // We only want to inherit firstPartyDomain here, other attributes should
3745 MOZ_ASSERT(attrs
.EqualsIgnoringFPD(docAttrs
));
3746 attrs
.mFirstPartyDomain
= docAttrs
.mFirstPartyDomain
;
3749 // Inherit the PrivateBrowsing flag across content/chrome boundaries.
3750 attrs
.SyncAttributesWithPrivateBrowsing(usePrivateBrowsing
);
3752 // A <browser> element may have overridden userContextId or
3753 // geckoViewUserContextId.
3754 rv
= PopulateOriginContextIdsFromAttributes(attrs
);
3755 if (NS_WARN_IF(NS_FAILED(rv
))) {
3759 // <iframe mozbrowser> is allowed to set `mozprivatebrowsing` to
3760 // force-enable private browsing.
3761 if (OwnerIsMozBrowserFrame()) {
3762 if (mOwnerContent
->HasAttr(kNameSpaceID_None
,
3763 nsGkAtoms::mozprivatebrowsing
)) {
3764 attrs
.SyncAttributesWithPrivateBrowsing(true);
3765 usePrivateBrowsing
= true;
3770 // If we've already been attached, return.
3771 if (mPendingBrowsingContext
->EverAttached()) {
3772 MOZ_DIAGNOSTIC_ASSERT(mPendingBrowsingContext
->UsePrivateBrowsing() ==
3773 usePrivateBrowsing
);
3774 MOZ_DIAGNOSTIC_ASSERT(mPendingBrowsingContext
->UseRemoteTabs() ==
3776 MOZ_DIAGNOSTIC_ASSERT(mPendingBrowsingContext
->UseRemoteSubframes() ==
3777 useRemoteSubframes
);
3778 // Don't assert that our OriginAttributes match, as we could have different
3779 // OriginAttributes in the case where we were opened using window.open.
3783 // Initialize non-synced OriginAttributes and related fields.
3784 rv
= mPendingBrowsingContext
->SetOriginAttributes(attrs
);
3785 NS_ENSURE_SUCCESS(rv
, false);
3786 rv
= mPendingBrowsingContext
->SetUsePrivateBrowsing(usePrivateBrowsing
);
3787 NS_ENSURE_SUCCESS(rv
, false);
3788 rv
= mPendingBrowsingContext
->SetRemoteTabs(useRemoteTabs
);
3789 NS_ENSURE_SUCCESS(rv
, false);
3790 rv
= mPendingBrowsingContext
->SetRemoteSubframes(useRemoteSubframes
);
3791 NS_ENSURE_SUCCESS(rv
, false);
3793 // Finish attaching.
3794 mPendingBrowsingContext
->EnsureAttached();
3798 void nsFrameLoader::InvokeBrowsingContextReadyCallback() {
3799 if (mOpenWindowInfo
) {
3800 if (RefPtr
<nsIBrowsingContextReadyCallback
> callback
=
3801 mOpenWindowInfo
->BrowsingContextReadyCallback()) {
3802 callback
->BrowsingContextReady(mPendingBrowsingContext
);