Bug 1665252 - remove allowpaymentrequest attribute from HTMLIFrameElement r=dom-worke...
[gecko.git] / dom / base / nsFrameLoader.cpp
blob3c921a57beb555795950d69405c50f72320e68a9
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /*
8 * Class for managing loading of a subframe (creation of the docshell,
9 * handling of loads in it, recursion-checking).
12 #include "base/basictypes.h"
14 #include "prenv.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"
35 #include "nsIFrame.h"
36 #include "nsIScrollableFrame.h"
37 #include "nsSubDocumentFrame.h"
38 #include "nsError.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"
47 #include "nsView.h"
48 #include "nsBaseWidget.h"
49 #include "nsQueryObject.h"
50 #include "ReferrerInfo.h"
51 #include "nsIOpenWindowInfo.h"
52 #include "nsISHistory.h"
53 #include "nsIURI.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"
65 #include "Layers.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"
94 #include "jsapi.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"
117 #ifdef XP_WIN
118 # include "mozilla/plugins/PPluginWidgetParent.h"
119 # include "../plugins/ipc/PluginWidgetParent.h"
120 #endif
122 #ifdef MOZ_XUL
123 # include "nsXULPopupManager.h"
124 #endif
126 #ifdef NS_PRINTING
127 # include "mozilla/embedding/printingui/PrintingParent.h"
128 # include "nsIWebBrowserPrint.h"
129 #endif
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,
160 mRemoteBrowser)
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)
169 NS_INTERFACE_MAP_END
171 nsFrameLoader::nsFrameLoader(Element* aOwner, BrowsingContext* aBrowsingContext,
172 bool aIsRemoteFrame, bool aNetworkCreated)
173 : mPendingBrowsingContext(aBrowsingContext),
174 mOwnerContent(aOwner),
175 mDetachedSubdocFrame(nullptr),
176 mPendingSwitchID(0),
177 mChildID(0),
178 mRemoteType(NOT_REMOTE_TYPE),
179 mDepthTooGreat(false),
180 mIsTopLevelContent(false),
181 mDestroyCalled(false),
182 mNeedsAsyncDestroy(false),
183 mInSwap(false),
184 mInShow(false),
185 mHideCalled(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);
211 } else {
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
242 // content.
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()) {
249 return false;
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()) {
256 return true;
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());
284 return do_AddRef(
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()) {
298 return nullptr;
301 BrowsingContext* parentBC = parentInner->GetBrowsingContext();
302 if (NS_WARN_IF(!parentBC) || parentBC->IsDiscarded()) {
303 return nullptr;
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,
319 frameName,
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(),
330 !aNetworkCreated);
333 static bool InitialLoadIsRemote(Element* aOwner) {
334 if (PR_GetEnv("MOZ_DISABLE_OOP_TABS") ||
335 Preferences::GetBool("dom.ipc.tabs.disabled", false)) {
336 return 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()) {
342 return false;
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(
362 Element* aOwner) {
363 nsAutoString attrString;
364 if (aOwner->GetNameSpaceID() != kNameSpaceID_XUL ||
365 !aOwner->GetAttr(nsGkAtoms::initialBrowsingContextGroupId, attrString)) {
366 return nullptr;
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
372 // a JS number.
373 nsresult rv = NS_OK;
374 int64_t signedGroupId{attrString.ToInteger(&rv, 10)};
375 if (NS_FAILED(rv) || signedGroupId <= 0) {
376 return nullptr;
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()),
410 nullptr);
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.
434 if (isRemoteFrame) {
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);
440 } else {
441 fl->mRemoteType = DEFAULT_REMOTE_TYPE;
444 return fl.forget();
447 /* static */
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);
454 #ifdef DEBUG
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());
461 #endif
463 RefPtr<BrowsingContext> context = aContext;
464 if (!context || !aPreserveContext) {
465 context = CreateBrowsingContext(aOwner, /* openWindowInfo */ nullptr,
466 aSpecificGroup);
467 if (aContext) {
468 MOZ_ASSERT(
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);
478 return fl.forget();
481 void nsFrameLoader::LoadFrame(bool aOriginalSrc) {
482 if (NS_WARN_IF(!mOwnerContent)) {
483 return;
486 nsAutoString src;
487 nsCOMPtr<nsIPrincipal> principal;
488 nsCOMPtr<nsIContentSecurityPolicy> csp;
490 bool isSrcdoc = mOwnerContent->IsHTMLElement(nsGkAtoms::iframe) &&
491 mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::srcdoc);
492 if (isSrcdoc) {
493 src.AssignLiteral("about:srcdoc");
494 principal = mOwnerContent->NodePrincipal();
495 csp = mOwnerContent->GetCsp();
496 } else {
497 GetURL(src, getter_AddRefs(principal), getter_AddRefs(csp));
499 src.Trim(" \t\n\r");
501 if (src.IsEmpty()) {
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)) {
508 return;
510 src.AssignLiteral("about:blank");
511 principal = mOwnerContent->NodePrincipal();
512 csp = mOwnerContent->GetCsp();
516 Document* doc = mOwnerContent->OwnerDoc();
517 if (doc->IsStaticDocument()) {
518 return;
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);
536 if (NS_FAILED(rv)) {
537 FireErrorEvent();
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) {
554 return;
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,
565 bool aOriginalSrc) {
566 if (!aURI) return NS_ERROR_INVALID_POINTER;
567 NS_ENSURE_STATE(!mDestroyCalled && mOwnerContent);
568 MOZ_ASSERT(
569 aTriggeringPrincipal,
570 "Must have an explicit triggeringPrincipal to nsFrameLoader::LoadURI.");
572 mLoadingOriginalSrc = aOriginalSrc;
574 nsCOMPtr<Document> doc = mOwnerContent->OwnerDoc();
576 nsresult rv;
577 rv = CheckURILoad(aURI, aTriggeringPrincipal);
578 NS_ENSURE_SUCCESS(rv, rv);
580 mURIToLoad = aURI;
581 mTriggeringPrincipal = aTriggeringPrincipal;
582 mCsp = aCsp;
583 rv = doc->InitializeFrameLoader(this);
584 if (NS_FAILED(rv)) {
585 mURIToLoad = nullptr;
586 mTriggeringPrincipal = nullptr;
587 mCsp = nullptr;
589 return rv;
592 void nsFrameLoader::ResumeLoad(uint64_t aPendingSwitchID) {
593 Document* doc = mOwnerContent->OwnerDoc();
594 if (doc->IsStaticDocument()) {
595 // Static doc shouldn't load sub-documents.
596 return;
599 if (NS_WARN_IF(mDestroyCalled || !mOwnerContent)) {
600 FireErrorEvent();
601 return;
604 mLoadingOriginalSrc = false;
605 mURIToLoad = nullptr;
606 mPendingSwitchID = aPendingSwitchID;
607 mTriggeringPrincipal = mOwnerContent->NodePrincipal();
608 mCsp = mOwnerContent->GetCsp();
610 nsresult rv = doc->InitializeFrameLoader(this);
611 if (NS_FAILED(rv)) {
612 mPendingSwitchID = 0;
613 mTriggeringPrincipal = nullptr;
614 mCsp = nullptr;
616 FireErrorEvent();
620 nsresult nsFrameLoader::ReallyStartLoading() {
621 nsresult rv = ReallyStartLoadingInternal();
622 if (NS_FAILED(rv)) {
623 FireErrorEvent();
626 return rv;
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);
645 } else {
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.
654 if (mCsp) {
655 loadState->SetCsp(mCsp);
656 } else if (!mTriggeringPrincipal) {
657 nsCOMPtr<nsIContentSecurityPolicy> csp = mOwnerContent->GetCsp();
658 loadState->SetCsp(csp);
661 nsAutoString srcdoc;
662 bool isSrcdoc =
663 mOwnerContent->IsHTMLElement(nsGkAtoms::iframe) &&
664 mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::srcdoc, srcdoc);
666 if (isSrcdoc) {
667 loadState->SetSrcdocData(srcdoc);
668 loadState->SetBaseURI(mOwnerContent->GetBaseURI());
671 auto referrerInfo = MakeRefPtr<ReferrerInfo>(*mOwnerContent);
672 loadState->SetReferrerInfo(referrerInfo);
674 loadState->SetIsFromProcessingFrameAttributes();
676 // Default flags:
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;
698 } else {
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));
707 return NS_OK;
710 if (GetDocShell()) {
711 // If we already have a docshell, ensure that the docshell's storage access
712 // flag is cleared.
713 GetDocShell()->MaybeClearStorageAccessFlag();
716 nsresult rv = MaybeCreateDocShell();
717 if (NS_FAILED(rv)) {
718 return rv;
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;
730 return rv;
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);
748 return NS_OK;
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();
769 // Get our principal
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());
778 if (NS_FAILED(rv)) {
779 return rv; // We're not
782 // Bail out if this is an infinite recursion scenario
783 if (IsRemoteFrame()) {
784 return NS_OK;
786 return CheckForRecursiveLoad(aURI);
789 nsDocShell* nsFrameLoader::GetDocShell(ErrorResult& aRv) {
790 if (IsRemoteFrame()) {
791 return nullptr;
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.
797 if (mOwnerContent) {
798 nsresult rv = MaybeCreateDocShell();
799 if (NS_FAILED(rv)) {
800 aRv.Throw(rv);
801 return nullptr;
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));
822 if (aHandler) {
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);
836 if (!isContent) {
837 nsCOMPtr<nsIMozBrowserFrame> mozbrowser =
838 aOwnerContent->GetAsMozBrowserFrame();
839 if (mozbrowser) {
840 mozbrowser->GetMozbrowser(&isContent);
844 if (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);
873 if (aOwner) {
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)) {
885 return false;
889 return true;
892 static bool ParentWindowIsActive(Document* aDoc) {
893 nsCOMPtr<nsPIWindowRoot> root = nsContentUtils::GetWindowRoot(aDoc);
894 if (root) {
895 nsPIDOMWindowOuter* rootWin = root->GetWindow();
896 return rootWin && rootWin->IsActive();
898 return false;
901 void nsFrameLoader::MaybeShowFrame() {
902 nsIFrame* frame = GetPrimaryFrameOfOwningContent();
903 if (frame) {
904 nsSubDocumentFrame* subDocFrame = do_QueryFrame(frame);
905 if (subDocFrame) {
906 subDocFrame->MaybeShowViewer();
911 static ScrollbarPreference GetScrollbarPreference(const Element* aOwner) {
912 if (!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);
922 if (!content) {
923 return result;
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();
933 return result;
936 bool nsFrameLoader::Show(nsSubDocumentFrame* frame) {
937 if (mInShow) {
938 return false;
940 mInShow = true;
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();
952 if (NS_FAILED(rv)) {
953 return false;
955 nsDocShell* ds = GetDocShell();
956 MOZ_ASSERT(ds, "MaybeCreateDocShell succeeded, but null docShell");
957 if (!ds) {
958 return false;
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,
969 NS_FRAME_IS_DIRTY);
972 return true;
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,
980 size.height);
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;
993 if (htmlDoc) {
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());
1005 } else {
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();
1018 mInShow = false;
1019 if (mHideCalled) {
1020 mHideCalled = false;
1021 Hide();
1022 return false;
1024 return true;
1027 void nsFrameLoader::MarginsChanged() {
1028 // We assume that the margins are always zero for remote frames.
1029 if (IsRemoteFrame()) {
1030 return;
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
1036 // margins.
1037 if (!docShell) {
1038 return;
1041 if (!docShell->UpdateFrameMargins(GetMarginAttributes(mOwnerContent))) {
1042 return;
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.");
1073 return false;
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()) {
1081 return false;
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()) {
1087 return false;
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()),
1098 sizeMode);
1099 if (!mRemoteBrowser->Show(info)) {
1100 return false;
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);
1110 } else {
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);
1120 return true;
1123 void nsFrameLoader::Hide() {
1124 if (mHideCalled) {
1125 return;
1127 if (mInShow) {
1128 mHideCalled = true;
1129 return;
1132 if (!GetDocShell()) {
1133 return;
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();
1149 if (!frame) {
1150 return;
1153 nsPresContext* presContext = frame->PresContext();
1154 if (!presContext) {
1155 return;
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());
1172 #ifdef DEBUG
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");
1177 #endif
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
1188 bool equal;
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
1223 // happened.
1224 // This is the reason why now we must retrieve the correct value from the
1225 // usercontextid attribute before comparing our originAttributes with the
1226 // other one.
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;
1272 return rv;
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);
1295 #ifdef XP_WIN
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);
1307 #endif // XP_WIN
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
1340 // ourself.
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))) {
1360 return rv;
1362 MutableTabContext otherContext;
1363 rv = aOther->GetNewTabContext(&otherContext);
1364 if (NS_WARN_IF(NS_FAILED(rv))) {
1365 return rv;
1368 Unused << browserParent->SendSwappedWithOtherRemoteLoader(
1369 ourContext.AsIPCTabContext());
1370 Unused << otherBrowserParent->SendSwappedWithOtherRemoteLoader(
1371 otherContext.AsIPCTabContext());
1372 return NS_OK;
1375 class MOZ_RAII AutoResetInFrameSwap final {
1376 public:
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,
1401 mThisEventTarget);
1402 nsContentUtils::FirePageHideEventForFrameLoaderSwap(mOtherDocShell,
1403 mOtherEventTarget);
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();
1427 private:
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) {
1439 #ifdef DEBUG
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");
1444 #endif
1446 NS_ENSURE_STATE(!mInShow && !aOther->mInShow);
1448 if (IsRemoteFrame() != aOther->IsRemoteFrame()) {
1449 NS_WARNING(
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
1514 bool equal;
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) {
1527 // How odd
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
1573 // type as us.
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
1647 // happened.
1648 // This is the reason why now we must retrieve the correct value from the
1649 // usercontextid attribute before comparing our originAttributes with the
1650 // other one.
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)) {
1677 return 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
1724 // complications.
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
1758 // ourself.
1759 RefPtr<nsFrameLoader> kungFuDeathGrip(this);
1760 aThisOwner->SetFrameLoader(aOther);
1761 aOtherOwner->SetFrameLoader(kungFuDeathGrip);
1763 // Drop any cached content viewers in the two session histories.
1764 if (ourHistory) {
1765 ourHistory->EvictLocalContentViewers();
1767 if (otherHistory) {
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();
1789 return NS_OK;
1792 void nsFrameLoader::Destroy(bool aForProcessSwitch) {
1793 StartDestroy(aForProcessSwitch);
1796 class nsFrameLoaderDestroyRunnable : public Runnable {
1797 enum DestroyPhase {
1798 // See the implementation of Run for an explanation of these phases.
1799 eDestroyDocShell,
1800 eWaitForUnloadMessage,
1801 eDestroyComplete
1804 RefPtr<nsFrameLoader> mFrameLoader;
1805 DestroyPhase mPhase;
1807 public:
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) {
1822 return;
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();
1878 } else {
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);
1893 if (owner) {
1894 owner->ContentShellRemoved(GetDocShell());
1899 // Let our window know that we are gone
1900 if (GetDocShell()) {
1901 nsCOMPtr<nsPIDOMWindowOuter> win_private(GetDocShell()->GetWindow());
1902 if (win_private) {
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() {
1916 switch (mPhase) {
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);
1934 break;
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);
1945 break;
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();
1952 break;
1955 return NS_OK;
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);
2045 AutoJSAPI jsapi;
2046 jsapi.Init();
2048 JS::RootedObject wrapper(jsapi.cx(), GetWrapper());
2049 if (wrapper) {
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()) {
2071 return NS_OK;
2073 if (IsRemoteFrame()) {
2074 return NS_OK;
2076 NS_ENSURE_STATE(!mDestroyCalled);
2078 AssertSafeToInit();
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));
2154 } else {
2155 // Our parent shell is a chrome shell. It is therefore our nearest enclosing
2156 // chrome shell.
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);
2206 if (iframe) {
2207 sandboxFlags = iframe->GetSandboxFlags();
2209 ApplySandboxFlags(sandboxFlags);
2211 if (OwnerIsMozBrowserFrame()) {
2212 // For inproc frames, set the docshell properties.
2213 nsAutoString name;
2214 if (mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::name, name)) {
2215 docShell->SetName(name);
2219 ReallyLoadFrameScripts();
2220 InitializeBrowserAPI();
2222 return NS_OK;
2225 void nsFrameLoader::GetURL(nsString& aURI, nsIPrincipal** aTriggeringPrincipal,
2226 nsIContentSecurityPolicy** aCsp) {
2227 aURI.Truncate();
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);
2238 } else {
2239 mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::src, aURI);
2240 if (RefPtr<nsGenericHTMLFrameElement> frame =
2241 do_QueryObject(mOwnerContent)) {
2242 nsCOMPtr<nsIPrincipal> srcPrincipal = frame->GetSrcTriggeringPrincipal();
2243 if (srcPrincipal) {
2244 triggeringPrincipal = srcPrincipal;
2245 nsCOMPtr<nsIExpandedPrincipal> ep =
2246 do_QueryInterface(triggeringPrincipal);
2247 if (ep) {
2248 csp = ep->GetCsp();
2253 triggeringPrincipal.forget(aTriggeringPrincipal);
2254 csp.forget(aCsp);
2257 nsresult nsFrameLoader::CheckForRecursiveLoad(nsIURI* aURI) {
2258 nsresult rv;
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()) {
2269 return NS_OK;
2272 // Bug 8065: Don't exceed some maximum depth in content frames
2273 // (MAX_DEPTH_CONTENT_FRAMES)
2274 int32_t depth = 0;
2275 for (BrowsingContext* bc = parentBC; bc; bc = bc->GetParent()) {
2276 ++depth;
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
2296 return NS_OK;
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));
2306 if (parentURI) {
2307 // Bug 98158/193011: We need to ignore data after the #
2308 bool equal;
2309 rv = aURI->EqualsExceptRef(parentURI, &equal);
2310 NS_ENSURE_SUCCESS(rv, rv);
2312 if (equal) {
2313 matchCount++;
2314 if (matchCount >= MAX_SAME_URL_CONTENT_FRAMES) {
2315 NS_WARNING(
2316 "Too many nested content frames have the same url (recursion?) "
2317 "so giving up");
2318 return NS_ERROR_UNEXPECTED;
2325 return NS_OK;
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();
2335 if (!doc) {
2336 return NS_ERROR_FAILURE;
2339 MOZ_RELEASE_ASSERT(!doc->IsResourceDoc(), "We shouldn't even exist");
2341 nsCOMPtr<nsPIDOMWindowOuter> win = doc->GetWindow();
2342 if (!win) {
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))) ||
2353 !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);
2360 return NS_OK;
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);
2374 mLazySize = size;
2375 mRemoteBrowser->UpdateDimensions(dimensions, size);
2377 return NS_OK;
2379 UpdateBaseWindowPositionAndSize(aIFrame);
2380 return NS_OK;
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
2398 if (baseWindow) {
2399 int32_t x = 0;
2400 int32_t y = 0;
2402 AutoWeakFrame weakFrame(aIFrame);
2404 baseWindow->GetPosition(&x, &y);
2406 if (!weakFrame.IsAlive()) {
2407 // GetPosition() killed us
2408 return;
2411 ScreenIntSize size = aIFrame->GetSubdocumentSize();
2412 mLazySize = size;
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();
2423 if (frame) {
2424 lazyWidth = frame->PresContext()->DevPixelsToIntCSSPixels(lazyWidth);
2427 return lazyWidth;
2430 uint32_t nsFrameLoader::LazyHeight() const {
2431 uint32_t lazyHeight = mLazySize.height;
2433 nsIFrame* frame = GetPrimaryFrameOfOwningContent();
2434 if (frame) {
2435 lazyHeight = frame->PresContext()->DevPixelsToIntCSSPixels(lazyHeight);
2438 return 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");
2453 AssertSafeToInit();
2455 if (!mOwnerContent) {
2456 return false;
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();
2463 if (!doc) {
2464 return false;
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
2471 // widget.
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) {
2482 return true;
2485 // Ensure the world hasn't changed that much as a result of that.
2486 if (!mOwnerContent || mOwnerContent->OwnerDoc() != doc ||
2487 !mOwnerContent->IsInComposedDoc()) {
2488 return false;
2491 if (!doc->IsActive()) {
2492 // Don't allow subframe loads in non-active documents.
2493 // (See bug 610571 comment 5.)
2494 return false;
2497 nsCOMPtr<nsPIDOMWindowOuter> parentWin = doc->GetWindow();
2498 if (!parentWin) {
2499 return false;
2502 nsCOMPtr<nsIDocShell> parentDocShell = parentWin->GetDocShell();
2503 if (!parentDocShell) {
2504 return false;
2507 if (!EnsureBrowsingContextAttached()) {
2508 return false;
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
2514 // the plugin.
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
2525 // frame.
2526 nsIURI* parentURI = parentWin->GetDocumentURI();
2527 if (!parentURI) {
2528 return false;
2531 nsAutoCString specIgnoringRef;
2532 if (NS_FAILED(parentURI->GetSpecIgnoringRef(specIgnoringRef))) {
2533 return false;
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"))) {
2541 return false;
2545 if (!mOwnerContent->IsXULElement()) {
2546 return false;
2549 if (!mOwnerContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
2550 nsGkAtoms::content, eIgnoreCase)) {
2551 return false;
2555 uint32_t chromeFlags = 0;
2556 nsCOMPtr<nsIDocShellTreeOwner> parentOwner;
2557 if (NS_FAILED(parentDocShell->GetTreeOwner(getter_AddRefs(parentOwner))) ||
2558 !parentOwner) {
2559 return false;
2561 nsCOMPtr<nsIAppWindow> window(do_GetInterface(parentOwner));
2562 if (window && NS_FAILED(window->GetChromeFlags(&chromeFlags))) {
2563 return false;
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");
2580 return false;
2582 nextRemoteBrowser->SetOwnerElement(ownerElement);
2583 } else {
2584 RefPtr<ContentParent> contentParent;
2585 if (mChildID != 0) {
2586 ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
2587 contentParent = cpm->GetContentProcessById(ContentParentId(mChildID));
2589 mRemoteBrowser =
2590 ContentParent::CreateBrowser(context, ownerElement, mRemoteType,
2591 mPendingBrowsingContext, contentParent);
2593 if (!mRemoteBrowser) {
2594 return false;
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();
2647 return true;
2650 bool nsFrameLoader::TryRemoteBrowser() {
2651 // Try to create the internal remote browser.
2652 if (TryRemoteBrowserInternal()) {
2653 return true;
2656 // Check if we should report a browser-crashed error because the browser
2657 // failed to start.
2658 if (XRE_IsParentProcess() && mOwnerContent && mOwnerContent->IsXULElement()) {
2659 MaybeNotifyCrashed(nullptr, nullptr);
2662 return false;
2665 bool nsFrameLoader::IsRemoteFrame() {
2666 if (mIsRemoteFrame) {
2667 MOZ_ASSERT(!GetDocShell(), "Found a remote frame with a DocShell");
2668 return true;
2670 return false;
2673 RemoteBrowser* nsFrameLoader::GetRemoteBrowser() const {
2674 return mRemoteBrowser;
2677 BrowserParent* nsFrameLoader::GetBrowserParent() const {
2678 if (!mRemoteBrowser) {
2679 return nullptr;
2681 RefPtr<BrowserHost> browserHost = mRemoteBrowser->AsBrowserHost();
2682 if (!browserHost) {
2683 return nullptr;
2685 return browserHost->GetActor();
2688 BrowserBridgeChild* nsFrameLoader::GetBrowserBridgeChild() const {
2689 if (!mRemoteBrowser) {
2690 return nullptr;
2692 RefPtr<BrowserBridgeHost> browserBridgeHost =
2693 mRemoteBrowser->AsBrowserBridgeHost();
2694 if (!browserBridgeHost) {
2695 return nullptr;
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);
2709 return;
2712 browserParent->Activate();
2715 void nsFrameLoader::DeactivateRemoteFrame(ErrorResult& aRv) {
2716 auto* browserParent = GetBrowserParent();
2717 if (!browserParent) {
2718 aRv.Throw(NS_ERROR_UNEXPECTED);
2719 return;
2722 browserParent->Deactivate(false);
2725 void nsFrameLoader::SendCrossProcessMouseEvent(const nsAString& aType, float aX,
2726 float aY, int32_t aButton,
2727 int32_t aClickCount,
2728 int32_t aModifiers,
2729 ErrorResult& aRv) {
2730 auto* browserParent = GetBrowserParent();
2731 if (!browserParent) {
2732 aRv.Throw(NS_ERROR_FAILURE);
2733 return;
2736 browserParent->SendMouseEvent(aType, aX, aY, aButton, aClickCount,
2737 aModifiers);
2740 void nsFrameLoader::ActivateFrameEvent(const nsAString& aType, bool aCapture,
2741 ErrorResult& aRv) {
2742 auto* browserParent = GetBrowserParent();
2743 if (!browserParent) {
2744 aRv.Throw(NS_ERROR_FAILURE);
2745 return;
2748 bool ok = browserParent->SendActivateFrameEvent(nsString(aType), aCapture);
2749 if (!ok) {
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();
2758 if (!cc) {
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());
2768 return NS_OK;
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);
2813 return NS_OK;
2816 bool nsFrameLoader::DoLoadMessageManagerScript(const nsAString& aURL,
2817 bool aRunInGlobalScope) {
2818 if (auto* browserParent = GetBrowserParent()) {
2819 return browserParent->SendLoadRemoteScript(nsString(aURL),
2820 aRunInGlobalScope);
2822 RefPtr<InProcessBrowserChildMessageManager> browserChild =
2823 GetBrowserChildMessageManager();
2824 if (browserChild) {
2825 browserChild->LoadFrameScript(aURL, aRunInGlobalScope);
2827 return true;
2830 class nsAsyncMessageToChild : public nsSameProcessAsyncMessageBase,
2831 public Runnable {
2832 public:
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());
2850 return NS_OK;
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)) {
2862 MOZ_CRASH();
2863 return NS_ERROR_DOM_DATA_CLONE_ERR;
2865 if (browserParent->SendAsyncMessage(nsString(aMessage), data)) {
2866 return NS_OK;
2867 } else {
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)) {
2876 return rv;
2878 rv = NS_DispatchToCurrentThread(ev);
2879 if (NS_FAILED(rv)) {
2880 return rv;
2882 return 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) {
2898 return NS_OK;
2901 if (!mIsTopLevelContent && !OwnerIsMozBrowserFrame() && !IsRemoteFrame() &&
2902 !(mOwnerContent->IsXULElement() &&
2903 mOwnerContent->AttrValueIs(kNameSpaceID_None,
2904 nsGkAtoms::forcemessagemanager,
2905 nsGkAtoms::_true, eCaseMatters))) {
2906 return NS_OK;
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();
2925 } else {
2926 parentManager = nsFrameMessageManager::GetGlobalMessageManager();
2929 mMessageManager = new ChromeMessageSender(parentManager);
2930 if (!IsRemoteFrame()) {
2931 nsresult rv = MaybeCreateDocShell();
2932 if (NS_FAILED(rv)) {
2933 return 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) && \
2945 !defined(MOZ_SUITE)
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);
2952 #endif
2954 return NS_OK;
2957 nsresult nsFrameLoader::ReallyLoadFrameScripts() {
2958 nsresult rv = EnsureMessageManager();
2959 if (NS_WARN_IF(NS_FAILED(rv))) {
2960 return rv;
2962 if (mMessageManager) {
2963 mMessageManager->InitWithCallback(this);
2965 return NS_OK;
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();
2991 if (!context) {
2992 MOZ_ASSERT(!IsRemoteFrame(),
2993 "cannot apply sandbox flags to an uninitialized "
2994 "initially-remote frame");
2995 return;
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));
3018 /* virtual */
3019 void nsFrameLoader::AttributeChanged(mozilla::dom::Element* aElement,
3020 int32_t aNameSpaceID, nsAtom* aAttribute,
3021 int32_t aModType,
3022 const nsAttrValue* aOldValue) {
3023 MOZ_ASSERT(mObservingOwnerContent);
3025 if (aElement != mOwnerContent) {
3026 return;
3029 if (aNameSpaceID != kNameSpaceID_None ||
3030 (aAttribute != TypeAttrName(aElement) &&
3031 aAttribute != nsGkAtoms::primary)) {
3032 return;
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);
3043 return;
3046 nsCOMPtr<nsIDocShellTreeItem> parentItem;
3047 GetDocShell()->GetInProcessParent(getter_AddRefs(parentItem));
3048 if (!parentItem) {
3049 return;
3052 if (parentItem->ItemType() != nsIDocShellTreeItem::typeChrome) {
3053 return;
3056 nsCOMPtr<nsIDocShellTreeOwner> parentTreeOwner;
3057 parentItem->GetTreeOwner(getter_AddRefs(parentTreeOwner));
3058 if (!parentTreeOwner) {
3059 return;
3062 bool is_primary = aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::primary,
3063 nsGkAtoms::_true, eIgnoreCase);
3065 #ifdef MOZ_XUL
3066 // when a content panel is no longer primary, hide any open popups it may have
3067 if (!is_primary) {
3068 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
3069 if (pm) {
3070 pm->HidePopupsInDocShell(GetDocShell());
3073 #endif
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)) {
3097 aRv.Throw(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
3106 return false;
3109 // If remote browsing (e10s), handle this with the BrowserParent.
3110 if (auto* browserParent = GetBrowserParent()) {
3111 Unused << browserParent->SendFlushTabState(aFlushId, aIsFinal);
3112 return true;
3115 return false;
3118 void nsFrameLoader::RequestEpochUpdate(uint32_t aEpoch) {
3119 if (mSessionStoreListener) {
3120 mSessionStoreListener->SetEpoch(aEpoch);
3121 return;
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);
3133 return;
3136 // If remote browsing (e10s), handle this with the BrowserParent.
3137 if (auto* browserParent = GetBrowserParent()) {
3138 Unused << browserParent->SendUpdateSHistory(aImmediately);
3142 #ifdef NS_PRINTING
3143 class WebProgressListenerToPromise final : public nsIWebProgressListener {
3144 public:
3145 explicit WebProgressListenerToPromise(Promise* aPromise)
3146 : mPromise(aPromise) {}
3148 NS_DECL_ISUPPORTS
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 &&
3155 mPromise) {
3156 mPromise->MaybeResolveWithUndefined();
3157 mPromise = nullptr;
3159 return NS_OK;
3161 NS_IMETHOD OnStatusChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
3162 nsresult aStatus,
3163 const char16_t* aMessage) override {
3164 if (aStatus != NS_OK && mPromise) {
3165 mPromise->MaybeReject(ErrorResult(aStatus));
3166 mPromise = nullptr;
3168 return NS_OK;
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 {
3175 return NS_OK;
3177 NS_IMETHOD OnLocationChange(nsIWebProgress* aWebProgress,
3178 nsIRequest* aRequest, nsIURI* aLocation,
3179 uint32_t aFlags) override {
3180 return NS_OK;
3182 NS_IMETHOD OnSecurityChange(nsIWebProgress* aWebProgress,
3183 nsIRequest* aRequest, uint32_t aState) override {
3184 return NS_OK;
3186 NS_IMETHOD OnContentBlockingEvent(nsIWebProgress* aWebProgress,
3187 nsIRequest* aRequest,
3188 uint32_t aEvent) override {
3189 return NS_OK;
3192 private:
3193 ~WebProgressListenerToPromise() = default;
3195 RefPtr<Promise> mPromise;
3198 NS_IMPL_ISUPPORTS(WebProgressListenerToPromise, nsIWebProgressListener)
3199 #endif
3201 already_AddRefed<Promise> nsFrameLoader::PrintPreview(
3202 nsIPrintSettings* aPrintSettings,
3203 const Optional<uint64_t>& aSourceOuterWindowID, ErrorResult& aRv) {
3204 auto* ownerDoc = GetOwnerDoc();
3205 if (!ownerDoc) {
3206 aRv.ThrowNotSupportedError("No owner document");
3207 return nullptr;
3209 RefPtr<Promise> promise = Promise::Create(ownerDoc->GetOwnerGlobal(), aRv);
3210 if (!promise) {
3211 return nullptr;
3214 #ifndef NS_PRINTING
3215 promise->MaybeRejectWithNotSupportedError("Build does not support printing");
3216 return promise.forget();
3217 #else
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);
3225 } else {
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;
3239 nsresult rv =
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())
3248 : Nothing());
3250 browserParent->SendPrintPreview(printData, winID)
3251 ->Then(
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()) {
3262 sourceWindow =
3263 nsGlobalWindowOuter::GetOuterWindowWithId(aSourceOuterWindowID.Value());
3264 } else {
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
3283 // into.
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.
3294 ErrorResult rv;
3295 sourceWindow->Print(
3296 aPrintSettings,
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();
3306 #endif
3309 void nsFrameLoader::ExitPrintPreview() {
3310 #ifdef NS_PRINTING
3311 if (auto* browserParent = GetBrowserParent()) {
3312 Unused << browserParent->SendExitPrintPreview();
3313 return;
3315 if (NS_WARN_IF(!GetExistingDocShell())) {
3316 return;
3318 nsCOMPtr<nsIWebBrowserPrint> webBrowserPrint =
3319 do_GetInterface(ToSupports(GetExistingDocShell()->GetWindow()));
3320 if (NS_WARN_IF(!webBrowserPrint)) {
3321 return;
3323 webBrowserPrint->ExitPrintPreview();
3324 #endif
3327 already_AddRefed<Promise> nsFrameLoader::Print(uint64_t aOuterWindowID,
3328 nsIPrintSettings* aPrintSettings,
3329 ErrorResult& aRv) {
3330 RefPtr<Promise> promise =
3331 Promise::Create(GetOwnerDoc()->GetOwnerGlobal(), aRv);
3333 #ifndef NS_PRINTING
3334 promise->MaybeReject(ErrorResult(NS_ERROR_NOT_AVAILABLE));
3335 return promise.forget();
3336 #else
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);
3354 if (!success) {
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();
3367 ErrorResult rv;
3368 outerWindow->Print(aPrintSettings, listener,
3369 /* aDocShellToCloneInto = */ nullptr,
3370 nsGlobalWindowOuter::IsPreview::No,
3371 nsGlobalWindowOuter::BlockUntilDone::No,
3372 /* aPrintPreviewCallback = */ nullptr, rv);
3373 if (rv.Failed()) {
3374 promise->MaybeReject(std::move(rv));
3377 return promise.forget();
3378 #endif
3381 already_AddRefed<nsIRemoteTab> nsFrameLoader::GetRemoteTab() {
3382 if (!mRemoteBrowser) {
3383 return nullptr;
3385 if (auto* browserHost = mRemoteBrowser->AsBrowserHost()) {
3386 return do_AddRef(browserHost);
3388 return nullptr;
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;
3400 return nullptr;
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.
3415 return nullptr;
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()) {
3431 return;
3434 nsresult rv = EnsureMessageManager();
3435 if (NS_WARN_IF(NS_FAILED(rv))) {
3436 return;
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);
3444 if (browserFrame) {
3445 browserFrame->InitializeBrowserAPI();
3449 void nsFrameLoader::DestroyBrowserFrameScripts() {
3450 if (!OwnerIsMozBrowserFrame()) {
3451 return;
3453 nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwnerContent);
3454 if (browserFrame) {
3455 browserFrame->DestroyBrowserFrameScripts();
3459 void nsFrameLoader::StartPersistence(
3460 BrowsingContext* aContext, nsIWebBrowserPersistDocumentReceiver* aRecv,
3461 ErrorResult& aRv) {
3462 MOZ_ASSERT(aRecv);
3463 RefPtr<BrowsingContext> context = aContext ? aContext : GetBrowsingContext();
3465 if (!context || !context->IsInSubtreeOf(GetBrowsingContext())) {
3466 aRecv->OnError(NS_ERROR_NO_CONTENT);
3467 return;
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);
3476 return;
3479 nsCOMPtr<Document> foundDoc = context->GetDocument();
3481 if (!foundDoc) {
3482 aRecv->OnError(NS_ERROR_NO_CONTENT);
3483 } else {
3484 nsCOMPtr<nsIWebBrowserPersistDocument> pdoc =
3485 new mozilla::WebBrowserPersistLocalDocument(foundDoc);
3486 aRecv->OnDocumentReady(pdoc);
3490 void nsFrameLoader::MaybeUpdatePrimaryBrowserParent(
3491 BrowserParentChange aChange) {
3492 if (!mOwnerContent || !mRemoteBrowser) {
3493 return;
3496 RefPtr<BrowserHost> browserHost = mRemoteBrowser->AsBrowserHost();
3497 if (!browserHost) {
3498 return;
3501 nsCOMPtr<nsIDocShell> docShell = mOwnerContent->OwnerDoc()->GetDocShell();
3502 if (!docShell) {
3503 return;
3506 BrowsingContext* browsingContext = docShell->GetBrowsingContext();
3507 if (!browsingContext->IsChrome()) {
3508 return;
3511 nsCOMPtr<nsIDocShellTreeOwner> parentTreeOwner;
3512 docShell->GetTreeOwner(getter_AddRefs(parentTreeOwner));
3513 if (!parentTreeOwner) {
3514 return;
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,
3531 nsIURI* aURI) {
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();
3546 if (doc) {
3547 nsCOMPtr<nsPIWindowRoot> root = nsContentUtils::GetWindowRoot(doc);
3548 if (root) {
3549 showFocusRings = root->ShowFocusRings() ? UIStateChangeType_Set
3550 : UIStateChangeType_Clear;
3552 nsPIDOMWindowOuter* outerWin = root->GetWindow();
3553 if (outerWin) {
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);
3565 return NS_OK;
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()) {
3573 return NS_OK;
3576 nsAutoString attributeValue;
3577 if (aAttr.mUserContextId ==
3578 nsIScriptSecurityManager::DEFAULT_USER_CONTEXT_ID &&
3579 mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::usercontextid,
3580 attributeValue) &&
3581 !attributeValue.IsEmpty()) {
3582 nsresult rv;
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,
3590 attributeValue) &&
3591 !attributeValue.IsEmpty()) {
3592 // XXX: Should we check the format from `GeckoViewNavigation.jsm` here?
3593 aAttr.mGeckoViewSessionContextId = attributeValue;
3596 return NS_OK;
3599 ProcessMessageManager* nsFrameLoader::GetProcessMessageManager() const {
3600 if (auto* browserParent = GetBrowserParent()) {
3601 return browserParent->Manager()->GetMessageManager();
3603 return nullptr;
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);
3610 return 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
3633 // solution.
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();
3647 return;
3650 // In process
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) {
3659 return;
3662 if (mPendingBrowsingContext == aBrowsingContext) {
3663 mTabProcessCrashFired = true;
3666 // Fire the crashed observer notification.
3667 nsCOMPtr<nsIObserverService> os = services::GetObserverService();
3668 if (!os) {
3669 return;
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);
3681 if (!owner) {
3682 return;
3685 RefPtr<nsFrameLoader> currentFrameLoader = owner->GetFrameLoader();
3686 if (currentFrameLoader != this) {
3687 return;
3690 // Fire the actual crashed event.
3691 nsString eventName;
3692 if (aChannel && !aChannel->DoBuildIDsMatch()) {
3693 eventName = u"oop-browser-buildid-mismatch"_ns;
3694 } else {
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,
3710 nullptr);
3713 bool nsFrameLoader::EnsureBrowsingContextAttached() {
3714 nsresult rv;
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
3744 // be constant.
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))) {
3756 return false;
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() ==
3775 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.
3780 return true;
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();
3795 return true;
3798 void nsFrameLoader::InvokeBrowsingContextReadyCallback() {
3799 if (mOpenWindowInfo) {
3800 if (RefPtr<nsIBrowsingContextReadyCallback> callback =
3801 mOpenWindowInfo->BrowsingContextReadyCallback()) {
3802 callback->BrowsingContextReady(mPendingBrowsingContext);