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