Source code

Revision control

Other Tools

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 "base/basictypes.h"
13
14
#include "prenv.h"
15
16
#include "nsDocShell.h"
17
#include "nsIDOMMozBrowserFrame.h"
18
#include "nsIDOMWindow.h"
19
#include "nsIContentInlines.h"
20
#include "nsIContentViewer.h"
21
#include "mozilla/dom/Document.h"
22
#include "nsPIDOMWindow.h"
23
#include "nsIWebNavigation.h"
24
#include "nsIWebProgress.h"
25
#include "nsIDocShell.h"
26
#include "nsIDocShellTreeOwner.h"
27
#include "nsDocShellLoadState.h"
28
#include "nsIBaseWindow.h"
29
#include "nsIBrowser.h"
30
#include "nsContentUtils.h"
31
#include "nsIXPConnect.h"
32
#include "nsUnicharUtils.h"
33
#include "nsIScriptGlobalObject.h"
34
#include "nsIScriptSecurityManager.h"
35
#include "nsIScrollable.h"
36
#include "nsFrameLoader.h"
37
#include "nsFrameLoaderOwner.h"
38
#include "nsIFrame.h"
39
#include "nsIScrollableFrame.h"
40
#include "nsSubDocumentFrame.h"
41
#include "nsError.h"
42
#include "nsISHistory.h"
43
#include "nsIXULWindow.h"
44
#include "nsIMozBrowserFrame.h"
45
#include "nsISHistory.h"
46
#include "nsIScriptError.h"
47
#include "nsGlobalWindow.h"
48
#include "nsHTMLDocument.h"
49
#include "nsPIWindowRoot.h"
50
#include "nsLayoutUtils.h"
51
#include "nsMappedAttributes.h"
52
#include "nsView.h"
53
#include "nsBaseWidget.h"
54
#include "nsQueryObject.h"
55
#include "ReferrerInfo.h"
56
57
#include "nsIURI.h"
58
#include "nsIURL.h"
59
#include "nsNetUtil.h"
60
61
#include "nsGkAtoms.h"
62
#include "nsNameSpaceManager.h"
63
64
#include "nsThreadUtils.h"
65
66
#include "nsIDOMChromeWindow.h"
67
#include "InProcessBrowserChildMessageManager.h"
68
69
#include "Layers.h"
70
#include "ClientLayerManager.h"
71
72
#include "ContentParent.h"
73
#include "BrowserParent.h"
74
#include "mozilla/AsyncEventDispatcher.h"
75
#include "mozilla/BasePrincipal.h"
76
#include "mozilla/ExpandedPrincipal.h"
77
#include "mozilla/GuardObjects.h"
78
#include "mozilla/HTMLEditor.h"
79
#include "mozilla/NullPrincipal.h"
80
#include "mozilla/Preferences.h"
81
#include "mozilla/PresShell.h"
82
#include "mozilla/PresShellInlines.h"
83
#include "mozilla/Unused.h"
84
#include "mozilla/dom/ChromeMessageSender.h"
85
#include "mozilla/dom/Element.h"
86
#include "mozilla/dom/FrameCrashedEvent.h"
87
#include "mozilla/dom/FrameLoaderBinding.h"
88
#include "mozilla/dom/MozFrameLoaderOwnerBinding.h"
89
#include "mozilla/dom/SessionStoreListener.h"
90
#include "mozilla/dom/WindowGlobalParent.h"
91
#include "mozilla/gfx/CrossProcessPaint.h"
92
#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
93
#include "nsGenericHTMLFrameElement.h"
94
#include "GeckoProfiler.h"
95
96
#include "jsapi.h"
97
#include "mozilla/dom/HTMLIFrameElement.h"
98
#include "nsSandboxFlags.h"
99
#include "mozilla/layers/CompositorBridgeChild.h"
100
#include "mozilla/dom/CustomEvent.h"
101
102
#include "mozilla/dom/ipc/StructuredCloneData.h"
103
#include "mozilla/WebBrowserPersistLocalDocument.h"
104
#include "mozilla/dom/Promise.h"
105
#include "mozilla/dom/PromiseNativeHandler.h"
106
#include "mozilla/dom/ParentSHistory.h"
107
#include "mozilla/dom/ChildSHistory.h"
108
#include "mozilla/dom/CanonicalBrowsingContext.h"
109
#include "mozilla/dom/ContentChild.h"
110
#include "mozilla/dom/BrowserBridgeChild.h"
111
#include "mozilla/dom/BrowserHost.h"
112
#include "mozilla/dom/BrowserBridgeHost.h"
113
114
#include "mozilla/dom/HTMLBodyElement.h"
115
116
#include "mozilla/ContentPrincipal.h"
117
118
#ifdef XP_WIN
119
# include "mozilla/plugins/PPluginWidgetParent.h"
120
# include "../plugins/ipc/PluginWidgetParent.h"
121
#endif
122
123
#ifdef MOZ_XUL
124
# include "nsXULPopupManager.h"
125
#endif
126
127
#ifdef NS_PRINTING
128
# include "mozilla/embedding/printingui/PrintingParent.h"
129
# include "nsIWebBrowserPrint.h"
130
#endif
131
132
using namespace mozilla;
133
using namespace mozilla::hal;
134
using namespace mozilla::dom;
135
using namespace mozilla::dom::ipc;
136
using namespace mozilla::layers;
137
using namespace mozilla::layout;
138
typedef ScrollableLayerGuid::ViewID ViewID;
139
140
// Bug 136580: Limit to the number of nested content frames that can have the
141
// same URL. This is to stop content that is recursively loading
142
// itself. Note that "#foo" on the end of URL doesn't affect
143
// whether it's considered identical, but "?foo" or ";foo" are
144
// considered and compared.
145
// Limit this to 2, like chromium does.
146
#define MAX_SAME_URL_CONTENT_FRAMES 2
147
148
// Bug 8065: Limit content frame depth to some reasonable level. This
149
// does not count chrome frames when determining depth, nor does it
150
// prevent chrome recursion. Number is fairly arbitrary, but meant to
151
// keep number of shells to a reasonable number on accidental recursion with a
152
// small (but not 1) branching factor. With large branching factors the number
153
// of shells can rapidly become huge and run us out of memory. To solve that,
154
// we'd need to re-institute a fixed version of bug 98158.
155
#define MAX_DEPTH_CONTENT_FRAMES 10
156
157
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsFrameLoader, mBrowsingContext,
158
mMessageManager, mChildMessageManager,
159
mParentSHistory, mRemoteBrowser,
160
mStaticCloneOf)
161
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFrameLoader)
162
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFrameLoader)
163
164
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFrameLoader)
165
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
166
NS_INTERFACE_MAP_ENTRY_CONCRETE(nsFrameLoader)
167
NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
168
NS_INTERFACE_MAP_ENTRY(nsISupports)
169
NS_INTERFACE_MAP_END
170
171
nsFrameLoader::nsFrameLoader(Element* aOwner, BrowsingContext* aBrowsingContext,
172
const nsAString& aRemoteType, bool aNetworkCreated)
173
: mBrowsingContext(aBrowsingContext),
174
mOwnerContent(aOwner),
175
mDetachedSubdocFrame(nullptr),
176
mPendingSwitchID(0),
177
mChildID(0),
178
mRemoteType(aRemoteType),
179
mDepthTooGreat(false),
180
mIsTopLevelContent(false),
181
mDestroyCalled(false),
182
mNeedsAsyncDestroy(false),
183
mInSwap(false),
184
mInShow(false),
185
mHideCalled(false),
186
mNetworkCreated(aNetworkCreated),
187
mLoadingOriginalSrc(false),
188
mRemoteBrowserShown(false),
189
mIsRemoteFrame(!aRemoteType.IsEmpty()),
190
mObservingOwnerContent(false),
191
mTabProcessCrashFired(false) {}
192
193
nsFrameLoader::~nsFrameLoader() {
194
if (mMessageManager) {
195
mMessageManager->Disconnect();
196
}
197
MOZ_RELEASE_ASSERT(mDestroyCalled);
198
}
199
200
static nsAtom* TypeAttrName(Element* aOwnerContent) {
201
return aOwnerContent->IsXULElement() ? nsGkAtoms::type
202
: nsGkAtoms::mozframetype;
203
}
204
205
static void GetFrameName(Element* aOwnerContent, nsAString& aFrameName) {
206
int32_t namespaceID = aOwnerContent->GetNameSpaceID();
207
if (namespaceID == kNameSpaceID_XHTML && !aOwnerContent->IsInHTMLDocument()) {
208
aOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::id, aFrameName);
209
} else {
210
aOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::name, aFrameName);
211
// XXX if no NAME then use ID, after a transition period this will be
212
// changed so that XUL only uses ID too (bug 254284).
213
if (aFrameName.IsEmpty() && namespaceID == kNameSpaceID_XUL) {
214
aOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::id, aFrameName);
215
}
216
}
217
}
218
219
// If this method returns true, the nsFrameLoader will act as a boundary, as is
220
// the case for <iframe mozbrowser> and <browser type="content"> elements.
221
//
222
// # Historical Notes (10 April 2019)
223
//
224
// In the past, this boundary was defined by the "typeContent" and "typeChrome"
225
// nsIDocShellTreeItem types. There was only ever a single split in the tree,
226
// and it occurred at the boundary between these two types of docshells. When
227
// <iframe mozbrowser> was introduced, it was given special casing to make it
228
// act like a second boundary, without having to change the existing code.
229
//
230
// The about:addons page, which is loaded within a content browser, then added a
231
// remote <browser type="content" remote="true"> element. When remote, this
232
// would also act as a mechanism for creating a disjoint tree, due to the
233
// process keeping the embedder and embedee separate.
234
//
235
// However, when initial out-of-process iframe support was implemented, this
236
// codepath became a risk, as it could've caused the oop iframe remote
237
// WindowProxy code to be activated for the addons page. This was fixed by
238
// extendng the isolation logic previously reserved to <iframe mozbrowser> to
239
// also cover <browser> elements with the explicit `remote` property loaded in
240
// content.
241
//
242
// To keep these boundaries clear, and allow them to work in a cross-process
243
// manner, they are no longer handled by typeContent and typeChrome. Instead,
244
// the actual BrowsingContext tree is broken at these edges.
245
static bool IsTopContent(BrowsingContext* aParent, Element* aOwner) {
246
nsCOMPtr<nsIMozBrowserFrame> mozbrowser = aOwner->GetAsMozBrowserFrame();
247
248
if (aParent->IsContent()) {
249
// If we're already in content, we may still want to create a new
250
// BrowsingContext tree if our element is either:
251
// a) a real <iframe mozbrowser> frame, or
252
// b) a xul browser element with a `remote="true"` marker.
253
return (mozbrowser && mozbrowser->GetReallyIsBrowser()) ||
254
(aOwner->IsXULElement() &&
255
aOwner->AttrValueIs(kNameSpaceID_None, nsGkAtoms::remote,
256
nsGkAtoms::_true, eCaseMatters));
257
}
258
259
// If we're in a chrome context, we want to start a new tree if:
260
// a) we have any mozbrowser frame (even if disabled), or
261
// b) we are an element with a `type="content"` marker.
262
return (mozbrowser && mozbrowser->GetMozbrowser()) ||
263
(aOwner->AttrValueIs(kNameSpaceID_None, TypeAttrName(aOwner),
264
nsGkAtoms::content, eIgnoreCase));
265
}
266
267
static already_AddRefed<BrowsingContext> CreateBrowsingContext(
268
Element* aOwner, BrowsingContext* aOpener) {
269
Document* doc = aOwner->OwnerDoc();
270
// Get our parent docshell off the document of mOwnerContent
271
// XXXbz this is such a total hack.... We really need to have a
272
// better setup for doing this.
273
274
// Determine our parent nsDocShell
275
RefPtr<nsDocShell> parentDocShell = nsDocShell::Cast(doc->GetDocShell());
276
277
if (NS_WARN_IF(!parentDocShell)) {
278
return nullptr;
279
}
280
281
RefPtr<BrowsingContext> parentContext = parentDocShell->GetBrowsingContext();
282
283
// Don't create a child docshell for a discarded browsing context.
284
if (NS_WARN_IF(!parentContext) || parentContext->IsDiscarded()) {
285
return nullptr;
286
}
287
288
// Determine the frame name for the new browsing context.
289
nsAutoString frameName;
290
GetFrameName(aOwner, frameName);
291
292
if (IsTopContent(parentContext, aOwner)) {
293
// Create toplevel content without a parent & as Type::Content.
294
return BrowsingContext::Create(nullptr, aOpener, frameName,
295
BrowsingContext::Type::Content);
296
}
297
298
auto type = parentContext->IsContent() ? BrowsingContext::Type::Content
299
: BrowsingContext::Type::Chrome;
300
301
return BrowsingContext::Create(parentContext, aOpener, frameName, type);
302
}
303
304
static bool InitialLoadIsRemote(Element* aOwner) {
305
if (PR_GetEnv("MOZ_DISABLE_OOP_TABS") ||
306
Preferences::GetBool("dom.ipc.tabs.disabled", false)) {
307
return false;
308
}
309
310
// The initial load in an content process iframe should never be made remote.
311
// Content process iframes always become remote due to navigation.
312
if (XRE_IsContentProcess()) {
313
return false;
314
}
315
316
// If we're an <iframe mozbrowser> and we don't have a "remote" attribute,
317
// fall back to the default.
318
nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(aOwner);
319
bool isMozBrowserFrame = browserFrame && browserFrame->GetReallyIsBrowser();
320
if (isMozBrowserFrame &&
321
!aOwner->HasAttr(kNameSpaceID_None, nsGkAtoms::remote)) {
322
return Preferences::GetBool("dom.ipc.browser_frames.oop_by_default", false);
323
}
324
325
// Otherwise, we're remote if we have "remote=true" and we're either a
326
// browser frame or a XUL element.
327
return (isMozBrowserFrame || aOwner->GetNameSpaceID() == kNameSpaceID_XUL) &&
328
aOwner->AttrValueIs(kNameSpaceID_None, nsGkAtoms::remote,
329
nsGkAtoms::_true, eCaseMatters);
330
}
331
332
already_AddRefed<nsFrameLoader> nsFrameLoader::Create(Element* aOwner,
333
BrowsingContext* aOpener,
334
bool aNetworkCreated) {
335
NS_ENSURE_TRUE(aOwner, nullptr);
336
Document* doc = aOwner->OwnerDoc();
337
338
// We never create nsFrameLoaders for elements in resource documents.
339
//
340
// We never create nsFrameLoaders for elements in data documents, unless the
341
// document is a static document.
342
// Static documents are an exception because any sub-documents need an
343
// nsFrameLoader to keep the relevant docShell alive, even though the
344
// nsFrameLoader isn't used to load anything (the sub-document is created by
345
// the static clone process).
346
//
347
// We never create nsFrameLoaders for elements that are not
348
// in-composed-document, unless the element belongs to a static document.
349
// Static documents are an exception because this method is called at a point
350
// in the static clone process before aOwner has been inserted into its
351
// document. For other types of documents this wouldn't be a problem since
352
// we'd create the nsFrameLoader as necessary after aOwner is inserted into a
353
// document, but the mechanisms that take care of that don't apply for static
354
// documents so we need to create the nsFrameLoader now. (This isn't wasteful
355
// since for a static document we know aOwner will end up in a document and
356
// the nsFrameLoader will be used for its docShell.)
357
//
358
NS_ENSURE_TRUE(!doc->IsResourceDoc() &&
359
((!doc->IsLoadedAsData() && aOwner->IsInComposedDoc()) ||
360
doc->IsStaticDocument()),
361
nullptr);
362
363
RefPtr<BrowsingContext> context = CreateBrowsingContext(aOwner, aOpener);
364
NS_ENSURE_TRUE(context, nullptr);
365
366
// Determine the initial RemoteType from the load environment. An empty or
367
// void remote type denotes a non-remote frame, while a named remote type
368
// denotes a remote frame.
369
nsAutoString remoteType(VoidString());
370
if (InitialLoadIsRemote(aOwner)) {
371
MOZ_ASSERT(!aOpener, "Cannot pass `aOpener` for a remote frame!");
372
373
// If the `remoteType` attribute is specified and valid, use it. Otherwise,
374
// use a default remote type.
375
bool hasRemoteType =
376
aOwner->GetAttr(kNameSpaceID_None, nsGkAtoms::RemoteType, remoteType);
377
if (!hasRemoteType || remoteType.IsEmpty()) {
378
remoteType.AssignLiteral(DEFAULT_REMOTE_TYPE);
379
}
380
}
381
382
RefPtr<nsFrameLoader> fl =
383
new nsFrameLoader(aOwner, context, remoteType, aNetworkCreated);
384
return fl.forget();
385
}
386
387
/* static */
388
already_AddRefed<nsFrameLoader> nsFrameLoader::Recreate(
389
mozilla::dom::Element* aOwner, BrowsingContext* aContext,
390
const nsAString& aRemoteType, bool aNetworkCreated) {
391
NS_ENSURE_TRUE(aOwner, nullptr);
392
393
#ifdef DEBUG
394
// This version of Create is only called for Remoteness updates, so we can
395
// assume we need a FrameLoader here and skip the check in the other Create.
396
Document* doc = aOwner->OwnerDoc();
397
MOZ_ASSERT(!doc->IsResourceDoc());
398
MOZ_ASSERT((!doc->IsLoadedAsData() && aOwner->IsInComposedDoc()) ||
399
doc->IsStaticDocument());
400
#endif
401
402
RefPtr<BrowsingContext> context = aContext;
403
if (!context) {
404
context = CreateBrowsingContext(aOwner, /* opener */ nullptr);
405
}
406
NS_ENSURE_TRUE(context, nullptr);
407
408
RefPtr<nsFrameLoader> fl =
409
new nsFrameLoader(aOwner, context, aRemoteType, aNetworkCreated);
410
return fl.forget();
411
}
412
413
void nsFrameLoader::LoadFrame(bool aOriginalSrc) {
414
if (NS_WARN_IF(!mOwnerContent)) {
415
return;
416
}
417
418
nsAutoString src;
419
nsCOMPtr<nsIPrincipal> principal;
420
nsCOMPtr<nsIContentSecurityPolicy> csp;
421
422
bool isSrcdoc = mOwnerContent->IsHTMLElement(nsGkAtoms::iframe) &&
423
mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::srcdoc);
424
if (isSrcdoc) {
425
src.AssignLiteral("about:srcdoc");
426
principal = mOwnerContent->NodePrincipal();
427
csp = mOwnerContent->GetCsp();
428
} else {
429
GetURL(src, getter_AddRefs(principal), getter_AddRefs(csp));
430
431
src.Trim(" \t\n\r");
432
433
if (src.IsEmpty()) {
434
// If the frame is a XUL element and has the attribute 'nodefaultsrc=true'
435
// then we will not use 'about:blank' as fallback but return early without
436
// starting a load if no 'src' attribute is given (or it's empty).
437
if (mOwnerContent->IsXULElement() &&
438
mOwnerContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::nodefaultsrc,
439
nsGkAtoms::_true, eCaseMatters)) {
440
return;
441
}
442
src.AssignLiteral("about:blank");
443
principal = mOwnerContent->NodePrincipal();
444
csp = mOwnerContent->GetCsp();
445
}
446
}
447
448
Document* doc = mOwnerContent->OwnerDoc();
449
if (doc->IsStaticDocument()) {
450
return;
451
}
452
453
if (doc->IsLoadedAsInteractiveData()) {
454
// XBL bindings doc shouldn't load sub-documents.
455
return;
456
}
457
458
nsIURI* base_uri = mOwnerContent->GetBaseURI();
459
auto encoding = doc->GetDocumentCharacterSet();
460
461
nsCOMPtr<nsIURI> uri;
462
nsresult rv = NS_NewURI(getter_AddRefs(uri), src, encoding, base_uri);
463
464
// If the URI was malformed, try to recover by loading about:blank.
465
if (rv == NS_ERROR_MALFORMED_URI) {
466
rv = NS_NewURI(getter_AddRefs(uri), NS_LITERAL_STRING("about:blank"),
467
encoding, base_uri);
468
}
469
470
if (NS_SUCCEEDED(rv)) {
471
rv = LoadURI(uri, principal, csp, aOriginalSrc);
472
}
473
474
if (NS_FAILED(rv)) {
475
FireErrorEvent();
476
}
477
}
478
479
void nsFrameLoader::FireErrorEvent() {
480
if (!mOwnerContent) {
481
return;
482
}
483
RefPtr<AsyncEventDispatcher> loadBlockingAsyncDispatcher =
484
new LoadBlockingAsyncEventDispatcher(
485
mOwnerContent, NS_LITERAL_STRING("error"), CanBubble::eNo,
486
ChromeOnlyDispatch::eNo);
487
loadBlockingAsyncDispatcher->PostDOMEvent();
488
}
489
490
nsresult nsFrameLoader::LoadURI(nsIURI* aURI,
491
nsIPrincipal* aTriggeringPrincipal,
492
nsIContentSecurityPolicy* aCsp,
493
bool aOriginalSrc) {
494
if (!aURI) return NS_ERROR_INVALID_POINTER;
495
NS_ENSURE_STATE(!mDestroyCalled && mOwnerContent);
496
MOZ_ASSERT(
497
aTriggeringPrincipal,
498
"Must have an explicit triggeringPrincipal to nsFrameLoader::LoadURI.");
499
500
mLoadingOriginalSrc = aOriginalSrc;
501
502
nsCOMPtr<Document> doc = mOwnerContent->OwnerDoc();
503
504
nsresult rv;
505
rv = CheckURILoad(aURI, aTriggeringPrincipal);
506
NS_ENSURE_SUCCESS(rv, rv);
507
508
mURIToLoad = aURI;
509
mTriggeringPrincipal = aTriggeringPrincipal;
510
mCsp = aCsp;
511
rv = doc->InitializeFrameLoader(this);
512
if (NS_FAILED(rv)) {
513
mURIToLoad = nullptr;
514
mTriggeringPrincipal = nullptr;
515
mCsp = nullptr;
516
}
517
return rv;
518
}
519
520
void nsFrameLoader::ResumeLoad(uint64_t aPendingSwitchID) {
521
Document* doc = mOwnerContent->OwnerDoc();
522
if (doc->IsStaticDocument() || doc->IsLoadedAsInteractiveData()) {
523
// Static & XBL bindings doc shouldn't load sub-documents.
524
return;
525
}
526
527
if (NS_WARN_IF(mDestroyCalled || !mOwnerContent)) {
528
FireErrorEvent();
529
return;
530
}
531
532
mLoadingOriginalSrc = false;
533
mURIToLoad = nullptr;
534
mPendingSwitchID = aPendingSwitchID;
535
mTriggeringPrincipal = mOwnerContent->NodePrincipal();
536
mCsp = mOwnerContent->GetCsp();
537
538
nsresult rv = doc->InitializeFrameLoader(this);
539
if (NS_FAILED(rv)) {
540
mPendingSwitchID = 0;
541
mTriggeringPrincipal = nullptr;
542
mCsp = nullptr;
543
544
FireErrorEvent();
545
}
546
}
547
548
nsresult nsFrameLoader::ReallyStartLoading() {
549
nsresult rv = ReallyStartLoadingInternal();
550
if (NS_FAILED(rv)) {
551
FireErrorEvent();
552
}
553
554
return rv;
555
}
556
557
nsresult nsFrameLoader::ReallyStartLoadingInternal() {
558
NS_ENSURE_STATE((mURIToLoad || mPendingSwitchID) && mOwnerContent &&
559
mOwnerContent->IsInComposedDoc());
560
561
AUTO_PROFILER_LABEL("nsFrameLoader::ReallyStartLoadingInternal", OTHER);
562
563
if (IsRemoteFrame()) {
564
if (!EnsureRemoteBrowser()) {
565
NS_WARNING("Couldn't create child process for iframe.");
566
return NS_ERROR_FAILURE;
567
}
568
569
if (mPendingSwitchID) {
570
mRemoteBrowser->ResumeLoad(mPendingSwitchID);
571
mPendingSwitchID = 0;
572
} else {
573
mRemoteBrowser->LoadURL(mURIToLoad);
574
}
575
576
if (!mRemoteBrowserShown) {
577
// This can fail if it's too early to show the frame, we will retry later.
578
Unused << ShowRemoteFrame(ScreenIntSize(0, 0));
579
}
580
581
return NS_OK;
582
}
583
584
if (GetDocShell()) {
585
// If we already have a docshell, ensure that the docshell's storage access
586
// flag is cleared.
587
GetDocShell()->MaybeClearStorageAccessFlag();
588
}
589
590
nsresult rv = MaybeCreateDocShell();
591
if (NS_FAILED(rv)) {
592
return rv;
593
}
594
MOZ_ASSERT(GetDocShell(),
595
"MaybeCreateDocShell succeeded with a null docShell");
596
597
// If we have a pending switch, just resume our load.
598
if (mPendingSwitchID) {
599
bool tmpState = mNeedsAsyncDestroy;
600
mNeedsAsyncDestroy = true;
601
rv = GetDocShell()->ResumeRedirectedLoad(mPendingSwitchID, -1);
602
mNeedsAsyncDestroy = tmpState;
603
mPendingSwitchID = 0;
604
return rv;
605
}
606
607
// Just to be safe, recheck uri.
608
rv = CheckURILoad(mURIToLoad, mTriggeringPrincipal);
609
NS_ENSURE_SUCCESS(rv, rv);
610
611
RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(mURIToLoad);
612
613
loadState->SetOriginalFrameSrc(mLoadingOriginalSrc);
614
mLoadingOriginalSrc = false;
615
616
// If this frame is sandboxed with respect to origin we will set it up with
617
// a null principal later in nsDocShell::DoURILoad.
618
// We do it there to correctly sandbox content that was loaded into
619
// the frame via other methods than the src attribute.
620
// We'll use our principal, not that of the document loaded inside us. This
621
// is very important; needed to prevent XSS attacks on documents loaded in
622
// subframes!
623
if (mTriggeringPrincipal) {
624
loadState->SetTriggeringPrincipal(mTriggeringPrincipal);
625
} else {
626
loadState->SetTriggeringPrincipal(mOwnerContent->NodePrincipal());
627
}
628
629
// If we have an explicit CSP, we set it. If not, we only query it from
630
// the document in case there was no explicit triggeringPrincipal.
631
// Otherwise it's possible that the original triggeringPrincipal did not
632
// have a CSP which causes the CSP on the Principal and explicit CSP
633
// to be out of sync.
634
if (mCsp) {
635
loadState->SetCsp(mCsp);
636
} else if (!mTriggeringPrincipal) {
637
nsCOMPtr<nsIContentSecurityPolicy> csp = mOwnerContent->GetCsp();
638
loadState->SetCsp(csp);
639
}
640
641
nsAutoString srcdoc;
642
bool isSrcdoc =
643
mOwnerContent->IsHTMLElement(nsGkAtoms::iframe) &&
644
mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::srcdoc, srcdoc);
645
646
if (isSrcdoc) {
647
loadState->SetSrcdocData(srcdoc);
648
loadState->SetBaseURI(mOwnerContent->GetBaseURI());
649
}
650
651
nsCOMPtr<nsIReferrerInfo> referrerInfo = new ReferrerInfo();
652
referrerInfo->InitWithNode(mOwnerContent);
653
loadState->SetReferrerInfo(referrerInfo);
654
655
// Default flags:
656
int32_t flags = nsIWebNavigation::LOAD_FLAGS_NONE;
657
658
// Flags for browser frame:
659
if (OwnerIsMozBrowserFrame()) {
660
flags = nsIWebNavigation::LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP |
661
nsIWebNavigation::LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL;
662
}
663
664
loadState->SetIsFromProcessingFrameAttributes();
665
666
// Kick off the load...
667
bool tmpState = mNeedsAsyncDestroy;
668
mNeedsAsyncDestroy = true;
669
loadState->SetLoadFlags(flags);
670
loadState->SetFirstParty(false);
671
rv = GetDocShell()->LoadURI(loadState);
672
mNeedsAsyncDestroy = tmpState;
673
mURIToLoad = nullptr;
674
NS_ENSURE_SUCCESS(rv, rv);
675
676
return NS_OK;
677
}
678
679
nsresult nsFrameLoader::CheckURILoad(nsIURI* aURI,
680
nsIPrincipal* aTriggeringPrincipal) {
681
// Check for security. The fun part is trying to figure out what principals
682
// to use. The way I figure it, if we're doing a LoadFrame() accidentally
683
// (eg someone created a frame/iframe node, we're being parsed, XUL iframes
684
// are being reframed, etc.) then we definitely want to use the node
685
// principal of mOwnerContent for security checks. If, on the other hand,
686
// someone's setting the src on our owner content, or created it via script,
687
// or whatever, then they can clearly access it... and we should still use
688
// the principal of mOwnerContent. I don't think that leads to privilege
689
// escalation, and it's reasonably guaranteed to not lead to XSS issues
690
// (since caller can already access mOwnerContent in this case). So just use
691
// the principal of mOwnerContent no matter what. If script wants to run
692
// things with its own permissions, which differ from those of mOwnerContent
693
// (which means the script is privileged in some way) it should set
694
// window.location instead.
695
nsIScriptSecurityManager* secMan = nsContentUtils::GetSecurityManager();
696
697
// Get our principal
698
nsIPrincipal* principal =
699
(aTriggeringPrincipal ? aTriggeringPrincipal
700
: mOwnerContent->NodePrincipal());
701
702
// Check if we are allowed to load absURL
703
nsresult rv = secMan->CheckLoadURIWithPrincipal(
704
principal, aURI, nsIScriptSecurityManager::STANDARD);
705
if (NS_FAILED(rv)) {
706
return rv; // We're not
707
}
708
709
// Bail out if this is an infinite recursion scenario
710
if (IsRemoteFrame()) {
711
return NS_OK;
712
}
713
return CheckForRecursiveLoad(aURI);
714
}
715
716
nsDocShell* nsFrameLoader::GetDocShell(ErrorResult& aRv) {
717
if (IsRemoteFrame()) {
718
return nullptr;
719
}
720
721
// If we have an owner, make sure we have a docshell and return
722
// that. If not, we're most likely in the middle of being torn down,
723
// then we just return null.
724
if (mOwnerContent) {
725
nsresult rv = MaybeCreateDocShell();
726
if (NS_FAILED(rv)) {
727
aRv.Throw(rv);
728
return nullptr;
729
}
730
MOZ_ASSERT(GetDocShell(),
731
"MaybeCreateDocShell succeeded, but null docShell");
732
}
733
734
return GetDocShell();
735
}
736
737
static void SetTreeOwnerAndChromeEventHandlerOnDocshellTree(
738
nsIDocShellTreeItem* aItem, nsIDocShellTreeOwner* aOwner,
739
EventTarget* aHandler) {
740
MOZ_ASSERT(aItem, "Must have item");
741
742
aItem->SetTreeOwner(aOwner);
743
744
int32_t childCount = 0;
745
aItem->GetInProcessChildCount(&childCount);
746
for (int32_t i = 0; i < childCount; ++i) {
747
nsCOMPtr<nsIDocShellTreeItem> item;
748
aItem->GetInProcessChildAt(i, getter_AddRefs(item));
749
if (aHandler) {
750
nsCOMPtr<nsIDocShell> shell(do_QueryInterface(item));
751
shell->SetChromeEventHandler(aHandler);
752
}
753
SetTreeOwnerAndChromeEventHandlerOnDocshellTree(item, aOwner, aHandler);
754
}
755
}
756
757
#if defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
758
static bool CheckDocShellType(mozilla::dom::Element* aOwnerContent,
759
nsIDocShellTreeItem* aDocShell, nsAtom* aAtom) {
760
bool isContent = aOwnerContent->AttrValueIs(kNameSpaceID_None, aAtom,
761
nsGkAtoms::content, eIgnoreCase);
762
763
if (!isContent) {
764
nsCOMPtr<nsIMozBrowserFrame> mozbrowser =
765
aOwnerContent->GetAsMozBrowserFrame();
766
if (mozbrowser) {
767
mozbrowser->GetMozbrowser(&isContent);
768
}
769
}
770
771
if (isContent) {
772
return aDocShell->ItemType() == nsIDocShellTreeItem::typeContent;
773
}
774
775
nsCOMPtr<nsIDocShellTreeItem> parent;
776
aDocShell->GetInProcessParent(getter_AddRefs(parent));
777
778
return parent && parent->ItemType() == aDocShell->ItemType();
779
}
780
#endif // defined(MOZ_DIAGNOSTIC_ASSERT_ENABLED)
781
782
/**
783
* Hook up a given TreeItem to its tree owner. aItem's type must have already
784
* been set, and it should already be part of the DocShellTree.
785
* @param aItem the treeitem we're working with
786
* @param aTreeOwner the relevant treeowner; might be null
787
*/
788
void nsFrameLoader::AddTreeItemToTreeOwner(nsIDocShellTreeItem* aItem,
789
nsIDocShellTreeOwner* aOwner) {
790
MOZ_ASSERT(aItem, "Must have docshell treeitem");
791
MOZ_ASSERT(mOwnerContent, "Must have owning content");
792
793
MOZ_DIAGNOSTIC_ASSERT(
794
CheckDocShellType(mOwnerContent, aItem, TypeAttrName(mOwnerContent)),
795
"Correct ItemType should be set when creating BrowsingContext");
796
797
if (mIsTopLevelContent) {
798
bool is_primary = mOwnerContent->AttrValueIs(
799
kNameSpaceID_None, nsGkAtoms::primary, nsGkAtoms::_true, eIgnoreCase);
800
if (aOwner) {
801
mOwnerContent->AddMutationObserver(this);
802
mObservingOwnerContent = true;
803
aOwner->ContentShellAdded(aItem, is_primary);
804
}
805
}
806
}
807
808
static bool AllDescendantsOfType(nsIDocShellTreeItem* aParentItem,
809
int32_t aType) {
810
int32_t childCount = 0;
811
aParentItem->GetInProcessChildCount(&childCount);
812
813
for (int32_t i = 0; i < childCount; ++i) {
814
nsCOMPtr<nsIDocShellTreeItem> kid;
815
aParentItem->GetInProcessChildAt(i, getter_AddRefs(kid));
816
817
if (kid->ItemType() != aType || !AllDescendantsOfType(kid, aType)) {
818
return false;
819
}
820
}
821
822
return true;
823
}
824
825
/**
826
* A class that automatically sets mInShow to false when it goes
827
* out of scope.
828
*/
829
class MOZ_RAII AutoResetInShow {
830
private:
831
nsFrameLoader* mFrameLoader;
832
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
833
public:
834
explicit AutoResetInShow(
835
nsFrameLoader* aFrameLoader MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
836
: mFrameLoader(aFrameLoader) {
837
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
838
}
839
~AutoResetInShow() { mFrameLoader->mInShow = false; }
840
};
841
842
static bool ParentWindowIsActive(Document* aDoc) {
843
nsCOMPtr<nsPIWindowRoot> root = nsContentUtils::GetWindowRoot(aDoc);
844
if (root) {
845
nsPIDOMWindowOuter* rootWin = root->GetWindow();
846
return rootWin && rootWin->IsActive();
847
}
848
return false;
849
}
850
851
void nsFrameLoader::MaybeShowFrame() {
852
nsIFrame* frame = GetPrimaryFrameOfOwningContent();
853
if (frame) {
854
nsSubDocumentFrame* subDocFrame = do_QueryFrame(frame);
855
if (subDocFrame) {
856
subDocFrame->MaybeShowViewer();
857
}
858
}
859
}
860
861
bool nsFrameLoader::Show(int32_t marginWidth, int32_t marginHeight,
862
int32_t scrollbarPrefX, int32_t scrollbarPrefY,
863
nsSubDocumentFrame* frame) {
864
if (mInShow) {
865
return false;
866
}
867
// Reset mInShow if we exit early.
868
AutoResetInShow resetInShow(this);
869
mInShow = true;
870
871
ScreenIntSize size = frame->GetSubdocumentSize();
872
if (IsRemoteFrame()) {
873
return ShowRemoteFrame(size, frame);
874
}
875
876
nsresult rv = MaybeCreateDocShell();
877
if (NS_FAILED(rv)) {
878
return false;
879
}
880
MOZ_ASSERT(GetDocShell(), "MaybeCreateDocShell succeeded, but null docShell");
881
if (!GetDocShell()) {
882
return false;
883
}
884
885
GetDocShell()->SetMarginWidth(marginWidth);
886
GetDocShell()->SetMarginHeight(marginHeight);
887
888
GetDocShell()->SetDefaultScrollbarPreferences(
889
nsIScrollable::ScrollOrientation_X, scrollbarPrefX);
890
GetDocShell()->SetDefaultScrollbarPreferences(
891
nsIScrollable::ScrollOrientation_Y, scrollbarPrefY);
892
893
if (PresShell* presShell = GetDocShell()->GetPresShell()) {
894
// Ensure root scroll frame is reflowed in case scroll preferences or
895
// margins have changed
896
nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame();
897
if (rootScrollFrame) {
898
presShell->FrameNeedsReflow(rootScrollFrame, IntrinsicDirty::Resize,
899
NS_FRAME_IS_DIRTY);
900
}
901
return true;
902
}
903
904
nsView* view = frame->EnsureInnerView();
905
if (!view) return false;
906
907
RefPtr<nsDocShell> baseWindow = GetDocShell();
908
baseWindow->InitWindow(nullptr, view->GetWidget(), 0, 0, size.width,
909
size.height);
910
// This is kinda whacky, this "Create()" call doesn't really
911
// create anything, one starts to wonder why this was named
912
// "Create"...
913
baseWindow->Create();
914
baseWindow->SetVisibility(true);
915
NS_ENSURE_TRUE(GetDocShell(), false);
916
917
// Trigger editor re-initialization if midas is turned on in the
918
// sub-document. This shouldn't be necessary, but given the way our
919
// editor works, it is. See
921
if (RefPtr<PresShell> presShell = GetDocShell()->GetPresShell()) {
922
Document* doc = presShell->GetDocument();
923
nsHTMLDocument* htmlDoc =
924
doc && doc->IsHTMLOrXHTML() ? doc->AsHTMLDocument() : nullptr;
925
926
if (htmlDoc) {
927
nsAutoString designMode;
928
htmlDoc->GetDesignMode(designMode);
929
930
if (designMode.EqualsLiteral("on")) {
931
// Hold on to the editor object to let the document reattach to the
932
// same editor object, instead of creating a new one.
933
RefPtr<HTMLEditor> htmlEditor = GetDocShell()->GetHTMLEditor();
934
Unused << htmlEditor;
935
htmlDoc->SetDesignMode(NS_LITERAL_STRING("off"), Nothing(),
936
IgnoreErrors());
937
938
htmlDoc->SetDesignMode(NS_LITERAL_STRING("on"), Nothing(),
939
IgnoreErrors());
940
} else {
941
// Re-initialize the presentation for contenteditable documents
942
bool editable = false, hasEditingSession = false;
943
GetDocShell()->GetEditable(&editable);
944
GetDocShell()->GetHasEditingSession(&hasEditingSession);
945
RefPtr<HTMLEditor> htmlEditor = GetDocShell()->GetHTMLEditor();
946
if (editable && hasEditingSession && htmlEditor) {
947
htmlEditor->PostCreate();
948
}
949
}
950
}
951
}
952
953
mInShow = false;
954
if (mHideCalled) {
955
mHideCalled = false;
956
Hide();
957
return false;
958
}
959
return true;
960
}
961
962
void nsFrameLoader::MarginsChanged(uint32_t aMarginWidth,
963
uint32_t aMarginHeight) {
964
// We assume that the margins are always zero for remote frames.
965
if (IsRemoteFrame()) {
966
return;
967
}
968
969
// If there's no docshell, we're probably not up and running yet.
970
// nsFrameLoader::Show() will take care of setting the right
971
// margins.
972
if (!GetDocShell()) {
973
return;
974
}
975
976
// Set the margins
977
GetDocShell()->SetMarginWidth(aMarginWidth);
978
GetDocShell()->SetMarginHeight(aMarginHeight);
979
980
// There's a cached property declaration block
981
// that needs to be updated
982
if (Document* doc = GetDocShell()->GetDocument()) {
983
for (nsINode* cur = doc; cur; cur = cur->GetNextNode()) {
984
if (cur->IsHTMLElement(nsGkAtoms::body)) {
985
static_cast<HTMLBodyElement*>(cur)->ClearMappedServoStyle();
986
}
987
}
988
}
989
990
// Trigger a restyle if there's a prescontext
991
// FIXME: This could do something much less expensive.
992
if (nsPresContext* presContext = GetDocShell()->GetPresContext()) {
993
// rebuild, because now the same nsMappedAttributes* will produce
994
// a different style
995
presContext->RebuildAllStyleData(nsChangeHint(0),
996
RestyleHint::RestyleSubtree());
997
}
998
}
999
1000
bool nsFrameLoader::ShowRemoteFrame(const ScreenIntSize& size,
1001
nsSubDocumentFrame* aFrame) {
1002
AUTO_PROFILER_LABEL("nsFrameLoader::ShowRemoteFrame", OTHER);
1003
NS_ASSERTION(IsRemoteFrame(),
1004
"ShowRemote only makes sense on remote frames.");
1005
1006
if (!EnsureRemoteBrowser()) {
1007
NS_ERROR("Couldn't create child process.");
1008
return false;
1009
}
1010
1011
// FIXME/bug 589337: Show()/Hide() is pretty expensive for
1012
// cross-process layers; need to figure out what behavior we really
1013
// want here. For now, hack.
1014
if (!mRemoteBrowserShown) {
1015
if (!mOwnerContent || !mOwnerContent->GetComposedDoc()) {
1016
return false;
1017
}
1018
1019
// We never want to host remote frameloaders in simple popups, like menus.
1020
nsIWidget* widget = nsContentUtils::WidgetForContent(mOwnerContent);
1021
if (!widget || static_cast<nsBaseWidget*>(widget)->IsSmallPopup()) {
1022
return false;
1023
}
1024
1025
if (RefPtr<BrowserBridgeChild> browserBridgeChild =
1026
GetBrowserBridgeChild()) {
1027
nsCOMPtr<nsISupports> container =
1028
mOwnerContent->OwnerDoc()->GetContainer();
1029
nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(container);
1030
nsCOMPtr<nsIWidget> mainWidget;
1031
baseWindow->GetMainWidget(getter_AddRefs(mainWidget));
1032
nsSizeMode sizeMode =
1033
mainWidget ? mainWidget->SizeMode() : nsSizeMode_Normal;
1034
1035
Unused << browserBridgeChild->SendShow(
1036
size, ParentWindowIsActive(mOwnerContent->OwnerDoc()), sizeMode);
1037
mRemoteBrowserShown = true;
1038
return true;
1039
}
1040
1041
if (!mRemoteBrowser->Show(
1042
size, ParentWindowIsActive(mOwnerContent->OwnerDoc()))) {
1043
return false;
1044
}
1045
mRemoteBrowserShown = true;
1046
1047
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
1048
if (os) {
1049
os->NotifyObservers(ToSupports(this), "remote-browser-shown", nullptr);
1050
}
1051
} else {
1052
nsIntRect dimensions;
1053
NS_ENSURE_SUCCESS(GetWindowDimensions(dimensions), false);
1054
1055
// Don't show remote iframe if we are waiting for the completion of reflow.
1056
if (!aFrame || !(aFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
1057
mRemoteBrowser->UpdateDimensions(dimensions, size);
1058
}
1059
}
1060
1061
return true;
1062
}
1063
1064
void nsFrameLoader::Hide() {
1065
if (mHideCalled) {
1066
return;
1067
}
1068
if (mInShow) {
1069
mHideCalled = true;
1070
return;
1071
}
1072
1073
if (!GetDocShell()) {
1074
return;
1075
}
1076
1077
GetDocShell()->MaybeClearStorageAccessFlag();
1078
1079
nsCOMPtr<nsIContentViewer> contentViewer;
1080
GetDocShell()->GetContentViewer(getter_AddRefs(contentViewer));
1081
if (contentViewer) contentViewer->SetSticky(false);
1082
1083
RefPtr<nsDocShell> baseWin = GetDocShell();
1084
baseWin->SetVisibility(false);
1085
baseWin->SetParentWidget(nullptr);
1086
}
1087
1088
void nsFrameLoader::ForceLayoutIfNecessary() {
1089
nsIFrame* frame = GetPrimaryFrameOfOwningContent();
1090
if (!frame) {
1091
return;
1092
}
1093
1094
nsPresContext* presContext = frame->PresContext();
1095
if (!presContext) {
1096
return;
1097
}
1098
1099
// Only force the layout flush if the frameloader hasn't ever been
1100
// run through layout.
1101
if (frame->GetStateBits() & NS_FRAME_FIRST_REFLOW) {
1102
if (RefPtr<PresShell> presShell = presContext->GetPresShell()) {
1103
presShell->FlushPendingNotifications(FlushType::Layout);
1104
}
1105
}
1106
}
1107
1108
nsresult nsFrameLoader::SwapWithOtherRemoteLoader(
1109
nsFrameLoader* aOther, nsFrameLoaderOwner* aThisOwner,
1110
nsFrameLoaderOwner* aOtherOwner) {
1111
MOZ_ASSERT(NS_IsMainThread());
1112
1113
#ifdef DEBUG
1114
RefPtr<nsFrameLoader> first = aThisOwner->GetFrameLoader();
1115
RefPtr<nsFrameLoader> second = aOtherOwner->GetFrameLoader();
1116
MOZ_ASSERT(first == this, "aThisOwner must own this");
1117
MOZ_ASSERT(second == aOther, "aOtherOwner must own aOther");
1118
#endif
1119
1120
Element* ourContent = mOwnerContent;
1121
Element* otherContent = aOther->mOwnerContent;
1122
1123
if (!ourContent || !otherContent) {
1124
// Can't handle this
1125
return NS_ERROR_NOT_IMPLEMENTED;
1126
}
1127
1128
// Make sure there are no same-origin issues
1129
bool equal;
1130
nsresult rv = ourContent->NodePrincipal()->Equals(
1131
otherContent->NodePrincipal(), &equal);
1132
if (NS_FAILED(rv) || !equal) {
1133
// Security problems loom. Just bail on it all
1134
return NS_ERROR_DOM_SECURITY_ERR;
1135
}
1136
1137
Document* ourDoc = ourContent->GetComposedDoc();
1138
Document* otherDoc = otherContent->GetComposedDoc();
1139
if (!ourDoc || !otherDoc) {
1140
// Again, how odd, given that we had docshells
1141
return NS_ERROR_NOT_IMPLEMENTED;
1142
}
1143
1144
PresShell* ourPresShell = ourDoc->GetPresShell();
1145
PresShell* otherPresShell = otherDoc->GetPresShell();
1146
if (!ourPresShell || !otherPresShell) {
1147
return NS_ERROR_NOT_IMPLEMENTED;
1148
}
1149
1150
auto* browserParent = GetBrowserParent();
1151
auto* otherBrowserParent = aOther->GetBrowserParent();
1152
1153
if (!browserParent || !otherBrowserParent) {
1154
return NS_ERROR_NOT_IMPLEMENTED;
1155
}
1156
1157
// When we swap docShells, maybe we have to deal with a new page created just
1158
// for this operation. In this case, the browser code should already have set
1159
// the correct userContextId attribute value in the owning element, but our
1160
// docShell, that has been created way before) doesn't know that that
1161
// happened.
1162
// This is the reason why now we must retrieve the correct value from the
1163
// usercontextid attribute before comparing our originAttributes with the
1164
// other one.
1165
OriginAttributes ourOriginAttributes = browserParent->OriginAttributesRef();
1166
rv = PopulateUserContextIdFromAttribute(ourOriginAttributes);
1167
NS_ENSURE_SUCCESS(rv, rv);
1168
1169
OriginAttributes otherOriginAttributes =
1170
otherBrowserParent->OriginAttributesRef();
1171
rv = aOther->PopulateUserContextIdFromAttribute(otherOriginAttributes);
1172
NS_ENSURE_SUCCESS(rv, rv);
1173
1174
if (ourOriginAttributes != otherOriginAttributes) {
1175
return NS_ERROR_NOT_IMPLEMENTED;
1176
}
1177
1178
bool ourHasHistory =
1179
mIsTopLevelContent && ourContent->IsXULElement(nsGkAtoms::browser) &&
1180
!ourContent->HasAttr(kNameSpaceID_None, nsGkAtoms::disablehistory);
1181
bool otherHasHistory =
1182
aOther->mIsTopLevelContent &&
1183
otherContent->IsXULElement(nsGkAtoms::browser) &&
1184
!otherContent->HasAttr(kNameSpaceID_None, nsGkAtoms::disablehistory);
1185
if (ourHasHistory != otherHasHistory) {
1186
return NS_ERROR_NOT_IMPLEMENTED;
1187
}
1188
1189
if (mInSwap || aOther->mInSwap) {
1190
return NS_ERROR_NOT_IMPLEMENTED;
1191
}
1192
mInSwap = aOther->mInSwap = true;
1193
1194
nsIFrame* ourFrame = ourContent->GetPrimaryFrame();
1195
nsIFrame* otherFrame = otherContent->GetPrimaryFrame();
1196
if (!ourFrame || !otherFrame) {
1197
mInSwap = aOther->mInSwap = false;
1198
return NS_ERROR_NOT_IMPLEMENTED;
1199
}
1200
1201
nsSubDocumentFrame* ourFrameFrame = do_QueryFrame(ourFrame);
1202
if (!ourFrameFrame) {
1203
mInSwap = aOther->mInSwap = false;
1204
return NS_ERROR_NOT_IMPLEMENTED;
1205
}
1206
1207
rv = ourFrameFrame->BeginSwapDocShells(otherFrame);
1208
if (NS_FAILED(rv)) {
1209
mInSwap = aOther->mInSwap = false;
1210
return rv;
1211
}
1212
1213
nsCOMPtr<nsIBrowserDOMWindow> otherBrowserDOMWindow =
1214
otherBrowserParent->GetBrowserDOMWindow();
1215
nsCOMPtr<nsIBrowserDOMWindow> browserDOMWindow =
1216
browserParent->GetBrowserDOMWindow();
1217
1218
if (!!otherBrowserDOMWindow != !!browserDOMWindow) {
1219
return NS_ERROR_NOT_IMPLEMENTED;
1220
}
1221
1222
// Destroy browser frame scripts for content leaving a frame with browser API
1223
if (OwnerIsMozBrowserFrame() && !aOther->OwnerIsMozBrowserFrame()) {
1224
DestroyBrowserFrameScripts();
1225
}
1226
if (!OwnerIsMozBrowserFrame() && aOther->OwnerIsMozBrowserFrame()) {
1227
aOther->DestroyBrowserFrameScripts();
1228
}
1229
1230
otherBrowserParent->SetBrowserDOMWindow(browserDOMWindow);
1231
browserParent->SetBrowserDOMWindow(otherBrowserDOMWindow);
1232
1233
#ifdef XP_WIN
1234
// Native plugin windows used by this remote content need to be reparented.
1235
if (nsPIDOMWindowOuter* newWin = ourDoc->GetWindow()) {
1236
RefPtr<nsIWidget> newParent =
1237
nsGlobalWindowOuter::Cast(newWin)->GetMainWidget();
1238
const ManagedContainer<mozilla::plugins::PPluginWidgetParent>& plugins =
1239
otherBrowserParent->ManagedPPluginWidgetParent();
1240
for (auto iter = plugins.ConstIter(); !iter.Done(); iter.Next()) {
1241
static_cast<mozilla::plugins::PluginWidgetParent*>(iter.Get()->GetKey())
1242
->SetParent(newParent);
1243
}
1244
}
1245
#endif // XP_WIN
1246
1247
MaybeUpdatePrimaryBrowserParent(eBrowserParentRemoved);
1248
aOther->MaybeUpdatePrimaryBrowserParent(eBrowserParentRemoved);
1249
1250
SetOwnerContent(otherContent);
1251
aOther->SetOwnerContent(ourContent);
1252
1253
browserParent->SetOwnerElement(otherContent);
1254
otherBrowserParent->SetOwnerElement(ourContent);
1255
1256
// Update window activation state for the swapped owner content.
1257
Unused << browserParent->SendParentActivated(
1258
ParentWindowIsActive(otherContent->OwnerDoc()));
1259
Unused << otherBrowserParent->SendParentActivated(
1260
ParentWindowIsActive(ourContent->OwnerDoc()));
1261
1262
MaybeUpdatePrimaryBrowserParent(eBrowserParentChanged);
1263
aOther->MaybeUpdatePrimaryBrowserParent(eBrowserParentChanged);
1264
1265
RefPtr<nsFrameMessageManager> ourMessageManager = mMessageManager;
1266
RefPtr<nsFrameMessageManager> otherMessageManager = aOther->mMessageManager;
1267
// Swap and setup things in parent message managers.
1268
if (ourMessageManager) {
1269
ourMessageManager->SetCallback(aOther);
1270
}
1271
if (otherMessageManager) {
1272
otherMessageManager->SetCallback(this);
1273
}
1274
mMessageManager.swap(aOther->mMessageManager);
1275
1276
// Perform the actual swap of the internal refptrs. We keep a strong reference
1277
// to ourselves to make sure we don't die while we overwrite our reference to
1278
// ourself.
1279
RefPtr<nsFrameLoader> kungFuDeathGrip(this);
1280
aThisOwner->SetFrameLoader(aOther);
1281
aOtherOwner->SetFrameLoader(kungFuDeathGrip);
1282
1283
ourFrameFrame->EndSwapDocShells(otherFrame);
1284
1285
ourPresShell->BackingScaleFactorChanged();
1286
otherPresShell->BackingScaleFactorChanged();
1287
1288
// Initialize browser API if needed now that owner content has changed.
1289
InitializeBrowserAPI();
1290
aOther->InitializeBrowserAPI();
1291
1292
mInSwap = aOther->mInSwap = false;
1293
1294
// Send an updated tab context since owner content type may have changed.
1295
MutableTabContext ourContext;
1296
rv = GetNewTabContext(&ourContext);
1297
if (NS_WARN_IF(NS_FAILED(rv))) {
1298
return rv;
1299
}
1300
MutableTabContext otherContext;
1301
rv = aOther->GetNewTabContext(&otherContext);
1302
if (NS_WARN_IF(NS_FAILED(rv))) {
1303
return rv;
1304
}
1305
1306
Unused << browserParent->SendSwappedWithOtherRemoteLoader(
1307
ourContext.AsIPCTabContext());
1308
Unused << otherBrowserParent->SendSwappedWithOtherRemoteLoader(
1309
otherContext.AsIPCTabContext());
1310
return NS_OK;
1311
}
1312
1313
class MOZ_RAII AutoResetInFrameSwap final {
1314
public:
1315
AutoResetInFrameSwap(
1316
nsFrameLoader* aThisFrameLoader, nsFrameLoader* aOtherFrameLoader,
1317
nsDocShell* aThisDocShell, nsDocShell* aOtherDocShell,
1318
EventTarget* aThisEventTarget,
1319
EventTarget* aOtherEventTarget MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
1320
: mThisFrameLoader(aThisFrameLoader),
1321
mOtherFrameLoader(aOtherFrameLoader),
1322
mThisDocShell(aThisDocShell),
1323
mOtherDocShell(aOtherDocShell),
1324
mThisEventTarget(aThisEventTarget),
1325
mOtherEventTarget(aOtherEventTarget) {
1326
MOZ_GUARD_OBJECT_NOTIFIER_INIT;
1327
1328
mThisFrameLoader->mInSwap = true;
1329
mOtherFrameLoader->mInSwap = true;
1330
mThisDocShell->SetInFrameSwap(true);
1331
mOtherDocShell->SetInFrameSwap(true);
1332
1333
// Fire pageshow events on still-loading pages, and then fire pagehide
1334
// events. Note that we do NOT fire these in the normal way, but just fire
1335
// them on the chrome event handlers.
1336
nsContentUtils::FirePageShowEvent(mThisDocShell, mThisEventTarget, false);
1337
nsContentUtils::FirePageShowEvent(mOtherDocShell, mOtherEventTarget, false);
1338
nsContentUtils::FirePageHideEvent(mThisDocShell, mThisEventTarget);
1339
nsContentUtils::FirePageHideEvent(mOtherDocShell, mOtherEventTarget);
1340
}
1341
1342
~AutoResetInFrameSwap() {
1343
nsContentUtils::FirePageShowEvent(mThisDocShell, mThisEventTarget, true);
1344
nsContentUtils::FirePageShowEvent(mOtherDocShell, mOtherEventTarget, true);
1345
1346
mThisFrameLoader->mInSwap = false;
1347
mOtherFrameLoader->mInSwap = false;
1348
mThisDocShell->SetInFrameSwap(false);
1349
mOtherDocShell->SetInFrameSwap(false);
1350
}
1351
1352
private:
1353
RefPtr<nsFrameLoader> mThisFrameLoader;
1354
RefPtr<nsFrameLoader> mOtherFrameLoader;
1355
RefPtr<nsDocShell> mThisDocShell;
1356
RefPtr<nsDocShell> mOtherDocShell;
1357
nsCOMPtr<EventTarget> mThisEventTarget;
1358
nsCOMPtr<EventTarget> mOtherEventTarget;
1359
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
1360
};
1361
1362
nsresult nsFrameLoader::SwapWithOtherLoader(nsFrameLoader* aOther,
1363
nsFrameLoaderOwner* aThisOwner,
1364
nsFrameLoaderOwner* aOtherOwner) {
1365
#ifdef DEBUG
1366
RefPtr<nsFrameLoader> first = aThisOwner->GetFrameLoader();
1367
RefPtr<nsFrameLoader> second = aOtherOwner->GetFrameLoader();
1368
MOZ_ASSERT(first == this, "aThisOwner must own this");
1369
MOZ_ASSERT(second == aOther, "aOtherOwner must own aOther");
1370
#endif
1371
1372
NS_ENSURE_STATE(!mInShow && !aOther->mInShow);
1373
1374
if (IsRemoteFrame() != aOther->IsRemoteFrame()) {
1375
NS_WARNING(
1376
"Swapping remote and non-remote frames is not currently supported");
1377
return NS_ERROR_NOT_IMPLEMENTED;
1378
}
1379
1380
Element* ourContent = mOwnerContent;
1381
Element* otherContent = aOther->mOwnerContent;
1382
1383
if (!ourContent || !otherContent) {
1384
// Can't handle this
1385
return NS_ERROR_NOT_IMPLEMENTED;
1386
}
1387
1388
bool ourHasSrcdoc = ourContent->IsHTMLElement(nsGkAtoms::iframe) &&
1389
ourContent->HasAttr(kNameSpaceID_None, nsGkAtoms::srcdoc);
1390
bool otherHasSrcdoc =
1391
otherContent->IsHTMLElement(nsGkAtoms::iframe) &&
1392
otherContent->HasAttr(kNameSpaceID_None, nsGkAtoms::srcdoc);
1393
if (ourHasSrcdoc || otherHasSrcdoc) {
1394
// Ignore this case entirely for now, since we support XUL <-> HTML swapping
1395
return NS_ERROR_NOT_IMPLEMENTED;
1396
}
1397
1398
bool ourFullscreenAllowed =
1399
ourContent->IsXULElement() ||
1400
(OwnerIsMozBrowserFrame() &&
1401
(ourContent->HasAttr(kNameSpaceID_None, nsGkAtoms::allowfullscreen) ||
1402
ourContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozallowfullscreen)));
1403
bool otherFullscreenAllowed =
1404
otherContent->IsXULElement() ||
1405
(aOther->OwnerIsMozBrowserFrame() &&
1406
(otherContent->HasAttr(kNameSpaceID_None, nsGkAtoms::allowfullscreen) ||
1407
otherContent->HasAttr(kNameSpaceID_None,
1408
nsGkAtoms::mozallowfullscreen)));
1409
if (ourFullscreenAllowed != otherFullscreenAllowed) {
1410
return NS_ERROR_NOT_IMPLEMENTED;
1411
}
1412
1413
bool ourPaymentRequestAllowed =
1414
ourContent->HasAttr(kNameSpaceID_None, nsGkAtoms::allowpaymentrequest);
1415
bool otherPaymentRequestAllowed =
1416
otherContent->HasAttr(kNameSpaceID_None, nsGkAtoms::allowpaymentrequest);
1417
if (ourPaymentRequestAllowed != otherPaymentRequestAllowed) {
1418
return NS_ERROR_NOT_IMPLEMENTED;
1419
}
1420
1421
// Divert to a separate path for the remaining steps in the remote case
1422
if (IsRemoteFrame()) {
1423
MOZ_ASSERT(aOther->IsRemoteFrame());
1424
return SwapWithOtherRemoteLoader(aOther, aThisOwner, aOtherOwner);
1425
}
1426
1427
// Make sure there are no same-origin issues
1428
bool equal;
1429
nsresult rv = ourContent->NodePrincipal()->Equals(
1430
otherContent->NodePrincipal(), &equal);
1431
if (NS_FAILED(rv) || !equal) {
1432
// Security problems loom. Just bail on it all
1433
return NS_ERROR_DOM_SECURITY_ERR;
1434
}
1435
1436
RefPtr<nsDocShell> ourDocshell =
1437
static_cast<nsDocShell*>(GetExistingDocShell());
1438
RefPtr<nsDocShell> otherDocshell =
1439
static_cast<nsDocShell*>(aOther->GetExistingDocShell());
1440
if (!ourDocshell || !otherDocshell) {
1441
// How odd
1442
return NS_ERROR_NOT_IMPLEMENTED;
1443
}
1444
1445
// To avoid having to mess with session history, avoid swapping
1446
// frameloaders that don't correspond to root same-type docshells,
1447
// unless both roots have session history disabled.
1448
nsCOMPtr<nsIDocShellTreeItem> ourRootTreeItem, otherRootTreeItem;
1449
ourDocshell->GetInProcessSameTypeRootTreeItem(
1450
getter_AddRefs(ourRootTreeItem));
1451
otherDocshell->GetInProcessSameTypeRootTreeItem(
1452
getter_AddRefs(otherRootTreeItem));
1453
nsCOMPtr<nsIWebNavigation> ourRootWebnav = do_QueryInterface(ourRootTreeItem);
1454
nsCOMPtr<nsIWebNavigation> otherRootWebnav =
1455
do_QueryInterface(otherRootTreeItem);
1456
1457
if (!ourRootWebnav || !otherRootWebnav) {
1458
return NS_ERROR_NOT_IMPLEMENTED;
1459
}
1460
1461
RefPtr<ChildSHistory> ourHistory = ourRootWebnav->GetSessionHistory();
1462
RefPtr<ChildSHistory> otherHistory = otherRootWebnav->GetSessionHistory();
1463
1464
if ((ourRootTreeItem != ourDocshell || otherRootTreeItem != otherDocshell) &&
1465
(ourHistory || otherHistory)) {
1466
return NS_ERROR_NOT_IMPLEMENTED;
1467
}
1468
1469
// Also make sure that the two docshells are the same type. Otherwise
1470
// swapping is certainly not safe. If this needs to be changed then
1471
// the code below needs to be audited as it assumes identical types.
1472
int32_t ourType = ourDocshell->ItemType();
1473
int32_t otherType = otherDocshell->ItemType();
1474
if (ourType != otherType) {
1475
return NS_ERROR_NOT_IMPLEMENTED;
1476
}
1477
1478
// One more twist here. Setting up the right treeowners in a heterogeneous
1479
// tree is a bit of a pain. So make sure that if ourType is not
1480
// nsIDocShellTreeItem::typeContent then all of our descendants are the same
1481
// type as us.
1482
if (ourType != nsIDocShellTreeItem::typeContent &&
1483
(!AllDescendantsOfType(ourDocshell, ourType) ||
1484
!AllDescendantsOfType(otherDocshell, otherType))) {
1485
return NS_ERROR_NOT_IMPLEMENTED;
1486
}
1487
1488
// Save off the tree owners, frame elements, chrome event handlers, and
1489
// docshell and document parents before doing anything else.
1490
nsCOMPtr<nsIDocShellTreeOwner> ourOwner, otherOwner;
1491
ourDocshell->GetTreeOwner(getter_AddRefs(ourOwner));
1492
otherDocshell->GetTreeOwner(getter_AddRefs(otherOwner));
1493
// Note: it's OK to have null treeowners.
1494
1495
nsCOMPtr<nsIDocShellTreeItem> ourParentItem, otherParentItem;
1496
ourDocshell->GetInProcessParent(getter_AddRefs(ourParentItem));
1497
otherDocshell->GetInProcessParent(getter_AddRefs(otherParentItem));
1498
if (!ourParentItem || !otherParentItem) {
1499
return NS_ERROR_NOT_IMPLEMENTED;
1500
}
1501
1502
// Make sure our parents are the same type too
1503
int32_t ourParentType = ourParentItem->ItemType();
1504
int32_t otherParentType = otherParentItem->ItemType();
1505
if (ourParentType != otherParentType) {
1506
return NS_ERROR_NOT_IMPLEMENTED;
1507
}
1508
1509
nsCOMPtr<nsPIDOMWindowOuter> ourWindow = ourDocshell->GetWindow();
1510
nsCOMPtr<nsPIDOMWindowOuter> otherWindow = otherDocshell->GetWindow();
1511
1512
nsCOMPtr<Element> ourFrameElement = ourWindow->GetFrameElementInternal();
1513
nsCOMPtr<Element> otherFrameElement = otherWindow->GetFrameElementInternal();
1514
1515
nsCOMPtr<EventTarget> ourChromeEventHandler =
1516
ourWindow->GetChromeEventHandler();
1517
nsCOMPtr<EventTarget> otherChromeEventHandler =
1518
otherWindow->GetChromeEventHandler();
1519
1520
nsCOMPtr<EventTarget> ourEventTarget = ourWindow->GetParentTarget();
1521
nsCOMPtr<EventTarget> otherEventTarget = otherWindow->GetParentTarget();
1522
1523
NS_ASSERTION(SameCOMIdentity(ourFrameElement, ourContent) &&
1524
SameCOMIdentity(otherFrameElement, otherContent) &&
1525
SameCOMIdentity(ourChromeEventHandler, ourContent) &&
1526
SameCOMIdentity(otherChromeEventHandler, otherContent),
1527
"How did that happen, exactly?");
1528
1529
nsCOMPtr<Document> ourChildDocument = ourWindow->GetExtantDoc();
1530
nsCOMPtr<Document> otherChildDocument = otherWindow->GetExtantDoc();
1531
if (!ourChildDocument || !otherChildDocument) {
1532
// This shouldn't be happening
1533
return NS_ERROR_NOT_IMPLEMENTED;
1534
}
1535
1536
nsCOMPtr<Document> ourParentDocument =
1537
ourChildDocument->GetInProcessParentDocument();
1538
nsCOMPtr<Document> otherParentDocument =
1539
otherChildDocument->GetInProcessParentDocument();
1540
1541
// Make sure to swap docshells between the two frames.
1542
Document* ourDoc = ourContent->GetComposedDoc();
1543
Document* otherDoc = otherContent->GetComposedDoc();
1544
if (!ourDoc || !otherDoc) {
1545
// Again, how odd, given that we had docshells
1546
return NS_ERROR_NOT_IMPLEMENTED;
1547
}
1548
1549
NS_ASSERTION(ourDoc == ourParentDocument, "Unexpected parent document");
1550
NS_ASSERTION(otherDoc == otherParentDocument, "Unexpected parent document");
1551
1552
PresShell* ourPresShell = ourDoc->GetPresShell();
1553
PresShell* otherPresShell = otherDoc->GetPresShell();
1554
if (!ourPresShell || !otherPresShell) {
1555
return NS_ERROR_NOT_IMPLEMENTED;
1556
}
1557
1558
// When we swap docShells, maybe we have to deal with a new page created just
1559
// for this operation. In this case, the browser code should already have set
1560
// the correct userContextId attribute value in the owning element, but our
1561
// docShell, that has been created way before) doesn't know that that
1562
// happened.
1563
// This is the reason why now we must retrieve the correct value from the
1564
// usercontextid attribute before comparing our originAttributes with the
1565
// other one.
1566
OriginAttributes ourOriginAttributes = ourDocshell->GetOriginAttributes();
1567
rv = PopulateUserContextIdFromAttribute(ourOriginAttributes);
1568
NS_ENSURE_SUCCESS(rv, rv);
1569
1570
OriginAttributes otherOriginAttributes = otherDocshell->GetOriginAttributes();
1571
rv = aOther->PopulateUserContextIdFromAttribute(otherOriginAttributes);
1572
NS_ENSURE_SUCCESS(rv, rv);
1573
1574
if (ourOriginAttributes != otherOriginAttributes) {
1575
return NS_ERROR_NOT_IMPLEMENTED;
1576
}
1577
1578
if (mInSwap || aOther->mInSwap) {
1579
return NS_ERROR_NOT_IMPLEMENTED;
1580
}
1581
AutoResetInFrameSwap autoFrameSwap(this, aOther, ourDocshell, otherDocshell,
1582
ourEventTarget, otherEventTarget);
1583
1584
nsIFrame* ourFrame = ourContent->GetPrimaryFrame();
1585
nsIFrame* otherFrame = otherContent->GetPrimaryFrame();
1586
if (!ourFrame || !otherFrame) {
1587
return NS_ERROR_NOT_IMPLEMENTED;
1588
}
1589
1590
nsSubDocumentFrame* ourFrameFrame = do_QueryFrame(ourFrame);
1591
if (!ourFrameFrame) {
1592
return NS_ERROR_NOT_IMPLEMENTED;
1593
}
1594
1595
// OK. First begin to swap the docshells in the two nsIFrames
1596
rv = ourFrameFrame->BeginSwapDocShells(otherFrame);
1597
if (NS_FAILED(rv)) {
1598
return rv;
1599
}
1600
1601
// Destroy browser frame scripts for content leaving a frame with browser API
1602
if (OwnerIsMozBrowserFrame() && !aOther->OwnerIsMozBrowserFrame()) {
1603
DestroyBrowserFrameScripts();
1604
}
1605
if (!OwnerIsMozBrowserFrame() && aOther->OwnerIsMozBrowserFrame()) {
1606
aOther->DestroyBrowserFrameScripts();
1607
}
1608
1609
// Now move the docshells to the right docshell trees. Note that this
1610
// resets their treeowners to null.
1611
ourParentItem->RemoveChild(ourDocshell);
1612
otherParentItem->RemoveChild(otherDocshell);
1613
if (ourType == nsIDocShellTreeItem::typeContent) {
1614
ourOwner->ContentShellRemoved(ourDocshell);
1615
otherOwner->ContentShellRemoved(otherDocshell);
1616
}
1617
1618
ourParentItem->AddChild(otherDocshell);
1619
otherParentItem->AddChild(ourDocshell);
1620
1621
// Restore the correct chrome event handlers.
1622
ourDocshell->SetChromeEventHandler(otherChromeEventHandler);
1623
otherDocshell->SetChromeEventHandler(ourChromeEventHandler);
1624
// Restore the correct treeowners
1625
// (and also chrome event handlers for content frames only).
1626
SetTreeOwnerAndChromeEventHandlerOnDocshellTree(
1627
ourDocshell, otherOwner,
1628
ourType == nsIDocShellTreeItem::typeContent
1629
? otherChromeEventHandler.get()
1630
: nullptr);
1631
SetTreeOwnerAndChromeEventHandlerOnDocshellTree(
1632
otherDocshell, ourOwner,
1633
ourType == nsIDocShellTreeItem::typeContent ? ourChromeEventHandler.get()
1634
: nullptr);
1635
1636
// Switch the owner content before we start calling AddTreeItemToTreeOwner.
1637
// Note that we rely on this to deal with setting mObservingOwnerContent to
1638
// false and calling RemoveMutationObserver as needed.
1639
SetOwnerContent(otherContent);
1640
aOther->SetOwnerContent(ourContent);
1641
1642
AddTreeItemToTreeOwner(ourDocshell, otherOwner);
1643
aOther->AddTreeItemToTreeOwner(otherDocshell, ourOwner);
1644
1645
// SetSubDocumentFor nulls out parent documents on the old child doc if a
1646
// new non-null document is passed in, so just go ahead and remove both
1647
// kids before reinserting in the parent subdoc maps, to avoid
1648
// complications.
1649
ourParentDocument->SetSubDocumentFor(ourContent, nullptr);
1650
otherParentDocument->SetSubDocumentFor(otherContent, nullptr);
1651
ourParentDocument->SetSubDocumentFor(ourContent, otherChildDocument);
1652
otherParentDocument->SetSubDocumentFor(otherContent, ourChildDocument);
1653
1654
ourWindow->SetFrameElementInternal(otherFrameElement);
1655
otherWindow->SetFrameElementInternal(ourFrameElement);
1656
1657
RefPtr<nsFrameMessageManager> ourMessageManager = mMessageManager;
1658
RefPtr<nsFrameMessageManager> otherMessageManager = aOther->mMessageManager;
1659
// Swap pointers in child message managers.
1660
if (mChildMessageManager) {
1661
InProcessBrowserChildMessageManager* browserChild = mChildMessageManager;
1662
browserChild->SetOwner(otherContent);
1663
browserChild->SetChromeMessageManager(otherMessageManager);
1664
}
1665
if (aOther->mChildMessageManager) {
1666
InProcessBrowserChildMessageManager* otherBrowserChild =
1667
aOther->mChildMessageManager;
1668
otherBrowserChild->SetOwner(ourContent);
1669
otherBrowserChild->SetChromeMessageManager(ourMessageManager);
1670
}
1671
// Swap and setup things in parent message managers.
1672
if (mMessageManager) {
1673
mMessageManager->SetCallback(aOther);
1674
}
1675
if (aOther->mMessageManager) {
1676
aOther->mMessageManager->SetCallback(this);
1677
}
1678
mMessageManager.swap(aOther->mMessageManager);
1679
1680
// Perform the actual swap of the internal refptrs. We keep a strong reference
1681
// to ourselves to make sure we don't die while we overwrite our reference to
1682
// ourself.
1683
RefPtr<nsFrameLoader> kungFuDeathGrip(this);
1684
aThisOwner->SetFrameLoader(aOther);
1685
aOtherOwner->SetFrameLoader(kungFuDeathGrip);
1686
1687
// Drop any cached content viewers in the two session histories.
1688
if (ourHistory) {
1689
ourHistory->EvictLocalContentViewers();
1690
}
1691
if (otherHistory) {
1692
otherHistory->EvictLocalContentViewers();
1693
}
1694
1695
NS_ASSERTION(ourFrame == ourContent->GetPrimaryFrame() &&
1696
otherFrame == otherContent->GetPrimaryFrame(),
1697
"changed primary frame");
1698
1699
ourFrameFrame->EndSwapDocShells(otherFrame);
1700
1701
// If the content being swapped came from windows on two screens with
1702
// incompatible backing resolution (e.g. dragging a tab between windows on
1703
// hi-dpi and low-dpi screens), it will have style data that is based on
1704
// the wrong appUnitsPerDevPixel value. So we tell the PresShells that their
1705
// backing scale factor may have changed. (Bug 822266)
1706
ourPresShell->BackingScaleFactorChanged();
1707
otherPresShell->BackingScaleFactorChanged();
1708
1709
// Initialize browser API if needed now that owner content has changed
1710
InitializeBrowserAPI();
1711
aOther->InitializeBrowserAPI();
1712
1713
return NS_OK;
1714
}
1715
1716
void nsFrameLoader::Destroy() { StartDestroy(); }
1717
1718
class nsFrameLoaderDestroyRunnable : public Runnable {
1719
enum DestroyPhase {
1720
// See the implementation of Run for an explanation of these phases.
1721
eDestroyDocShell,
1722
eWaitForUnloadMessage,
1723
eDestroyComplete
1724
};
1725
1726
RefPtr<nsFrameLoader> mFrameLoader;
1727
DestroyPhase mPhase;
1728
1729
public:
1730
explicit nsFrameLoaderDestroyRunnable(nsFrameLoader* aFrameLoader)
1731
: mozilla::Runnable("nsFrameLoaderDestroyRunnable"),
1732
mFrameLoader(aFrameLoader),
1733
mPhase(eDestroyDocShell) {}
1734
1735
NS_IMETHOD Run() override;
1736
};
1737
1738
void nsFrameLoader::StartDestroy() {
1739
// nsFrameLoader::StartDestroy is called just before the frameloader is
1740
// detached from the <browser> element. Destruction continues in phases via
1741
// the nsFrameLoaderDestroyRunnable.
1742
1743
if (mDestroyCalled) {
1744
return;
1745
}
1746
mDestroyCalled = true;
1747
1748
// request a tabStateFlush before tab is closed
1749
RequestTabStateFlush(/*flushId*/ 0, /*isFinal*/ true);
1750
1751
// After this point, we return an error when trying to send a message using
1752
// the message manager on the frame.
1753
if (mMessageManager) {
1754
mMessageManager->Close();
1755
}
1756
1757
// Retain references to the <browser> element and the frameloader in case we
1758
// receive any messages from the message manager on the frame. These
1759
// references are dropped in DestroyComplete.
1760
if (mChildMessageManager || mRemoteBrowser) {
1761
mOwnerContentStrong = mOwnerContent;
1762
if (auto* browserParent = GetBrowserParent()) {
1763
browserParent->CacheFrameLoader(this);
1764
}
1765
if (mChildMessageManager) {
1766
mChildMessageManager->CacheFrameLoader(this);
1767
}
1768
}
1769
1770
// If the BrowserParent has installed any event listeners on the window, this
1771
// is its last chance to remove them while we're still in the document.
1772
if (auto* browserParent = GetBrowserParent()) {
1773
browserParent->RemoveWindowListeners();
1774
}
1775
1776
nsCOMPtr<Document> doc;
1777
bool dynamicSubframeRemoval = false;
1778
if (mOwnerContent) {
1779
doc = mOwnerContent->OwnerDoc();
1780
dynamicSubframeRemoval = !mIsTopLevelContent && !doc->InUnlinkOrDeletion();
1781
doc->SetSubDocumentFor(mOwnerContent, nullptr);
1782
MaybeUpdatePrimaryBrowserParent(eBrowserParentRemoved);
1783
SetOwnerContent(nullptr);
1784
}
1785
1786
// Seems like this is a dynamic frame removal.
1787
if (dynamicSubframeRemoval) {
1788
if (GetDocShell()) {
1789
GetDocShell()->RemoveFromSessionHistory();
1790
}
1791
}
1792
1793
// Let the tree owner know we're gone.
1794
if (mIsTopLevelContent) {
1795
if (GetDocShell()) {
1796
nsCOMPtr<nsIDocShellTreeItem> parentItem;
1797
GetDocShell()->GetInProcessParent(getter_AddRefs(parentItem));
1798
nsCOMPtr<nsIDocShellTreeOwner> owner = do_GetInterface(parentItem);
1799
if (owner) {
1800
owner->ContentShellRemoved(GetDocShell());
1801
}
1802
}
1803
}
1804
1805
// Let our window know that we are gone
1806
if (GetDocShell()) {
1807
nsCOMPtr<nsPIDOMWindowOuter> win_private(GetDocShell()->GetWindow());
1808
if (win_private) {
1809
win_private->SetFrameElementInternal(nullptr);
1810
}
1811
}
1812
1813
nsCOMPtr<nsIRunnable> destroyRunnable =
1814
new nsFrameLoaderDestroyRunnable(this);
1815
if (mNeedsAsyncDestroy || !doc ||
1816
NS_FAILED(doc->FinalizeFrameLoader(this, destroyRunnable))) {
1817
NS_DispatchToCurrentThread(destroyRunnable);
1818
}
1819
}
1820
1821
nsresult nsFrameLoaderDestroyRunnable::Run() {
1822
switch (mPhase) {
1823
case eDestroyDocShell:
1824
mFrameLoader->DestroyDocShell();
1825
1826
// In the out-of-process case, BrowserParent will eventually call
1827
// DestroyComplete once it receives a __delete__ message from the child.
1828
// In the in-process case, we dispatch a series of runnables to ensure
1829
// that DestroyComplete gets called at the right time. The frame loader is
1830
// kept alive by mFrameLoader during this time.
1831
if (mFrameLoader->mChildMessageManager) {
1832
// When the docshell is destroyed, NotifyWindowIDDestroyed is called to
1833
// asynchronously notify {outer,inner}-window-destroyed via a runnable.
1834
// We don't want DestroyComplete to run until after those runnables have
1835
// run. Since we're enqueueing ourselves after the window-destroyed
1836
// runnables are enqueued, we're guaranteed to run after.
1837
mPhase = eWaitForUnloadMessage;
1838
NS_DispatchToCurrentThread(this); <