/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=2 sw=2 et tw=78: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ /* * Class for managing loading of a subframe (creation of the docshell, * handling of loads in it, recursion-checking). */ #include "base/basictypes.h" #include "prenv.h" #include "mozIApplication.h" #include "nsIDOMHTMLIFrameElement.h" #include "nsIDOMHTMLFrameElement.h" #include "nsIDOMMozBrowserFrame.h" #include "nsIDOMWindow.h" #include "nsIPresShell.h" #include "nsIContentInlines.h" #include "nsIContentViewer.h" #include "nsIDocument.h" #include "nsIDOMDocument.h" #include "nsIDOMFile.h" #include "nsPIDOMWindow.h" #include "nsIWebNavigation.h" #include "nsIWebProgress.h" #include "nsIDocShell.h" #include "nsIDocShellTreeOwner.h" #include "nsIDocShellLoadInfo.h" #include "nsIBaseWindow.h" #include "nsContentUtils.h" #include "nsIXPConnect.h" #include "nsUnicharUtils.h" #include "nsIScriptGlobalObject.h" #include "nsIScriptSecurityManager.h" #include "nsIScrollable.h" #include "nsFrameLoader.h" #include "nsIDOMEventTarget.h" #include "nsIFrame.h" #include "nsIScrollableFrame.h" #include "nsSubDocumentFrame.h" #include "nsError.h" #include "nsISHistory.h" #include "nsISHistoryInternal.h" #include "nsIDOMHTMLDocument.h" #include "nsIXULWindow.h" #include "nsIEditor.h" #include "nsIMozBrowserFrame.h" #include "nsIPermissionManager.h" #include "nsISHistory.h" #include "nsNullPrincipal.h" #include "nsLayoutUtils.h" #include "nsView.h" #include "nsIURI.h" #include "nsIURL.h" #include "nsNetUtil.h" #include "nsGkAtoms.h" #include "nsNameSpaceManager.h" #include "nsThreadUtils.h" #include "nsIDOMChromeWindow.h" #include "nsInProcessTabChildGlobal.h" #include "Layers.h" #include "ClientLayerManager.h" #include "AppProcessChecker.h" #include "ContentParent.h" #include "TabParent.h" #include "mozilla/AsyncEventDispatcher.h" #include "mozilla/GuardObjects.h" #include "mozilla/Preferences.h" #include "mozilla/unused.h" #include "mozilla/dom/Element.h" #include "mozilla/layout/RenderFrameParent.h" #include "nsIAppsService.h" #include "GeckoProfiler.h" #include "jsapi.h" #include "mozilla/dom/HTMLIFrameElement.h" #include "mozilla/dom/SVGIFrameElement.h" #include "nsSandboxFlags.h" #include "JavaScriptParent.h" #include "mozilla/layers/CompositorChild.h" #include "mozilla/dom/StructuredCloneUtils.h" #ifdef MOZ_XUL #include "nsXULPopupManager.h" #endif using namespace mozilla; using namespace mozilla::hal; using namespace mozilla::dom; using namespace mozilla::dom::ipc; using namespace mozilla::layers; using namespace mozilla::layout; typedef FrameMetrics::ViewID ViewID; class nsAsyncDocShellDestroyer : public nsRunnable { public: explicit nsAsyncDocShellDestroyer(nsIDocShell* aDocShell) : mDocShell(aDocShell) { } NS_IMETHOD Run() { nsCOMPtr base_win(do_QueryInterface(mDocShell)); if (base_win) { base_win->Destroy(); } return NS_OK; } nsRefPtr mDocShell; }; // Bug 136580: Limit to the number of nested content frames that can have the // same URL. This is to stop content that is recursively loading // itself. Note that "#foo" on the end of URL doesn't affect // whether it's considered identical, but "?foo" or ";foo" are // considered and compared. // Bug 228829: Limit this to 1, like IE does. #define MAX_SAME_URL_CONTENT_FRAMES 1 // Bug 8065: Limit content frame depth to some reasonable level. This // does not count chrome frames when determining depth, nor does it // prevent chrome recursion. Number is fairly arbitrary, but meant to // keep number of shells to a reasonable number on accidental recursion with a // small (but not 1) branching factor. With large branching factors the number // of shells can rapidly become huge and run us out of memory. To solve that, // we'd need to re-institute a fixed version of bug 98158. #define MAX_DEPTH_CONTENT_FRAMES 10 NS_IMPL_CYCLE_COLLECTION(nsFrameLoader, mDocShell, mMessageManager, mChildMessageManager) NS_IMPL_CYCLE_COLLECTING_ADDREF(nsFrameLoader) NS_IMPL_CYCLE_COLLECTING_RELEASE(nsFrameLoader) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsFrameLoader) NS_INTERFACE_MAP_ENTRY(nsIFrameLoader) NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIFrameLoader) NS_INTERFACE_MAP_END nsFrameLoader::nsFrameLoader(Element* aOwner, bool aNetworkCreated) : mOwnerContent(aOwner) , mAppIdSentToPermissionManager(nsIScriptSecurityManager::NO_APP_ID) , mDetachedSubdocViews(nullptr) , mDepthTooGreat(false) , mIsTopLevelContent(false) , mDestroyCalled(false) , mNeedsAsyncDestroy(false) , mInSwap(false) , mInShow(false) , mHideCalled(false) , mNetworkCreated(aNetworkCreated) , mRemoteBrowserShown(false) , mRemoteFrame(false) , mClipSubdocument(true) , mClampScrollPosition(true) , mRemoteBrowserInitialized(false) , mObservingOwnerContent(false) , mVisible(true) , mCurrentRemoteFrame(nullptr) , mRemoteBrowser(nullptr) , mChildID(0) , mEventMode(EVENT_MODE_NORMAL_DISPATCH) , mPendingFrameSent(false) { ResetPermissionManagerStatus(); } nsFrameLoader::~nsFrameLoader() { mNeedsAsyncDestroy = true; if (mMessageManager) { mMessageManager->Disconnect(); } nsFrameLoader::Destroy(); } nsFrameLoader* nsFrameLoader::Create(Element* aOwner, bool aNetworkCreated) { NS_ENSURE_TRUE(aOwner, nullptr); nsIDocument* doc = aOwner->OwnerDoc(); NS_ENSURE_TRUE(!doc->IsResourceDoc() && ((!doc->IsLoadedAsData() && aOwner->GetUncomposedDoc()) || doc->IsStaticDocument()), nullptr); return new nsFrameLoader(aOwner, aNetworkCreated); } NS_IMETHODIMP nsFrameLoader::LoadFrame() { NS_ENSURE_TRUE(mOwnerContent, NS_ERROR_NOT_INITIALIZED); nsAutoString src; bool isSrcdoc = (mOwnerContent->IsHTML(nsGkAtoms::iframe) || mOwnerContent->IsSVG(nsGkAtoms::iframe)) && mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::srcdoc); if (isSrcdoc) { src.AssignLiteral("about:srcdoc"); } else { GetURL(src); src.Trim(" \t\n\r"); if (src.IsEmpty()) { // If the frame is a XUL element and has the attribute 'nodefaultsrc=true' // then we will not use 'about:blank' as fallback but return early without // starting a load if no 'src' attribute is given (or it's empty). if (mOwnerContent->IsXUL() && mOwnerContent->AttrValueIs(kNameSpaceID_None, nsGkAtoms::nodefaultsrc, nsGkAtoms::_true, eCaseMatters)) { return NS_OK; } src.AssignLiteral("about:blank"); } } nsIDocument* doc = mOwnerContent->OwnerDoc(); if (doc->IsStaticDocument()) { return NS_OK; } nsCOMPtr base_uri = mOwnerContent->GetBaseURI(); const nsAFlatCString &doc_charset = doc->GetDocumentCharacterSet(); const char *charset = doc_charset.IsEmpty() ? nullptr : doc_charset.get(); nsCOMPtr uri; nsresult rv = NS_NewURI(getter_AddRefs(uri), src, charset, base_uri); // If the URI was malformed, try to recover by loading about:blank. if (rv == NS_ERROR_MALFORMED_URI) { rv = NS_NewURI(getter_AddRefs(uri), NS_LITERAL_STRING("about:blank"), charset, base_uri); } if (NS_SUCCEEDED(rv)) { rv = LoadURI(uri); } if (NS_FAILED(rv)) { FireErrorEvent(); return rv; } return NS_OK; } void nsFrameLoader::FireErrorEvent() { if (!mOwnerContent) { return; } nsRefPtr loadBlockingAsyncDispatcher = new LoadBlockingAsyncEventDispatcher(mOwnerContent, NS_LITERAL_STRING("error"), false, false); loadBlockingAsyncDispatcher->PostDOMEvent(); } NS_IMETHODIMP nsFrameLoader::LoadURI(nsIURI* aURI) { if (!aURI) return NS_ERROR_INVALID_POINTER; NS_ENSURE_STATE(!mDestroyCalled && mOwnerContent); nsCOMPtr doc = mOwnerContent->OwnerDoc(); nsresult rv = CheckURILoad(aURI); NS_ENSURE_SUCCESS(rv, rv); mURIToLoad = aURI; rv = doc->InitializeFrameLoader(this); if (NS_FAILED(rv)) { mURIToLoad = nullptr; } return rv; } nsresult nsFrameLoader::ReallyStartLoading() { nsresult rv = ReallyStartLoadingInternal(); if (NS_FAILED(rv)) { FireErrorEvent(); } return rv; } class DelayedStartLoadingRunnable : public nsRunnable { public: explicit DelayedStartLoadingRunnable(nsFrameLoader* aFrameLoader) : mFrameLoader(aFrameLoader) { } NS_IMETHOD Run() { // Retry the request. mFrameLoader->ReallyStartLoading(); // We delayed nsFrameLoader::ReallyStartLoading() after the child process is // ready and might not be able to notify the remote browser in // UpdatePositionAndSize() when reflow finished. Retrigger reflow. nsIFrame* frame = mFrameLoader->GetPrimaryFrameOfOwningContent(); if (!frame) { return NS_OK; } frame->InvalidateFrame(); frame->PresContext()->PresShell()-> FrameNeedsReflow(frame, nsIPresShell::eResize, NS_FRAME_IS_DIRTY); return NS_OK; } private: nsRefPtr mFrameLoader; }; nsresult nsFrameLoader::ReallyStartLoadingInternal() { NS_ENSURE_STATE(mURIToLoad && mOwnerContent && mOwnerContent->IsInDoc()); PROFILER_LABEL("nsFrameLoader", "ReallyStartLoading", js::ProfileEntry::Category::OTHER); nsresult rv = MaybeCreateDocShell(); if (NS_FAILED(rv)) { return rv; } if (mRemoteFrame) { if (!mRemoteBrowser) { if (!mPendingFrameSent) { nsCOMPtr os = services::GetObserverService(); if (os && !mRemoteBrowserInitialized) { os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this), "remote-browser-pending", nullptr); mPendingFrameSent = true; } } TryRemoteBrowser(); if (!mRemoteBrowser) { NS_WARNING("Couldn't create child process for iframe."); return NS_ERROR_FAILURE; } } if (mRemoteBrowserShown || ShowRemoteFrame(nsIntSize(0, 0))) { // FIXME get error codes from child mRemoteBrowser->LoadURL(mURIToLoad); } else { NS_WARNING("[nsFrameLoader] ReallyStartLoadingInternal tried but couldn't show remote browser.\n"); } return NS_OK; } NS_ASSERTION(mDocShell, "MaybeCreateDocShell succeeded with a null mDocShell"); // Just to be safe, recheck uri. rv = CheckURILoad(mURIToLoad); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr loadInfo; mDocShell->CreateLoadInfo(getter_AddRefs(loadInfo)); NS_ENSURE_TRUE(loadInfo, NS_ERROR_FAILURE); // If this frame is sandboxed with respect to origin we will set it up with // a null principal later in nsDocShell::DoURILoad. // We do it there to correctly sandbox content that was loaded into // the frame via other methods than the src attribute. // We'll use our principal, not that of the document loaded inside us. This // is very important; needed to prevent XSS attacks on documents loaded in // subframes! loadInfo->SetOwner(mOwnerContent->NodePrincipal()); nsCOMPtr referrer; nsAutoString srcdoc; bool isSrcdoc = (mOwnerContent->IsHTML(nsGkAtoms::iframe) || mOwnerContent->IsSVG(nsGkAtoms::iframe)) && mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::srcdoc, srcdoc); if (isSrcdoc) { nsAutoString referrerStr; mOwnerContent->OwnerDoc()->GetReferrer(referrerStr); rv = NS_NewURI(getter_AddRefs(referrer), referrerStr); loadInfo->SetSrcdocData(srcdoc); nsCOMPtr baseURI = mOwnerContent->GetBaseURI(); loadInfo->SetBaseURI(baseURI); } else { rv = mOwnerContent->NodePrincipal()->GetURI(getter_AddRefs(referrer)); NS_ENSURE_SUCCESS(rv, rv); } // Use referrer as long as it is not an nsNullPrincipalURI. // We could add a method such as GetReferrerURI to principals to make this // cleaner, but given that we need to start using Source Browsing Context for // referrer (see Bug 960639) this may be wasted effort at this stage. if (referrer) { bool isNullPrincipalScheme; rv = referrer->SchemeIs(NS_NULLPRINCIPAL_SCHEME, &isNullPrincipalScheme); if (NS_SUCCEEDED(rv) && !isNullPrincipalScheme) { loadInfo->SetReferrer(referrer); } } loadInfo->SetReferrerPolicy(mOwnerContent->OwnerDoc()->GetReferrerPolicy()); // Default flags: int32_t flags = nsIWebNavigation::LOAD_FLAGS_NONE; // Flags for browser frame: if (OwnerIsBrowserFrame()) { flags = nsIWebNavigation::LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP | nsIWebNavigation::LOAD_FLAGS_DISALLOW_INHERIT_OWNER; } // Kick off the load... bool tmpState = mNeedsAsyncDestroy; mNeedsAsyncDestroy = true; nsCOMPtr uriToLoad = mURIToLoad; rv = mDocShell->LoadURI(uriToLoad, loadInfo, flags, false); mNeedsAsyncDestroy = tmpState; mURIToLoad = nullptr; NS_ENSURE_SUCCESS(rv, rv); return NS_OK; } nsresult nsFrameLoader::CheckURILoad(nsIURI* aURI) { // Check for security. The fun part is trying to figure out what principals // to use. The way I figure it, if we're doing a LoadFrame() accidentally // (eg someone created a frame/iframe node, we're being parsed, XUL iframes // are being reframed, etc.) then we definitely want to use the node // principal of mOwnerContent for security checks. If, on the other hand, // someone's setting the src on our owner content, or created it via script, // or whatever, then they can clearly access it... and we should still use // the principal of mOwnerContent. I don't think that leads to privilege // escalation, and it's reasonably guaranteed to not lead to XSS issues // (since caller can already access mOwnerContent in this case). So just use // the principal of mOwnerContent no matter what. If script wants to run // things with its own permissions, which differ from those of mOwnerContent // (which means the script is privileged in some way) it should set // window.location instead. nsIScriptSecurityManager *secMan = nsContentUtils::GetSecurityManager(); // Get our principal nsIPrincipal* principal = mOwnerContent->NodePrincipal(); // Check if we are allowed to load absURL nsresult rv = secMan->CheckLoadURIWithPrincipal(principal, aURI, nsIScriptSecurityManager::STANDARD); if (NS_FAILED(rv)) { return rv; // We're not } // Bail out if this is an infinite recursion scenario rv = MaybeCreateDocShell(); if (NS_FAILED(rv)) { return rv; } if (mRemoteFrame) { return NS_OK; } return CheckForRecursiveLoad(aURI); } NS_IMETHODIMP nsFrameLoader::GetDocShell(nsIDocShell **aDocShell) { *aDocShell = nullptr; nsresult rv = NS_OK; // If we have an owner, make sure we have a docshell and return // that. If not, we're most likely in the middle of being torn down, // then we just return null. if (mOwnerContent) { nsresult rv = MaybeCreateDocShell(); if (NS_FAILED(rv)) return rv; if (mRemoteFrame) { NS_WARNING("No docshells for remote frames!"); return rv; } NS_ASSERTION(mDocShell, "MaybeCreateDocShell succeeded, but null mDocShell"); } *aDocShell = mDocShell; NS_IF_ADDREF(*aDocShell); return rv; } void nsFrameLoader::Finalize() { nsCOMPtr base_win(do_QueryInterface(mDocShell)); if (base_win) { base_win->Destroy(); } mDocShell = nullptr; } static void FirePageHideEvent(nsIDocShellTreeItem* aItem, EventTarget* aChromeEventHandler) { nsCOMPtr doc = aItem->GetDocument(); NS_ASSERTION(doc, "What happened here?"); doc->OnPageHide(true, aChromeEventHandler); int32_t childCount = 0; aItem->GetChildCount(&childCount); nsAutoTArray, 8> kids; kids.AppendElements(childCount); for (int32_t i = 0; i < childCount; ++i) { aItem->GetChildAt(i, getter_AddRefs(kids[i])); } for (uint32_t i = 0; i < kids.Length(); ++i) { if (kids[i]) { FirePageHideEvent(kids[i], aChromeEventHandler); } } } // The pageshow event is fired for a given document only if IsShowing() returns // the same thing as aFireIfShowing. This gives us a way to fire pageshow only // on documents that are still loading or only on documents that are already // loaded. static void FirePageShowEvent(nsIDocShellTreeItem* aItem, EventTarget* aChromeEventHandler, bool aFireIfShowing) { int32_t childCount = 0; aItem->GetChildCount(&childCount); nsAutoTArray, 8> kids; kids.AppendElements(childCount); for (int32_t i = 0; i < childCount; ++i) { aItem->GetChildAt(i, getter_AddRefs(kids[i])); } for (uint32_t i = 0; i < kids.Length(); ++i) { if (kids[i]) { FirePageShowEvent(kids[i], aChromeEventHandler, aFireIfShowing); } } nsCOMPtr doc = aItem->GetDocument(); NS_ASSERTION(doc, "What happened here?"); if (doc->IsShowing() == aFireIfShowing) { doc->OnPageShow(true, aChromeEventHandler); } } static void SetTreeOwnerAndChromeEventHandlerOnDocshellTree(nsIDocShellTreeItem* aItem, nsIDocShellTreeOwner* aOwner, EventTarget* aHandler) { NS_PRECONDITION(aItem, "Must have item"); aItem->SetTreeOwner(aOwner); int32_t childCount = 0; aItem->GetChildCount(&childCount); for (int32_t i = 0; i < childCount; ++i) { nsCOMPtr item; aItem->GetChildAt(i, getter_AddRefs(item)); if (aHandler) { nsCOMPtr shell(do_QueryInterface(item)); shell->SetChromeEventHandler(aHandler); } SetTreeOwnerAndChromeEventHandlerOnDocshellTree(item, aOwner, aHandler); } } /** * Set the type of the treeitem and hook it up to the treeowner. * @param aItem the treeitem we're working with * @param aTreeOwner the relevant treeowner; might be null * @param aParentType the nsIDocShellTreeItem::GetType of our parent docshell * @param aParentNode if non-null, the docshell we should be added as a child to * * @return whether aItem is top-level content */ bool nsFrameLoader::AddTreeItemToTreeOwner(nsIDocShellTreeItem* aItem, nsIDocShellTreeOwner* aOwner, int32_t aParentType, nsIDocShell* aParentNode) { NS_PRECONDITION(aItem, "Must have docshell treeitem"); NS_PRECONDITION(mOwnerContent, "Must have owning content"); nsAutoString value; bool isContent = false; mOwnerContent->GetAttr(kNameSpaceID_None, TypeAttrName(), value); // we accept "content" and "content-xxx" values. // at time of writing, we expect "xxx" to be "primary" or "targetable", but // someday it might be an integer expressing priority or something else. isContent = value.LowerCaseEqualsLiteral("content") || StringBeginsWith(value, NS_LITERAL_STRING("content-"), nsCaseInsensitiveStringComparator()); // Force mozbrowser frames to always be typeContent, even if the // mozbrowser interfaces are disabled. nsCOMPtr mozbrowser = do_QueryInterface(mOwnerContent); if (mozbrowser) { bool isMozbrowser = false; mozbrowser->GetMozbrowser(&isMozbrowser); isContent |= isMozbrowser; } if (isContent) { // The web shell's type is content. aItem->SetItemType(nsIDocShellTreeItem::typeContent); } else { // Inherit our type from our parent docshell. If it is // chrome, we'll be chrome. If it is content, we'll be // content. aItem->SetItemType(aParentType); } // Now that we have our type set, add ourselves to the parent, as needed. if (aParentNode) { aParentNode->AddChild(aItem); } bool retval = false; if (aParentType == nsIDocShellTreeItem::typeChrome && isContent) { retval = true; bool is_primary = value.LowerCaseEqualsLiteral("content-primary"); if (aOwner) { bool is_targetable = is_primary || value.LowerCaseEqualsLiteral("content-targetable"); mOwnerContent->AddMutationObserver(this); mObservingOwnerContent = true; aOwner->ContentShellAdded(aItem, is_primary, is_targetable, value); } } return retval; } static bool AllDescendantsOfType(nsIDocShellTreeItem* aParentItem, int32_t aType) { int32_t childCount = 0; aParentItem->GetChildCount(&childCount); for (int32_t i = 0; i < childCount; ++i) { nsCOMPtr kid; aParentItem->GetChildAt(i, getter_AddRefs(kid)); if (kid->ItemType() != aType || !AllDescendantsOfType(kid, aType)) { return false; } } return true; } /** * A class that automatically sets mInShow to false when it goes * out of scope. */ class MOZ_STACK_CLASS AutoResetInShow { private: nsFrameLoader* mFrameLoader; MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER public: explicit AutoResetInShow(nsFrameLoader* aFrameLoader MOZ_GUARD_OBJECT_NOTIFIER_PARAM) : mFrameLoader(aFrameLoader) { MOZ_GUARD_OBJECT_NOTIFIER_INIT; } ~AutoResetInShow() { mFrameLoader->mInShow = false; } }; bool nsFrameLoader::Show(int32_t marginWidth, int32_t marginHeight, int32_t scrollbarPrefX, int32_t scrollbarPrefY, nsSubDocumentFrame* frame) { if (mInShow) { return false; } // Reset mInShow if we exit early. AutoResetInShow resetInShow(this); mInShow = true; nsresult rv = MaybeCreateDocShell(); if (NS_FAILED(rv)) { return false; } if (!mRemoteFrame) { if (!mDocShell) return false; mDocShell->SetMarginWidth(marginWidth); mDocShell->SetMarginHeight(marginHeight); nsCOMPtr sc = do_QueryInterface(mDocShell); if (sc) { sc->SetDefaultScrollbarPreferences(nsIScrollable::ScrollOrientation_X, scrollbarPrefX); sc->SetDefaultScrollbarPreferences(nsIScrollable::ScrollOrientation_Y, scrollbarPrefY); } nsCOMPtr presShell = mDocShell->GetPresShell(); if (presShell) { // Ensure root scroll frame is reflowed in case scroll preferences or // margins have changed nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame(); if (rootScrollFrame) { presShell->FrameNeedsReflow(rootScrollFrame, nsIPresShell::eResize, NS_FRAME_IS_DIRTY); } return true; } } nsIntSize size = frame->GetSubdocumentSize(); if (mRemoteFrame) { return ShowRemoteFrame(size, frame); } nsView* view = frame->EnsureInnerView(); if (!view) return false; nsCOMPtr baseWindow = do_QueryInterface(mDocShell); NS_ASSERTION(baseWindow, "Found a nsIDocShell that isn't a nsIBaseWindow."); baseWindow->InitWindow(nullptr, view->GetWidget(), 0, 0, size.width, size.height); // This is kinda whacky, this "Create()" call doesn't really // create anything, one starts to wonder why this was named // "Create"... baseWindow->Create(); baseWindow->SetVisibility(true); NS_ENSURE_TRUE(mDocShell, false); // Trigger editor re-initialization if midas is turned on in the // sub-document. This shouldn't be necessary, but given the way our // editor works, it is. See // https://bugzilla.mozilla.org/show_bug.cgi?id=284245 nsCOMPtr presShell = mDocShell->GetPresShell(); if (presShell) { nsCOMPtr doc = do_QueryInterface(presShell->GetDocument()); if (doc) { nsAutoString designMode; doc->GetDesignMode(designMode); if (designMode.EqualsLiteral("on")) { // Hold on to the editor object to let the document reattach to the // same editor object, instead of creating a new one. nsCOMPtr editor; nsresult rv = mDocShell->GetEditor(getter_AddRefs(editor)); NS_ENSURE_SUCCESS(rv, false); doc->SetDesignMode(NS_LITERAL_STRING("off")); doc->SetDesignMode(NS_LITERAL_STRING("on")); } else { // Re-initialize the presentation for contenteditable documents bool editable = false, hasEditingSession = false; mDocShell->GetEditable(&editable); mDocShell->GetHasEditingSession(&hasEditingSession); nsCOMPtr editor; mDocShell->GetEditor(getter_AddRefs(editor)); if (editable && hasEditingSession && editor) { editor->PostCreate(); } } } } mInShow = false; if (mHideCalled) { mHideCalled = false; Hide(); return false; } return true; } void nsFrameLoader::MarginsChanged(uint32_t aMarginWidth, uint32_t aMarginHeight) { // We assume that the margins are always zero for remote frames. if (mRemoteFrame) return; // If there's no docshell, we're probably not up and running yet. // nsFrameLoader::Show() will take care of setting the right // margins. if (!mDocShell) return; // Set the margins mDocShell->SetMarginWidth(aMarginWidth); mDocShell->SetMarginHeight(aMarginHeight); // Trigger a restyle if there's a prescontext // FIXME: This could do something much less expensive. nsRefPtr presContext; mDocShell->GetPresContext(getter_AddRefs(presContext)); if (presContext) presContext->RebuildAllStyleData(nsChangeHint(0), eRestyle_Subtree); } bool nsFrameLoader::ShowRemoteFrame(const nsIntSize& size, nsSubDocumentFrame *aFrame) { NS_ASSERTION(mRemoteFrame, "ShowRemote only makes sense on remote frames."); if (!mRemoteBrowser) { TryRemoteBrowser(); if (!mRemoteBrowser) { NS_ERROR("Couldn't create child process."); return false; } } // FIXME/bug 589337: Show()/Hide() is pretty expensive for // cross-process layers; need to figure out what behavior we really // want here. For now, hack. if (!mRemoteBrowserShown) { if (!mOwnerContent || !mOwnerContent->GetUncomposedDoc()) { return false; } nsRefPtr layerManager = nsContentUtils::LayerManagerForDocument(mOwnerContent->GetUncomposedDoc()); if (!layerManager) { // This is just not going to work. return false; } mRemoteBrowser->Show(size); mRemoteBrowserShown = true; EnsureMessageManager(); nsCOMPtr os = services::GetObserverService(); if (os && !mRemoteBrowserInitialized) { if (!mPendingFrameSent) { os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this), "remote-browser-pending", nullptr); mPendingFrameSent = true; } os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this), "remote-browser-shown", nullptr); mRemoteBrowserInitialized = true; } } else { nsIntRect dimensions; NS_ENSURE_SUCCESS(GetWindowDimensions(dimensions), false); // Don't show remote iframe if we are waiting for the completion of reflow. if (!aFrame || !(aFrame->GetStateBits() & NS_FRAME_FIRST_REFLOW)) { mRemoteBrowser->UpdateDimensions(dimensions, size); } } return true; } void nsFrameLoader::Hide() { if (mHideCalled) { return; } if (mInShow) { mHideCalled = true; return; } if (!mDocShell) return; nsCOMPtr contentViewer; mDocShell->GetContentViewer(getter_AddRefs(contentViewer)); if (contentViewer) contentViewer->SetSticky(false); nsCOMPtr baseWin = do_QueryInterface(mDocShell); NS_ASSERTION(baseWin, "Found an nsIDocShell which doesn't implement nsIBaseWindow."); baseWin->SetVisibility(false); baseWin->SetParentWidget(nullptr); } nsresult nsFrameLoader::SwapWithOtherRemoteLoader(nsFrameLoader* aOther, nsRefPtr& aFirstToSwap, nsRefPtr& aSecondToSwap) { Element* ourContent = mOwnerContent; Element* otherContent = aOther->mOwnerContent; if (!ourContent || !otherContent) { // Can't handle this return NS_ERROR_NOT_IMPLEMENTED; } // Make sure there are no same-origin issues bool equal; nsresult rv = ourContent->NodePrincipal()->Equals(otherContent->NodePrincipal(), &equal); if (NS_FAILED(rv) || !equal) { // Security problems loom. Just bail on it all return NS_ERROR_DOM_SECURITY_ERR; } nsIDocument* ourDoc = ourContent->GetCurrentDoc(); nsIDocument* otherDoc = otherContent->GetCurrentDoc(); if (!ourDoc || !otherDoc) { // Again, how odd, given that we had docshells return NS_ERROR_NOT_IMPLEMENTED; } nsIPresShell* ourShell = ourDoc->GetShell(); nsIPresShell* otherShell = otherDoc->GetShell(); if (!ourShell || !otherShell) { return NS_ERROR_NOT_IMPLEMENTED; } if (mInSwap || aOther->mInSwap) { return NS_ERROR_NOT_IMPLEMENTED; } mInSwap = aOther->mInSwap = true; nsIFrame* ourFrame = ourContent->GetPrimaryFrame(); nsIFrame* otherFrame = otherContent->GetPrimaryFrame(); if (!ourFrame || !otherFrame) { mInSwap = aOther->mInSwap = false; return NS_ERROR_NOT_IMPLEMENTED; } nsSubDocumentFrame* ourFrameFrame = do_QueryFrame(ourFrame); if (!ourFrameFrame) { mInSwap = aOther->mInSwap = false; return NS_ERROR_NOT_IMPLEMENTED; } rv = ourFrameFrame->BeginSwapDocShells(otherFrame); if (NS_FAILED(rv)) { mInSwap = aOther->mInSwap = false; return rv; } SetOwnerContent(otherContent); aOther->SetOwnerContent(ourContent); mRemoteBrowser->SetOwnerElement(otherContent); aOther->mRemoteBrowser->SetOwnerElement(ourContent); nsRefPtr ourMessageManager = mMessageManager; nsRefPtr otherMessageManager = aOther->mMessageManager; // Swap and setup things in parent message managers. if (mMessageManager) { mMessageManager->SetCallback(aOther); } if (aOther->mMessageManager) { aOther->mMessageManager->SetCallback(this); } mMessageManager.swap(aOther->mMessageManager); aFirstToSwap.swap(aSecondToSwap); ourFrameFrame->EndSwapDocShells(otherFrame); ourDoc->FlushPendingNotifications(Flush_Layout); otherDoc->FlushPendingNotifications(Flush_Layout); mInSwap = aOther->mInSwap = false; return NS_OK; } nsresult nsFrameLoader::SwapWithOtherLoader(nsFrameLoader* aOther, nsRefPtr& aFirstToSwap, nsRefPtr& aSecondToSwap) { NS_PRECONDITION((aFirstToSwap == this && aSecondToSwap == aOther) || (aFirstToSwap == aOther && aSecondToSwap == this), "Swapping some sort of random loaders?"); NS_ENSURE_STATE(!mInShow && !aOther->mInShow); if (mRemoteFrame && aOther->mRemoteFrame) { return SwapWithOtherRemoteLoader(aOther, aFirstToSwap, aSecondToSwap); } if (mRemoteFrame || aOther->mRemoteFrame) { NS_WARNING("Swapping remote and non-remote frames is not currently supported"); return NS_ERROR_NOT_IMPLEMENTED; } Element* ourContent = mOwnerContent; Element* otherContent = aOther->mOwnerContent; if (!ourContent || !otherContent) { // Can't handle this return NS_ERROR_NOT_IMPLEMENTED; } // Make sure there are no same-origin issues bool equal; nsresult rv = ourContent->NodePrincipal()->Equals(otherContent->NodePrincipal(), &equal); if (NS_FAILED(rv) || !equal) { // Security problems loom. Just bail on it all return NS_ERROR_DOM_SECURITY_ERR; } nsCOMPtr ourDocshell = GetExistingDocShell(); nsCOMPtr otherDocshell = aOther->GetExistingDocShell(); if (!ourDocshell || !otherDocshell) { // How odd return NS_ERROR_NOT_IMPLEMENTED; } // To avoid having to mess with session history, avoid swapping // frameloaders that don't correspond to root same-type docshells, // unless both roots have session history disabled. nsCOMPtr ourRootTreeItem, otherRootTreeItem; ourDocshell->GetSameTypeRootTreeItem(getter_AddRefs(ourRootTreeItem)); otherDocshell->GetSameTypeRootTreeItem(getter_AddRefs(otherRootTreeItem)); nsCOMPtr ourRootWebnav = do_QueryInterface(ourRootTreeItem); nsCOMPtr otherRootWebnav = do_QueryInterface(otherRootTreeItem); if (!ourRootWebnav || !otherRootWebnav) { return NS_ERROR_NOT_IMPLEMENTED; } nsCOMPtr ourHistory; nsCOMPtr otherHistory; ourRootWebnav->GetSessionHistory(getter_AddRefs(ourHistory)); otherRootWebnav->GetSessionHistory(getter_AddRefs(otherHistory)); if ((ourRootTreeItem != ourDocshell || otherRootTreeItem != otherDocshell) && (ourHistory || otherHistory)) { return NS_ERROR_NOT_IMPLEMENTED; } // Also make sure that the two docshells are the same type. Otherwise // swapping is certainly not safe. If this needs to be changed then // the code below needs to be audited as it assumes identical types. int32_t ourType = ourDocshell->ItemType(); int32_t otherType = otherDocshell->ItemType(); if (ourType != otherType) { return NS_ERROR_NOT_IMPLEMENTED; } // One more twist here. Setting up the right treeowners in a heterogeneous // tree is a bit of a pain. So make sure that if ourType is not // nsIDocShellTreeItem::typeContent then all of our descendants are the same // type as us. if (ourType != nsIDocShellTreeItem::typeContent && (!AllDescendantsOfType(ourDocshell, ourType) || !AllDescendantsOfType(otherDocshell, otherType))) { return NS_ERROR_NOT_IMPLEMENTED; } // Save off the tree owners, frame elements, chrome event handlers, and // docshell and document parents before doing anything else. nsCOMPtr ourOwner, otherOwner; ourDocshell->GetTreeOwner(getter_AddRefs(ourOwner)); otherDocshell->GetTreeOwner(getter_AddRefs(otherOwner)); // Note: it's OK to have null treeowners. nsCOMPtr ourParentItem, otherParentItem; ourDocshell->GetParent(getter_AddRefs(ourParentItem)); otherDocshell->GetParent(getter_AddRefs(otherParentItem)); if (!ourParentItem || !otherParentItem) { return NS_ERROR_NOT_IMPLEMENTED; } // Make sure our parents are the same type too int32_t ourParentType = ourParentItem->ItemType(); int32_t otherParentType = otherParentItem->ItemType(); if (ourParentType != otherParentType) { return NS_ERROR_NOT_IMPLEMENTED; } nsCOMPtr ourWindow = ourDocshell->GetWindow(); nsCOMPtr otherWindow = otherDocshell->GetWindow(); nsCOMPtr ourFrameElement = ourWindow->GetFrameElementInternal(); nsCOMPtr otherFrameElement = otherWindow->GetFrameElementInternal(); nsCOMPtr ourChromeEventHandler = do_QueryInterface(ourWindow->GetChromeEventHandler()); nsCOMPtr otherChromeEventHandler = do_QueryInterface(otherWindow->GetChromeEventHandler()); nsCOMPtr ourEventTarget = ourWindow->GetParentTarget(); nsCOMPtr otherEventTarget = otherWindow->GetParentTarget(); NS_ASSERTION(SameCOMIdentity(ourFrameElement, ourContent) && SameCOMIdentity(otherFrameElement, otherContent) && SameCOMIdentity(ourChromeEventHandler, ourContent) && SameCOMIdentity(otherChromeEventHandler, otherContent), "How did that happen, exactly?"); nsCOMPtr ourChildDocument = ourWindow->GetExtantDoc(); nsCOMPtr otherChildDocument = otherWindow ->GetExtantDoc(); if (!ourChildDocument || !otherChildDocument) { // This shouldn't be happening return NS_ERROR_NOT_IMPLEMENTED; } nsCOMPtr ourParentDocument = ourChildDocument->GetParentDocument(); nsCOMPtr otherParentDocument = otherChildDocument->GetParentDocument(); // Make sure to swap docshells between the two frames. nsIDocument* ourDoc = ourContent->GetUncomposedDoc(); nsIDocument* otherDoc = otherContent->GetUncomposedDoc(); if (!ourDoc || !otherDoc) { // Again, how odd, given that we had docshells return NS_ERROR_NOT_IMPLEMENTED; } NS_ASSERTION(ourDoc == ourParentDocument, "Unexpected parent document"); NS_ASSERTION(otherDoc == otherParentDocument, "Unexpected parent document"); nsIPresShell* ourShell = ourDoc->GetShell(); nsIPresShell* otherShell = otherDoc->GetShell(); if (!ourShell || !otherShell) { return NS_ERROR_NOT_IMPLEMENTED; } if (ourDocshell->GetIsBrowserElement() != otherDocshell->GetIsBrowserElement() || ourDocshell->GetIsApp() != otherDocshell->GetIsApp()) { return NS_ERROR_NOT_IMPLEMENTED; } if (mInSwap || aOther->mInSwap) { return NS_ERROR_NOT_IMPLEMENTED; } mInSwap = aOther->mInSwap = true; // Fire pageshow events on still-loading pages, and then fire pagehide // events. Note that we do NOT fire these in the normal way, but just fire // them on the chrome event handlers. FirePageShowEvent(ourDocshell, ourEventTarget, false); FirePageShowEvent(otherDocshell, otherEventTarget, false); FirePageHideEvent(ourDocshell, ourEventTarget); FirePageHideEvent(otherDocshell, otherEventTarget); nsIFrame* ourFrame = ourContent->GetPrimaryFrame(); nsIFrame* otherFrame = otherContent->GetPrimaryFrame(); if (!ourFrame || !otherFrame) { mInSwap = aOther->mInSwap = false; FirePageShowEvent(ourDocshell, ourEventTarget, true); FirePageShowEvent(otherDocshell, otherEventTarget, true); return NS_ERROR_NOT_IMPLEMENTED; } nsSubDocumentFrame* ourFrameFrame = do_QueryFrame(ourFrame); if (!ourFrameFrame) { mInSwap = aOther->mInSwap = false; FirePageShowEvent(ourDocshell, ourEventTarget, true); FirePageShowEvent(otherDocshell, otherEventTarget, true); return NS_ERROR_NOT_IMPLEMENTED; } // OK. First begin to swap the docshells in the two nsIFrames rv = ourFrameFrame->BeginSwapDocShells(otherFrame); if (NS_FAILED(rv)) { mInSwap = aOther->mInSwap = false; FirePageShowEvent(ourDocshell, ourEventTarget, true); FirePageShowEvent(otherDocshell, otherEventTarget, true); return rv; } // Now move the docshells to the right docshell trees. Note that this // resets their treeowners to null. ourParentItem->RemoveChild(ourDocshell); otherParentItem->RemoveChild(otherDocshell); if (ourType == nsIDocShellTreeItem::typeContent) { ourOwner->ContentShellRemoved(ourDocshell); otherOwner->ContentShellRemoved(otherDocshell); } ourParentItem->AddChild(otherDocshell); otherParentItem->AddChild(ourDocshell); // Restore the correct chrome event handlers. ourDocshell->SetChromeEventHandler(otherChromeEventHandler); otherDocshell->SetChromeEventHandler(ourChromeEventHandler); // Restore the correct treeowners // (and also chrome event handlers for content frames only). SetTreeOwnerAndChromeEventHandlerOnDocshellTree(ourDocshell, otherOwner, ourType == nsIDocShellTreeItem::typeContent ? otherChromeEventHandler : nullptr); SetTreeOwnerAndChromeEventHandlerOnDocshellTree(otherDocshell, ourOwner, ourType == nsIDocShellTreeItem::typeContent ? ourChromeEventHandler : nullptr); // Switch the owner content before we start calling AddTreeItemToTreeOwner. // Note that we rely on this to deal with setting mObservingOwnerContent to // false and calling RemoveMutationObserver as needed. SetOwnerContent(otherContent); aOther->SetOwnerContent(ourContent); AddTreeItemToTreeOwner(ourDocshell, otherOwner, otherParentType, nullptr); aOther->AddTreeItemToTreeOwner(otherDocshell, ourOwner, ourParentType, nullptr); // SetSubDocumentFor nulls out parent documents on the old child doc if a // new non-null document is passed in, so just go ahead and remove both // kids before reinserting in the parent subdoc maps, to avoid // complications. ourParentDocument->SetSubDocumentFor(ourContent, nullptr); otherParentDocument->SetSubDocumentFor(otherContent, nullptr); ourParentDocument->SetSubDocumentFor(ourContent, otherChildDocument); otherParentDocument->SetSubDocumentFor(otherContent, ourChildDocument); ourWindow->SetFrameElementInternal(otherFrameElement); otherWindow->SetFrameElementInternal(ourFrameElement); nsRefPtr ourMessageManager = mMessageManager; nsRefPtr otherMessageManager = aOther->mMessageManager; // Swap pointers in child message managers. if (mChildMessageManager) { nsInProcessTabChildGlobal* tabChild = static_cast(mChildMessageManager.get()); tabChild->SetOwner(otherContent); tabChild->SetChromeMessageManager(otherMessageManager); } if (aOther->mChildMessageManager) { nsInProcessTabChildGlobal* otherTabChild = static_cast(aOther->mChildMessageManager.get()); otherTabChild->SetOwner(ourContent); otherTabChild->SetChromeMessageManager(ourMessageManager); } // Swap and setup things in parent message managers. if (mMessageManager) { mMessageManager->SetCallback(aOther); } if (aOther->mMessageManager) { aOther->mMessageManager->SetCallback(this); } mMessageManager.swap(aOther->mMessageManager); aFirstToSwap.swap(aSecondToSwap); // Drop any cached content viewers in the two session histories. nsCOMPtr ourInternalHistory = do_QueryInterface(ourHistory); nsCOMPtr otherInternalHistory = do_QueryInterface(otherHistory); if (ourInternalHistory) { ourInternalHistory->EvictAllContentViewers(); } if (otherInternalHistory) { otherInternalHistory->EvictAllContentViewers(); } NS_ASSERTION(ourFrame == ourContent->GetPrimaryFrame() && otherFrame == otherContent->GetPrimaryFrame(), "changed primary frame"); ourFrameFrame->EndSwapDocShells(otherFrame); // If the content being swapped came from windows on two screens with // incompatible backing resolution (e.g. dragging a tab between windows on // hi-dpi and low-dpi screens), it will have style data that is based on // the wrong appUnitsPerDevPixel value. So we tell the PresShells that their // backing scale factor may have changed. (Bug 822266) ourShell->BackingScaleFactorChanged(); otherShell->BackingScaleFactorChanged(); ourParentDocument->FlushPendingNotifications(Flush_Layout); otherParentDocument->FlushPendingNotifications(Flush_Layout); FirePageShowEvent(ourDocshell, ourEventTarget, true); FirePageShowEvent(otherDocshell, otherEventTarget, true); mInSwap = aOther->mInSwap = false; return NS_OK; } void nsFrameLoader::DestroyChild() { if (mRemoteBrowser) { mRemoteBrowser->SetOwnerElement(nullptr); mRemoteBrowser->Destroy(); mRemoteBrowser = nullptr; } } NS_IMETHODIMP nsFrameLoader::Destroy() { if (mDestroyCalled) { return NS_OK; } mDestroyCalled = true; if (mMessageManager) { mMessageManager->Disconnect(); } if (mChildMessageManager) { static_cast(mChildMessageManager.get())->Disconnect(); } nsCOMPtr doc; bool dynamicSubframeRemoval = false; if (mOwnerContent) { doc = mOwnerContent->OwnerDoc(); dynamicSubframeRemoval = !mIsTopLevelContent && !doc->InUnlinkOrDeletion(); doc->SetSubDocumentFor(mOwnerContent, nullptr); SetOwnerContent(nullptr); } DestroyChild(); // Seems like this is a dynamic frame removal. if (dynamicSubframeRemoval) { if (mDocShell) { mDocShell->RemoveFromSessionHistory(); } } // Let the tree owner know we're gone. if (mIsTopLevelContent) { if (mDocShell) { nsCOMPtr parentItem; mDocShell->GetParent(getter_AddRefs(parentItem)); nsCOMPtr owner = do_GetInterface(parentItem); if (owner) { owner->ContentShellRemoved(mDocShell); } } } // Let our window know that we are gone if (mDocShell) { nsCOMPtr win_private(mDocShell->GetWindow()); if (win_private) { win_private->SetFrameElementInternal(nullptr); } } if ((mNeedsAsyncDestroy || !doc || NS_FAILED(doc->FinalizeFrameLoader(this))) && mDocShell) { nsCOMPtr event = new nsAsyncDocShellDestroyer(mDocShell); NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY); NS_DispatchToCurrentThread(event); // Let go of our docshell now that the async destroyer holds on to // the docshell. mDocShell = nullptr; } // NOTE: 'this' may very well be gone by now. return NS_OK; } NS_IMETHODIMP nsFrameLoader::GetDepthTooGreat(bool* aDepthTooGreat) { *aDepthTooGreat = mDepthTooGreat; return NS_OK; } void nsFrameLoader::SetOwnerContent(Element* aContent) { if (mObservingOwnerContent) { mObservingOwnerContent = false; mOwnerContent->RemoveMutationObserver(this); } mOwnerContent = aContent; if (RenderFrameParent* rfp = GetCurrentRemoteFrame()) { rfp->OwnerContentChanged(aContent); } ResetPermissionManagerStatus(); } bool nsFrameLoader::OwnerIsBrowserOrAppFrame() { nsCOMPtr browserFrame = do_QueryInterface(mOwnerContent); return browserFrame ? browserFrame->GetReallyIsBrowserOrApp() : false; } // The xpcom getter version NS_IMETHODIMP nsFrameLoader::GetOwnerIsBrowserOrAppFrame(bool* aResult) { *aResult = OwnerIsBrowserOrAppFrame(); return NS_OK; } bool nsFrameLoader::OwnerIsWidget() { nsCOMPtr browserFrame = do_QueryInterface(mOwnerContent); return browserFrame ? browserFrame->GetReallyIsWidget() : false; } // The xpcom getter version NS_IMETHODIMP nsFrameLoader::GetOwnerIsWidget(bool* aResult) { *aResult = OwnerIsWidget(); return NS_OK; } bool nsFrameLoader::OwnerIsAppFrame() { nsCOMPtr browserFrame = do_QueryInterface(mOwnerContent); return browserFrame ? browserFrame->GetReallyIsApp() : false; } bool nsFrameLoader::OwnerIsBrowserFrame() { return OwnerIsBrowserOrAppFrame() && !OwnerIsAppFrame(); } void nsFrameLoader::GetOwnerAppManifestURL(nsAString& aOut) { aOut.Truncate(); nsCOMPtr browserFrame = do_QueryInterface(mOwnerContent); if (browserFrame) { browserFrame->GetAppManifestURL(aOut); } } already_AddRefed nsFrameLoader::GetOwnApp() { nsAutoString manifest; GetOwnerAppManifestURL(manifest); if (manifest.IsEmpty()) { return nullptr; } nsCOMPtr appsService = do_GetService(APPS_SERVICE_CONTRACTID); NS_ENSURE_TRUE(appsService, nullptr); nsCOMPtr app; appsService->GetAppByManifestURL(manifest, getter_AddRefs(app)); return app.forget(); } already_AddRefed nsFrameLoader::GetContainingApp() { // See if our owner content's principal has an associated app. uint32_t appId = mOwnerContent->NodePrincipal()->GetAppId(); MOZ_ASSERT(appId != nsIScriptSecurityManager::UNKNOWN_APP_ID); if (appId == nsIScriptSecurityManager::NO_APP_ID || appId == nsIScriptSecurityManager::UNKNOWN_APP_ID) { return nullptr; } nsCOMPtr appsService = do_GetService(APPS_SERVICE_CONTRACTID); NS_ENSURE_TRUE(appsService, nullptr); nsCOMPtr app; appsService->GetAppByLocalId(appId, getter_AddRefs(app)); return app.forget(); } bool nsFrameLoader::ShouldUseRemoteProcess() { if (PR_GetEnv("MOZ_DISABLE_OOP_TABS") || Preferences::GetBool("dom.ipc.tabs.disabled", false)) { return false; } // Don't try to launch nested children if we don't have OMTC. // They won't render! if (XRE_GetProcessType() == GeckoProcessType_Content && !CompositorChild::ChildProcessHasCompositor()) { return false; } if (XRE_GetProcessType() == GeckoProcessType_Content && !(PR_GetEnv("MOZ_NESTED_OOP_TABS") || Preferences::GetBool("dom.ipc.tabs.nested.enabled", false))) { return false; } // If we're an