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