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