Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/base/nsFrameLoader.cpp
Line
Count
Source (jump to first uncovered line)
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 "nsIPresShell.h"
20
#include "nsIContentInlines.h"
21
#include "nsIContentViewer.h"
22
#include "nsIDocument.h"
23
#include "nsPIDOMWindow.h"
24
#include "nsIWebNavigation.h"
25
#include "nsIWebProgress.h"
26
#include "nsIDocShell.h"
27
#include "nsIDocShellTreeOwner.h"
28
#include "nsDocShellLoadInfo.h"
29
#include "nsIBaseWindow.h"
30
#include "nsIBrowser.h"
31
#include "nsContentUtils.h"
32
#include "nsIXPConnect.h"
33
#include "nsUnicharUtils.h"
34
#include "nsIScriptGlobalObject.h"
35
#include "nsIScriptSecurityManager.h"
36
#include "nsIScrollable.h"
37
#include "nsFrameLoader.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
56
#include "nsIURI.h"
57
#include "nsIURL.h"
58
#include "nsNetUtil.h"
59
60
#include "nsGkAtoms.h"
61
#include "nsNameSpaceManager.h"
62
63
#include "nsThreadUtils.h"
64
65
#include "nsIDOMChromeWindow.h"
66
#include "InProcessTabChildMessageManager.h"
67
68
#include "Layers.h"
69
#include "ClientLayerManager.h"
70
71
#include "ContentParent.h"
72
#include "TabParent.h"
73
#include "mozilla/AsyncEventDispatcher.h"
74
#include "mozilla/BasePrincipal.h"
75
#include "mozilla/GuardObjects.h"
76
#include "mozilla/HTMLEditor.h"
77
#include "mozilla/NullPrincipal.h"
78
#include "mozilla/Preferences.h"
79
#include "mozilla/Unused.h"
80
#include "mozilla/dom/ChromeMessageSender.h"
81
#include "mozilla/dom/Element.h"
82
#include "mozilla/dom/FrameLoaderBinding.h"
83
#include "mozilla/jsipc/CrossProcessObjectWrappers.h"
84
#include "mozilla/layout/RenderFrameParent.h"
85
#include "nsGenericHTMLFrameElement.h"
86
#include "GeckoProfiler.h"
87
88
#include "jsapi.h"
89
#include "mozilla/dom/HTMLIFrameElement.h"
90
#include "nsSandboxFlags.h"
91
#include "mozilla/layers/CompositorBridgeChild.h"
92
#include "mozilla/dom/CustomEvent.h"
93
94
#include "mozilla/dom/ipc/StructuredCloneData.h"
95
#include "mozilla/WebBrowserPersistLocalDocument.h"
96
#include "mozilla/dom/Promise.h"
97
#include "mozilla/dom/PromiseNativeHandler.h"
98
#include "mozilla/dom/GroupedHistoryEvent.h"
99
#include "mozilla/dom/ParentSHistory.h"
100
#include "mozilla/dom/ChildSHistory.h"
101
102
#include "mozilla/dom/HTMLBodyElement.h"
103
104
#include "mozilla/ContentPrincipal.h"
105
106
#ifdef XP_WIN
107
#include "mozilla/plugins/PPluginWidgetParent.h"
108
#include "../plugins/ipc/PluginWidgetParent.h"
109
#endif
110
111
#ifdef MOZ_XUL
112
#include "nsXULPopupManager.h"
113
#endif
114
115
#ifdef NS_PRINTING
116
#include "mozilla/embedding/printingui/PrintingParent.h"
117
#include "nsIWebBrowserPrint.h"
118
#endif
119
120
using namespace mozilla;
121
using namespace mozilla::hal;
122
using namespace mozilla::dom;
123
using namespace mozilla::dom::ipc;
124
using namespace mozilla::layers;
125
using namespace mozilla::layout;
126
typedef FrameMetrics::ViewID ViewID;
127
128
// Bug 136580: Limit to the number of nested content frames that can have the
129
//             same URL. This is to stop content that is recursively loading
130
//             itself.  Note that "#foo" on the end of URL doesn't affect
131
//             whether it's considered identical, but "?foo" or ";foo" are
132
//             considered and compared.
133
// Bug 228829: Limit this to 1, like IE does.
134
0
#define MAX_SAME_URL_CONTENT_FRAMES 1
135
136
// Bug 8065: Limit content frame depth to some reasonable level. This
137
// does not count chrome frames when determining depth, nor does it
138
// prevent chrome recursion.  Number is fairly arbitrary, but meant to
139
// keep number of shells to a reasonable number on accidental recursion with a
140
// small (but not 1) branching factor.  With large branching factors the number
141
// of shells can rapidly become huge and run us out of memory.  To solve that,
142
// we'd need to re-institute a fixed version of bug 98158.
143
0
#define MAX_DEPTH_CONTENT_FRAMES 10
144
145
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(nsFrameLoader,
146
                                      mDocShell,
147
                                      mMessageManager,
148
                                      mChildMessageManager,
149
                                      mOpener,
150
                                      mParentSHistory)
151
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFrameLoader)
152
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFrameLoader)
153
154
0
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFrameLoader)
155
0
  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
156
0
  NS_INTERFACE_MAP_ENTRY_CONCRETE(nsFrameLoader)
157
0
  NS_INTERFACE_MAP_ENTRY(nsIMutationObserver)
158
0
  NS_INTERFACE_MAP_ENTRY(nsISupports)
159
0
NS_INTERFACE_MAP_END
160
161
nsFrameLoader::nsFrameLoader(Element* aOwner, nsPIDOMWindowOuter* aOpener,
162
                             bool aNetworkCreated, int32_t aJSPluginID)
163
  : mOwnerContent(aOwner)
164
  , mDetachedSubdocFrame(nullptr)
165
  , mOpener(aOpener)
166
  , mRemoteBrowser(nullptr)
167
  , mChildID(0)
168
  , mJSPluginID(aJSPluginID)
169
  , mBrowserChangingProcessBlockers(nullptr)
170
  , mDepthTooGreat(false)
171
  , mIsTopLevelContent(false)
172
  , mDestroyCalled(false)
173
  , mNeedsAsyncDestroy(false)
174
  , mInSwap(false)
175
  , mInShow(false)
176
  , mHideCalled(false)
177
  , mNetworkCreated(aNetworkCreated)
178
  , mLoadingOriginalSrc(false)
179
  , mRemoteBrowserShown(false)
180
  , mRemoteFrame(false)
181
  , mClipSubdocument(true)
182
  , mClampScrollPosition(true)
183
  , mObservingOwnerContent(false)
184
0
{
185
0
  mRemoteFrame = ShouldUseRemoteProcess();
186
0
  MOZ_ASSERT(!mRemoteFrame || !aOpener,
187
0
             "Cannot pass aOpener for a remote frame!");
188
0
}
189
190
nsFrameLoader::~nsFrameLoader()
191
0
{
192
0
  if (mMessageManager) {
193
0
    mMessageManager->Disconnect();
194
0
  }
195
0
  MOZ_RELEASE_ASSERT(mDestroyCalled);
196
0
}
197
198
nsFrameLoader*
199
nsFrameLoader::Create(Element* aOwner, nsPIDOMWindowOuter* aOpener, bool aNetworkCreated,
200
                      int32_t aJSPluginId)
201
0
{
202
0
  NS_ENSURE_TRUE(aOwner, nullptr);
203
0
  nsIDocument* doc = aOwner->OwnerDoc();
204
0
205
0
  // We never create nsFrameLoaders for elements in resource documents.
206
0
  //
207
0
  // We never create nsFrameLoaders for elements in data documents, unless the
208
0
  // document is a static document.
209
0
  // Static documents are an exception because any sub-documents need an
210
0
  // nsFrameLoader to keep the relevant docShell alive, even though the
211
0
  // nsFrameLoader isn't used to load anything (the sub-document is created by
212
0
  // the static clone process).
213
0
  //
214
0
  // We never create nsFrameLoaders for elements that are not
215
0
  // in-composed-document, unless the element belongs to a static document.
216
0
  // Static documents are an exception because this method is called at a point
217
0
  // in the static clone process before aOwner has been inserted into its
218
0
  // document.  For other types of documents this wouldn't be a problem since
219
0
  // we'd create the nsFrameLoader as necessary after aOwner is inserted into a
220
0
  // document, but the mechanisms that take care of that don't apply for static
221
0
  // documents so we need to create the nsFrameLoader now. (This isn't wasteful
222
0
  // since for a static document we know aOwner will end up in a document and
223
0
  // the nsFrameLoader will be used for its docShell.)
224
0
  //
225
0
  NS_ENSURE_TRUE(!doc->IsResourceDoc() &&
226
0
                 ((!doc->IsLoadedAsData() && aOwner->IsInComposedDoc()) ||
227
0
                  doc->IsStaticDocument()),
228
0
                 nullptr);
229
0
230
0
  return new nsFrameLoader(aOwner, aOpener, aNetworkCreated, aJSPluginId);
231
0
}
232
233
void
234
nsFrameLoader::LoadFrame(bool aOriginalSrc)
235
0
{
236
0
  if (NS_WARN_IF(!mOwnerContent)) {
237
0
    return;
238
0
  }
239
0
240
0
  nsAutoString src;
241
0
  nsCOMPtr<nsIPrincipal> principal;
242
0
243
0
  bool isSrcdoc = mOwnerContent->IsHTMLElement(nsGkAtoms::iframe) &&
244
0
                  mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::srcdoc);
245
0
  if (isSrcdoc) {
246
0
    src.AssignLiteral("about:srcdoc");
247
0
  }
248
0
  else {
249
0
    GetURL(src, getter_AddRefs(principal));
250
0
251
0
    src.Trim(" \t\n\r");
252
0
253
0
    if (src.IsEmpty()) {
254
0
      // If the frame is a XUL element and has the attribute 'nodefaultsrc=true'
255
0
      // then we will not use 'about:blank' as fallback but return early without
256
0
      // starting a load if no 'src' attribute is given (or it's empty).
257
0
      if (mOwnerContent->IsXULElement() &&
258
0
          mOwnerContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::nodefaultsrc,
259
0
                                     nsGkAtoms::_true, eCaseMatters)) {
260
0
        return;
261
0
      }
262
0
      src.AssignLiteral("about:blank");
263
0
    }
264
0
  }
265
0
266
0
  nsIDocument* doc = mOwnerContent->OwnerDoc();
267
0
  if (doc->IsStaticDocument()) {
268
0
    return;
269
0
  }
270
0
271
0
  if (doc->IsLoadedAsInteractiveData()) {
272
0
    // XBL bindings doc shouldn't load sub-documents.
273
0
    return;
274
0
  }
275
0
276
0
  nsCOMPtr<nsIURI> base_uri = mOwnerContent->GetBaseURI();
277
0
  auto encoding = doc->GetDocumentCharacterSet();
278
0
279
0
  nsCOMPtr<nsIURI> uri;
280
0
  nsresult rv = NS_NewURI(getter_AddRefs(uri), src, encoding, base_uri);
281
0
282
0
  // If the URI was malformed, try to recover by loading about:blank.
283
0
  if (rv == NS_ERROR_MALFORMED_URI) {
284
0
    rv = NS_NewURI(getter_AddRefs(uri), NS_LITERAL_STRING("about:blank"),
285
0
                   encoding, base_uri);
286
0
  }
287
0
288
0
  if (NS_SUCCEEDED(rv)) {
289
0
    rv = LoadURI(uri, principal, aOriginalSrc);
290
0
  }
291
0
292
0
  if (NS_FAILED(rv)) {
293
0
    FireErrorEvent();
294
0
  }
295
0
}
296
297
void
298
nsFrameLoader::FireErrorEvent()
299
0
{
300
0
  if (!mOwnerContent) {
301
0
    return;
302
0
  }
303
0
  RefPtr<AsyncEventDispatcher> loadBlockingAsyncDispatcher =
304
0
    new LoadBlockingAsyncEventDispatcher(mOwnerContent,
305
0
                                         NS_LITERAL_STRING("error"),
306
0
                                         CanBubble::eNo,
307
0
                                         ChromeOnlyDispatch::eNo);
308
0
  loadBlockingAsyncDispatcher->PostDOMEvent();
309
0
}
310
311
nsresult
312
nsFrameLoader::LoadURI(nsIURI* aURI, bool aOriginalSrc)
313
0
{
314
0
  return LoadURI(aURI, nullptr, aOriginalSrc);
315
0
}
316
317
nsresult
318
nsFrameLoader::LoadURI(nsIURI* aURI, nsIPrincipal* aTriggeringPrincipal,
319
                       bool aOriginalSrc)
320
0
{
321
0
  if (!aURI)
322
0
    return NS_ERROR_INVALID_POINTER;
323
0
  NS_ENSURE_STATE(!mDestroyCalled && mOwnerContent);
324
0
325
0
  mLoadingOriginalSrc = aOriginalSrc;
326
0
327
0
  nsCOMPtr<nsIDocument> doc = mOwnerContent->OwnerDoc();
328
0
329
0
  nsresult rv;
330
0
  // If IsForJSPlugin() returns true then we want to allow the load. We're just
331
0
  // loading the source for the implementation of the JS plugin from a URI
332
0
  // that's under our control. We will already have done the security checks for
333
0
  // loading the plugin content itself in the object/embed loading code.
334
0
  if (!IsForJSPlugin()) {
335
0
    rv = CheckURILoad(aURI, aTriggeringPrincipal);
336
0
    NS_ENSURE_SUCCESS(rv, rv);
337
0
  }
338
0
339
0
  mURIToLoad = aURI;
340
0
  mTriggeringPrincipal = aTriggeringPrincipal;
341
0
  rv = doc->InitializeFrameLoader(this);
342
0
  if (NS_FAILED(rv)) {
343
0
    mURIToLoad = nullptr;
344
0
    mTriggeringPrincipal = nullptr;
345
0
  }
346
0
  return rv;
347
0
}
348
349
bool
350
nsFrameLoader::SwapBrowsersAndNotify(nsFrameLoader* aOther)
351
0
{
352
0
  // Cache the owner content before calling SwapBrowsers, which will change
353
0
  // these member variables.
354
0
  RefPtr<mozilla::dom::Element> primaryContent = mOwnerContent;
355
0
  RefPtr<mozilla::dom::Element> secondaryContent = aOther->mOwnerContent;
356
0
357
0
  // Swap loaders through our owner, so the owner's listeners will be correctly
358
0
  // setup.
359
0
  nsCOMPtr<nsIBrowser> ourBrowser = do_QueryInterface(primaryContent);
360
0
  nsCOMPtr<nsIBrowser> otherBrowser = do_QueryInterface(secondaryContent);
361
0
  if (NS_WARN_IF(!ourBrowser || !otherBrowser)) {
362
0
    return false;
363
0
  }
364
0
  nsresult rv = ourBrowser->SwapBrowsers(otherBrowser, nsIBrowser::SWAP_KEEP_PERMANENT_KEY);
365
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
366
0
    return false;
367
0
  }
368
0
369
0
  // Dispatch the BrowserChangedProcess event to tell JS that the process swap
370
0
  // has occurred.
371
0
  GroupedHistoryEventInit eventInit;
372
0
  eventInit.mBubbles = true;
373
0
  eventInit.mCancelable= false;
374
0
  eventInit.mOtherBrowser = secondaryContent;
375
0
  RefPtr<GroupedHistoryEvent> event =
376
0
    GroupedHistoryEvent::Constructor(primaryContent,
377
0
                                     NS_LITERAL_STRING("BrowserChangedProcess"),
378
0
                                     eventInit);
379
0
  event->SetTrusted(true);
380
0
  primaryContent->DispatchEvent(*event);
381
0
382
0
  return true;
383
0
}
384
385
already_AddRefed<Promise>
386
nsFrameLoader::FireWillChangeProcessEvent()
387
0
{
388
0
  AutoJSAPI jsapi;
389
0
  if (NS_WARN_IF(!jsapi.Init(mOwnerContent->GetOwnerGlobal()))) {
390
0
    return nullptr;
391
0
  }
392
0
  JSContext* cx = jsapi.cx();
393
0
394
0
  // Set our mBrowserChangingProcessBlockers property to refer to the blockers
395
0
  // list. We will synchronously dispatch a DOM event to collect this list of
396
0
  // blockers.
397
0
  nsTArray<RefPtr<Promise>> blockers;
398
0
  mBrowserChangingProcessBlockers = &blockers;
399
0
400
0
  GroupedHistoryEventInit eventInit;
401
0
  eventInit.mBubbles = true;
402
0
  eventInit.mCancelable = false;
403
0
  eventInit.mOtherBrowser = nullptr;
404
0
  RefPtr<GroupedHistoryEvent> event =
405
0
    GroupedHistoryEvent::Constructor(mOwnerContent,
406
0
                                     NS_LITERAL_STRING("BrowserWillChangeProcess"),
407
0
                                     eventInit);
408
0
  event->SetTrusted(true);
409
0
  mOwnerContent->DispatchEvent(*event);
410
0
411
0
  mBrowserChangingProcessBlockers = nullptr;
412
0
413
0
  ErrorResult rv;
414
0
  RefPtr<Promise> allPromise = Promise::All(cx, blockers, rv);
415
0
  return allPromise.forget();
416
0
}
417
418
void
419
nsFrameLoader::AddProcessChangeBlockingPromise(Promise& aPromise, ErrorResult& aRv)
420
0
{
421
0
  if (NS_WARN_IF(!mBrowserChangingProcessBlockers)) {
422
0
    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
423
0
  } else {
424
0
    mBrowserChangingProcessBlockers->AppendElement(&aPromise);
425
0
  }
426
0
}
427
428
nsresult
429
nsFrameLoader::ReallyStartLoading()
430
0
{
431
0
  nsresult rv = ReallyStartLoadingInternal();
432
0
  if (NS_FAILED(rv)) {
433
0
    FireErrorEvent();
434
0
  }
435
0
436
0
  return rv;
437
0
}
438
439
nsresult
440
nsFrameLoader::ReallyStartLoadingInternal()
441
0
{
442
0
  NS_ENSURE_STATE(mURIToLoad && mOwnerContent && mOwnerContent->IsInComposedDoc());
443
0
444
0
  AUTO_PROFILER_LABEL("nsFrameLoader::ReallyStartLoadingInternal", OTHER);
445
0
446
0
  if (IsRemoteFrame()) {
447
0
    if (!mRemoteBrowser && !TryRemoteBrowser()) {
448
0
        NS_WARNING("Couldn't create child process for iframe.");
449
0
        return NS_ERROR_FAILURE;
450
0
    }
451
0
452
0
    // FIXME get error codes from child
453
0
    mRemoteBrowser->LoadURL(mURIToLoad);
454
0
455
0
    if (!mRemoteBrowserShown) {
456
0
      // This can fail if it's too early to show the frame, we will retry later.
457
0
      Unused << ShowRemoteFrame(ScreenIntSize(0, 0));
458
0
    }
459
0
460
0
    return NS_OK;
461
0
  }
462
0
463
0
  nsresult rv = MaybeCreateDocShell();
464
0
  if (NS_FAILED(rv)) {
465
0
    return rv;
466
0
  }
467
0
  NS_ASSERTION(mDocShell,
468
0
               "MaybeCreateDocShell succeeded with a null mDocShell");
469
0
470
0
  // Just to be safe, recheck uri.
471
0
  rv = CheckURILoad(mURIToLoad, mTriggeringPrincipal);
472
0
  NS_ENSURE_SUCCESS(rv, rv);
473
0
474
0
  RefPtr<nsDocShellLoadInfo> loadInfo = new nsDocShellLoadInfo();
475
0
476
0
  loadInfo->SetOriginalFrameSrc(mLoadingOriginalSrc);
477
0
  mLoadingOriginalSrc = false;
478
0
479
0
  // If this frame is sandboxed with respect to origin we will set it up with
480
0
  // a null principal later in nsDocShell::DoURILoad.
481
0
  // We do it there to correctly sandbox content that was loaded into
482
0
  // the frame via other methods than the src attribute.
483
0
  // We'll use our principal, not that of the document loaded inside us.  This
484
0
  // is very important; needed to prevent XSS attacks on documents loaded in
485
0
  // subframes!
486
0
  if (mTriggeringPrincipal) {
487
0
    loadInfo->SetTriggeringPrincipal(mTriggeringPrincipal);
488
0
  } else {
489
0
    loadInfo->SetTriggeringPrincipal(mOwnerContent->NodePrincipal());
490
0
  }
491
0
492
0
  nsCOMPtr<nsIURI> referrer;
493
0
494
0
  nsAutoString srcdoc;
495
0
  bool isSrcdoc = mOwnerContent->IsHTMLElement(nsGkAtoms::iframe) &&
496
0
                  mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::srcdoc,
497
0
                                         srcdoc);
498
0
499
0
  if (isSrcdoc) {
500
0
    nsAutoString referrerStr;
501
0
    mOwnerContent->OwnerDoc()->GetReferrer(referrerStr);
502
0
    rv = NS_NewURI(getter_AddRefs(referrer), referrerStr);
503
0
504
0
    loadInfo->SetSrcdocData(srcdoc);
505
0
    nsCOMPtr<nsIURI> baseURI = mOwnerContent->GetBaseURI();
506
0
    loadInfo->SetBaseURI(baseURI);
507
0
  }
508
0
  else {
509
0
    rv = mOwnerContent->NodePrincipal()->GetURI(getter_AddRefs(referrer));
510
0
    NS_ENSURE_SUCCESS(rv, rv);
511
0
  }
512
0
513
0
  // Use referrer as long as it is not an NullPrincipalURI.
514
0
  // We could add a method such as GetReferrerURI to principals to make this
515
0
  // cleaner, but given that we need to start using Source Browsing Context for
516
0
  // referrer (see Bug 960639) this may be wasted effort at this stage.
517
0
  if (referrer) {
518
0
    bool isNullPrincipalScheme;
519
0
    rv = referrer->SchemeIs(NS_NULLPRINCIPAL_SCHEME, &isNullPrincipalScheme);
520
0
    if (NS_SUCCEEDED(rv) && !isNullPrincipalScheme) {
521
0
      loadInfo->SetReferrer(referrer);
522
0
    }
523
0
  }
524
0
525
0
  // get referrer policy for this iframe:
526
0
  // first load document wide policy, then
527
0
  // load iframe referrer attribute if enabled in preferences
528
0
  // per element referrer overrules document wide referrer if enabled
529
0
  net::ReferrerPolicy referrerPolicy = mOwnerContent->OwnerDoc()->GetReferrerPolicy();
530
0
  HTMLIFrameElement* iframe = HTMLIFrameElement::FromNode(mOwnerContent);
531
0
  if (iframe) {
532
0
    net::ReferrerPolicy iframeReferrerPolicy = iframe->GetReferrerPolicyAsEnum();
533
0
    if (iframeReferrerPolicy != net::RP_Unset) {
534
0
      referrerPolicy = iframeReferrerPolicy;
535
0
    }
536
0
  }
537
0
  loadInfo->SetReferrerPolicy(referrerPolicy);
538
0
539
0
  // Default flags:
540
0
  int32_t flags = nsIWebNavigation::LOAD_FLAGS_NONE;
541
0
542
0
  // Flags for browser frame:
543
0
  if (OwnerIsMozBrowserFrame()) {
544
0
    flags = nsIWebNavigation::LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP |
545
0
            nsIWebNavigation::LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL;
546
0
  }
547
0
548
0
  // Kick off the load...
549
0
  bool tmpState = mNeedsAsyncDestroy;
550
0
  mNeedsAsyncDestroy = true;
551
0
  nsCOMPtr<nsIURI> uriToLoad = mURIToLoad;
552
0
  rv = mDocShell->LoadURI(uriToLoad, loadInfo, flags, false);
553
0
  mNeedsAsyncDestroy = tmpState;
554
0
  mURIToLoad = nullptr;
555
0
  NS_ENSURE_SUCCESS(rv, rv);
556
0
557
0
  return NS_OK;
558
0
}
559
560
nsresult
561
nsFrameLoader::CheckURILoad(nsIURI* aURI, nsIPrincipal* aTriggeringPrincipal)
562
0
{
563
0
  // Check for security.  The fun part is trying to figure out what principals
564
0
  // to use.  The way I figure it, if we're doing a LoadFrame() accidentally
565
0
  // (eg someone created a frame/iframe node, we're being parsed, XUL iframes
566
0
  // are being reframed, etc.) then we definitely want to use the node
567
0
  // principal of mOwnerContent for security checks.  If, on the other hand,
568
0
  // someone's setting the src on our owner content, or created it via script,
569
0
  // or whatever, then they can clearly access it... and we should still use
570
0
  // the principal of mOwnerContent.  I don't think that leads to privilege
571
0
  // escalation, and it's reasonably guaranteed to not lead to XSS issues
572
0
  // (since caller can already access mOwnerContent in this case).  So just use
573
0
  // the principal of mOwnerContent no matter what.  If script wants to run
574
0
  // things with its own permissions, which differ from those of mOwnerContent
575
0
  // (which means the script is privileged in some way) it should set
576
0
  // window.location instead.
577
0
  nsIScriptSecurityManager *secMan = nsContentUtils::GetSecurityManager();
578
0
579
0
  // Get our principal
580
0
  nsIPrincipal* principal = (aTriggeringPrincipal
581
0
                             ? aTriggeringPrincipal
582
0
                             : mOwnerContent->NodePrincipal());
583
0
584
0
  // Check if we are allowed to load absURL
585
0
  nsresult rv =
586
0
    secMan->CheckLoadURIWithPrincipal(principal, aURI,
587
0
                                      nsIScriptSecurityManager::STANDARD);
588
0
  if (NS_FAILED(rv)) {
589
0
    return rv; // We're not
590
0
  }
591
0
592
0
  // Bail out if this is an infinite recursion scenario
593
0
  if (IsRemoteFrame()) {
594
0
    return NS_OK;
595
0
  }
596
0
  return CheckForRecursiveLoad(aURI);
597
0
}
598
599
nsIDocShell*
600
nsFrameLoader::GetDocShell(ErrorResult& aRv)
601
0
{
602
0
  if (IsRemoteFrame()) {
603
0
    return nullptr;
604
0
  }
605
0
606
0
  // If we have an owner, make sure we have a docshell and return
607
0
  // that. If not, we're most likely in the middle of being torn down,
608
0
  // then we just return null.
609
0
  if (mOwnerContent) {
610
0
    nsresult rv = MaybeCreateDocShell();
611
0
    if (NS_FAILED(rv)) {
612
0
      aRv.Throw(rv);
613
0
      return nullptr;
614
0
    }
615
0
    NS_ASSERTION(mDocShell,
616
0
                 "MaybeCreateDocShell succeeded, but null mDocShell");
617
0
  }
618
0
619
0
  return mDocShell;
620
0
}
621
622
static void
623
SetTreeOwnerAndChromeEventHandlerOnDocshellTree(nsIDocShellTreeItem* aItem,
624
                                                nsIDocShellTreeOwner* aOwner,
625
                                                EventTarget* aHandler)
626
0
{
627
0
  MOZ_ASSERT(aItem, "Must have item");
628
0
629
0
  aItem->SetTreeOwner(aOwner);
630
0
631
0
  int32_t childCount = 0;
632
0
  aItem->GetChildCount(&childCount);
633
0
  for (int32_t i = 0; i < childCount; ++i) {
634
0
    nsCOMPtr<nsIDocShellTreeItem> item;
635
0
    aItem->GetChildAt(i, getter_AddRefs(item));
636
0
    if (aHandler) {
637
0
      nsCOMPtr<nsIDocShell> shell(do_QueryInterface(item));
638
0
      shell->SetChromeEventHandler(aHandler);
639
0
    }
640
0
    SetTreeOwnerAndChromeEventHandlerOnDocshellTree(item, aOwner, aHandler);
641
0
  }
642
0
}
643
644
/**
645
 * Set the type of the treeitem and hook it up to the treeowner.
646
 * @param aItem the treeitem we're working with
647
 * @param aTreeOwner the relevant treeowner; might be null
648
 * @param aParentType the nsIDocShellTreeItem::GetType of our parent docshell
649
 * @param aParentNode if non-null, the docshell we should be added as a child to
650
 *
651
 * @return whether aItem is top-level content
652
 */
653
bool
654
nsFrameLoader::AddTreeItemToTreeOwner(nsIDocShellTreeItem* aItem,
655
                                      nsIDocShellTreeOwner* aOwner,
656
                                      int32_t aParentType,
657
                                      nsIDocShell* aParentNode)
658
0
{
659
0
  MOZ_ASSERT(aItem, "Must have docshell treeitem");
660
0
  MOZ_ASSERT(mOwnerContent, "Must have owning content");
661
0
662
0
  nsAutoString value;
663
0
  bool isContent = mOwnerContent->AttrValueIs(
664
0
    kNameSpaceID_None, TypeAttrName(), nsGkAtoms::content, eIgnoreCase);
665
0
666
0
  // Force mozbrowser frames to always be typeContent, even if the
667
0
  // mozbrowser interfaces are disabled.
668
0
  nsCOMPtr<nsIDOMMozBrowserFrame> mozbrowser =
669
0
    do_QueryInterface(mOwnerContent);
670
0
  if (mozbrowser) {
671
0
    bool isMozbrowser = false;
672
0
    mozbrowser->GetMozbrowser(&isMozbrowser);
673
0
    isContent |= isMozbrowser;
674
0
  }
675
0
676
0
  if (isContent) {
677
0
    // The web shell's type is content.
678
0
679
0
    aItem->SetItemType(nsIDocShellTreeItem::typeContent);
680
0
  } else {
681
0
    // Inherit our type from our parent docshell.  If it is
682
0
    // chrome, we'll be chrome.  If it is content, we'll be
683
0
    // content.
684
0
685
0
    aItem->SetItemType(aParentType);
686
0
  }
687
0
688
0
  // Now that we have our type set, add ourselves to the parent, as needed.
689
0
  if (aParentNode) {
690
0
    aParentNode->AddChild(aItem);
691
0
  } else if (nsCOMPtr<nsIDocShell> childAsDocShell = do_QueryInterface(aItem)) {
692
0
    childAsDocShell->AttachBrowsingContext(aParentNode);
693
0
  }
694
0
695
0
  bool retval = false;
696
0
  if (aParentType == nsIDocShellTreeItem::typeChrome && isContent) {
697
0
    retval = true;
698
0
699
0
    bool is_primary =
700
0
      mOwnerContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::primary,
701
0
                                 nsGkAtoms::_true, eIgnoreCase);
702
0
    if (aOwner) {
703
0
      mOwnerContent->AddMutationObserver(this);
704
0
      mObservingOwnerContent = true;
705
0
      aOwner->ContentShellAdded(aItem, is_primary);
706
0
    }
707
0
  }
708
0
709
0
  return retval;
710
0
}
711
712
static bool
713
AllDescendantsOfType(nsIDocShellTreeItem* aParentItem, int32_t aType)
714
0
{
715
0
  int32_t childCount = 0;
716
0
  aParentItem->GetChildCount(&childCount);
717
0
718
0
  for (int32_t i = 0; i < childCount; ++i) {
719
0
    nsCOMPtr<nsIDocShellTreeItem> kid;
720
0
    aParentItem->GetChildAt(i, getter_AddRefs(kid));
721
0
722
0
    if (kid->ItemType() != aType || !AllDescendantsOfType(kid, aType)) {
723
0
      return false;
724
0
    }
725
0
  }
726
0
727
0
  return true;
728
0
}
729
730
/**
731
 * A class that automatically sets mInShow to false when it goes
732
 * out of scope.
733
 */
734
class MOZ_RAII AutoResetInShow {
735
  private:
736
    nsFrameLoader* mFrameLoader;
737
    MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
738
  public:
739
    explicit AutoResetInShow(nsFrameLoader* aFrameLoader MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
740
      : mFrameLoader(aFrameLoader)
741
0
    {
742
0
      MOZ_GUARD_OBJECT_NOTIFIER_INIT;
743
0
    }
744
0
    ~AutoResetInShow() { mFrameLoader->mInShow = false; }
745
};
746
747
static bool
748
ParentWindowIsActive(nsIDocument* aDoc)
749
0
{
750
0
  nsCOMPtr<nsPIWindowRoot> root = nsContentUtils::GetWindowRoot(aDoc);
751
0
  if (root) {
752
0
    nsPIDOMWindowOuter* rootWin = root->GetWindow();
753
0
    return rootWin && rootWin->IsActive();
754
0
  }
755
0
  return false;
756
0
}
757
758
void
759
nsFrameLoader::MaybeShowFrame()
760
0
{
761
0
  nsIFrame* frame = GetPrimaryFrameOfOwningContent();
762
0
  if (frame) {
763
0
    nsSubDocumentFrame* subDocFrame = do_QueryFrame(frame);
764
0
    if (subDocFrame) {
765
0
      subDocFrame->MaybeShowViewer();
766
0
    }
767
0
  }
768
0
}
769
770
bool
771
nsFrameLoader::Show(int32_t marginWidth, int32_t marginHeight,
772
                    int32_t scrollbarPrefX, int32_t scrollbarPrefY,
773
                    nsSubDocumentFrame* frame)
774
0
{
775
0
  if (mInShow) {
776
0
    return false;
777
0
  }
778
0
  // Reset mInShow if we exit early.
779
0
  AutoResetInShow resetInShow(this);
780
0
  mInShow = true;
781
0
782
0
  ScreenIntSize size = frame->GetSubdocumentSize();
783
0
  if (IsRemoteFrame()) {
784
0
    return ShowRemoteFrame(size, frame);
785
0
  }
786
0
787
0
  nsresult rv = MaybeCreateDocShell();
788
0
  if (NS_FAILED(rv)) {
789
0
    return false;
790
0
  }
791
0
  NS_ASSERTION(mDocShell,
792
0
               "MaybeCreateDocShell succeeded, but null mDocShell");
793
0
  if (!mDocShell) {
794
0
    return false;
795
0
  }
796
0
797
0
  mDocShell->SetMarginWidth(marginWidth);
798
0
  mDocShell->SetMarginHeight(marginHeight);
799
0
800
0
  nsCOMPtr<nsIScrollable> sc = do_QueryInterface(mDocShell);
801
0
  if (sc) {
802
0
    sc->SetDefaultScrollbarPreferences(nsIScrollable::ScrollOrientation_X,
803
0
                                       scrollbarPrefX);
804
0
    sc->SetDefaultScrollbarPreferences(nsIScrollable::ScrollOrientation_Y,
805
0
                                       scrollbarPrefY);
806
0
  }
807
0
808
0
  nsCOMPtr<nsIPresShell> presShell = mDocShell->GetPresShell();
809
0
  if (presShell) {
810
0
    // Ensure root scroll frame is reflowed in case scroll preferences or
811
0
    // margins have changed
812
0
    nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame();
813
0
    if (rootScrollFrame) {
814
0
      presShell->FrameNeedsReflow(rootScrollFrame, nsIPresShell::eResize,
815
0
                                  NS_FRAME_IS_DIRTY);
816
0
    }
817
0
    return true;
818
0
  }
819
0
820
0
  nsView* view = frame->EnsureInnerView();
821
0
  if (!view)
822
0
    return false;
823
0
824
0
  nsCOMPtr<nsIBaseWindow> baseWindow = do_QueryInterface(mDocShell);
825
0
  NS_ASSERTION(baseWindow, "Found a nsIDocShell that isn't a nsIBaseWindow.");
826
0
  baseWindow->InitWindow(nullptr, view->GetWidget(), 0, 0,
827
0
                         size.width, size.height);
828
0
  // This is kinda whacky, this "Create()" call doesn't really
829
0
  // create anything, one starts to wonder why this was named
830
0
  // "Create"...
831
0
  baseWindow->Create();
832
0
  baseWindow->SetVisibility(true);
833
0
  NS_ENSURE_TRUE(mDocShell, false);
834
0
835
0
  // Trigger editor re-initialization if midas is turned on in the
836
0
  // sub-document. This shouldn't be necessary, but given the way our
837
0
  // editor works, it is. See
838
0
  // https://bugzilla.mozilla.org/show_bug.cgi?id=284245
839
0
  presShell = mDocShell->GetPresShell();
840
0
  if (presShell) {
841
0
    nsIDocument* doc = presShell->GetDocument();
842
0
    nsHTMLDocument* htmlDoc =
843
0
      doc && doc->IsHTMLOrXHTML() ? doc->AsHTMLDocument() : nullptr;
844
0
845
0
    if (htmlDoc) {
846
0
      nsAutoString designMode;
847
0
      htmlDoc->GetDesignMode(designMode);
848
0
849
0
      if (designMode.EqualsLiteral("on")) {
850
0
        // Hold on to the editor object to let the document reattach to the
851
0
        // same editor object, instead of creating a new one.
852
0
        RefPtr<HTMLEditor> htmlEditor = mDocShell->GetHTMLEditor();
853
0
        Unused << htmlEditor;
854
0
        htmlDoc->SetDesignMode(NS_LITERAL_STRING("off"), Nothing(),
855
0
                               IgnoreErrors());
856
0
857
0
        htmlDoc->SetDesignMode(NS_LITERAL_STRING("on"), Nothing(),
858
0
                               IgnoreErrors());
859
0
      } else {
860
0
        // Re-initialize the presentation for contenteditable documents
861
0
        bool editable = false,
862
0
             hasEditingSession = false;
863
0
        mDocShell->GetEditable(&editable);
864
0
        mDocShell->GetHasEditingSession(&hasEditingSession);
865
0
        RefPtr<HTMLEditor> htmlEditor = mDocShell->GetHTMLEditor();
866
0
        if (editable && hasEditingSession && htmlEditor) {
867
0
          htmlEditor->PostCreate();
868
0
        }
869
0
      }
870
0
    }
871
0
  }
872
0
873
0
  mInShow = false;
874
0
  if (mHideCalled) {
875
0
    mHideCalled = false;
876
0
    Hide();
877
0
    return false;
878
0
  }
879
0
  return true;
880
0
}
881
882
void
883
nsFrameLoader::MarginsChanged(uint32_t aMarginWidth,
884
                              uint32_t aMarginHeight)
885
0
{
886
0
  // We assume that the margins are always zero for remote frames.
887
0
  if (IsRemoteFrame())
888
0
    return;
889
0
890
0
  // If there's no docshell, we're probably not up and running yet.
891
0
  // nsFrameLoader::Show() will take care of setting the right
892
0
  // margins.
893
0
  if (!mDocShell)
894
0
    return;
895
0
896
0
  // Set the margins
897
0
  mDocShell->SetMarginWidth(aMarginWidth);
898
0
  mDocShell->SetMarginHeight(aMarginHeight);
899
0
900
0
  // There's a cached property declaration block
901
0
  // that needs to be updated
902
0
  if (nsIDocument* doc = mDocShell->GetDocument()) {
903
0
    for (nsINode* cur = doc; cur; cur = cur->GetNextNode()) {
904
0
      if (cur->IsHTMLElement(nsGkAtoms::body)) {
905
0
        static_cast<HTMLBodyElement*>(cur)->ClearMappedServoStyle();
906
0
      }
907
0
    }
908
0
  }
909
0
910
0
  // Trigger a restyle if there's a prescontext
911
0
  // FIXME: This could do something much less expensive.
912
0
  RefPtr<nsPresContext> presContext;
913
0
  mDocShell->GetPresContext(getter_AddRefs(presContext));
914
0
  if (presContext)
915
0
    // rebuild, because now the same nsMappedAttributes* will produce
916
0
    // a different style
917
0
    presContext->RebuildAllStyleData(nsChangeHint(0), eRestyle_Subtree);
918
0
}
919
920
bool
921
nsFrameLoader::ShowRemoteFrame(const ScreenIntSize& size,
922
                               nsSubDocumentFrame *aFrame)
923
0
{
924
0
  AUTO_PROFILER_LABEL("nsFrameLoader::ShowRemoteFrame", OTHER);
925
0
  NS_ASSERTION(IsRemoteFrame(), "ShowRemote only makes sense on remote frames.");
926
0
927
0
  if (!mRemoteBrowser && !TryRemoteBrowser()) {
928
0
    NS_ERROR("Couldn't create child process.");
929
0
    return false;
930
0
  }
931
0
932
0
  // FIXME/bug 589337: Show()/Hide() is pretty expensive for
933
0
  // cross-process layers; need to figure out what behavior we really
934
0
  // want here.  For now, hack.
935
0
  if (!mRemoteBrowserShown) {
936
0
    if (!mOwnerContent ||
937
0
        !mOwnerContent->GetComposedDoc()) {
938
0
      return false;
939
0
    }
940
0
941
0
    // We never want to host remote frameloaders in simple popups, like menus.
942
0
    nsIWidget* widget = nsContentUtils::WidgetForContent(mOwnerContent);
943
0
    if (!widget || static_cast<nsBaseWidget*>(widget)->IsSmallPopup()) {
944
0
      return false;
945
0
    }
946
0
947
0
    RenderFrameParent* rfp = GetCurrentRenderFrame();
948
0
    if (!rfp) {
949
0
      return false;
950
0
    }
951
0
952
0
    if (!rfp->AttachLayerManager()) {
953
0
      // This is just not going to work.
954
0
      return false;
955
0
    }
956
0
957
0
    mRemoteBrowser->Show(size, ParentWindowIsActive(mOwnerContent->OwnerDoc()));
958
0
    mRemoteBrowserShown = true;
959
0
960
0
    nsCOMPtr<nsIObserverService> os = services::GetObserverService();
961
0
    if (os) {
962
0
      os->NotifyObservers(ToSupports(this),
963
0
                          "remote-browser-shown", nullptr);
964
0
    }
965
0
  } else {
966
0
    nsIntRect dimensions;
967
0
    NS_ENSURE_SUCCESS(GetWindowDimensions(dimensions), false);
968
0
969
0
    // Don't show remote iframe if we are waiting for the completion of reflow.
970
0
    if (!aFrame || !(aFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
971
0
      mRemoteBrowser->UpdateDimensions(dimensions, size);
972
0
    }
973
0
  }
974
0
975
0
  return true;
976
0
}
977
978
void
979
nsFrameLoader::Hide()
980
0
{
981
0
  if (mHideCalled) {
982
0
    return;
983
0
  }
984
0
  if (mInShow) {
985
0
    mHideCalled = true;
986
0
    return;
987
0
  }
988
0
989
0
  if (!mDocShell)
990
0
    return;
991
0
992
0
  nsCOMPtr<nsIContentViewer> contentViewer;
993
0
  mDocShell->GetContentViewer(getter_AddRefs(contentViewer));
994
0
  if (contentViewer)
995
0
    contentViewer->SetSticky(false);
996
0
997
0
  nsCOMPtr<nsIBaseWindow> baseWin = do_QueryInterface(mDocShell);
998
0
  NS_ASSERTION(baseWin,
999
0
               "Found an nsIDocShell which doesn't implement nsIBaseWindow.");
1000
0
  baseWin->SetVisibility(false);
1001
0
  baseWin->SetParentWidget(nullptr);
1002
0
}
1003
1004
void
1005
nsFrameLoader::ForceLayoutIfNecessary()
1006
0
{
1007
0
  nsIFrame* frame = GetPrimaryFrameOfOwningContent();
1008
0
  if (!frame) {
1009
0
    return;
1010
0
  }
1011
0
1012
0
  nsPresContext* presContext = frame->PresContext();
1013
0
  if (!presContext) {
1014
0
    return;
1015
0
  }
1016
0
1017
0
  // Only force the layout flush if the frameloader hasn't ever been
1018
0
  // run through layout.
1019
0
  if (frame->GetStateBits() & NS_FRAME_FIRST_REFLOW) {
1020
0
    if (nsCOMPtr<nsIPresShell> shell = presContext->GetPresShell()) {
1021
0
      shell->FlushPendingNotifications(FlushType::Layout);
1022
0
    }
1023
0
  }
1024
0
}
1025
1026
nsresult
1027
nsFrameLoader::SwapWithOtherRemoteLoader(nsFrameLoader* aOther,
1028
                                         nsIFrameLoaderOwner* aThisOwner,
1029
                                         nsIFrameLoaderOwner* aOtherOwner)
1030
0
{
1031
0
  MOZ_ASSERT(NS_IsMainThread());
1032
0
1033
#ifdef DEBUG
1034
  RefPtr<nsFrameLoader> first = aThisOwner->GetFrameLoader();
1035
  RefPtr<nsFrameLoader> second = aOtherOwner->GetFrameLoader();
1036
  MOZ_ASSERT(first == this, "aThisOwner must own this");
1037
  MOZ_ASSERT(second == aOther, "aOtherOwner must own aOther");
1038
#endif
1039
1040
0
  Element* ourContent = mOwnerContent;
1041
0
  Element* otherContent = aOther->mOwnerContent;
1042
0
1043
0
  if (!ourContent || !otherContent) {
1044
0
    // Can't handle this
1045
0
    return NS_ERROR_NOT_IMPLEMENTED;
1046
0
  }
1047
0
1048
0
  // Make sure there are no same-origin issues
1049
0
  bool equal;
1050
0
  nsresult rv =
1051
0
    ourContent->NodePrincipal()->Equals(otherContent->NodePrincipal(), &equal);
1052
0
  if (NS_FAILED(rv) || !equal) {
1053
0
    // Security problems loom.  Just bail on it all
1054
0
    return NS_ERROR_DOM_SECURITY_ERR;
1055
0
  }
1056
0
1057
0
  nsIDocument* ourDoc = ourContent->GetComposedDoc();
1058
0
  nsIDocument* otherDoc = otherContent->GetComposedDoc();
1059
0
  if (!ourDoc || !otherDoc) {
1060
0
    // Again, how odd, given that we had docshells
1061
0
    return NS_ERROR_NOT_IMPLEMENTED;
1062
0
  }
1063
0
1064
0
  nsIPresShell* ourShell = ourDoc->GetShell();
1065
0
  nsIPresShell* otherShell = otherDoc->GetShell();
1066
0
  if (!ourShell || !otherShell) {
1067
0
    return NS_ERROR_NOT_IMPLEMENTED;
1068
0
  }
1069
0
1070
0
  if (!mRemoteBrowser || !aOther->mRemoteBrowser) {
1071
0
    return NS_ERROR_NOT_IMPLEMENTED;
1072
0
  }
1073
0
1074
0
  if (mRemoteBrowser->IsIsolatedMozBrowserElement() !=
1075
0
      aOther->mRemoteBrowser->IsIsolatedMozBrowserElement()) {
1076
0
    return NS_ERROR_NOT_IMPLEMENTED;
1077
0
  }
1078
0
1079
0
  // When we swap docShells, maybe we have to deal with a new page created just
1080
0
  // for this operation. In this case, the browser code should already have set
1081
0
  // the correct userContextId attribute value in the owning element, but our
1082
0
  // docShell, that has been created way before) doesn't know that that
1083
0
  // happened.
1084
0
  // This is the reason why now we must retrieve the correct value from the
1085
0
  // usercontextid attribute before comparing our originAttributes with the
1086
0
  // other one.
1087
0
  OriginAttributes ourOriginAttributes =
1088
0
    mRemoteBrowser->OriginAttributesRef();
1089
0
  rv = PopulateUserContextIdFromAttribute(ourOriginAttributes);
1090
0
  NS_ENSURE_SUCCESS(rv,rv);
1091
0
1092
0
  OriginAttributes otherOriginAttributes =
1093
0
    aOther->mRemoteBrowser->OriginAttributesRef();
1094
0
  rv = aOther->PopulateUserContextIdFromAttribute(otherOriginAttributes);
1095
0
  NS_ENSURE_SUCCESS(rv,rv);
1096
0
1097
0
  if (ourOriginAttributes != otherOriginAttributes) {
1098
0
    return NS_ERROR_NOT_IMPLEMENTED;
1099
0
  }
1100
0
1101
0
  bool ourHasHistory =
1102
0
    mIsTopLevelContent &&
1103
0
    ourContent->IsXULElement(nsGkAtoms::browser) &&
1104
0
    !ourContent->HasAttr(kNameSpaceID_None, nsGkAtoms::disablehistory);
1105
0
  bool otherHasHistory =
1106
0
    aOther->mIsTopLevelContent &&
1107
0
    otherContent->IsXULElement(nsGkAtoms::browser) &&
1108
0
    !otherContent->HasAttr(kNameSpaceID_None, nsGkAtoms::disablehistory);
1109
0
  if (ourHasHistory != otherHasHistory) {
1110
0
    return NS_ERROR_NOT_IMPLEMENTED;
1111
0
  }
1112
0
1113
0
  if (mInSwap || aOther->mInSwap) {
1114
0
    return NS_ERROR_NOT_IMPLEMENTED;
1115
0
  }
1116
0
  mInSwap = aOther->mInSwap = true;
1117
0
1118
0
  nsIFrame* ourFrame = ourContent->GetPrimaryFrame();
1119
0
  nsIFrame* otherFrame = otherContent->GetPrimaryFrame();
1120
0
  if (!ourFrame || !otherFrame) {
1121
0
    mInSwap = aOther->mInSwap = false;
1122
0
    return NS_ERROR_NOT_IMPLEMENTED;
1123
0
  }
1124
0
1125
0
  nsSubDocumentFrame* ourFrameFrame = do_QueryFrame(ourFrame);
1126
0
  if (!ourFrameFrame) {
1127
0
    mInSwap = aOther->mInSwap = false;
1128
0
    return NS_ERROR_NOT_IMPLEMENTED;
1129
0
  }
1130
0
1131
0
  rv = ourFrameFrame->BeginSwapDocShells(otherFrame);
1132
0
  if (NS_FAILED(rv)) {
1133
0
    mInSwap = aOther->mInSwap = false;
1134
0
    return rv;
1135
0
  }
1136
0
1137
0
  nsCOMPtr<nsIBrowserDOMWindow> otherBrowserDOMWindow =
1138
0
    aOther->mRemoteBrowser->GetBrowserDOMWindow();
1139
0
  nsCOMPtr<nsIBrowserDOMWindow> browserDOMWindow =
1140
0
    mRemoteBrowser->GetBrowserDOMWindow();
1141
0
1142
0
  if (!!otherBrowserDOMWindow != !!browserDOMWindow) {
1143
0
    return NS_ERROR_NOT_IMPLEMENTED;
1144
0
  }
1145
0
1146
0
  // Destroy browser frame scripts for content leaving a frame with browser API
1147
0
  if (OwnerIsMozBrowserFrame() && !aOther->OwnerIsMozBrowserFrame()) {
1148
0
    DestroyBrowserFrameScripts();
1149
0
  }
1150
0
  if (!OwnerIsMozBrowserFrame() && aOther->OwnerIsMozBrowserFrame()) {
1151
0
    aOther->DestroyBrowserFrameScripts();
1152
0
  }
1153
0
1154
0
  aOther->mRemoteBrowser->SetBrowserDOMWindow(browserDOMWindow);
1155
0
  mRemoteBrowser->SetBrowserDOMWindow(otherBrowserDOMWindow);
1156
0
1157
#ifdef XP_WIN
1158
  // Native plugin windows used by this remote content need to be reparented.
1159
  if (nsPIDOMWindowOuter* newWin = ourDoc->GetWindow()) {
1160
    RefPtr<nsIWidget> newParent = nsGlobalWindowOuter::Cast(newWin)->GetMainWidget();
1161
    const ManagedContainer<mozilla::plugins::PPluginWidgetParent>& plugins =
1162
      aOther->mRemoteBrowser->ManagedPPluginWidgetParent();
1163
    for (auto iter = plugins.ConstIter(); !iter.Done(); iter.Next()) {
1164
      static_cast<mozilla::plugins::PluginWidgetParent*>(iter.Get()->GetKey())->SetParent(newParent);
1165
    }
1166
  }
1167
#endif // XP_WIN
1168
1169
0
  MaybeUpdatePrimaryTabParent(eTabParentRemoved);
1170
0
  aOther->MaybeUpdatePrimaryTabParent(eTabParentRemoved);
1171
0
1172
0
  SetOwnerContent(otherContent);
1173
0
  aOther->SetOwnerContent(ourContent);
1174
0
1175
0
  mRemoteBrowser->SetOwnerElement(otherContent);
1176
0
  aOther->mRemoteBrowser->SetOwnerElement(ourContent);
1177
0
1178
0
  // Update window activation state for the swapped owner content.
1179
0
  Unused << mRemoteBrowser->SendParentActivated(
1180
0
    ParentWindowIsActive(otherContent->OwnerDoc()));
1181
0
  Unused << aOther->mRemoteBrowser->SendParentActivated(
1182
0
    ParentWindowIsActive(ourContent->OwnerDoc()));
1183
0
1184
0
  MaybeUpdatePrimaryTabParent(eTabParentChanged);
1185
0
  aOther->MaybeUpdatePrimaryTabParent(eTabParentChanged);
1186
0
1187
0
  RefPtr<nsFrameMessageManager> ourMessageManager = mMessageManager;
1188
0
  RefPtr<nsFrameMessageManager> otherMessageManager = aOther->mMessageManager;
1189
0
  // Swap and setup things in parent message managers.
1190
0
  if (ourMessageManager) {
1191
0
    ourMessageManager->SetCallback(aOther);
1192
0
  }
1193
0
  if (otherMessageManager) {
1194
0
    otherMessageManager->SetCallback(this);
1195
0
  }
1196
0
  mMessageManager.swap(aOther->mMessageManager);
1197
0
1198
0
  // Perform the actual swap of the internal refptrs. We keep a strong reference
1199
0
  // to ourselves to make sure we don't die while we overwrite our reference to
1200
0
  // ourself.
1201
0
  RefPtr<nsFrameLoader> kungFuDeathGrip(this);
1202
0
  aThisOwner->InternalSetFrameLoader(aOther);
1203
0
  aOtherOwner->InternalSetFrameLoader(kungFuDeathGrip);
1204
0
1205
0
  ourFrameFrame->EndSwapDocShells(otherFrame);
1206
0
1207
0
  ourShell->BackingScaleFactorChanged();
1208
0
  otherShell->BackingScaleFactorChanged();
1209
0
1210
0
  // Initialize browser API if needed now that owner content has changed.
1211
0
  InitializeBrowserAPI();
1212
0
  aOther->InitializeBrowserAPI();
1213
0
1214
0
  mInSwap = aOther->mInSwap = false;
1215
0
1216
0
  // Send an updated tab context since owner content type may have changed.
1217
0
  MutableTabContext ourContext;
1218
0
  rv = GetNewTabContext(&ourContext);
1219
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1220
0
    return rv;
1221
0
  }
1222
0
  MutableTabContext otherContext;
1223
0
  rv = aOther->GetNewTabContext(&otherContext);
1224
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
1225
0
    return rv;
1226
0
  }
1227
0
1228
0
  // Swap the remoteType property as the frameloaders are being swapped
1229
0
  nsAutoString ourRemoteType;
1230
0
  if (!ourContent->GetAttr(kNameSpaceID_None, nsGkAtoms::RemoteType,
1231
0
                           ourRemoteType)) {
1232
0
    ourRemoteType.AssignLiteral(DEFAULT_REMOTE_TYPE);
1233
0
  }
1234
0
  nsAutoString otherRemoteType;
1235
0
  if (!otherContent->GetAttr(kNameSpaceID_None, nsGkAtoms::RemoteType,
1236
0
                             otherRemoteType)) {
1237
0
    otherRemoteType.AssignLiteral(DEFAULT_REMOTE_TYPE);
1238
0
  }
1239
0
  ourContent->SetAttr(kNameSpaceID_None, nsGkAtoms::RemoteType, otherRemoteType, false);
1240
0
  otherContent->SetAttr(kNameSpaceID_None, nsGkAtoms::RemoteType, ourRemoteType, false);
1241
0
1242
0
  Unused << mRemoteBrowser->SendSwappedWithOtherRemoteLoader(
1243
0
    ourContext.AsIPCTabContext());
1244
0
  Unused << aOther->mRemoteBrowser->SendSwappedWithOtherRemoteLoader(
1245
0
    otherContext.AsIPCTabContext());
1246
0
  return NS_OK;
1247
0
}
1248
1249
class MOZ_RAII AutoResetInFrameSwap final
1250
{
1251
public:
1252
  AutoResetInFrameSwap(nsFrameLoader* aThisFrameLoader,
1253
                       nsFrameLoader* aOtherFrameLoader,
1254
                       nsDocShell* aThisDocShell,
1255
                       nsDocShell* aOtherDocShell,
1256
                       EventTarget* aThisEventTarget,
1257
                       EventTarget* aOtherEventTarget
1258
                       MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
1259
    : mThisFrameLoader(aThisFrameLoader)
1260
    , mOtherFrameLoader(aOtherFrameLoader)
1261
    , mThisDocShell(aThisDocShell)
1262
    , mOtherDocShell(aOtherDocShell)
1263
    , mThisEventTarget(aThisEventTarget)
1264
    , mOtherEventTarget(aOtherEventTarget)
1265
0
  {
1266
0
    MOZ_GUARD_OBJECT_NOTIFIER_INIT;
1267
0
1268
0
    mThisFrameLoader->mInSwap = true;
1269
0
    mOtherFrameLoader->mInSwap = true;
1270
0
    mThisDocShell->SetInFrameSwap(true);
1271
0
    mOtherDocShell->SetInFrameSwap(true);
1272
0
1273
0
    // Fire pageshow events on still-loading pages, and then fire pagehide
1274
0
    // events.  Note that we do NOT fire these in the normal way, but just fire
1275
0
    // them on the chrome event handlers.
1276
0
    nsContentUtils::FirePageShowEvent(mThisDocShell, mThisEventTarget, false);
1277
0
    nsContentUtils::FirePageShowEvent(mOtherDocShell, mOtherEventTarget, false);
1278
0
    nsContentUtils::FirePageHideEvent(mThisDocShell, mThisEventTarget);
1279
0
    nsContentUtils::FirePageHideEvent(mOtherDocShell, mOtherEventTarget);
1280
0
  }
1281
1282
  ~AutoResetInFrameSwap()
1283
0
  {
1284
0
    nsContentUtils::FirePageShowEvent(mThisDocShell, mThisEventTarget, true);
1285
0
    nsContentUtils::FirePageShowEvent(mOtherDocShell, mOtherEventTarget, true);
1286
0
1287
0
    mThisFrameLoader->mInSwap = false;
1288
0
    mOtherFrameLoader->mInSwap = false;
1289
0
    mThisDocShell->SetInFrameSwap(false);
1290
0
    mOtherDocShell->SetInFrameSwap(false);
1291
0
  }
1292
1293
private:
1294
  RefPtr<nsFrameLoader> mThisFrameLoader;
1295
  RefPtr<nsFrameLoader> mOtherFrameLoader;
1296
  RefPtr<nsDocShell> mThisDocShell;
1297
  RefPtr<nsDocShell> mOtherDocShell;
1298
  nsCOMPtr<EventTarget> mThisEventTarget;
1299
  nsCOMPtr<EventTarget> mOtherEventTarget;
1300
  MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
1301
};
1302
1303
nsresult
1304
nsFrameLoader::SwapWithOtherLoader(nsFrameLoader* aOther,
1305
                                   nsIFrameLoaderOwner* aThisOwner,
1306
                                   nsIFrameLoaderOwner* aOtherOwner)
1307
0
{
1308
#ifdef DEBUG
1309
  RefPtr<nsFrameLoader> first = aThisOwner->GetFrameLoader();
1310
  RefPtr<nsFrameLoader> second = aOtherOwner->GetFrameLoader();
1311
  MOZ_ASSERT(first == this, "aThisOwner must own this");
1312
  MOZ_ASSERT(second == aOther, "aOtherOwner must own aOther");
1313
#endif
1314
1315
0
  NS_ENSURE_STATE(!mInShow && !aOther->mInShow);
1316
0
1317
0
  if (IsRemoteFrame() != aOther->IsRemoteFrame()) {
1318
0
    NS_WARNING("Swapping remote and non-remote frames is not currently supported");
1319
0
    return NS_ERROR_NOT_IMPLEMENTED;
1320
0
  }
1321
0
1322
0
  Element* ourContent = mOwnerContent;
1323
0
  Element* otherContent = aOther->mOwnerContent;
1324
0
1325
0
  if (!ourContent || !otherContent) {
1326
0
    // Can't handle this
1327
0
    return NS_ERROR_NOT_IMPLEMENTED;
1328
0
  }
1329
0
1330
0
  bool ourHasSrcdoc = ourContent->IsHTMLElement(nsGkAtoms::iframe) &&
1331
0
                      ourContent->HasAttr(kNameSpaceID_None, nsGkAtoms::srcdoc);
1332
0
  bool otherHasSrcdoc = otherContent->IsHTMLElement(nsGkAtoms::iframe) &&
1333
0
                        otherContent->HasAttr(kNameSpaceID_None, nsGkAtoms::srcdoc);
1334
0
  if (ourHasSrcdoc || otherHasSrcdoc) {
1335
0
    // Ignore this case entirely for now, since we support XUL <-> HTML swapping
1336
0
    return NS_ERROR_NOT_IMPLEMENTED;
1337
0
  }
1338
0
1339
0
  bool ourFullscreenAllowed =
1340
0
    ourContent->IsXULElement() ||
1341
0
    (OwnerIsMozBrowserFrame() &&
1342
0
      (ourContent->HasAttr(kNameSpaceID_None, nsGkAtoms::allowfullscreen) ||
1343
0
       ourContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozallowfullscreen)));
1344
0
  bool otherFullscreenAllowed =
1345
0
    otherContent->IsXULElement() ||
1346
0
    (aOther->OwnerIsMozBrowserFrame() &&
1347
0
      (otherContent->HasAttr(kNameSpaceID_None, nsGkAtoms::allowfullscreen) ||
1348
0
       otherContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozallowfullscreen)));
1349
0
  if (ourFullscreenAllowed != otherFullscreenAllowed) {
1350
0
    return NS_ERROR_NOT_IMPLEMENTED;
1351
0
  }
1352
0
1353
0
  bool ourPaymentRequestAllowed =
1354
0
    ourContent->HasAttr(kNameSpaceID_None, nsGkAtoms::allowpaymentrequest);
1355
0
  bool otherPaymentRequestAllowed =
1356
0
    otherContent->HasAttr(kNameSpaceID_None, nsGkAtoms::allowpaymentrequest);
1357
0
  if (ourPaymentRequestAllowed != otherPaymentRequestAllowed) {
1358
0
    return NS_ERROR_NOT_IMPLEMENTED;
1359
0
  }
1360
0
1361
0
  // Divert to a separate path for the remaining steps in the remote case
1362
0
  if (IsRemoteFrame()) {
1363
0
    MOZ_ASSERT(aOther->IsRemoteFrame());
1364
0
    return SwapWithOtherRemoteLoader(aOther, aThisOwner, aOtherOwner);
1365
0
  }
1366
0
1367
0
  // Make sure there are no same-origin issues
1368
0
  bool equal;
1369
0
  nsresult rv =
1370
0
    ourContent->NodePrincipal()->Equals(otherContent->NodePrincipal(), &equal);
1371
0
  if (NS_FAILED(rv) || !equal) {
1372
0
    // Security problems loom.  Just bail on it all
1373
0
    return NS_ERROR_DOM_SECURITY_ERR;
1374
0
  }
1375
0
1376
0
  RefPtr<nsDocShell> ourDocshell = static_cast<nsDocShell*>(GetExistingDocShell());
1377
0
  RefPtr<nsDocShell> otherDocshell = static_cast<nsDocShell*>(aOther->GetExistingDocShell());
1378
0
  if (!ourDocshell || !otherDocshell) {
1379
0
    // How odd
1380
0
    return NS_ERROR_NOT_IMPLEMENTED;
1381
0
  }
1382
0
1383
0
  // To avoid having to mess with session history, avoid swapping
1384
0
  // frameloaders that don't correspond to root same-type docshells,
1385
0
  // unless both roots have session history disabled.
1386
0
  nsCOMPtr<nsIDocShellTreeItem> ourRootTreeItem, otherRootTreeItem;
1387
0
  ourDocshell->GetSameTypeRootTreeItem(getter_AddRefs(ourRootTreeItem));
1388
0
  otherDocshell->GetSameTypeRootTreeItem(getter_AddRefs(otherRootTreeItem));
1389
0
  nsCOMPtr<nsIWebNavigation> ourRootWebnav =
1390
0
    do_QueryInterface(ourRootTreeItem);
1391
0
  nsCOMPtr<nsIWebNavigation> otherRootWebnav =
1392
0
    do_QueryInterface(otherRootTreeItem);
1393
0
1394
0
  if (!ourRootWebnav || !otherRootWebnav) {
1395
0
    return NS_ERROR_NOT_IMPLEMENTED;
1396
0
  }
1397
0
1398
0
  RefPtr<ChildSHistory> ourHistory = ourRootWebnav->GetSessionHistory();
1399
0
  RefPtr<ChildSHistory> otherHistory = otherRootWebnav->GetSessionHistory();
1400
0
1401
0
  if ((ourRootTreeItem != ourDocshell || otherRootTreeItem != otherDocshell) &&
1402
0
      (ourHistory || otherHistory)) {
1403
0
    return NS_ERROR_NOT_IMPLEMENTED;
1404
0
  }
1405
0
1406
0
  // Also make sure that the two docshells are the same type. Otherwise
1407
0
  // swapping is certainly not safe. If this needs to be changed then
1408
0
  // the code below needs to be audited as it assumes identical types.
1409
0
  int32_t ourType = ourDocshell->ItemType();
1410
0
  int32_t otherType = otherDocshell->ItemType();
1411
0
  if (ourType != otherType) {
1412
0
    return NS_ERROR_NOT_IMPLEMENTED;
1413
0
  }
1414
0
1415
0
  // One more twist here.  Setting up the right treeowners in a heterogeneous
1416
0
  // tree is a bit of a pain.  So make sure that if ourType is not
1417
0
  // nsIDocShellTreeItem::typeContent then all of our descendants are the same
1418
0
  // type as us.
1419
0
  if (ourType != nsIDocShellTreeItem::typeContent &&
1420
0
      (!AllDescendantsOfType(ourDocshell, ourType) ||
1421
0
       !AllDescendantsOfType(otherDocshell, otherType))) {
1422
0
    return NS_ERROR_NOT_IMPLEMENTED;
1423
0
  }
1424
0
1425
0
  // Save off the tree owners, frame elements, chrome event handlers, and
1426
0
  // docshell and document parents before doing anything else.
1427
0
  nsCOMPtr<nsIDocShellTreeOwner> ourOwner, otherOwner;
1428
0
  ourDocshell->GetTreeOwner(getter_AddRefs(ourOwner));
1429
0
  otherDocshell->GetTreeOwner(getter_AddRefs(otherOwner));
1430
0
  // Note: it's OK to have null treeowners.
1431
0
1432
0
  nsCOMPtr<nsIDocShellTreeItem> ourParentItem, otherParentItem;
1433
0
  ourDocshell->GetParent(getter_AddRefs(ourParentItem));
1434
0
  otherDocshell->GetParent(getter_AddRefs(otherParentItem));
1435
0
  if (!ourParentItem || !otherParentItem) {
1436
0
    return NS_ERROR_NOT_IMPLEMENTED;
1437
0
  }
1438
0
1439
0
  // Make sure our parents are the same type too
1440
0
  int32_t ourParentType = ourParentItem->ItemType();
1441
0
  int32_t otherParentType = otherParentItem->ItemType();
1442
0
  if (ourParentType != otherParentType) {
1443
0
    return NS_ERROR_NOT_IMPLEMENTED;
1444
0
  }
1445
0
1446
0
  nsCOMPtr<nsPIDOMWindowOuter> ourWindow = ourDocshell->GetWindow();
1447
0
  nsCOMPtr<nsPIDOMWindowOuter> otherWindow = otherDocshell->GetWindow();
1448
0
1449
0
  nsCOMPtr<Element> ourFrameElement =
1450
0
    ourWindow->GetFrameElementInternal();
1451
0
  nsCOMPtr<Element> otherFrameElement =
1452
0
    otherWindow->GetFrameElementInternal();
1453
0
1454
0
  nsCOMPtr<EventTarget> ourChromeEventHandler = ourWindow->GetChromeEventHandler();
1455
0
  nsCOMPtr<EventTarget> otherChromeEventHandler = otherWindow->GetChromeEventHandler();
1456
0
1457
0
  nsCOMPtr<EventTarget> ourEventTarget = ourWindow->GetParentTarget();
1458
0
  nsCOMPtr<EventTarget> otherEventTarget = otherWindow->GetParentTarget();
1459
0
1460
0
  NS_ASSERTION(SameCOMIdentity(ourFrameElement, ourContent) &&
1461
0
               SameCOMIdentity(otherFrameElement, otherContent) &&
1462
0
               SameCOMIdentity(ourChromeEventHandler, ourContent) &&
1463
0
               SameCOMIdentity(otherChromeEventHandler, otherContent),
1464
0
               "How did that happen, exactly?");
1465
0
1466
0
  nsCOMPtr<nsIDocument> ourChildDocument = ourWindow->GetExtantDoc();
1467
0
  nsCOMPtr<nsIDocument> otherChildDocument = otherWindow ->GetExtantDoc();
1468
0
  if (!ourChildDocument || !otherChildDocument) {
1469
0
    // This shouldn't be happening
1470
0
    return NS_ERROR_NOT_IMPLEMENTED;
1471
0
  }
1472
0
1473
0
  nsCOMPtr<nsIDocument> ourParentDocument =
1474
0
    ourChildDocument->GetParentDocument();
1475
0
  nsCOMPtr<nsIDocument> otherParentDocument =
1476
0
    otherChildDocument->GetParentDocument();
1477
0
1478
0
  // Make sure to swap docshells between the two frames.
1479
0
  nsIDocument* ourDoc = ourContent->GetComposedDoc();
1480
0
  nsIDocument* otherDoc = otherContent->GetComposedDoc();
1481
0
  if (!ourDoc || !otherDoc) {
1482
0
    // Again, how odd, given that we had docshells
1483
0
    return NS_ERROR_NOT_IMPLEMENTED;
1484
0
  }
1485
0
1486
0
  NS_ASSERTION(ourDoc == ourParentDocument, "Unexpected parent document");
1487
0
  NS_ASSERTION(otherDoc == otherParentDocument, "Unexpected parent document");
1488
0
1489
0
  nsIPresShell* ourShell = ourDoc->GetShell();
1490
0
  nsIPresShell* otherShell = otherDoc->GetShell();
1491
0
  if (!ourShell || !otherShell) {
1492
0
    return NS_ERROR_NOT_IMPLEMENTED;
1493
0
  }
1494
0
1495
0
  if (ourDocshell->GetIsIsolatedMozBrowserElement() !=
1496
0
      otherDocshell->GetIsIsolatedMozBrowserElement()) {
1497
0
    return NS_ERROR_NOT_IMPLEMENTED;
1498
0
  }
1499
0
1500
0
  // When we swap docShells, maybe we have to deal with a new page created just
1501
0
  // for this operation. In this case, the browser code should already have set
1502
0
  // the correct userContextId attribute value in the owning element, but our
1503
0
  // docShell, that has been created way before) doesn't know that that
1504
0
  // happened.
1505
0
  // This is the reason why now we must retrieve the correct value from the
1506
0
  // usercontextid attribute before comparing our originAttributes with the
1507
0
  // other one.
1508
0
  OriginAttributes ourOriginAttributes =
1509
0
    ourDocshell->GetOriginAttributes();
1510
0
  rv = PopulateUserContextIdFromAttribute(ourOriginAttributes);
1511
0
  NS_ENSURE_SUCCESS(rv,rv);
1512
0
1513
0
  OriginAttributes otherOriginAttributes =
1514
0
    otherDocshell->GetOriginAttributes();
1515
0
  rv = aOther->PopulateUserContextIdFromAttribute(otherOriginAttributes);
1516
0
  NS_ENSURE_SUCCESS(rv,rv);
1517
0
1518
0
  if (ourOriginAttributes != otherOriginAttributes) {
1519
0
    return NS_ERROR_NOT_IMPLEMENTED;
1520
0
  }
1521
0
1522
0
  if (mInSwap || aOther->mInSwap) {
1523
0
    return NS_ERROR_NOT_IMPLEMENTED;
1524
0
  }
1525
0
  AutoResetInFrameSwap autoFrameSwap(this, aOther, ourDocshell, otherDocshell,
1526
0
                                     ourEventTarget, otherEventTarget);
1527
0
1528
0
  nsIFrame* ourFrame = ourContent->GetPrimaryFrame();
1529
0
  nsIFrame* otherFrame = otherContent->GetPrimaryFrame();
1530
0
  if (!ourFrame || !otherFrame) {
1531
0
    return NS_ERROR_NOT_IMPLEMENTED;
1532
0
  }
1533
0
1534
0
  nsSubDocumentFrame* ourFrameFrame = do_QueryFrame(ourFrame);
1535
0
  if (!ourFrameFrame) {
1536
0
    return NS_ERROR_NOT_IMPLEMENTED;
1537
0
  }
1538
0
1539
0
  // OK.  First begin to swap the docshells in the two nsIFrames
1540
0
  rv = ourFrameFrame->BeginSwapDocShells(otherFrame);
1541
0
  if (NS_FAILED(rv)) {
1542
0
    return rv;
1543
0
  }
1544
0
1545
0
  // Destroy browser frame scripts for content leaving a frame with browser API
1546
0
  if (OwnerIsMozBrowserFrame() && !aOther->OwnerIsMozBrowserFrame()) {
1547
0
    DestroyBrowserFrameScripts();
1548
0
  }
1549
0
  if (!OwnerIsMozBrowserFrame() && aOther->OwnerIsMozBrowserFrame()) {
1550
0
    aOther->DestroyBrowserFrameScripts();
1551
0
  }
1552
0
1553
0
  // Now move the docshells to the right docshell trees.  Note that this
1554
0
  // resets their treeowners to null.
1555
0
  ourParentItem->RemoveChild(ourDocshell);
1556
0
  otherParentItem->RemoveChild(otherDocshell);
1557
0
  if (ourType == nsIDocShellTreeItem::typeContent) {
1558
0
    ourOwner->ContentShellRemoved(ourDocshell);
1559
0
    otherOwner->ContentShellRemoved(otherDocshell);
1560
0
  }
1561
0
1562
0
  ourParentItem->AddChild(otherDocshell);
1563
0
  otherParentItem->AddChild(ourDocshell);
1564
0
1565
0
  // Restore the correct chrome event handlers.
1566
0
  ourDocshell->SetChromeEventHandler(otherChromeEventHandler);
1567
0
  otherDocshell->SetChromeEventHandler(ourChromeEventHandler);
1568
0
  // Restore the correct treeowners
1569
0
  // (and also chrome event handlers for content frames only).
1570
0
  SetTreeOwnerAndChromeEventHandlerOnDocshellTree(ourDocshell, otherOwner,
1571
0
    ourType == nsIDocShellTreeItem::typeContent ? otherChromeEventHandler.get() : nullptr);
1572
0
  SetTreeOwnerAndChromeEventHandlerOnDocshellTree(otherDocshell, ourOwner,
1573
0
    ourType == nsIDocShellTreeItem::typeContent ? ourChromeEventHandler.get() : nullptr);
1574
0
1575
0
  // Switch the owner content before we start calling AddTreeItemToTreeOwner.
1576
0
  // Note that we rely on this to deal with setting mObservingOwnerContent to
1577
0
  // false and calling RemoveMutationObserver as needed.
1578
0
  SetOwnerContent(otherContent);
1579
0
  aOther->SetOwnerContent(ourContent);
1580
0
1581
0
  AddTreeItemToTreeOwner(ourDocshell, otherOwner, otherParentType, nullptr);
1582
0
  aOther->AddTreeItemToTreeOwner(otherDocshell, ourOwner, ourParentType,
1583
0
                                 nullptr);
1584
0
1585
0
  // SetSubDocumentFor nulls out parent documents on the old child doc if a
1586
0
  // new non-null document is passed in, so just go ahead and remove both
1587
0
  // kids before reinserting in the parent subdoc maps, to avoid
1588
0
  // complications.
1589
0
  ourParentDocument->SetSubDocumentFor(ourContent, nullptr);
1590
0
  otherParentDocument->SetSubDocumentFor(otherContent, nullptr);
1591
0
  ourParentDocument->SetSubDocumentFor(ourContent, otherChildDocument);
1592
0
  otherParentDocument->SetSubDocumentFor(otherContent, ourChildDocument);
1593
0
1594
0
  ourWindow->SetFrameElementInternal(otherFrameElement);
1595
0
  otherWindow->SetFrameElementInternal(ourFrameElement);
1596
0
1597
0
  RefPtr<nsFrameMessageManager> ourMessageManager = mMessageManager;
1598
0
  RefPtr<nsFrameMessageManager> otherMessageManager = aOther->mMessageManager;
1599
0
  // Swap pointers in child message managers.
1600
0
  if (mChildMessageManager) {
1601
0
    InProcessTabChildMessageManager* tabChild = mChildMessageManager;
1602
0
    tabChild->SetOwner(otherContent);
1603
0
    tabChild->SetChromeMessageManager(otherMessageManager);
1604
0
  }
1605
0
  if (aOther->mChildMessageManager) {
1606
0
    InProcessTabChildMessageManager* otherTabChild = aOther->mChildMessageManager;
1607
0
    otherTabChild->SetOwner(ourContent);
1608
0
    otherTabChild->SetChromeMessageManager(ourMessageManager);
1609
0
  }
1610
0
  // Swap and setup things in parent message managers.
1611
0
  if (mMessageManager) {
1612
0
    mMessageManager->SetCallback(aOther);
1613
0
  }
1614
0
  if (aOther->mMessageManager) {
1615
0
    aOther->mMessageManager->SetCallback(this);
1616
0
  }
1617
0
  mMessageManager.swap(aOther->mMessageManager);
1618
0
1619
0
  // Perform the actual swap of the internal refptrs. We keep a strong reference
1620
0
  // to ourselves to make sure we don't die while we overwrite our reference to
1621
0
  // ourself.
1622
0
  RefPtr<nsFrameLoader> kungFuDeathGrip(this);
1623
0
  aThisOwner->InternalSetFrameLoader(aOther);
1624
0
  aOtherOwner->InternalSetFrameLoader(kungFuDeathGrip);
1625
0
1626
0
  // Drop any cached content viewers in the two session histories.
1627
0
  if (ourHistory) {
1628
0
    ourHistory->EvictLocalContentViewers();
1629
0
  }
1630
0
  if (otherHistory) {
1631
0
    otherHistory->EvictLocalContentViewers();
1632
0
  }
1633
0
1634
0
  NS_ASSERTION(ourFrame == ourContent->GetPrimaryFrame() &&
1635
0
               otherFrame == otherContent->GetPrimaryFrame(),
1636
0
               "changed primary frame");
1637
0
1638
0
  ourFrameFrame->EndSwapDocShells(otherFrame);
1639
0
1640
0
  // If the content being swapped came from windows on two screens with
1641
0
  // incompatible backing resolution (e.g. dragging a tab between windows on
1642
0
  // hi-dpi and low-dpi screens), it will have style data that is based on
1643
0
  // the wrong appUnitsPerDevPixel value. So we tell the PresShells that their
1644
0
  // backing scale factor may have changed. (Bug 822266)
1645
0
  ourShell->BackingScaleFactorChanged();
1646
0
  otherShell->BackingScaleFactorChanged();
1647
0
1648
0
  // Initialize browser API if needed now that owner content has changed
1649
0
  InitializeBrowserAPI();
1650
0
  aOther->InitializeBrowserAPI();
1651
0
1652
0
  return NS_OK;
1653
0
}
1654
1655
void
1656
nsFrameLoader::Destroy()
1657
0
{
1658
0
  StartDestroy();
1659
0
}
1660
1661
class nsFrameLoaderDestroyRunnable : public Runnable
1662
{
1663
  enum DestroyPhase
1664
  {
1665
    // See the implementation of Run for an explanation of these phases.
1666
    eDestroyDocShell,
1667
    eWaitForUnloadMessage,
1668
    eDestroyComplete
1669
  };
1670
1671
  RefPtr<nsFrameLoader> mFrameLoader;
1672
  DestroyPhase mPhase;
1673
1674
public:
1675
  explicit nsFrameLoaderDestroyRunnable(nsFrameLoader* aFrameLoader)
1676
    : mozilla::Runnable("nsFrameLoaderDestroyRunnable")
1677
    , mFrameLoader(aFrameLoader)
1678
    , mPhase(eDestroyDocShell)
1679
0
  {
1680
0
  }
1681
1682
  NS_IMETHOD Run() override;
1683
};
1684
1685
void
1686
nsFrameLoader::StartDestroy()
1687
0
{
1688
0
  // nsFrameLoader::StartDestroy is called just before the frameloader is
1689
0
  // detached from the <browser> element. Destruction continues in phases via
1690
0
  // the nsFrameLoaderDestroyRunnable.
1691
0
1692
0
  if (mDestroyCalled) {
1693
0
    return;
1694
0
  }
1695
0
  mDestroyCalled = true;
1696
0
1697
0
  // After this point, we return an error when trying to send a message using
1698
0
  // the message manager on the frame.
1699
0
  if (mMessageManager) {
1700
0
    mMessageManager->Close();
1701
0
  }
1702
0
1703
0
  // Retain references to the <browser> element and the frameloader in case we
1704
0
  // receive any messages from the message manager on the frame. These
1705
0
  // references are dropped in DestroyComplete.
1706
0
  if (mChildMessageManager || mRemoteBrowser) {
1707
0
    mOwnerContentStrong = mOwnerContent;
1708
0
    if (mRemoteBrowser) {
1709
0
      mRemoteBrowser->CacheFrameLoader(this);
1710
0
    }
1711
0
    if (mChildMessageManager) {
1712
0
      mChildMessageManager->CacheFrameLoader(this);
1713
0
    }
1714
0
  }
1715
0
1716
0
  // If the TabParent has installed any event listeners on the window, this is
1717
0
  // its last chance to remove them while we're still in the document.
1718
0
  if (mRemoteBrowser) {
1719
0
    mRemoteBrowser->RemoveWindowListeners();
1720
0
  }
1721
0
1722
0
  nsCOMPtr<nsIDocument> doc;
1723
0
  bool dynamicSubframeRemoval = false;
1724
0
  if (mOwnerContent) {
1725
0
    doc = mOwnerContent->OwnerDoc();
1726
0
    dynamicSubframeRemoval = !mIsTopLevelContent && !doc->InUnlinkOrDeletion();
1727
0
    doc->SetSubDocumentFor(mOwnerContent, nullptr);
1728
0
    MaybeUpdatePrimaryTabParent(eTabParentRemoved);
1729
0
    SetOwnerContent(nullptr);
1730
0
  }
1731
0
1732
0
  // Seems like this is a dynamic frame removal.
1733
0
  if (dynamicSubframeRemoval) {
1734
0
    if (mDocShell) {
1735
0
      mDocShell->RemoveFromSessionHistory();
1736
0
    }
1737
0
  }
1738
0
1739
0
  // Let the tree owner know we're gone.
1740
0
  if (mIsTopLevelContent) {
1741
0
    if (mDocShell) {
1742
0
      nsCOMPtr<nsIDocShellTreeItem> parentItem;
1743
0
      mDocShell->GetParent(getter_AddRefs(parentItem));
1744
0
      nsCOMPtr<nsIDocShellTreeOwner> owner = do_GetInterface(parentItem);
1745
0
      if (owner) {
1746
0
        owner->ContentShellRemoved(mDocShell);
1747
0
      }
1748
0
    }
1749
0
  }
1750
0
1751
0
  // Let our window know that we are gone
1752
0
  if (mDocShell) {
1753
0
    nsCOMPtr<nsPIDOMWindowOuter> win_private(mDocShell->GetWindow());
1754
0
    if (win_private) {
1755
0
      win_private->SetFrameElementInternal(nullptr);
1756
0
    }
1757
0
  }
1758
0
1759
0
  nsCOMPtr<nsIRunnable> destroyRunnable = new nsFrameLoaderDestroyRunnable(this);
1760
0
  if (mNeedsAsyncDestroy || !doc ||
1761
0
      NS_FAILED(doc->FinalizeFrameLoader(this, destroyRunnable))) {
1762
0
    NS_DispatchToCurrentThread(destroyRunnable);
1763
0
  }
1764
0
}
1765
1766
nsresult
1767
nsFrameLoaderDestroyRunnable::Run()
1768
0
{
1769
0
  switch (mPhase) {
1770
0
  case eDestroyDocShell:
1771
0
    mFrameLoader->DestroyDocShell();
1772
0
1773
0
    // In the out-of-process case, TabParent will eventually call
1774
0
    // DestroyComplete once it receives a __delete__ message from the child. In
1775
0
    // the in-process case, we dispatch a series of runnables to ensure that
1776
0
    // DestroyComplete gets called at the right time. The frame loader is kept
1777
0
    // alive by mFrameLoader during this time.
1778
0
    if (mFrameLoader->mChildMessageManager) {
1779
0
      // When the docshell is destroyed, NotifyWindowIDDestroyed is called to
1780
0
      // asynchronously notify {outer,inner}-window-destroyed via a runnable. We
1781
0
      // don't want DestroyComplete to run until after those runnables have
1782
0
      // run. Since we're enqueueing ourselves after the window-destroyed
1783
0
      // runnables are enqueued, we're guaranteed to run after.
1784
0
      mPhase = eWaitForUnloadMessage;
1785
0
      NS_DispatchToCurrentThread(this);
1786
0
    }
1787
0
    break;
1788
0
1789
0
   case eWaitForUnloadMessage:
1790
0
     // The *-window-destroyed observers have finished running at this
1791
0
     // point. However, it's possible that a *-window-destroyed observer might
1792
0
     // have sent a message using the message manager. These messages might not
1793
0
     // have been processed yet. So we enqueue ourselves again to ensure that
1794
0
     // DestroyComplete runs after all messages sent by *-window-destroyed
1795
0
     // observers have been processed.
1796
0
     mPhase = eDestroyComplete;
1797
0
     NS_DispatchToCurrentThread(this);
1798
0
     break;
1799
0
1800
0
   case eDestroyComplete:
1801
0
     // Now that all messages sent by unload listeners and window destroyed
1802
0
     // observers have been processed, we disconnect the message manager and
1803
0
     // finish destruction.
1804
0
     mFrameLoader->DestroyComplete();
1805
0
     break;
1806
0
  }
1807
0
1808
0
  return NS_OK;
1809
0
}
1810
1811
void
1812
nsFrameLoader::DestroyDocShell()
1813
0
{
1814
0
  // This code runs after the frameloader has been detached from the <browser>
1815
0
  // element. We postpone this work because we may not be allowed to run
1816
0
  // script at that time.
1817
0
1818
0
  // Ask the TabChild to fire the frame script "unload" event, destroy its
1819
0
  // docshell, and finally destroy the PBrowser actor. This eventually leads to
1820
0
  // nsFrameLoader::DestroyComplete being called.
1821
0
  if (mRemoteBrowser) {
1822
0
    mRemoteBrowser->Destroy();
1823
0
  }
1824
0
1825
0
  // Fire the "unload" event if we're in-process.
1826
0
  if (mChildMessageManager) {
1827
0
    mChildMessageManager->FireUnloadEvent();
1828
0
  }
1829
0
1830
0
  // Destroy the docshell.
1831
0
  nsCOMPtr<nsIBaseWindow> base_win(do_QueryInterface(mDocShell));
1832
0
  if (base_win) {
1833
0
    base_win->Destroy();
1834
0
  }
1835
0
  mDocShell = nullptr;
1836
0
1837
0
  if (mChildMessageManager) {
1838
0
    // Stop handling events in the in-process frame script.
1839
0
    mChildMessageManager->DisconnectEventListeners();
1840
0
  }
1841
0
}
1842
1843
void
1844
nsFrameLoader::DestroyComplete()
1845
0
{
1846
0
  // We get here, as part of StartDestroy, after the docshell has been destroyed
1847
0
  // and all message manager messages sent during docshell destruction have been
1848
0
  // dispatched.  We also get here if the child process crashes. In the latter
1849
0
  // case, StartDestroy might not have been called.
1850
0
1851
0
  // Drop the strong references created in StartDestroy.
1852
0
  if (mChildMessageManager || mRemoteBrowser) {
1853
0
    mOwnerContentStrong = nullptr;
1854
0
    if (mRemoteBrowser) {
1855
0
      mRemoteBrowser->CacheFrameLoader(nullptr);
1856
0
    }
1857
0
    if (mChildMessageManager) {
1858
0
      mChildMessageManager->CacheFrameLoader(nullptr);
1859
0
    }
1860
0
  }
1861
0
1862
0
  // Call TabParent::Destroy if we haven't already (in case of a crash).
1863
0
  if (mRemoteBrowser) {
1864
0
    mRemoteBrowser->SetOwnerElement(nullptr);
1865
0
    mRemoteBrowser->Destroy();
1866
0
    mRemoteBrowser = nullptr;
1867
0
  }
1868
0
1869
0
  if (mMessageManager) {
1870
0
    mMessageManager->Disconnect();
1871
0
  }
1872
0
1873
0
  if (mChildMessageManager) {
1874
0
    mChildMessageManager->Disconnect();
1875
0
  }
1876
0
1877
0
  mMessageManager = nullptr;
1878
0
  mChildMessageManager = nullptr;
1879
0
}
1880
1881
void
1882
nsFrameLoader::SetOwnerContent(Element* aContent)
1883
0
{
1884
0
  if (mObservingOwnerContent) {
1885
0
    mObservingOwnerContent = false;
1886
0
    mOwnerContent->RemoveMutationObserver(this);
1887
0
  }
1888
0
  mOwnerContent = aContent;
1889
0
1890
0
  AutoJSAPI jsapi;
1891
0
  jsapi.Init();
1892
0
1893
0
  JS::RootedObject wrapper(jsapi.cx(), GetWrapper());
1894
0
  if (wrapper) {
1895
0
    JSAutoRealm ar(jsapi.cx(), wrapper);
1896
0
    IgnoredErrorResult rv;
1897
0
    ReparentWrapper(jsapi.cx(), wrapper, rv);
1898
0
    Unused << NS_WARN_IF(rv.Failed());
1899
0
  }
1900
0
1901
0
  if (RenderFrameParent* rfp = GetCurrentRenderFrame()) {
1902
0
    rfp->OwnerContentChanged(aContent);
1903
0
  }
1904
0
}
1905
1906
bool
1907
nsFrameLoader::OwnerIsMozBrowserFrame()
1908
0
{
1909
0
  nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwnerContent);
1910
0
  return browserFrame ? browserFrame->GetReallyIsBrowser() : false;
1911
0
}
1912
1913
bool
1914
nsFrameLoader::OwnerIsIsolatedMozBrowserFrame()
1915
0
{
1916
0
  nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwnerContent);
1917
0
  if (!browserFrame) {
1918
0
    return false;
1919
0
  }
1920
0
1921
0
  if (!OwnerIsMozBrowserFrame()) {
1922
0
    return false;
1923
0
  }
1924
0
1925
0
  bool isolated = browserFrame->GetIsolated();
1926
0
  if (isolated) {
1927
0
    return true;
1928
0
  }
1929
0
1930
0
  return false;
1931
0
}
1932
1933
bool
1934
nsFrameLoader::ShouldUseRemoteProcess()
1935
0
{
1936
0
  if (IsForJSPlugin()) {
1937
0
    return true;
1938
0
  }
1939
0
1940
0
  if (PR_GetEnv("MOZ_DISABLE_OOP_TABS") ||
1941
0
      Preferences::GetBool("dom.ipc.tabs.disabled", false)) {
1942
0
    return false;
1943
0
  }
1944
0
1945
0
  // Don't try to launch nested children if we don't have OMTC.
1946
0
  // They won't render!
1947
0
  if (XRE_IsContentProcess() &&
1948
0
      !CompositorBridgeChild::ChildProcessHasCompositorBridge()) {
1949
0
    return false;
1950
0
  }
1951
0
1952
0
  if (XRE_IsContentProcess() &&
1953
0
      !(PR_GetEnv("MOZ_NESTED_OOP_TABS") ||
1954
0
        Preferences::GetBool("dom.ipc.tabs.nested.enabled", false))) {
1955
0
    return false;
1956
0
  }
1957
0
1958
0
  // If we're an <iframe mozbrowser> and we don't have a "remote" attribute,
1959
0
  // fall back to the default.
1960
0
  if (OwnerIsMozBrowserFrame() &&
1961
0
      !mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::remote)) {
1962
0
1963
0
    return Preferences::GetBool("dom.ipc.browser_frames.oop_by_default", false);
1964
0
  }
1965
0
1966
0
  // Otherwise, we're remote if we have "remote=true" and we're either a
1967
0
  // browser frame or a XUL element.
1968
0
  return (OwnerIsMozBrowserFrame() ||
1969
0
          mOwnerContent->GetNameSpaceID() == kNameSpaceID_XUL) &&
1970
0
         mOwnerContent->AttrValueIs(kNameSpaceID_None,
1971
0
                                    nsGkAtoms::remote,
1972
0
                                    nsGkAtoms::_true,
1973
0
                                    eCaseMatters);
1974
0
}
1975
1976
bool
1977
nsFrameLoader::IsRemoteFrame()
1978
0
{
1979
0
  if (mRemoteFrame) {
1980
0
    MOZ_ASSERT(!mDocShell, "Found a remote frame with a DocShell");
1981
0
    return true;
1982
0
  }
1983
0
  return false;
1984
0
}
1985
1986
nsresult
1987
nsFrameLoader::MaybeCreateDocShell()
1988
0
{
1989
0
  if (mDocShell) {
1990
0
    return NS_OK;
1991
0
  }
1992
0
  if (IsRemoteFrame()) {
1993
0
    return NS_OK;
1994
0
  }
1995
0
  NS_ENSURE_STATE(!mDestroyCalled);
1996
0
1997
0
  // Get our parent docshell off the document of mOwnerContent
1998
0
  // XXXbz this is such a total hack.... We really need to have a
1999
0
  // better setup for doing this.
2000
0
  nsIDocument* doc = mOwnerContent->OwnerDoc();
2001
0
2002
0
  MOZ_RELEASE_ASSERT(!doc->IsResourceDoc(), "We shouldn't even exist");
2003
0
2004
0
  // Check if the document still has a window since it is possible for an
2005
0
  // iframe to be inserted and cause the creation of the docshell in a
2006
0
  // partially unloaded document (see Bug 1305237 comment 127).
2007
0
  if (!doc->IsStaticDocument() &&
2008
0
      (!doc->GetWindow() || !mOwnerContent->IsInComposedDoc())) {
2009
0
    return NS_ERROR_UNEXPECTED;
2010
0
  }
2011
0
2012
0
  if (!doc->IsActive()) {
2013
0
    // Don't allow subframe loads in non-active documents.
2014
0
    // (See bug 610571 comment 5.)
2015
0
    return NS_ERROR_NOT_AVAILABLE;
2016
0
  }
2017
0
2018
0
  nsCOMPtr<nsIDocShell> parentDocShell = doc->GetDocShell();
2019
0
  nsCOMPtr<nsIWebNavigation> parentAsWebNav = do_QueryInterface(parentDocShell);
2020
0
  NS_ENSURE_STATE(parentAsWebNav);
2021
0
2022
0
  // Create the docshell...
2023
0
  mDocShell = do_CreateInstance("@mozilla.org/docshell;1");
2024
0
  NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE);
2025
0
2026
0
  if (!mNetworkCreated) {
2027
0
    if (mDocShell) {
2028
0
      mDocShell->SetCreatedDynamically(true);
2029
0
    }
2030
0
  }
2031
0
2032
0
  // Get the frame name and tell the docshell about it.
2033
0
  NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE);
2034
0
  nsAutoString frameName;
2035
0
2036
0
  int32_t namespaceID = mOwnerContent->GetNameSpaceID();
2037
0
  if (namespaceID == kNameSpaceID_XHTML && !mOwnerContent->IsInHTMLDocument()) {
2038
0
    mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::id, frameName);
2039
0
  } else {
2040
0
    mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::name, frameName);
2041
0
    // XXX if no NAME then use ID, after a transition period this will be
2042
0
    // changed so that XUL only uses ID too (bug 254284).
2043
0
    if (frameName.IsEmpty() && namespaceID == kNameSpaceID_XUL) {
2044
0
      mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::id, frameName);
2045
0
    }
2046
0
  }
2047
0
2048
0
  if (!frameName.IsEmpty()) {
2049
0
    mDocShell->SetName(frameName);
2050
0
  }
2051
0
2052
0
  // Inform our docShell that it has a new child.
2053
0
  // Note: This logic duplicates a lot of logic in
2054
0
  // nsSubDocumentFrame::AttributeChanged.  We should fix that.
2055
0
2056
0
  const int32_t parentType = parentDocShell->ItemType();
2057
0
2058
0
  // XXXbz why is this in content code, exactly?  We should handle
2059
0
  // this some other way.....  Not sure how yet.
2060
0
  nsCOMPtr<nsIDocShellTreeOwner> parentTreeOwner;
2061
0
  parentDocShell->GetTreeOwner(getter_AddRefs(parentTreeOwner));
2062
0
  NS_ENSURE_STATE(parentTreeOwner);
2063
0
  mIsTopLevelContent =
2064
0
    AddTreeItemToTreeOwner(mDocShell, parentTreeOwner, parentType,
2065
0
                           parentDocShell);
2066
0
2067
0
  if (mIsTopLevelContent) {
2068
0
    mDocShell->SetCreatedDynamically(false);
2069
0
  }
2070
0
2071
0
  // Make sure all shells have links back to the content element
2072
0
  // in the nearest enclosing chrome shell.
2073
0
  RefPtr<EventTarget> chromeEventHandler;
2074
0
2075
0
  if (parentType == nsIDocShellTreeItem::typeChrome) {
2076
0
    // Our parent shell is a chrome shell. It is therefore our nearest
2077
0
    // enclosing chrome shell.
2078
0
2079
0
    chromeEventHandler = mOwnerContent;
2080
0
    NS_ASSERTION(chromeEventHandler,
2081
0
                 "This mContent should implement this.");
2082
0
  } else {
2083
0
    // Our parent shell is a content shell. Get the chrome event
2084
0
    // handler from it and use that for our shell as well.
2085
0
2086
0
    parentDocShell->GetChromeEventHandler(getter_AddRefs(chromeEventHandler));
2087
0
  }
2088
0
2089
0
  mDocShell->SetChromeEventHandler(chromeEventHandler);
2090
0
2091
0
  // This is nasty, this code (the mDocShell->GetWindow() below)
2092
0
  // *must* come *after* the above call to
2093
0
  // mDocShell->SetChromeEventHandler() for the global window to get
2094
0
  // the right chrome event handler.
2095
0
2096
0
  // Tell the window about the frame that hosts it.
2097
0
  nsCOMPtr<Element> frame_element = mOwnerContent;
2098
0
  NS_ASSERTION(frame_element, "frame loader owner element not a DOM element!");
2099
0
2100
0
  nsCOMPtr<nsPIDOMWindowOuter> win_private(mDocShell->GetWindow());
2101
0
  nsCOMPtr<nsIBaseWindow> base_win(do_QueryInterface(mDocShell));
2102
0
  if (win_private) {
2103
0
    win_private->SetFrameElementInternal(frame_element);
2104
0
2105
0
    // Set the opener window if we have one provided here
2106
0
    if (mOpener) {
2107
0
      win_private->SetOpenerWindow(mOpener, true);
2108
0
      mOpener = nullptr;
2109
0
    }
2110
0
  }
2111
0
2112
0
  // Allow scripts to close the docshell if specified.
2113
0
  if (win_private && mOwnerContent->IsXULElement(nsGkAtoms::browser) &&
2114
0
      mOwnerContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::allowscriptstoclose,
2115
0
                                 nsGkAtoms::_true, eCaseMatters)) {
2116
0
    nsGlobalWindowOuter::Cast(win_private)->AllowScriptsToClose();
2117
0
  }
2118
0
2119
0
  // This is kinda whacky, this call doesn't really create anything,
2120
0
  // but it must be called to make sure things are properly
2121
0
  // initialized.
2122
0
  if (NS_FAILED(base_win->Create()) || !win_private) {
2123
0
    // Do not call Destroy() here. See bug 472312.
2124
0
    NS_WARNING("Something wrong when creating the docshell for a frameloader!");
2125
0
    return NS_ERROR_FAILURE;
2126
0
  }
2127
0
2128
0
  // If we are an in-process browser, we want to set up our session history. We
2129
0
  // do this by creating both the child SHistory (which is in the nsDocShell),
2130
0
  // and creating the corresponding in-process ParentSHistory.
2131
0
  if (mIsTopLevelContent &&
2132
0
      mOwnerContent->IsXULElement(nsGkAtoms::browser) &&
2133
0
      !mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::disablehistory)) {
2134
0
    // XXX(nika): Set this up more explicitly?
2135
0
    nsresult rv = mDocShell->InitSessionHistory();
2136
0
    NS_ENSURE_SUCCESS(rv, rv);
2137
0
    mParentSHistory = new ParentSHistory(this);
2138
0
  }
2139
0
2140
0
  OriginAttributes attrs;
2141
0
  if (parentDocShell->ItemType() == mDocShell->ItemType()) {
2142
0
    attrs = nsDocShell::Cast(parentDocShell)->GetOriginAttributes();
2143
0
  }
2144
0
2145
0
  // Inherit origin attributes from parent document if
2146
0
  // 1. It's in a content docshell.
2147
0
  // 2. its nodePrincipal is not a SystemPrincipal.
2148
0
  // 3. It's not a mozbrowser frame.
2149
0
  //
2150
0
  // For example, firstPartyDomain is computed from top-level document, it
2151
0
  // doesn't exist in the top-level docshell.
2152
0
  if (parentType == nsIDocShellTreeItem::typeContent &&
2153
0
      !nsContentUtils::IsSystemPrincipal(doc->NodePrincipal()) &&
2154
0
      !OwnerIsMozBrowserFrame()) {
2155
0
    OriginAttributes oa = doc->NodePrincipal()->OriginAttributesRef();
2156
0
2157
0
    // Assert on the firstPartyDomain from top-level docshell should be empty
2158
0
    MOZ_ASSERT_IF(mIsTopLevelContent, attrs.mFirstPartyDomain.IsEmpty());
2159
0
2160
0
    // So far we want to make sure Inherit doesn't override any other origin
2161
0
    // attribute than firstPartyDomain.
2162
0
    MOZ_ASSERT(attrs.mAppId == oa.mAppId,
2163
0
              "docshell and document should have the same appId attribute.");
2164
0
    MOZ_ASSERT(attrs.mUserContextId == oa.mUserContextId,
2165
0
              "docshell and document should have the same userContextId attribute.");
2166
0
    MOZ_ASSERT(attrs.mInIsolatedMozBrowser == oa.mInIsolatedMozBrowser,
2167
0
              "docshell and document should have the same inIsolatedMozBrowser attribute.");
2168
0
    MOZ_ASSERT(attrs.mPrivateBrowsingId == oa.mPrivateBrowsingId,
2169
0
              "docshell and document should have the same privateBrowsingId attribute.");
2170
0
2171
0
    attrs = oa;
2172
0
  }
2173
0
2174
0
  if (OwnerIsMozBrowserFrame()) {
2175
0
    attrs.mAppId = nsIScriptSecurityManager::NO_APP_ID;
2176
0
    attrs.mInIsolatedMozBrowser = OwnerIsIsolatedMozBrowserFrame();
2177
0
    mDocShell->SetFrameType(nsIDocShell::FRAME_TYPE_BROWSER);
2178
0
  }
2179
0
2180
0
  // Apply sandbox flags even if our owner is not an iframe, as this copies
2181
0
  // flags from our owning content's owning document.
2182
0
  // Note: ApplySandboxFlags should be called after mDocShell->SetFrameType
2183
0
  // because we need to get the correct presentation URL in ApplySandboxFlags.
2184
0
  uint32_t sandboxFlags = 0;
2185
0
  HTMLIFrameElement* iframe = HTMLIFrameElement::FromNode(mOwnerContent);
2186
0
  if (iframe) {
2187
0
    sandboxFlags = iframe->GetSandboxFlags();
2188
0
  }
2189
0
  ApplySandboxFlags(sandboxFlags);
2190
0
2191
0
  // Grab the userContextId from owner
2192
0
  nsresult rv = PopulateUserContextIdFromAttribute(attrs);
2193
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
2194
0
    return rv;
2195
0
  }
2196
0
2197
0
  bool isPrivate = false;
2198
0
  nsCOMPtr<nsILoadContext> parentContext = do_QueryInterface(parentDocShell);
2199
0
  NS_ENSURE_STATE(parentContext);
2200
0
2201
0
  rv = parentContext->GetUsePrivateBrowsing(&isPrivate);
2202
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
2203
0
    return rv;
2204
0
  }
2205
0
  attrs.SyncAttributesWithPrivateBrowsing(isPrivate);
2206
0
2207
0
  if (OwnerIsMozBrowserFrame()) {
2208
0
    // For inproc frames, set the docshell properties.
2209
0
    nsAutoString name;
2210
0
    if (mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::name, name)) {
2211
0
      mDocShell->SetName(name);
2212
0
    }
2213
0
    mDocShell->SetFullscreenAllowed(
2214
0
      mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::allowfullscreen) ||
2215
0
      mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozallowfullscreen));
2216
0
    bool isPrivate = mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozprivatebrowsing);
2217
0
    if (isPrivate) {
2218
0
      if (mDocShell->GetHasLoadedNonBlankURI()) {
2219
0
        nsContentUtils::ReportToConsoleNonLocalized(
2220
0
          NS_LITERAL_STRING("We should not switch to Private Browsing after loading a document."),
2221
0
          nsIScriptError::warningFlag,
2222
0
          NS_LITERAL_CSTRING("mozprivatebrowsing"),
2223
0
          nullptr);
2224
0
      } else {
2225
0
        // This handles the case where a frames private browsing is set by chrome flags
2226
0
        // and not inherited by its parent.
2227
0
        attrs.SyncAttributesWithPrivateBrowsing(isPrivate);
2228
0
      }
2229
0
    }
2230
0
  }
2231
0
2232
0
  nsDocShell::Cast(mDocShell)->SetOriginAttributes(attrs);
2233
0
2234
0
  // Typically there will be a window, however for some cases such as printing
2235
0
  // the document is cloned with a docshell that has no window.  We check
2236
0
  // that the window exists to ensure we don't try to gather ancestors for
2237
0
  // those cases.
2238
0
  nsCOMPtr<nsPIDOMWindowOuter> win = doc->GetWindow();
2239
0
  if (!mDocShell->GetIsMozBrowser() &&
2240
0
      parentType == mDocShell->ItemType() &&
2241
0
      !doc->IsStaticDocument() && win) {
2242
0
    // Propagate through the ancestor principals.
2243
0
    nsTArray<nsCOMPtr<nsIPrincipal>> ancestorPrincipals;
2244
0
    // Make a copy, so we can modify it.
2245
0
    ancestorPrincipals = doc->AncestorPrincipals();
2246
0
    ancestorPrincipals.InsertElementAt(0, doc->NodePrincipal());
2247
0
    nsDocShell::Cast(mDocShell)->SetAncestorPrincipals(std::move(ancestorPrincipals));
2248
0
2249
0
    // Repeat for outer window IDs.
2250
0
    nsTArray<uint64_t> ancestorOuterWindowIDs;
2251
0
    ancestorOuterWindowIDs = doc->AncestorOuterWindowIDs();
2252
0
    ancestorOuterWindowIDs.InsertElementAt(0, win->WindowID());
2253
0
    nsDocShell::Cast(mDocShell)->SetAncestorOuterWindowIDs(std::move(ancestorOuterWindowIDs));
2254
0
  }
2255
0
2256
0
  ReallyLoadFrameScripts();
2257
0
  InitializeBrowserAPI();
2258
0
2259
0
  nsCOMPtr<nsIObserverService> os = services::GetObserverService();
2260
0
  if (os) {
2261
0
    os->NotifyObservers(ToSupports(this),
2262
0
                        "inprocess-browser-shown", nullptr);
2263
0
  }
2264
0
2265
0
  return NS_OK;
2266
0
}
2267
2268
void
2269
nsFrameLoader::GetURL(nsString& aURI, nsIPrincipal** aTriggeringPrincipal)
2270
0
{
2271
0
  aURI.Truncate();
2272
0
2273
0
  if (mOwnerContent->IsHTMLElement(nsGkAtoms::object)) {
2274
0
    mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::data, aURI);
2275
0
  } else {
2276
0
    mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::src, aURI);
2277
0
    if (RefPtr<nsGenericHTMLFrameElement> frame = do_QueryObject(mOwnerContent)) {
2278
0
      nsCOMPtr<nsIPrincipal> prin = frame->GetSrcTriggeringPrincipal();
2279
0
      prin.forget(aTriggeringPrincipal);
2280
0
    }
2281
0
  }
2282
0
}
2283
2284
nsresult
2285
nsFrameLoader::CheckForRecursiveLoad(nsIURI* aURI)
2286
0
{
2287
0
  nsresult rv;
2288
0
2289
0
  MOZ_ASSERT(!IsRemoteFrame(),
2290
0
             "Shouldn't call CheckForRecursiveLoad on remote frames.");
2291
0
2292
0
  mDepthTooGreat = false;
2293
0
  rv = MaybeCreateDocShell();
2294
0
  if (NS_FAILED(rv)) {
2295
0
    return rv;
2296
0
  }
2297
0
  NS_ASSERTION(mDocShell,
2298
0
               "MaybeCreateDocShell succeeded, but null mDocShell");
2299
0
  if (!mDocShell) {
2300
0
    return NS_ERROR_FAILURE;
2301
0
  }
2302
0
2303
0
  // Check that we're still in the docshell tree.
2304
0
  nsCOMPtr<nsIDocShellTreeOwner> treeOwner;
2305
0
  mDocShell->GetTreeOwner(getter_AddRefs(treeOwner));
2306
0
  NS_WARNING_ASSERTION(treeOwner,
2307
0
                       "Trying to load a new url to a docshell without owner!");
2308
0
  NS_ENSURE_STATE(treeOwner);
2309
0
2310
0
  if (mDocShell->ItemType() != nsIDocShellTreeItem::typeContent) {
2311
0
    // No need to do recursion-protection here XXXbz why not??  Do we really
2312
0
    // trust people not to screw up with non-content docshells?
2313
0
    return NS_OK;
2314
0
  }
2315
0
2316
0
  // Bug 8065: Don't exceed some maximum depth in content frames
2317
0
  // (MAX_DEPTH_CONTENT_FRAMES)
2318
0
  nsCOMPtr<nsIDocShellTreeItem> parentAsItem;
2319
0
  mDocShell->GetSameTypeParent(getter_AddRefs(parentAsItem));
2320
0
  int32_t depth = 0;
2321
0
  while (parentAsItem) {
2322
0
    ++depth;
2323
0
2324
0
    if (depth >= MAX_DEPTH_CONTENT_FRAMES) {
2325
0
      mDepthTooGreat = true;
2326
0
      NS_WARNING("Too many nested content frames so giving up");
2327
0
2328
0
      return NS_ERROR_UNEXPECTED; // Too deep, give up!  (silently?)
2329
0
    }
2330
0
2331
0
    nsCOMPtr<nsIDocShellTreeItem> temp;
2332
0
    temp.swap(parentAsItem);
2333
0
    temp->GetSameTypeParent(getter_AddRefs(parentAsItem));
2334
0
  }
2335
0
2336
0
  // Bug 136580: Check for recursive frame loading excluding about:srcdoc URIs.
2337
0
  // srcdoc URIs require their contents to be specified inline, so it isn't
2338
0
  // possible for undesirable recursion to occur without the aid of a
2339
0
  // non-srcdoc URI,  which this method will block normally.
2340
0
  // Besides, URI is not enough to guarantee uniqueness of srcdoc documents.
2341
0
  nsAutoCString buffer;
2342
0
  rv = aURI->GetScheme(buffer);
2343
0
  if (NS_SUCCEEDED(rv) && buffer.EqualsLiteral("about")) {
2344
0
    rv = aURI->GetPathQueryRef(buffer);
2345
0
    if (NS_SUCCEEDED(rv) && buffer.EqualsLiteral("srcdoc")) {
2346
0
      // Duplicates allowed up to depth limits
2347
0
      return NS_OK;
2348
0
    }
2349
0
  }
2350
0
  int32_t matchCount = 0;
2351
0
  mDocShell->GetSameTypeParent(getter_AddRefs(parentAsItem));
2352
0
  while (parentAsItem) {
2353
0
    // Check the parent URI with the URI we're loading
2354
0
    nsCOMPtr<nsIWebNavigation> parentAsNav(do_QueryInterface(parentAsItem));
2355
0
    if (parentAsNav) {
2356
0
      // Does the URI match the one we're about to load?
2357
0
      nsCOMPtr<nsIURI> parentURI;
2358
0
      parentAsNav->GetCurrentURI(getter_AddRefs(parentURI));
2359
0
      if (parentURI) {
2360
0
        // Bug 98158/193011: We need to ignore data after the #
2361
0
        bool equal;
2362
0
        rv = aURI->EqualsExceptRef(parentURI, &equal);
2363
0
        NS_ENSURE_SUCCESS(rv, rv);
2364
0
2365
0
        if (equal) {
2366
0
          matchCount++;
2367
0
          if (matchCount >= MAX_SAME_URL_CONTENT_FRAMES) {
2368
0
            NS_WARNING("Too many nested content frames have the same url (recursion?) so giving up");
2369
0
            return NS_ERROR_UNEXPECTED;
2370
0
          }
2371
0
        }
2372
0
      }
2373
0
    }
2374
0
    nsCOMPtr<nsIDocShellTreeItem> temp;
2375
0
    temp.swap(parentAsItem);
2376
0
    temp->GetSameTypeParent(getter_AddRefs(parentAsItem));
2377
0
  }
2378
0
2379
0
  return NS_OK;
2380
0
}
2381
2382
nsresult
2383
nsFrameLoader::GetWindowDimensions(nsIntRect& aRect)
2384
0
{
2385
0
  // Need to get outer window position here
2386
0
  nsIDocument* doc = mOwnerContent->GetComposedDoc();
2387
0
  if (!doc) {
2388
0
    return NS_ERROR_FAILURE;
2389
0
  }
2390
0
2391
0
  MOZ_RELEASE_ASSERT(!doc->IsResourceDoc(), "We shouldn't even exist");
2392
0
2393
0
  nsCOMPtr<nsPIDOMWindowOuter> win = doc->GetWindow();
2394
0
  if (!win) {
2395
0
    return NS_ERROR_FAILURE;
2396
0
  }
2397
0
2398
0
  nsCOMPtr<nsIDocShellTreeItem> parentAsItem(win->GetDocShell());
2399
0
  if (!parentAsItem) {
2400
0
    return NS_ERROR_FAILURE;
2401
0
  }
2402
0
2403
0
  nsCOMPtr<nsIDocShellTreeOwner> parentOwner;
2404
0
  if (NS_FAILED(parentAsItem->GetTreeOwner(getter_AddRefs(parentOwner))) ||
2405
0
      !parentOwner) {
2406
0
    return NS_ERROR_FAILURE;
2407
0
  }
2408
0
2409
0
  nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_GetInterface(parentOwner));
2410
0
  treeOwnerAsWin->GetPosition(&aRect.x, &aRect.y);
2411
0
  treeOwnerAsWin->GetSize(&aRect.width, &aRect.height);
2412
0
  return NS_OK;
2413
0
}
2414
2415
nsresult
2416
nsFrameLoader::UpdatePositionAndSize(nsSubDocumentFrame *aIFrame)
2417
0
{
2418
0
  if (IsRemoteFrame()) {
2419
0
    if (mRemoteBrowser) {
2420
0
      ScreenIntSize size = aIFrame->GetSubdocumentSize();
2421
0
      // If we were not able to show remote frame before, we should probably
2422
0
      // retry now to send correct showInfo.
2423
0
      if (!mRemoteBrowserShown) {
2424
0
        ShowRemoteFrame(size, aIFrame);
2425
0
      }
2426
0
      nsIntRect dimensions;
2427
0
      NS_ENSURE_SUCCESS(GetWindowDimensions(dimensions), NS_ERROR_FAILURE);
2428
0
      mLazySize = size;
2429
0
      mRemoteBrowser->UpdateDimensions(dimensions, size);
2430
0
    }
2431
0
    return NS_OK;
2432
0
  }
2433
0
  UpdateBaseWindowPositionAndSize(aIFrame);
2434
0
  return NS_OK;
2435
0
}
2436
2437
void
2438
nsFrameLoader::UpdateBaseWindowPositionAndSize(nsSubDocumentFrame *aIFrame)
2439
0
{
2440
0
  nsCOMPtr<nsIBaseWindow> baseWindow =
2441
0
    do_QueryInterface(GetDocShell(IgnoreErrors()));
2442
0
2443
0
  // resize the sub document
2444
0
  if (baseWindow) {
2445
0
    int32_t x = 0;
2446
0
    int32_t y = 0;
2447
0
2448
0
    AutoWeakFrame weakFrame(aIFrame);
2449
0
2450
0
    baseWindow->GetPosition(&x, &y);
2451
0
2452
0
    if (!weakFrame.IsAlive()) {
2453
0
      // GetPosition() killed us
2454
0
      return;
2455
0
    }
2456
0
2457
0
    ScreenIntSize size = aIFrame->GetSubdocumentSize();
2458
0
    mLazySize = size;
2459
0
2460
0
    baseWindow->SetPositionAndSize(x, y, size.width, size.height,
2461
0
                                   nsIBaseWindow::eDelayResize);
2462
0
  }
2463
0
}
2464
2465
uint32_t
2466
nsFrameLoader::LazyWidth() const
2467
0
{
2468
0
  uint32_t lazyWidth = mLazySize.width;
2469
0
2470
0
  nsIFrame* frame = GetPrimaryFrameOfOwningContent();
2471
0
  if (frame) {
2472
0
    lazyWidth = frame->PresContext()->DevPixelsToIntCSSPixels(lazyWidth);
2473
0
  }
2474
0
2475
0
  return lazyWidth;
2476
0
}
2477
2478
uint32_t
2479
nsFrameLoader::LazyHeight() const
2480
0
{
2481
0
  uint32_t lazyHeight = mLazySize.height;
2482
0
2483
0
  nsIFrame* frame = GetPrimaryFrameOfOwningContent();
2484
0
  if (frame) {
2485
0
    lazyHeight = frame->PresContext()->DevPixelsToIntCSSPixels(lazyHeight);
2486
0
  }
2487
0
2488
0
  return lazyHeight;
2489
0
}
2490
2491
void
2492
nsFrameLoader::SetClipSubdocument(bool aClip)
2493
0
{
2494
0
  mClipSubdocument = aClip;
2495
0
  nsIFrame* frame = GetPrimaryFrameOfOwningContent();
2496
0
  if (frame) {
2497
0
    frame->InvalidateFrame();
2498
0
    frame->PresShell()->
2499
0
      FrameNeedsReflow(frame, nsIPresShell::eResize, NS_FRAME_IS_DIRTY);
2500
0
    nsSubDocumentFrame* subdocFrame = do_QueryFrame(frame);
2501
0
    if (subdocFrame) {
2502
0
      nsIFrame* subdocRootFrame = subdocFrame->GetSubdocumentRootFrame();
2503
0
      if (subdocRootFrame) {
2504
0
        nsIFrame* subdocRootScrollFrame = subdocRootFrame->PresShell()->
2505
0
          GetRootScrollFrame();
2506
0
        if (subdocRootScrollFrame) {
2507
0
          frame->PresShell()->
2508
0
            FrameNeedsReflow(frame, nsIPresShell::eResize, NS_FRAME_IS_DIRTY);
2509
0
        }
2510
0
      }
2511
0
    }
2512
0
  }
2513
0
}
2514
2515
void
2516
nsFrameLoader::SetClampScrollPosition(bool aClamp)
2517
0
{
2518
0
  mClampScrollPosition = aClamp;
2519
0
2520
0
  // When turning clamping on, make sure the current position is clamped.
2521
0
  if (aClamp) {
2522
0
    nsIFrame* frame = GetPrimaryFrameOfOwningContent();
2523
0
    nsSubDocumentFrame* subdocFrame = do_QueryFrame(frame);
2524
0
    if (subdocFrame) {
2525
0
      nsIFrame* subdocRootFrame = subdocFrame->GetSubdocumentRootFrame();
2526
0
      if (subdocRootFrame) {
2527
0
        nsIScrollableFrame* subdocRootScrollFrame = subdocRootFrame->PresShell()->
2528
0
          GetRootScrollFrameAsScrollable();
2529
0
        if (subdocRootScrollFrame) {
2530
0
          subdocRootScrollFrame->ScrollTo(subdocRootScrollFrame->GetScrollPosition(), nsIScrollableFrame::INSTANT);
2531
0
        }
2532
0
      }
2533
0
    }
2534
0
  }
2535
0
}
2536
2537
static
2538
Tuple<ContentParent*, TabParent*>
2539
GetContentParent(Element* aBrowser)
2540
0
{
2541
0
  using ReturnTuple = Tuple<ContentParent*, TabParent*>;
2542
0
2543
0
  nsCOMPtr<nsIBrowser> browser = do_QueryInterface(aBrowser);
2544
0
  if (!browser) {
2545
0
    return ReturnTuple(nullptr, nullptr);
2546
0
  }
2547
0
2548
0
  RefPtr<nsFrameLoader> otherLoader;
2549
0
  browser->GetSameProcessAsFrameLoader(getter_AddRefs(otherLoader));
2550
0
  if (!otherLoader) {
2551
0
    return ReturnTuple(nullptr, nullptr);
2552
0
  }
2553
0
2554
0
  TabParent* tabParent = TabParent::GetFrom(otherLoader);
2555
0
  if (tabParent &&
2556
0
      tabParent->Manager() &&
2557
0
      tabParent->Manager()->IsContentParent()) {
2558
0
    return MakeTuple(tabParent->Manager()->AsContentParent(), tabParent);
2559
0
  }
2560
0
2561
0
  return ReturnTuple(nullptr, nullptr);
2562
0
}
2563
2564
bool
2565
nsFrameLoader::TryRemoteBrowser()
2566
0
{
2567
0
  NS_ASSERTION(!mRemoteBrowser, "TryRemoteBrowser called with a remote browser already?");
2568
0
2569
0
  if (!mOwnerContent) {
2570
0
    return false;
2571
0
  }
2572
0
2573
0
  //XXXsmaug Per spec (2014/08/21) frameloader should not work in case the
2574
0
  //         element isn't in document, only in shadow dom, but that will change
2575
0
  //         https://www.w3.org/Bugs/Public/show_bug.cgi?id=26365#c0
2576
0
  nsIDocument* doc = mOwnerContent->GetComposedDoc();
2577
0
  if (!doc) {
2578
0
    return false;
2579
0
  }
2580
0
2581
0
  MOZ_RELEASE_ASSERT(!doc->IsResourceDoc(), "We shouldn't even exist");
2582
0
2583
0
  if (!doc->IsActive()) {
2584
0
    // Don't allow subframe loads in non-active documents.
2585
0
    // (See bug 610571 comment 5.)
2586
0
    return false;
2587
0
  }
2588
0
2589
0
  nsCOMPtr<nsPIDOMWindowOuter> parentWin = doc->GetWindow();
2590
0
  if (!parentWin) {
2591
0
    return false;
2592
0
  }
2593
0
2594
0
  nsCOMPtr<nsIDocShell> parentDocShell = parentWin->GetDocShell();
2595
0
  if (!parentDocShell) {
2596
0
    return false;
2597
0
  }
2598
0
2599
0
  TabParent* openingTab = TabParent::GetFrom(parentDocShell->GetOpener());
2600
0
  RefPtr<ContentParent> openerContentParent;
2601
0
  RefPtr<TabParent> sameTabGroupAs;
2602
0
2603
0
  if (openingTab &&
2604
0
      openingTab->Manager() &&
2605
0
      openingTab->Manager()->IsContentParent()) {
2606
0
    openerContentParent = openingTab->Manager()->AsContentParent();
2607
0
  }
2608
0
2609
0
  // <iframe mozbrowser> gets to skip these checks.
2610
0
  // iframes for JS plugins also get to skip these checks. We control the URL that gets
2611
0
  // loaded, but the load is triggered from the document containing the plugin.
2612
0
  if (!OwnerIsMozBrowserFrame() && !IsForJSPlugin()) {
2613
0
    if (parentDocShell->ItemType() != nsIDocShellTreeItem::typeChrome) {
2614
0
      // Allow about:addon an exception to this rule so it can load remote
2615
0
      // extension options pages.
2616
0
      //
2617
0
      // Note that the new frame's message manager will not be a child of the
2618
0
      // chrome window message manager, and, the values of window.top and
2619
0
      // window.parent will be different than they would be for a non-remote
2620
0
      // frame.
2621
0
      nsCOMPtr<nsIWebNavigation> parentWebNav;
2622
0
      nsCOMPtr<nsIURI> aboutAddons;
2623
0
      nsCOMPtr<nsIURI> parentURI;
2624
0
      bool equals;
2625
0
      if (!((parentWebNav = do_GetInterface(parentDocShell)) &&
2626
0
            NS_SUCCEEDED(NS_NewURI(getter_AddRefs(aboutAddons), "about:addons")) &&
2627
0
            NS_SUCCEEDED(parentWebNav->GetCurrentURI(getter_AddRefs(parentURI))) &&
2628
0
            NS_SUCCEEDED(parentURI->EqualsExceptRef(aboutAddons, &equals)) && equals)) {
2629
0
        return false;
2630
0
      }
2631
0
    }
2632
0
2633
0
    if (!mOwnerContent->IsXULElement()) {
2634
0
      return false;
2635
0
    }
2636
0
2637
0
    if (!mOwnerContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::type,
2638
0
                                    nsGkAtoms::content, eIgnoreCase)) {
2639
0
      return false;
2640
0
    }
2641
0
2642
0
    // Try to get the related content parent from our browser element.
2643
0
    Tie(openerContentParent, sameTabGroupAs) = GetContentParent(mOwnerContent);
2644
0
  }
2645
0
2646
0
  uint32_t chromeFlags = 0;
2647
0
  nsCOMPtr<nsIDocShellTreeOwner> parentOwner;
2648
0
  if (NS_FAILED(parentDocShell->GetTreeOwner(getter_AddRefs(parentOwner))) ||
2649
0
      !parentOwner) {
2650
0
    return false;
2651
0
  }
2652
0
  nsCOMPtr<nsIXULWindow> window(do_GetInterface(parentOwner));
2653
0
  if (window && NS_FAILED(window->GetChromeFlags(&chromeFlags))) {
2654
0
    return false;
2655
0
  }
2656
0
2657
0
  AUTO_PROFILER_LABEL("nsFrameLoader::TryRemoteBrowser:Create", OTHER);
2658
0
2659
0
  MutableTabContext context;
2660
0
  nsresult rv = GetNewTabContext(&context);
2661
0
  NS_ENSURE_SUCCESS(rv, false);
2662
0
2663
0
  uint64_t nextTabParentId = 0;
2664
0
  if (mOwnerContent) {
2665
0
    nsAutoString nextTabParentIdAttr;
2666
0
    mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::nextTabParentId,
2667
0
                           nextTabParentIdAttr);
2668
0
    nextTabParentId = strtoull(NS_ConvertUTF16toUTF8(nextTabParentIdAttr).get(),
2669
0
                               nullptr, 10);
2670
0
2671
0
    // We may be in a window that was just opened, so try the
2672
0
    // nsIBrowserDOMWindow API as a backup.
2673
0
    if (!nextTabParentId && window) {
2674
0
      Unused << window->GetNextTabParentId(&nextTabParentId);
2675
0
    }
2676
0
  }
2677
0
2678
0
  nsCOMPtr<Element> ownerElement = mOwnerContent;
2679
0
  mRemoteBrowser = ContentParent::CreateBrowser(context, ownerElement,
2680
0
                                                openerContentParent,
2681
0
                                                sameTabGroupAs,
2682
0
                                                nextTabParentId);
2683
0
  if (!mRemoteBrowser) {
2684
0
    return false;
2685
0
  }
2686
0
  // Now that mRemoteBrowser is set, we can initialize the RenderFrameParent
2687
0
  mRemoteBrowser->InitRenderFrame();
2688
0
2689
0
  MaybeUpdatePrimaryTabParent(eTabParentChanged);
2690
0
2691
0
  mChildID = mRemoteBrowser->Manager()->ChildID();
2692
0
2693
0
  nsCOMPtr<nsIDocShellTreeItem> rootItem;
2694
0
  parentDocShell->GetRootTreeItem(getter_AddRefs(rootItem));
2695
0
  nsCOMPtr<nsPIDOMWindowOuter> rootWin = rootItem->GetWindow();
2696
0
  nsCOMPtr<nsIDOMChromeWindow> rootChromeWin = do_QueryInterface(rootWin);
2697
0
2698
0
  if (rootChromeWin) {
2699
0
    nsCOMPtr<nsIBrowserDOMWindow> browserDOMWin;
2700
0
    rootChromeWin->GetBrowserDOMWindow(getter_AddRefs(browserDOMWin));
2701
0
    mRemoteBrowser->SetBrowserDOMWindow(browserDOMWin);
2702
0
  }
2703
0
2704
0
  // Set up a parent SHistory
2705
0
  if (XRE_IsParentProcess()) {
2706
0
    // XXX(nika): Once we get out of process iframes we won't want to
2707
0
    // unconditionally set this up. What do we do for iframes in a chrome loaded
2708
0
    // document for example?
2709
0
    mParentSHistory = new ParentSHistory(this);
2710
0
  }
2711
0
2712
0
  // For xul:browsers, update some settings based on attributes:
2713
0
  if (mOwnerContent->IsXULElement()) {
2714
0
    // Send down the name of the browser through mRemoteBrowser if it is set.
2715
0
    nsAutoString frameName;
2716
0
    mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::name, frameName);
2717
0
    if (nsContentUtils::IsOverridingWindowName(frameName)) {
2718
0
      Unused << mRemoteBrowser->SendSetWindowName(frameName);
2719
0
    }
2720
0
    // Allow scripts to close the window if the browser specified so:
2721
0
    if (mOwnerContent->AttrValueIs(kNameSpaceID_None,
2722
0
                                   nsGkAtoms::allowscriptstoclose,
2723
0
                                   nsGkAtoms::_true, eCaseMatters)) {
2724
0
      Unused << mRemoteBrowser->SendAllowScriptsToClose();
2725
0
    }
2726
0
  }
2727
0
2728
0
  ReallyLoadFrameScripts();
2729
0
  InitializeBrowserAPI();
2730
0
2731
0
 return true;
2732
0
}
2733
2734
mozilla::dom::PBrowserParent*
2735
nsFrameLoader::GetRemoteBrowser() const
2736
0
{
2737
0
  return mRemoteBrowser;
2738
0
}
2739
2740
RenderFrameParent*
2741
nsFrameLoader::GetCurrentRenderFrame() const
2742
0
{
2743
0
  if (mRemoteBrowser) {
2744
0
    return mRemoteBrowser->GetRenderFrame();
2745
0
  }
2746
0
  return nullptr;
2747
0
}
2748
2749
void
2750
nsFrameLoader::ActivateRemoteFrame(ErrorResult& aRv)
2751
0
{
2752
0
  if (!mRemoteBrowser) {
2753
0
    aRv.Throw(NS_ERROR_UNEXPECTED);
2754
0
    return;
2755
0
  }
2756
0
2757
0
  mRemoteBrowser->Activate();
2758
0
}
2759
2760
void
2761
nsFrameLoader::DeactivateRemoteFrame(ErrorResult& aRv)
2762
0
{
2763
0
  if (!mRemoteBrowser) {
2764
0
    aRv.Throw(NS_ERROR_UNEXPECTED);
2765
0
    return;
2766
0
  }
2767
0
2768
0
  mRemoteBrowser->Deactivate();
2769
0
}
2770
2771
void
2772
nsFrameLoader::SendCrossProcessMouseEvent(const nsAString& aType,
2773
                                          float aX,
2774
                                          float aY,
2775
                                          int32_t aButton,
2776
                                          int32_t aClickCount,
2777
                                          int32_t aModifiers,
2778
                                          bool aIgnoreRootScrollFrame,
2779
                                          ErrorResult& aRv)
2780
0
{
2781
0
  if (!mRemoteBrowser) {
2782
0
    aRv.Throw(NS_ERROR_FAILURE);
2783
0
    return;
2784
0
  }
2785
0
2786
0
  mRemoteBrowser->SendMouseEvent(aType, aX, aY, aButton,
2787
0
                                 aClickCount, aModifiers,
2788
0
                                 aIgnoreRootScrollFrame);
2789
0
}
2790
2791
void
2792
nsFrameLoader::ActivateFrameEvent(const nsAString& aType, bool aCapture, ErrorResult& aRv)
2793
0
{
2794
0
  if (!mRemoteBrowser) {
2795
0
    aRv.Throw(NS_ERROR_FAILURE);
2796
0
    return;
2797
0
  }
2798
0
2799
0
  bool ok = mRemoteBrowser->SendActivateFrameEvent(nsString(aType), aCapture);
2800
0
  if (!ok) {
2801
0
    aRv.Throw(NS_ERROR_NOT_AVAILABLE);
2802
0
  }
2803
0
}
2804
2805
nsresult
2806
nsFrameLoader::CreateStaticClone(nsFrameLoader* aDest)
2807
0
{
2808
0
  aDest->MaybeCreateDocShell();
2809
0
  NS_ENSURE_STATE(aDest->mDocShell);
2810
0
2811
0
  nsCOMPtr<nsIDocument> kungFuDeathGrip = aDest->mDocShell->GetDocument();
2812
0
  Unused << kungFuDeathGrip;
2813
0
2814
0
  nsCOMPtr<nsIContentViewer> viewer;
2815
0
  aDest->mDocShell->GetContentViewer(getter_AddRefs(viewer));
2816
0
  NS_ENSURE_STATE(viewer);
2817
0
2818
0
  nsIDocShell* origDocShell = GetDocShell(IgnoreErrors());
2819
0
  NS_ENSURE_STATE(origDocShell);
2820
0
2821
0
  nsCOMPtr<nsIDocument> doc = origDocShell->GetDocument();
2822
0
  NS_ENSURE_STATE(doc);
2823
0
2824
0
  nsCOMPtr<nsIDocument> clonedDoc = doc->CreateStaticClone(aDest->mDocShell);
2825
0
2826
0
  viewer->SetDocument(clonedDoc);
2827
0
  return NS_OK;
2828
0
}
2829
2830
bool
2831
nsFrameLoader::DoLoadMessageManagerScript(const nsAString& aURL, bool aRunInGlobalScope)
2832
0
{
2833
0
  auto* tabParent = TabParent::GetFrom(GetRemoteBrowser());
2834
0
  if (tabParent) {
2835
0
    return tabParent->SendLoadRemoteScript(nsString(aURL), aRunInGlobalScope);
2836
0
  }
2837
0
  RefPtr<InProcessTabChildMessageManager> tabChild = GetTabChildMessageManager();
2838
0
  if (tabChild) {
2839
0
    tabChild->LoadFrameScript(aURL, aRunInGlobalScope);
2840
0
  }
2841
0
  return true;
2842
0
}
2843
2844
class nsAsyncMessageToChild : public nsSameProcessAsyncMessageBase,
2845
                              public Runnable
2846
{
2847
public:
2848
  nsAsyncMessageToChild(JS::RootingContext* aRootingCx,
2849
                        JS::Handle<JSObject*> aCpows,
2850
                        nsFrameLoader* aFrameLoader)
2851
    : nsSameProcessAsyncMessageBase(aRootingCx, aCpows)
2852
    , mozilla::Runnable("nsAsyncMessageToChild")
2853
    , mFrameLoader(aFrameLoader)
2854
0
  {
2855
0
  }
2856
2857
  NS_IMETHOD Run() override
2858
0
  {
2859
0
    InProcessTabChildMessageManager* tabChild = mFrameLoader->mChildMessageManager;
2860
0
    // Since bug 1126089, messages can arrive even when the docShell is destroyed.
2861
0
    // Here we make sure that those messages are not delivered.
2862
0
    if (tabChild && tabChild->GetInnerManager() && mFrameLoader->GetExistingDocShell()) {
2863
0
      JS::Rooted<JSObject*> kungFuDeathGrip(dom::RootingCx(), tabChild->GetWrapper());
2864
0
      ReceiveMessage(static_cast<EventTarget*>(tabChild), mFrameLoader,
2865
0
                     tabChild->GetInnerManager());
2866
0
    }
2867
0
    return NS_OK;
2868
0
  }
2869
  RefPtr<nsFrameLoader> mFrameLoader;
2870
};
2871
2872
nsresult
2873
nsFrameLoader::DoSendAsyncMessage(JSContext* aCx,
2874
                                  const nsAString& aMessage,
2875
                                  StructuredCloneData& aData,
2876
                                  JS::Handle<JSObject *> aCpows,
2877
                                  nsIPrincipal* aPrincipal)
2878
0
{
2879
0
  TabParent* tabParent = mRemoteBrowser;
2880
0
  if (tabParent) {
2881
0
    ClonedMessageData data;
2882
0
    nsIContentParent* cp = tabParent->Manager();
2883
0
    if (!BuildClonedMessageDataForParent(cp, aData, data)) {
2884
0
      MOZ_CRASH();
2885
0
      return NS_ERROR_DOM_DATA_CLONE_ERR;
2886
0
    }
2887
0
    InfallibleTArray<mozilla::jsipc::CpowEntry> cpows;
2888
0
    jsipc::CPOWManager* mgr = cp->GetCPOWManager();
2889
0
    if (aCpows && (!mgr || !mgr->Wrap(aCx, aCpows, &cpows))) {
2890
0
      return NS_ERROR_UNEXPECTED;
2891
0
    }
2892
0
    if (tabParent->SendAsyncMessage(nsString(aMessage), cpows,
2893
0
                                    IPC::Principal(aPrincipal), data)) {
2894
0
      return NS_OK;
2895
0
    } else {
2896
0
      return NS_ERROR_UNEXPECTED;
2897
0
    }
2898
0
  }
2899
0
2900
0
  if (mChildMessageManager) {
2901
0
    JS::RootingContext* rcx = JS::RootingContext::get(aCx);
2902
0
    RefPtr<nsAsyncMessageToChild> ev = new nsAsyncMessageToChild(rcx, aCpows, this);
2903
0
    nsresult rv = ev->Init(aMessage, aData, aPrincipal);
2904
0
    if (NS_FAILED(rv)) {
2905
0
      return rv;
2906
0
    }
2907
0
    rv = NS_DispatchToCurrentThread(ev);
2908
0
    if (NS_FAILED(rv)) {
2909
0
      return rv;
2910
0
    }
2911
0
    return rv;
2912
0
  }
2913
0
2914
0
  // We don't have any targets to send our asynchronous message to.
2915
0
  return NS_ERROR_UNEXPECTED;
2916
0
}
2917
2918
already_AddRefed<MessageSender>
2919
nsFrameLoader::GetMessageManager()
2920
0
{
2921
0
  EnsureMessageManager();
2922
0
  return do_AddRef(mMessageManager);
2923
0
}
2924
2925
nsresult
2926
nsFrameLoader::EnsureMessageManager()
2927
0
{
2928
0
  NS_ENSURE_STATE(mOwnerContent);
2929
0
2930
0
  if (mMessageManager) {
2931
0
    return NS_OK;
2932
0
  }
2933
0
2934
0
  if (!mIsTopLevelContent &&
2935
0
      !OwnerIsMozBrowserFrame() &&
2936
0
      !IsRemoteFrame() &&
2937
0
      !(mOwnerContent->IsXULElement() &&
2938
0
        mOwnerContent->AttrValueIs(kNameSpaceID_None,
2939
0
                                   nsGkAtoms::forcemessagemanager,
2940
0
                                   nsGkAtoms::_true, eCaseMatters))) {
2941
0
    return NS_OK;
2942
0
  }
2943
0
2944
0
  RefPtr<nsGlobalWindowOuter> window =
2945
0
    nsGlobalWindowOuter::Cast(GetOwnerDoc()->GetWindow());
2946
0
  RefPtr<ChromeMessageBroadcaster> parentManager;
2947
0
2948
0
  if (window && window->IsChromeWindow()) {
2949
0
    nsAutoString messagemanagergroup;
2950
0
    if (mOwnerContent->IsXULElement() &&
2951
0
        mOwnerContent->GetAttr(kNameSpaceID_None,
2952
0
                               nsGkAtoms::messagemanagergroup,
2953
0
                               messagemanagergroup)) {
2954
0
      parentManager = window->GetGroupMessageManager(messagemanagergroup);
2955
0
    }
2956
0
2957
0
    if (!parentManager) {
2958
0
      parentManager = window->GetMessageManager();
2959
0
    }
2960
0
  } else {
2961
0
    parentManager = nsFrameMessageManager::GetGlobalMessageManager();
2962
0
  }
2963
0
2964
0
  mMessageManager = new ChromeMessageSender(parentManager);
2965
0
  if (!IsRemoteFrame()) {
2966
0
    nsresult rv = MaybeCreateDocShell();
2967
0
    if (NS_FAILED(rv)) {
2968
0
      return rv;
2969
0
    }
2970
0
    NS_ASSERTION(mDocShell,
2971
0
                 "MaybeCreateDocShell succeeded, but null mDocShell");
2972
0
    if (!mDocShell) {
2973
0
      return NS_ERROR_FAILURE;
2974
0
    }
2975
0
    mChildMessageManager =
2976
0
      InProcessTabChildMessageManager::Create(mDocShell, mOwnerContent, mMessageManager);
2977
0
    NS_ENSURE_TRUE(mChildMessageManager, NS_ERROR_UNEXPECTED);
2978
0
  }
2979
0
  return NS_OK;
2980
0
}
2981
2982
nsresult
2983
nsFrameLoader::ReallyLoadFrameScripts()
2984
0
{
2985
0
  nsresult rv = EnsureMessageManager();
2986
0
  if (NS_WARN_IF(NS_FAILED(rv))) {
2987
0
    return rv;
2988
0
  }
2989
0
  if (mMessageManager) {
2990
0
    mMessageManager->InitWithCallback(this);
2991
0
  }
2992
0
  return NS_OK;
2993
0
}
2994
2995
already_AddRefed<Element>
2996
nsFrameLoader::GetOwnerElement()
2997
0
{
2998
0
  return do_AddRef(mOwnerContent);
2999
0
}
3000
3001
void
3002
nsFrameLoader::SetRemoteBrowser(nsITabParent* aTabParent)
3003
0
{
3004
0
  MOZ_ASSERT(!mRemoteBrowser);
3005
0
  mRemoteFrame = true;
3006
0
  mRemoteBrowser = TabParent::GetFrom(aTabParent);
3007
0
  mChildID = mRemoteBrowser ? mRemoteBrowser->Manager()->ChildID() : 0;
3008
0
  MaybeUpdatePrimaryTabParent(eTabParentChanged);
3009
0
  ReallyLoadFrameScripts();
3010
0
  InitializeBrowserAPI();
3011
0
  ShowRemoteFrame(ScreenIntSize(0, 0));
3012
0
}
3013
3014
void
3015
nsFrameLoader::SetDetachedSubdocFrame(nsIFrame* aDetachedFrame,
3016
                                      nsIDocument* aContainerDoc)
3017
0
{
3018
0
  mDetachedSubdocFrame = aDetachedFrame;
3019
0
  mContainerDocWhileDetached = aContainerDoc;
3020
0
}
3021
3022
nsIFrame*
3023
nsFrameLoader::GetDetachedSubdocFrame(nsIDocument** aContainerDoc) const
3024
0
{
3025
0
  NS_IF_ADDREF(*aContainerDoc = mContainerDocWhileDetached);
3026
0
  return mDetachedSubdocFrame.GetFrame();
3027
0
}
3028
3029
void
3030
nsFrameLoader::ApplySandboxFlags(uint32_t sandboxFlags)
3031
0
{
3032
0
  if (mDocShell) {
3033
0
    uint32_t parentSandboxFlags = mOwnerContent->OwnerDoc()->GetSandboxFlags();
3034
0
3035
0
    // The child can only add restrictions, never remove them.
3036
0
    sandboxFlags |= parentSandboxFlags;
3037
0
3038
0
    // If this frame is a receiving browsing context, we should add
3039
0
    // sandboxed auxiliary navigation flag to sandboxFlags. See
3040
0
    // https://w3c.github.io/presentation-api/#creating-a-receiving-browsing-context
3041
0
    nsAutoString presentationURL;
3042
0
    nsContentUtils::GetPresentationURL(mDocShell, presentationURL);
3043
0
    if (!presentationURL.IsEmpty()) {
3044
0
      sandboxFlags |= SANDBOXED_AUXILIARY_NAVIGATION;
3045
0
    }
3046
0
    mDocShell->SetSandboxFlags(sandboxFlags);
3047
0
  }
3048
0
}
3049
3050
/* virtual */ void
3051
nsFrameLoader::AttributeChanged(mozilla::dom::Element* aElement,
3052
                                int32_t aNameSpaceID,
3053
                                nsAtom* aAttribute,
3054
                                int32_t aModType,
3055
                                const nsAttrValue* aOldValue)
3056
0
{
3057
0
  MOZ_ASSERT(mObservingOwnerContent);
3058
0
3059
0
  if (aNameSpaceID != kNameSpaceID_None ||
3060
0
      (aAttribute != TypeAttrName() && aAttribute != nsGkAtoms::primary)) {
3061
0
    return;
3062
0
  }
3063
0
3064
0
  if (aElement != mOwnerContent) {
3065
0
    return;
3066
0
  }
3067
0
3068
0
  // Note: This logic duplicates a lot of logic in
3069
0
  // MaybeCreateDocshell.  We should fix that.
3070
0
3071
0
  // Notify our enclosing chrome that our type has changed.  We only do this
3072
0
  // if our parent is chrome, since in all other cases we're random content
3073
0
  // subframes and the treeowner shouldn't worry about us.
3074
0
  if (!mDocShell) {
3075
0
    MaybeUpdatePrimaryTabParent(eTabParentChanged);
3076
0
    return;
3077
0
  }
3078
0
3079
0
  nsCOMPtr<nsIDocShellTreeItem> parentItem;
3080
0
  mDocShell->GetParent(getter_AddRefs(parentItem));
3081
0
  if (!parentItem) {
3082
0
    return;
3083
0
  }
3084
0
3085
0
  if (parentItem->ItemType() != nsIDocShellTreeItem::typeChrome) {
3086
0
    return;
3087
0
  }
3088
0
3089
0
  nsCOMPtr<nsIDocShellTreeOwner> parentTreeOwner;
3090
0
  parentItem->GetTreeOwner(getter_AddRefs(parentTreeOwner));
3091
0
  if (!parentTreeOwner) {
3092
0
    return;
3093
0
  }
3094
0
3095
0
  bool is_primary =
3096
0
    aElement->AttrValueIs(kNameSpaceID_None, nsGkAtoms::primary, nsGkAtoms::_true, eIgnoreCase);
3097
0
3098
0
#ifdef MOZ_XUL
3099
0
  // when a content panel is no longer primary, hide any open popups it may have
3100
0
  if (!is_primary) {
3101
0
    nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
3102
0
    if (pm)
3103
0
      pm->HidePopupsInDocShell(mDocShell);
3104
0
  }
3105
0
#endif
3106
0
3107
0
  parentTreeOwner->ContentShellRemoved(mDocShell);
3108
0
  if (aElement->AttrValueIs(kNameSpaceID_None, TypeAttrName(), nsGkAtoms::content, eIgnoreCase)) {
3109
0
    parentTreeOwner->ContentShellAdded(mDocShell, is_primary);
3110
0
  }
3111
0
}
3112
3113
/**
3114
 * Send the RequestNotifyAfterRemotePaint message to the current Tab.
3115
 */
3116
void
3117
nsFrameLoader::RequestNotifyAfterRemotePaint()
3118
0
{
3119
0
  // If remote browsing (e10s), handle this with the TabParent.
3120
0
  if (mRemoteBrowser) {
3121
0
    Unused << mRemoteBrowser->SendRequestNotifyAfterRemotePaint();
3122
0
  }
3123
0
}
3124
3125
void
3126
nsFrameLoader::RequestFrameLoaderClose(ErrorResult& aRv)
3127
0
{
3128
0
  nsCOMPtr<nsIBrowser> browser = do_QueryInterface(mOwnerContent);
3129
0
  if (NS_WARN_IF(!browser)) {
3130
0
    // OwnerElement other than nsIBrowser is not supported yet.
3131
0
    aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
3132
0
    return;
3133
0
  }
3134
0
3135
0
  nsresult rv = browser->CloseBrowser();
3136
0
  if (NS_FAILED(rv)) {
3137
0
    aRv.Throw(rv);
3138
0
  }
3139
0
}
3140
3141
void
3142
nsFrameLoader::RequestUpdatePosition(ErrorResult& aRv)
3143
0
{
3144
0
  if (auto* tabParent = TabParent::GetFrom(GetRemoteBrowser())) {
3145
0
    nsresult rv = tabParent->UpdatePosition();
3146
0
3147
0
    if (NS_FAILED(rv)) {
3148
0
      aRv.Throw(rv);
3149
0
    }
3150
0
  }
3151
0
}
3152
3153
void
3154
nsFrameLoader::Print(uint64_t aOuterWindowID,
3155
                     nsIPrintSettings* aPrintSettings,
3156
                     nsIWebProgressListener* aProgressListener,
3157
                     ErrorResult& aRv)
3158
0
{
3159
0
#if defined(NS_PRINTING)
3160
0
  if (mRemoteBrowser) {
3161
0
    RefPtr<embedding::PrintingParent> printingParent =
3162
0
      mRemoteBrowser->Manager()->AsContentParent()->GetPrintingParent();
3163
0
3164
0
    embedding::PrintData printData;
3165
0
    nsresult rv = printingParent->SerializeAndEnsureRemotePrintJob(
3166
0
      aPrintSettings, aProgressListener, nullptr, &printData);
3167
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
3168
0
      aRv.Throw(rv);
3169
0
      return;
3170
0
    }
3171
0
3172
0
    bool success = mRemoteBrowser->SendPrint(aOuterWindowID, printData);
3173
0
    if (!success) {
3174
0
      aRv.Throw(NS_ERROR_FAILURE);
3175
0
    }
3176
0
    return;
3177
0
  }
3178
0
3179
0
  nsGlobalWindowOuter* outerWindow =
3180
0
    nsGlobalWindowOuter::GetOuterWindowWithId(aOuterWindowID);
3181
0
  if (NS_WARN_IF(!outerWindow)) {
3182
0
    aRv.Throw(NS_ERROR_FAILURE);
3183
0
    return;
3184
0
  }
3185
0
3186
0
  nsCOMPtr<nsIWebBrowserPrint> webBrowserPrint =
3187
0
    do_GetInterface(outerWindow->AsOuter());
3188
0
  if (NS_WARN_IF(!webBrowserPrint)) {
3189
0
    aRv.Throw(NS_ERROR_FAILURE);
3190
0
    return;
3191
0
  }
3192
0
3193
0
  nsresult rv = webBrowserPrint->Print(aPrintSettings, aProgressListener);
3194
0
  if (NS_FAILED(rv)) {
3195
0
    aRv.Throw(rv);
3196
0
    return;
3197
0
  }
3198
0
#endif
3199
0
}
3200
3201
already_AddRefed<nsITabParent>
3202
nsFrameLoader::GetTabParent()
3203
0
{
3204
0
  return do_AddRef(mRemoteBrowser);
3205
0
}
3206
3207
already_AddRefed<nsILoadContext>
3208
nsFrameLoader::LoadContext()
3209
0
{
3210
0
  nsCOMPtr<nsILoadContext> loadContext;
3211
0
  if (IsRemoteFrame() &&
3212
0
      (mRemoteBrowser || TryRemoteBrowser())) {
3213
0
    loadContext = mRemoteBrowser->GetLoadContext();
3214
0
  } else {
3215
0
    loadContext = do_GetInterface(GetDocShell(IgnoreErrors()));
3216
0
  }
3217
0
  return loadContext.forget();
3218
0
}
3219
3220
void
3221
nsFrameLoader::InitializeBrowserAPI()
3222
0
{
3223
0
  if (!OwnerIsMozBrowserFrame()) {
3224
0
    return;
3225
0
  }
3226
0
  if (!IsRemoteFrame()) {
3227
0
    nsresult rv = EnsureMessageManager();
3228
0
    if (NS_WARN_IF(NS_FAILED(rv))) {
3229
0
      return;
3230
0
    }
3231
0
    if (mMessageManager) {
3232
0
      mMessageManager->LoadFrameScript(
3233
0
        NS_LITERAL_STRING("chrome://global/content/BrowserElementChild.js"),
3234
0
        /* allowDelayedLoad = */ true,
3235
0
        /* aRunInGlobalScope */ true,
3236
0
        IgnoreErrors());
3237
0
    }
3238
0
  }
3239
0
  nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwnerContent);
3240
0
  if (browserFrame) {
3241
0
    browserFrame->InitializeBrowserAPI();
3242
0
  }
3243
0
}
3244
3245
void
3246
nsFrameLoader::DestroyBrowserFrameScripts()
3247
0
{
3248
0
  if (!OwnerIsMozBrowserFrame()) {
3249
0
    return;
3250
0
  }
3251
0
  nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwnerContent);
3252
0
  if (browserFrame) {
3253
0
    browserFrame->DestroyBrowserFrameScripts();
3254
0
  }
3255
0
}
3256
3257
void
3258
nsFrameLoader::StartPersistence(uint64_t aOuterWindowID,
3259
                                nsIWebBrowserPersistDocumentReceiver* aRecv,
3260
                                ErrorResult& aRv)
3261
0
{
3262
0
  MOZ_ASSERT(aRecv);
3263
0
3264
0
  if (mRemoteBrowser) {
3265
0
    mRemoteBrowser->StartPersistence(aOuterWindowID, aRecv, aRv);
3266
0
    return;
3267
0
  }
3268
0
3269
0
  nsCOMPtr<nsIDocument> rootDoc =
3270
0
    mDocShell ? mDocShell->GetDocument() : nullptr;
3271
0
  nsCOMPtr<nsIDocument> foundDoc;
3272
0
  if (aOuterWindowID) {
3273
0
    foundDoc = nsContentUtils::GetSubdocumentWithOuterWindowId(rootDoc, aOuterWindowID);
3274
0
  } else {
3275
0
    foundDoc = rootDoc;
3276
0
  }
3277
0
3278
0
  if (!foundDoc) {
3279
0
    aRecv->OnError(NS_ERROR_NO_CONTENT);
3280
0
  } else {
3281
0
    nsCOMPtr<nsIWebBrowserPersistDocument> pdoc =
3282
0
      new mozilla::WebBrowserPersistLocalDocument(foundDoc);
3283
0
    aRecv->OnDocumentReady(pdoc);
3284
0
  }
3285
0
}
3286
3287
void
3288
nsFrameLoader::MaybeUpdatePrimaryTabParent(TabParentChange aChange)
3289
0
{
3290
0
  if (mRemoteBrowser && mOwnerContent) {
3291
0
    nsCOMPtr<nsIDocShell> docShell = mOwnerContent->OwnerDoc()->GetDocShell();
3292
0
    if (!docShell) {
3293
0
      return;
3294
0
    }
3295
0
3296
0
    int32_t parentType = docShell->ItemType();
3297
0
    if (parentType != nsIDocShellTreeItem::typeChrome) {
3298
0
      return;
3299
0
    }
3300
0
3301
0
    nsCOMPtr<nsIDocShellTreeOwner> parentTreeOwner;
3302
0
    docShell->GetTreeOwner(getter_AddRefs(parentTreeOwner));
3303
0
    if (!parentTreeOwner) {
3304
0
      return;
3305
0
    }
3306
0
3307
0
    if (!mObservingOwnerContent) {
3308
0
      mOwnerContent->AddMutationObserver(this);
3309
0
      mObservingOwnerContent = true;
3310
0
    }
3311
0
3312
0
    parentTreeOwner->TabParentRemoved(mRemoteBrowser);
3313
0
    if (aChange == eTabParentChanged) {
3314
0
      bool isPrimary =
3315
0
        mOwnerContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::primary,
3316
0
                                   nsGkAtoms::_true, eIgnoreCase);
3317
0
      parentTreeOwner->TabParentAdded(mRemoteBrowser, isPrimary);
3318
0
    }
3319
0
  }
3320
0
}
3321
3322
nsresult
3323
nsFrameLoader::GetNewTabContext(MutableTabContext* aTabContext,
3324
                                nsIURI* aURI)
3325
0
{
3326
0
  if (IsForJSPlugin()) {
3327
0
    return aTabContext->SetTabContextForJSPluginFrame(mJSPluginID) ? NS_OK :
3328
0
           NS_ERROR_FAILURE;
3329
0
  }
3330
0
3331
0
  OriginAttributes attrs;
3332
0
  attrs.mInIsolatedMozBrowser = OwnerIsIsolatedMozBrowserFrame();
3333
0
  nsresult rv;
3334
0
3335
0
  attrs.mAppId = nsIScriptSecurityManager::NO_APP_ID;
3336
0
3337
0
  // set the userContextId on the attrs before we pass them into
3338
0
  // the tab context
3339
0
  rv = PopulateUserContextIdFromAttribute(attrs);
3340
0
  NS_ENSURE_SUCCESS(rv, rv);
3341
0
3342
0
  nsAutoString presentationURLStr;
3343
0
  mOwnerContent->GetAttr(kNameSpaceID_None,
3344
0
                         nsGkAtoms::mozpresentation,
3345
0
                         presentationURLStr);
3346
0
3347
0
  nsCOMPtr<nsIDocShell> docShell = mOwnerContent->OwnerDoc()->GetDocShell();
3348
0
  nsCOMPtr<nsILoadContext> parentContext = do_QueryInterface(docShell);
3349
0
  NS_ENSURE_STATE(parentContext);
3350
0
3351
0
  bool isPrivate = parentContext->UsePrivateBrowsing();
3352
0
  attrs.SyncAttributesWithPrivateBrowsing(isPrivate);
3353
0
3354
0
  UIStateChangeType showAccelerators = UIStateChangeType_NoChange;
3355
0
  UIStateChangeType showFocusRings = UIStateChangeType_NoChange;
3356
0
  uint64_t chromeOuterWindowID = 0;
3357
0
3358
0
  nsIDocument* doc = mOwnerContent->OwnerDoc();
3359
0
  if (doc) {
3360
0
    nsCOMPtr<nsPIWindowRoot> root = nsContentUtils::GetWindowRoot(doc);
3361
0
    if (root) {
3362
0
      showAccelerators =
3363
0
        root->ShowAccelerators() ? UIStateChangeType_Set : UIStateChangeType_Clear;
3364
0
      showFocusRings =
3365
0
        root->ShowFocusRings() ? UIStateChangeType_Set : UIStateChangeType_Clear;
3366
0
3367
0
      nsPIDOMWindowOuter* outerWin = root->GetWindow();
3368
0
      if (outerWin) {
3369
0
        chromeOuterWindowID = outerWin->WindowID();
3370
0
      }
3371
0
    }
3372
0
  }
3373
0
3374
0
  bool tabContextUpdated =
3375
0
    aTabContext->SetTabContext(OwnerIsMozBrowserFrame(),
3376
0
                               chromeOuterWindowID,
3377
0
                               showAccelerators,
3378
0
                               showFocusRings,
3379
0
                               attrs,
3380
0
                               presentationURLStr);
3381
0
  NS_ENSURE_STATE(tabContextUpdated);
3382
0
3383
0
  return NS_OK;
3384
0
}
3385
3386
nsresult
3387
nsFrameLoader::PopulateUserContextIdFromAttribute(OriginAttributes& aAttr)
3388
0
{
3389
0
  if (aAttr.mUserContextId ==
3390
0
        nsIScriptSecurityManager::DEFAULT_USER_CONTEXT_ID)  {
3391
0
    // Grab the userContextId from owner if XUL or mozbrowser frame
3392
0
    nsAutoString userContextIdStr;
3393
0
    int32_t namespaceID = mOwnerContent->GetNameSpaceID();
3394
0
    if ((namespaceID == kNameSpaceID_XUL || OwnerIsMozBrowserFrame()) &&
3395
0
        mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::usercontextid,
3396
0
                               userContextIdStr) &&
3397
0
        !userContextIdStr.IsEmpty()) {
3398
0
      nsresult rv;
3399
0
      aAttr.mUserContextId = userContextIdStr.ToInteger(&rv);
3400
0
      NS_ENSURE_SUCCESS(rv, rv);
3401
0
    }
3402
0
  }
3403
0
3404
0
  return NS_OK;
3405
0
}
3406
3407
ProcessMessageManager*
3408
nsFrameLoader::GetProcessMessageManager() const
3409
0
{
3410
0
  return mRemoteBrowser ? mRemoteBrowser->Manager()->GetMessageManager()
3411
0
                        : nullptr;
3412
0
};
3413
3414
JSObject*
3415
nsFrameLoader::WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto)
3416
0
{
3417
0
  JS::RootedObject result(cx);
3418
0
  FrameLoader_Binding::Wrap(cx, this, this, aGivenProto, &result);
3419
0
  return result;
3420
0
}