diff options
Diffstat (limited to 'dom/base/nsFrameLoader.cpp')
-rw-r--r-- | dom/base/nsFrameLoader.cpp | 3490 |
1 files changed, 3490 insertions, 0 deletions
diff --git a/dom/base/nsFrameLoader.cpp b/dom/base/nsFrameLoader.cpp new file mode 100644 index 000000000..23067becd --- /dev/null +++ b/dom/base/nsFrameLoader.cpp @@ -0,0 +1,3490 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 "nsDocShell.h" +#include "nsIAppsService.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 "nsPIDOMWindow.h" +#include "nsIWebNavigation.h" +#include "nsIWebProgress.h" +#include "nsIDocShell.h" +#include "nsIDocShellTreeOwner.h" +#include "nsIDocShellLoadInfo.h" +#include "nsIBaseWindow.h" +#include "nsIBrowser.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 "nsISHistory.h" +#include "nsNullPrincipal.h" +#include "nsIScriptError.h" +#include "nsGlobalWindow.h" +#include "nsPIWindowRoot.h" +#include "nsLayoutUtils.h" +#include "nsView.h" +#include "GroupedSHistory.h" +#include "PartialSHistory.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/plugins/PPluginWidgetParent.h" +#include "../plugins/ipc/PluginWidgetParent.h" +#include "mozilla/AsyncEventDispatcher.h" +#include "mozilla/BasePrincipal.h" +#include "mozilla/GuardObjects.h" +#include "mozilla/Preferences.h" +#include "mozilla/Unused.h" +#include "mozilla/dom/Element.h" +#include "mozilla/jsipc/CrossProcessObjectWrappers.h" +#include "mozilla/layout/RenderFrameParent.h" +#include "nsIAppsService.h" +#include "GeckoProfiler.h" + +#include "jsapi.h" +#include "mozilla/dom/HTMLIFrameElement.h" +#include "nsSandboxFlags.h" +#include "mozilla/layers/CompositorBridgeChild.h" + +#include "mozilla/dom/ipc/StructuredCloneData.h" +#include "mozilla/WebBrowserPersistLocalDocument.h" + +#include "nsPrincipal.h" + +#ifdef MOZ_XUL +#include "nsXULPopupManager.h" +#endif + +#ifdef NS_PRINTING +#include "mozilla/embedding/printingui/PrintingParent.h" +#include "nsIWebBrowserPrint.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; + +// 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, + mOpener, + mPartialSessionHistory, + mGroupedSessionHistory) +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_ENTRY(nsIWebBrowserPersistable) +NS_INTERFACE_MAP_END + +nsFrameLoader::nsFrameLoader(Element* aOwner, nsPIDOMWindowOuter* aOpener, bool aNetworkCreated) + : mOwnerContent(aOwner) + , mDetachedSubdocFrame(nullptr) + , mOpener(aOpener) + , mRemoteBrowser(nullptr) + , mChildID(0) + , mEventMode(EVENT_MODE_NORMAL_DISPATCH) + , mIsPrerendered(false) + , mDepthTooGreat(false) + , mIsTopLevelContent(false) + , mDestroyCalled(false) + , mNeedsAsyncDestroy(false) + , mInSwap(false) + , mInShow(false) + , mHideCalled(false) + , mNetworkCreated(aNetworkCreated) + , mRemoteBrowserShown(false) + , mRemoteFrame(false) + , mClipSubdocument(true) + , mClampScrollPosition(true) + , mObservingOwnerContent(false) + , mVisible(true) + , mFreshProcess(false) +{ + mRemoteFrame = ShouldUseRemoteProcess(); + MOZ_ASSERT(!mRemoteFrame || !aOpener, + "Cannot pass aOpener for a remote frame!"); + + // Check if we are supposed to load into a fresh process + mFreshProcess = mOwnerContent->AttrValueIs(kNameSpaceID_None, + nsGkAtoms::freshProcess, + nsGkAtoms::_true, + eCaseMatters); +} + +nsFrameLoader::~nsFrameLoader() +{ + if (mMessageManager) { + mMessageManager->Disconnect(); + } + MOZ_RELEASE_ASSERT(mDestroyCalled); +} + +nsFrameLoader* +nsFrameLoader::Create(Element* aOwner, nsPIDOMWindowOuter* aOpener, bool aNetworkCreated) +{ + NS_ENSURE_TRUE(aOwner, nullptr); + nsIDocument* doc = aOwner->OwnerDoc(); + + // We never create nsFrameLoaders for elements in resource documents. + // + // We never create nsFrameLoaders for elements in data documents, unless the + // document is a static document. + // Static documents are an exception because any sub-documents need an + // nsFrameLoader to keep the relevant docShell alive, even though the + // nsFrameLoader isn't used to load anything (the sub-document is created by + // the static clone process). + // + // We never create nsFrameLoaders for elements that are not + // in-composed-document, unless the element belongs to a static document. + // Static documents are an exception because this method is called at a point + // in the static clone process before aOwner has been inserted into its + // document. For other types of documents this wouldn't be a problem since + // we'd create the nsFrameLoader as necessary after aOwner is inserted into a + // document, but the mechanisms that take care of that don't apply for static + // documents so we need to create the nsFrameLoader now. (This isn't wasteful + // since for a static document we know aOwner will end up in a document and + // the nsFrameLoader will be used for its docShell.) + // + NS_ENSURE_TRUE(!doc->IsResourceDoc() && + ((!doc->IsLoadedAsData() && aOwner->IsInComposedDoc()) || + doc->IsStaticDocument()), + nullptr); + + return new nsFrameLoader(aOwner, aOpener, aNetworkCreated); +} + +NS_IMETHODIMP +nsFrameLoader::LoadFrame() +{ + NS_ENSURE_TRUE(mOwnerContent, NS_ERROR_NOT_INITIALIZED); + + nsAutoString src; + + bool isSrcdoc = mOwnerContent->IsHTMLElement(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->IsXULElement() && + 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; + } + + if (doc->IsLoadedAsInteractiveData()) { + // XBL bindings doc shouldn't load sub-documents. + return NS_OK; + } + + nsCOMPtr<nsIURI> base_uri = mOwnerContent->GetBaseURI(); + const nsAFlatCString &doc_charset = doc->GetDocumentCharacterSet(); + const char *charset = doc_charset.IsEmpty() ? nullptr : doc_charset.get(); + + nsCOMPtr<nsIURI> 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; + } + RefPtr<AsyncEventDispatcher > 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<nsIDocument> 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; +} + +NS_IMETHODIMP +nsFrameLoader::SetIsPrerendered() +{ + MOZ_ASSERT(!mDocShell, "Please call SetIsPrerendered before docShell is created"); + mIsPrerendered = true; + + return NS_OK; +} + +NS_IMETHODIMP +nsFrameLoader::MakePrerenderedLoaderActive() +{ + MOZ_ASSERT(mIsPrerendered, "This frameloader was not in prerendered mode."); + + mIsPrerendered = false; + if (IsRemoteFrame()) { + if (!mRemoteBrowser) { + NS_WARNING("Missing remote browser."); + return NS_ERROR_FAILURE; + } + + mRemoteBrowser->SetDocShellIsActive(true); + } else { + if (!mDocShell) { + NS_WARNING("Missing docshell."); + return NS_ERROR_FAILURE; + } + + nsresult rv = mDocShell->SetIsActive(true); + NS_ENSURE_SUCCESS(rv, rv); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsFrameLoader::GetPartialSessionHistory(nsIPartialSHistory** aResult) +{ + if (mRemoteBrowser && !mPartialSessionHistory) { + // For remote case we can lazy initialize PartialSHistory since + // it doens't need to be registered as a listener to nsISHistory directly. + mPartialSessionHistory = new PartialSHistory(this); + } + + nsCOMPtr<nsIPartialSHistory> partialHistory(mPartialSessionHistory); + partialHistory.forget(aResult); + return NS_OK; +} + + +NS_IMETHODIMP +nsFrameLoader::GetGroupedSessionHistory(nsIGroupedSHistory** aResult) +{ + nsCOMPtr<nsIGroupedSHistory> groupedHistory(mGroupedSessionHistory); + groupedHistory.forget(aResult); + return NS_OK; +} + +NS_IMETHODIMP +nsFrameLoader::AppendPartialSessionHistoryAndSwap(nsIFrameLoader* aOther) +{ + if (!aOther) { + return NS_ERROR_INVALID_POINTER; + } + + nsCOMPtr<nsIGroupedSHistory> otherGroupedHistory; + aOther->GetGroupedSessionHistory(getter_AddRefs(otherGroupedHistory)); + MOZ_ASSERT(!otherGroupedHistory, + "Cannot append a GroupedSHistory owner to another."); + if (otherGroupedHistory) { + return NS_ERROR_UNEXPECTED; + } + + // Append ourselves. + nsresult rv; + if (!mGroupedSessionHistory) { + mGroupedSessionHistory = new GroupedSHistory(); + rv = mGroupedSessionHistory->AppendPartialSessionHistory(mPartialSessionHistory); + if (NS_FAILED(rv)) { + return NS_ERROR_FAILURE; + } + } + + if (aOther == this) { + return NS_OK; + } + + // Append the other. + RefPtr<nsFrameLoader> otherLoader = static_cast<nsFrameLoader*>(aOther); + rv = mGroupedSessionHistory-> + AppendPartialSessionHistory(otherLoader->mPartialSessionHistory); + if (NS_FAILED(rv)) { + return NS_ERROR_FAILURE; + } + + // Swap loaders through our owner, so the owner's listeners will be correctly + // setup. + nsCOMPtr<nsIBrowser> ourBrowser = do_QueryInterface(mOwnerContent); + nsCOMPtr<nsIBrowser> otherBrowser = do_QueryInterface(otherLoader->mOwnerContent); + if (!ourBrowser || !otherBrowser) { + return NS_ERROR_FAILURE; + } + if (NS_FAILED(ourBrowser->SwapBrowsers(otherBrowser))) { + return NS_ERROR_FAILURE; + } + mGroupedSessionHistory.swap(otherLoader->mGroupedSessionHistory); + + return NS_OK; +} + +NS_IMETHODIMP +nsFrameLoader::RequestGroupedHistoryNavigation(uint32_t aGlobalIndex) +{ + if (!mGroupedSessionHistory) { + return NS_ERROR_UNEXPECTED; + } + + nsCOMPtr<nsIFrameLoader> targetLoader; + nsresult rv = mGroupedSessionHistory-> + GotoIndex(aGlobalIndex, getter_AddRefs(targetLoader)); + if (NS_FAILED(rv)) { + return NS_ERROR_FAILURE; + } + + RefPtr<nsFrameLoader> otherLoader = static_cast<nsFrameLoader*>(targetLoader.get()); + if (!targetLoader) { + return NS_ERROR_FAILURE; + } + + if (targetLoader == this) { + return NS_OK; + } + + nsCOMPtr<nsIBrowser> ourBrowser = do_QueryInterface(mOwnerContent); + nsCOMPtr<nsIBrowser> otherBrowser = do_QueryInterface(otherLoader->mOwnerContent); + if (!ourBrowser || !otherBrowser) { + return NS_ERROR_FAILURE; + } + if (NS_FAILED(ourBrowser->SwapBrowsers(otherBrowser))) { + return NS_ERROR_FAILURE; + } + mGroupedSessionHistory.swap(otherLoader->mGroupedSessionHistory); + + return NS_OK; +} + +nsresult +nsFrameLoader::ReallyStartLoading() +{ + nsresult rv = ReallyStartLoadingInternal(); + if (NS_FAILED(rv)) { + FireErrorEvent(); + } + + return rv; +} + +nsresult +nsFrameLoader::ReallyStartLoadingInternal() +{ + NS_ENSURE_STATE(mURIToLoad && mOwnerContent && mOwnerContent->IsInComposedDoc()); + + PROFILER_LABEL("nsFrameLoader", "ReallyStartLoading", + js::ProfileEntry::Category::OTHER); + + if (IsRemoteFrame()) { + if (!mRemoteBrowser && !TryRemoteBrowser()) { + NS_WARNING("Couldn't create child process for iframe."); + return NS_ERROR_FAILURE; + } + + // FIXME get error codes from child + mRemoteBrowser->LoadURL(mURIToLoad); + + if (!mRemoteBrowserShown && !ShowRemoteFrame(ScreenIntSize(0, 0))) { + NS_WARNING("[nsFrameLoader] ReallyStartLoadingInternal tried but couldn't show remote browser.\n"); + } + + return NS_OK; + } + + nsresult rv = MaybeCreateDocShell(); + if (NS_FAILED(rv)) { + return rv; + } + NS_ASSERTION(mDocShell, + "MaybeCreateDocShell succeeded with a null mDocShell"); + + // Just to be safe, recheck uri. + rv = CheckURILoad(mURIToLoad); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIDocShellLoadInfo> 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->SetTriggeringPrincipal(mOwnerContent->NodePrincipal()); + + nsCOMPtr<nsIURI> referrer; + + nsAutoString srcdoc; + bool isSrcdoc = mOwnerContent->IsHTMLElement(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<nsIURI> 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); + } + } + + // get referrer policy for this iframe: + // first load document wide policy, then + // load iframe referrer attribute if enabled in preferences + // per element referrer overrules document wide referrer if enabled + net::ReferrerPolicy referrerPolicy = mOwnerContent->OwnerDoc()->GetReferrerPolicy(); + HTMLIFrameElement* iframe = HTMLIFrameElement::FromContent(mOwnerContent); + if (iframe) { + net::ReferrerPolicy iframeReferrerPolicy = iframe->GetReferrerPolicyAsEnum(); + if (iframeReferrerPolicy != net::RP_Unset) { + referrerPolicy = iframeReferrerPolicy; + } + } + loadInfo->SetReferrerPolicy(referrerPolicy); + + // Default flags: + int32_t flags = nsIWebNavigation::LOAD_FLAGS_NONE; + + // Flags for browser frame: + if (OwnerIsMozBrowserFrame()) { + flags = nsIWebNavigation::LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP | + nsIWebNavigation::LOAD_FLAGS_DISALLOW_INHERIT_PRINCIPAL; + } + + // Kick off the load... + bool tmpState = mNeedsAsyncDestroy; + mNeedsAsyncDestroy = true; + nsCOMPtr<nsIURI> 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 + if (IsRemoteFrame()) { + return NS_OK; + } + return CheckForRecursiveLoad(aURI); +} + +NS_IMETHODIMP +nsFrameLoader::GetDocShell(nsIDocShell **aDocShell) +{ + *aDocShell = nullptr; + nsresult rv = NS_OK; + + if (IsRemoteFrame()) { + return rv; + } + + // 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; + } + NS_ASSERTION(mDocShell, + "MaybeCreateDocShell succeeded, but null mDocShell"); + } + + *aDocShell = mDocShell; + NS_IF_ADDREF(*aDocShell); + + return rv; +} + +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<nsIDocShellTreeItem> item; + aItem->GetChildAt(i, getter_AddRefs(item)); + if (aHandler) { + nsCOMPtr<nsIDocShell> 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<nsIDOMMozBrowserFrame> 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<nsIDocShellTreeItem> 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_RAII 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; + + ScreenIntSize size = frame->GetSubdocumentSize(); + if (IsRemoteFrame()) { + return ShowRemoteFrame(size, frame); + } + + nsresult rv = MaybeCreateDocShell(); + if (NS_FAILED(rv)) { + return false; + } + NS_ASSERTION(mDocShell, + "MaybeCreateDocShell succeeded, but null mDocShell"); + if (!mDocShell) { + return false; + } + + mDocShell->SetMarginWidth(marginWidth); + mDocShell->SetMarginHeight(marginHeight); + + nsCOMPtr<nsIScrollable> sc = do_QueryInterface(mDocShell); + if (sc) { + sc->SetDefaultScrollbarPreferences(nsIScrollable::ScrollOrientation_X, + scrollbarPrefX); + sc->SetDefaultScrollbarPreferences(nsIScrollable::ScrollOrientation_Y, + scrollbarPrefY); + } + + nsCOMPtr<nsIPresShell> 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; + } + + nsView* view = frame->EnsureInnerView(); + if (!view) + return false; + + nsCOMPtr<nsIBaseWindow> 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 + presShell = mDocShell->GetPresShell(); + if (presShell) { + nsCOMPtr<nsIDOMHTMLDocument> 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<nsIEditor> 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<nsIEditor> 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 (IsRemoteFrame()) + 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. + RefPtr<nsPresContext> presContext; + mDocShell->GetPresContext(getter_AddRefs(presContext)); + if (presContext) + presContext->RebuildAllStyleData(nsChangeHint(0), eRestyle_Subtree); +} + +bool +nsFrameLoader::ShowRemoteFrame(const ScreenIntSize& size, + nsSubDocumentFrame *aFrame) +{ + PROFILER_LABEL_FUNC(js::ProfileEntry::Category::GRAPHICS); + NS_ASSERTION(IsRemoteFrame(), "ShowRemote only makes sense on remote frames."); + + if (!mRemoteBrowser && !TryRemoteBrowser()) { + 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->GetComposedDoc()) { + return false; + } + + RefPtr<layers::LayerManager> layerManager = + nsContentUtils::LayerManagerForDocument(mOwnerContent->GetComposedDoc()); + if (!layerManager) { + // This is just not going to work. + return false; + } + + nsPIDOMWindowOuter* win = mOwnerContent->OwnerDoc()->GetWindow(); + bool parentIsActive = false; + if (win) { + nsCOMPtr<nsPIWindowRoot> windowRoot = + nsGlobalWindow::Cast(win)->GetTopWindowRoot(); + if (windowRoot) { + nsPIDOMWindowOuter* topWin = windowRoot->GetWindow(); + parentIsActive = topWin && topWin->IsActive(); + } + } + mRemoteBrowser->Show(size, parentIsActive); + mRemoteBrowserShown = true; + + nsCOMPtr<nsIObserverService> os = services::GetObserverService(); + if (os) { + os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this), + "remote-browser-shown", nullptr); + } + } 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<nsIContentViewer> contentViewer; + mDocShell->GetContentViewer(getter_AddRefs(contentViewer)); + if (contentViewer) + contentViewer->SetSticky(false); + + nsCOMPtr<nsIBaseWindow> 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, + nsIFrameLoaderOwner* aThisOwner, + nsIFrameLoaderOwner* aOtherOwner) +{ + MOZ_ASSERT(NS_IsMainThread()); + +#ifdef DEBUG + RefPtr<nsFrameLoader> first = aThisOwner->GetFrameLoader(); + RefPtr<nsFrameLoader> second = aOtherOwner->GetFrameLoader(); + MOZ_ASSERT(first == this, "aThisOwner must own this"); + MOZ_ASSERT(second == aOther, "aOtherOwner must own aOther"); +#endif + + 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->GetComposedDoc(); + nsIDocument* otherDoc = otherContent->GetComposedDoc(); + 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 (!mRemoteBrowser || !aOther->mRemoteBrowser) { + return NS_ERROR_NOT_IMPLEMENTED; + } + + if (mRemoteBrowser->IsIsolatedMozBrowserElement() != + aOther->mRemoteBrowser->IsIsolatedMozBrowserElement() || + mRemoteBrowser->HasOwnApp() != aOther->mRemoteBrowser->HasOwnApp()) { + return NS_ERROR_NOT_IMPLEMENTED; + } + + // When we swap docShells, maybe we have to deal with a new page created just + // for this operation. In this case, the browser code should already have set + // the correct userContextId attribute value in the owning XULElement, but our + // docShell, that has been created way before) doesn't know that that + // happened. + // This is the reason why now we must retrieve the correct value from the + // usercontextid attribute before comparing our originAttributes with the + // other one. + DocShellOriginAttributes ourOriginAttributes = + mRemoteBrowser->OriginAttributesRef(); + rv = PopulateUserContextIdFromAttribute(ourOriginAttributes); + NS_ENSURE_SUCCESS(rv,rv); + + DocShellOriginAttributes otherOriginAttributes = + aOther->mRemoteBrowser->OriginAttributesRef(); + rv = aOther->PopulateUserContextIdFromAttribute(otherOriginAttributes); + NS_ENSURE_SUCCESS(rv,rv); + + if (ourOriginAttributes != otherOriginAttributes) { + return NS_ERROR_NOT_IMPLEMENTED; + } + + bool ourHasHistory = + mIsTopLevelContent && + ourContent->IsXULElement(nsGkAtoms::browser) && + !ourContent->HasAttr(kNameSpaceID_None, nsGkAtoms::disablehistory); + bool otherHasHistory = + aOther->mIsTopLevelContent && + otherContent->IsXULElement(nsGkAtoms::browser) && + !otherContent->HasAttr(kNameSpaceID_None, nsGkAtoms::disablehistory); + if (ourHasHistory != otherHasHistory) { + 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; + } + + nsCOMPtr<nsIBrowserDOMWindow> otherBrowserDOMWindow = + aOther->mRemoteBrowser->GetBrowserDOMWindow(); + nsCOMPtr<nsIBrowserDOMWindow> browserDOMWindow = + mRemoteBrowser->GetBrowserDOMWindow(); + + if (!!otherBrowserDOMWindow != !!browserDOMWindow) { + return NS_ERROR_NOT_IMPLEMENTED; + } + + // Destroy browser frame scripts for content leaving a frame with browser API + if (OwnerIsMozBrowserOrAppFrame() && !aOther->OwnerIsMozBrowserOrAppFrame()) { + DestroyBrowserFrameScripts(); + } + if (!OwnerIsMozBrowserOrAppFrame() && aOther->OwnerIsMozBrowserOrAppFrame()) { + aOther->DestroyBrowserFrameScripts(); + } + + aOther->mRemoteBrowser->SetBrowserDOMWindow(browserDOMWindow); + mRemoteBrowser->SetBrowserDOMWindow(otherBrowserDOMWindow); + + // Native plugin windows used by this remote content need to be reparented. + if (nsPIDOMWindowOuter* newWin = ourDoc->GetWindow()) { + RefPtr<nsIWidget> newParent = nsGlobalWindow::Cast(newWin)->GetMainWidget(); + const ManagedContainer<mozilla::plugins::PPluginWidgetParent>& plugins = + aOther->mRemoteBrowser->ManagedPPluginWidgetParent(); + for (auto iter = plugins.ConstIter(); !iter.Done(); iter.Next()) { + static_cast<mozilla::plugins::PluginWidgetParent*>(iter.Get()->GetKey())->SetParent(newParent); + } + } + + MaybeUpdatePrimaryTabParent(eTabParentRemoved); + aOther->MaybeUpdatePrimaryTabParent(eTabParentRemoved); + + SetOwnerContent(otherContent); + aOther->SetOwnerContent(ourContent); + + mRemoteBrowser->SetOwnerElement(otherContent); + aOther->mRemoteBrowser->SetOwnerElement(ourContent); + + MaybeUpdatePrimaryTabParent(eTabParentChanged); + aOther->MaybeUpdatePrimaryTabParent(eTabParentChanged); + + RefPtr<nsFrameMessageManager> ourMessageManager = mMessageManager; + RefPtr<nsFrameMessageManager> otherMessageManager = aOther->mMessageManager; + // Swap and setup things in parent message managers. + if (ourMessageManager) { + ourMessageManager->SetCallback(aOther); + } + if (otherMessageManager) { + otherMessageManager->SetCallback(this); + } + mMessageManager.swap(aOther->mMessageManager); + + // Perform the actual swap of the internal refptrs. We keep a strong reference + // to ourselves to make sure we don't die while we overwrite our reference to + // ourself. + nsCOMPtr<nsIFrameLoader> kungFuDeathGrip(this); + aThisOwner->InternalSetFrameLoader(aOther); + aOtherOwner->InternalSetFrameLoader(kungFuDeathGrip); + + ourFrameFrame->EndSwapDocShells(otherFrame); + + ourShell->BackingScaleFactorChanged(); + otherShell->BackingScaleFactorChanged(); + + ourDoc->FlushPendingNotifications(Flush_Layout); + otherDoc->FlushPendingNotifications(Flush_Layout); + + // Initialize browser API if needed now that owner content has changed. + InitializeBrowserAPI(); + aOther->InitializeBrowserAPI(); + + mInSwap = aOther->mInSwap = false; + + // Send an updated tab context since owner content type may have changed. + MutableTabContext ourContext; + rv = GetNewTabContext(&ourContext); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + MutableTabContext otherContext; + rv = aOther->GetNewTabContext(&otherContext); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + Unused << mRemoteBrowser->SendSwappedWithOtherRemoteLoader( + ourContext.AsIPCTabContext()); + Unused << aOther->mRemoteBrowser->SendSwappedWithOtherRemoteLoader( + otherContext.AsIPCTabContext()); + return NS_OK; +} + +class MOZ_RAII AutoResetInFrameSwap final +{ +public: + AutoResetInFrameSwap(nsFrameLoader* aThisFrameLoader, + nsFrameLoader* aOtherFrameLoader, + nsDocShell* aThisDocShell, + nsDocShell* aOtherDocShell, + EventTarget* aThisEventTarget, + EventTarget* aOtherEventTarget + MOZ_GUARD_OBJECT_NOTIFIER_PARAM) + : mThisFrameLoader(aThisFrameLoader) + , mOtherFrameLoader(aOtherFrameLoader) + , mThisDocShell(aThisDocShell) + , mOtherDocShell(aOtherDocShell) + , mThisEventTarget(aThisEventTarget) + , mOtherEventTarget(aOtherEventTarget) + { + MOZ_GUARD_OBJECT_NOTIFIER_INIT; + + mThisFrameLoader->mInSwap = true; + mOtherFrameLoader->mInSwap = true; + mThisDocShell->SetInFrameSwap(true); + mOtherDocShell->SetInFrameSwap(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. + nsContentUtils::FirePageShowEvent(mThisDocShell, mThisEventTarget, false); + nsContentUtils::FirePageShowEvent(mOtherDocShell, mOtherEventTarget, false); + nsContentUtils::FirePageHideEvent(mThisDocShell, mThisEventTarget); + nsContentUtils::FirePageHideEvent(mOtherDocShell, mOtherEventTarget); + } + + ~AutoResetInFrameSwap() + { + nsContentUtils::FirePageShowEvent(mThisDocShell, mThisEventTarget, true); + nsContentUtils::FirePageShowEvent(mOtherDocShell, mOtherEventTarget, true); + + mThisFrameLoader->mInSwap = false; + mOtherFrameLoader->mInSwap = false; + mThisDocShell->SetInFrameSwap(false); + mOtherDocShell->SetInFrameSwap(false); + } + +private: + RefPtr<nsFrameLoader> mThisFrameLoader; + RefPtr<nsFrameLoader> mOtherFrameLoader; + RefPtr<nsDocShell> mThisDocShell; + RefPtr<nsDocShell> mOtherDocShell; + nsCOMPtr<EventTarget> mThisEventTarget; + nsCOMPtr<EventTarget> mOtherEventTarget; + MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER +}; + +nsresult +nsFrameLoader::SwapWithOtherLoader(nsFrameLoader* aOther, + nsIFrameLoaderOwner* aThisOwner, + nsIFrameLoaderOwner* aOtherOwner) +{ +#ifdef DEBUG + RefPtr<nsFrameLoader> first = aThisOwner->GetFrameLoader(); + RefPtr<nsFrameLoader> second = aOtherOwner->GetFrameLoader(); + MOZ_ASSERT(first == this, "aThisOwner must own this"); + MOZ_ASSERT(second == aOther, "aOtherOwner must own aOther"); +#endif + + NS_ENSURE_STATE(!mInShow && !aOther->mInShow); + + if (IsRemoteFrame() != aOther->IsRemoteFrame()) { + 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; + } + + bool ourHasSrcdoc = ourContent->IsHTMLElement(nsGkAtoms::iframe) && + ourContent->HasAttr(kNameSpaceID_None, nsGkAtoms::srcdoc); + bool otherHasSrcdoc = otherContent->IsHTMLElement(nsGkAtoms::iframe) && + otherContent->HasAttr(kNameSpaceID_None, nsGkAtoms::srcdoc); + if (ourHasSrcdoc || otherHasSrcdoc) { + // Ignore this case entirely for now, since we support XUL <-> HTML swapping + return NS_ERROR_NOT_IMPLEMENTED; + } + + bool ourFullscreenAllowed = + ourContent->IsXULElement() || + (OwnerIsMozBrowserOrAppFrame() && + (ourContent->HasAttr(kNameSpaceID_None, nsGkAtoms::allowfullscreen) || + ourContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozallowfullscreen))); + bool otherFullscreenAllowed = + otherContent->IsXULElement() || + (aOther->OwnerIsMozBrowserOrAppFrame() && + (otherContent->HasAttr(kNameSpaceID_None, nsGkAtoms::allowfullscreen) || + otherContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozallowfullscreen))); + if (ourFullscreenAllowed != otherFullscreenAllowed) { + return NS_ERROR_NOT_IMPLEMENTED; + } + + // Divert to a separate path for the remaining steps in the remote case + if (IsRemoteFrame()) { + MOZ_ASSERT(aOther->IsRemoteFrame()); + return SwapWithOtherRemoteLoader(aOther, aThisOwner, aOtherOwner); + } + + // 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; + } + + RefPtr<nsDocShell> ourDocshell = static_cast<nsDocShell*>(GetExistingDocShell()); + RefPtr<nsDocShell> otherDocshell = static_cast<nsDocShell*>(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<nsIDocShellTreeItem> ourRootTreeItem, otherRootTreeItem; + ourDocshell->GetSameTypeRootTreeItem(getter_AddRefs(ourRootTreeItem)); + otherDocshell->GetSameTypeRootTreeItem(getter_AddRefs(otherRootTreeItem)); + nsCOMPtr<nsIWebNavigation> ourRootWebnav = + do_QueryInterface(ourRootTreeItem); + nsCOMPtr<nsIWebNavigation> otherRootWebnav = + do_QueryInterface(otherRootTreeItem); + + if (!ourRootWebnav || !otherRootWebnav) { + return NS_ERROR_NOT_IMPLEMENTED; + } + + nsCOMPtr<nsISHistory> ourHistory; + nsCOMPtr<nsISHistory> 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<nsIDocShellTreeOwner> ourOwner, otherOwner; + ourDocshell->GetTreeOwner(getter_AddRefs(ourOwner)); + otherDocshell->GetTreeOwner(getter_AddRefs(otherOwner)); + // Note: it's OK to have null treeowners. + + nsCOMPtr<nsIDocShellTreeItem> 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<nsPIDOMWindowOuter> ourWindow = ourDocshell->GetWindow(); + nsCOMPtr<nsPIDOMWindowOuter> otherWindow = otherDocshell->GetWindow(); + + nsCOMPtr<Element> ourFrameElement = + ourWindow->GetFrameElementInternal(); + nsCOMPtr<Element> otherFrameElement = + otherWindow->GetFrameElementInternal(); + + nsCOMPtr<EventTarget> ourChromeEventHandler = + do_QueryInterface(ourWindow->GetChromeEventHandler()); + nsCOMPtr<EventTarget> otherChromeEventHandler = + do_QueryInterface(otherWindow->GetChromeEventHandler()); + + nsCOMPtr<EventTarget> ourEventTarget = ourWindow->GetParentTarget(); + nsCOMPtr<EventTarget> otherEventTarget = otherWindow->GetParentTarget(); + + NS_ASSERTION(SameCOMIdentity(ourFrameElement, ourContent) && + SameCOMIdentity(otherFrameElement, otherContent) && + SameCOMIdentity(ourChromeEventHandler, ourContent) && + SameCOMIdentity(otherChromeEventHandler, otherContent), + "How did that happen, exactly?"); + + nsCOMPtr<nsIDocument> ourChildDocument = ourWindow->GetExtantDoc(); + nsCOMPtr<nsIDocument> otherChildDocument = otherWindow ->GetExtantDoc(); + if (!ourChildDocument || !otherChildDocument) { + // This shouldn't be happening + return NS_ERROR_NOT_IMPLEMENTED; + } + + nsCOMPtr<nsIDocument> ourParentDocument = + ourChildDocument->GetParentDocument(); + nsCOMPtr<nsIDocument> otherParentDocument = + otherChildDocument->GetParentDocument(); + + // Make sure to swap docshells between the two frames. + nsIDocument* ourDoc = ourContent->GetComposedDoc(); + nsIDocument* otherDoc = otherContent->GetComposedDoc(); + 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->GetIsIsolatedMozBrowserElement() != + otherDocshell->GetIsIsolatedMozBrowserElement() || + ourDocshell->GetIsApp() != otherDocshell->GetIsApp()) { + return NS_ERROR_NOT_IMPLEMENTED; + } + + // When we swap docShells, maybe we have to deal with a new page created just + // for this operation. In this case, the browser code should already have set + // the correct userContextId attribute value in the owning XULElement, but our + // docShell, that has been created way before) doesn't know that that + // happened. + // This is the reason why now we must retrieve the correct value from the + // usercontextid attribute before comparing our originAttributes with the + // other one. + DocShellOriginAttributes ourOriginAttributes = + ourDocshell->GetOriginAttributes(); + rv = PopulateUserContextIdFromAttribute(ourOriginAttributes); + NS_ENSURE_SUCCESS(rv,rv); + + DocShellOriginAttributes otherOriginAttributes = + otherDocshell->GetOriginAttributes(); + rv = aOther->PopulateUserContextIdFromAttribute(otherOriginAttributes); + NS_ENSURE_SUCCESS(rv,rv); + + if (ourOriginAttributes != otherOriginAttributes) { + return NS_ERROR_NOT_IMPLEMENTED; + } + + if (mInSwap || aOther->mInSwap) { + return NS_ERROR_NOT_IMPLEMENTED; + } + AutoResetInFrameSwap autoFrameSwap(this, aOther, ourDocshell, otherDocshell, + ourEventTarget, otherEventTarget); + + nsIFrame* ourFrame = ourContent->GetPrimaryFrame(); + nsIFrame* otherFrame = otherContent->GetPrimaryFrame(); + if (!ourFrame || !otherFrame) { + return NS_ERROR_NOT_IMPLEMENTED; + } + + nsSubDocumentFrame* ourFrameFrame = do_QueryFrame(ourFrame); + if (!ourFrameFrame) { + return NS_ERROR_NOT_IMPLEMENTED; + } + + // OK. First begin to swap the docshells in the two nsIFrames + rv = ourFrameFrame->BeginSwapDocShells(otherFrame); + if (NS_FAILED(rv)) { + return rv; + } + + // Destroy browser frame scripts for content leaving a frame with browser API + if (OwnerIsMozBrowserOrAppFrame() && !aOther->OwnerIsMozBrowserOrAppFrame()) { + DestroyBrowserFrameScripts(); + } + if (!OwnerIsMozBrowserOrAppFrame() && aOther->OwnerIsMozBrowserOrAppFrame()) { + aOther->DestroyBrowserFrameScripts(); + } + + // 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.get() : nullptr); + SetTreeOwnerAndChromeEventHandlerOnDocshellTree(otherDocshell, ourOwner, + ourType == nsIDocShellTreeItem::typeContent ? ourChromeEventHandler.get() : 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); + + RefPtr<nsFrameMessageManager> ourMessageManager = mMessageManager; + RefPtr<nsFrameMessageManager> otherMessageManager = aOther->mMessageManager; + // Swap pointers in child message managers. + if (mChildMessageManager) { + nsInProcessTabChildGlobal* tabChild = + static_cast<nsInProcessTabChildGlobal*>(mChildMessageManager.get()); + tabChild->SetOwner(otherContent); + tabChild->SetChromeMessageManager(otherMessageManager); + } + if (aOther->mChildMessageManager) { + nsInProcessTabChildGlobal* otherTabChild = + static_cast<nsInProcessTabChildGlobal*>(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); + + // Perform the actual swap of the internal refptrs. We keep a strong reference + // to ourselves to make sure we don't die while we overwrite our reference to + // ourself. + nsCOMPtr<nsIFrameLoader> kungFuDeathGrip(this); + aThisOwner->InternalSetFrameLoader(aOther); + aOtherOwner->InternalSetFrameLoader(kungFuDeathGrip); + + // Drop any cached content viewers in the two session histories. + nsCOMPtr<nsISHistoryInternal> ourInternalHistory = + do_QueryInterface(ourHistory); + nsCOMPtr<nsISHistoryInternal> 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); + + // Initialize browser API if needed now that owner content has changed + InitializeBrowserAPI(); + aOther->InitializeBrowserAPI(); + + return NS_OK; +} + +NS_IMETHODIMP +nsFrameLoader::Destroy() +{ + StartDestroy(); + return NS_OK; +} + +class nsFrameLoaderDestroyRunnable : public Runnable +{ + enum DestroyPhase + { + // See the implementation of Run for an explanation of these phases. + eDestroyDocShell, + eWaitForUnloadMessage, + eDestroyComplete + }; + + RefPtr<nsFrameLoader> mFrameLoader; + DestroyPhase mPhase; + +public: + explicit nsFrameLoaderDestroyRunnable(nsFrameLoader* aFrameLoader) + : mFrameLoader(aFrameLoader), mPhase(eDestroyDocShell) {} + + NS_IMETHOD Run() override; +}; + +void +nsFrameLoader::StartDestroy() +{ + // nsFrameLoader::StartDestroy is called just before the frameloader is + // detached from the <browser> element. Destruction continues in phases via + // the nsFrameLoaderDestroyRunnable. + + if (mDestroyCalled) { + return; + } + mDestroyCalled = true; + + // After this point, we return an error when trying to send a message using + // the message manager on the frame. + if (mMessageManager) { + mMessageManager->Close(); + } + + // Retain references to the <browser> element and the frameloader in case we + // receive any messages from the message manager on the frame. These + // references are dropped in DestroyComplete. + if (mChildMessageManager || mRemoteBrowser) { + mOwnerContentStrong = mOwnerContent; + if (mRemoteBrowser) { + mRemoteBrowser->CacheFrameLoader(this); + } + if (mChildMessageManager) { + mChildMessageManager->CacheFrameLoader(this); + } + } + + // If the TabParent has installed any event listeners on the window, this is + // its last chance to remove them while we're still in the document. + if (mRemoteBrowser) { + mRemoteBrowser->RemoveWindowListeners(); + } + + nsCOMPtr<nsIDocument> doc; + bool dynamicSubframeRemoval = false; + if (mOwnerContent) { + doc = mOwnerContent->OwnerDoc(); + dynamicSubframeRemoval = !mIsTopLevelContent && !doc->InUnlinkOrDeletion(); + doc->SetSubDocumentFor(mOwnerContent, nullptr); + MaybeUpdatePrimaryTabParent(eTabParentRemoved); + SetOwnerContent(nullptr); + } + + // 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<nsIDocShellTreeItem> parentItem; + mDocShell->GetParent(getter_AddRefs(parentItem)); + nsCOMPtr<nsIDocShellTreeOwner> owner = do_GetInterface(parentItem); + if (owner) { + owner->ContentShellRemoved(mDocShell); + } + } + } + + // Let our window know that we are gone + if (mDocShell) { + nsCOMPtr<nsPIDOMWindowOuter> win_private(mDocShell->GetWindow()); + if (win_private) { + win_private->SetFrameElementInternal(nullptr); + } + } + + nsCOMPtr<nsIRunnable> destroyRunnable = new nsFrameLoaderDestroyRunnable(this); + if (mNeedsAsyncDestroy || !doc || + NS_FAILED(doc->FinalizeFrameLoader(this, destroyRunnable))) { + NS_DispatchToCurrentThread(destroyRunnable); + } +} + +nsresult +nsFrameLoaderDestroyRunnable::Run() +{ + switch (mPhase) { + case eDestroyDocShell: + mFrameLoader->DestroyDocShell(); + + // In the out-of-process case, TabParent will eventually call + // DestroyComplete once it receives a __delete__ message from the child. In + // the in-process case, we dispatch a series of runnables to ensure that + // DestroyComplete gets called at the right time. The frame loader is kept + // alive by mFrameLoader during this time. + if (mFrameLoader->mChildMessageManager) { + // When the docshell is destroyed, NotifyWindowIDDestroyed is called to + // asynchronously notify {outer,inner}-window-destroyed via a runnable. We + // don't want DestroyComplete to run until after those runnables have + // run. Since we're enqueueing ourselves after the window-destroyed + // runnables are enqueued, we're guaranteed to run after. + mPhase = eWaitForUnloadMessage; + NS_DispatchToCurrentThread(this); + } + break; + + case eWaitForUnloadMessage: + // The *-window-destroyed observers have finished running at this + // point. However, it's possible that a *-window-destroyed observer might + // have sent a message using the message manager. These messages might not + // have been processed yet. So we enqueue ourselves again to ensure that + // DestroyComplete runs after all messages sent by *-window-destroyed + // observers have been processed. + mPhase = eDestroyComplete; + NS_DispatchToCurrentThread(this); + break; + + case eDestroyComplete: + // Now that all messages sent by unload listeners and window destroyed + // observers have been processed, we disconnect the message manager and + // finish destruction. + mFrameLoader->DestroyComplete(); + break; + } + + return NS_OK; +} + +void +nsFrameLoader::DestroyDocShell() +{ + // This code runs after the frameloader has been detached from the <browser> + // element. We postpone this work because we may not be allowed to run + // script at that time. + + // Ask the TabChild to fire the frame script "unload" event, destroy its + // docshell, and finally destroy the PBrowser actor. This eventually leads to + // nsFrameLoader::DestroyComplete being called. + if (mRemoteBrowser) { + mRemoteBrowser->Destroy(); + } + + // Fire the "unload" event if we're in-process. + if (mChildMessageManager) { + static_cast<nsInProcessTabChildGlobal*>(mChildMessageManager.get())->FireUnloadEvent(); + } + + // Destroy the docshell. + nsCOMPtr<nsIBaseWindow> base_win(do_QueryInterface(mDocShell)); + if (base_win) { + base_win->Destroy(); + } + mDocShell = nullptr; + + if (mChildMessageManager) { + // Stop handling events in the in-process frame script. + static_cast<nsInProcessTabChildGlobal*>(mChildMessageManager.get())->DisconnectEventListeners(); + } +} + +void +nsFrameLoader::DestroyComplete() +{ + // We get here, as part of StartDestroy, after the docshell has been destroyed + // and all message manager messages sent during docshell destruction have been + // dispatched. We also get here if the child process crashes. In the latter + // case, StartDestroy might not have been called. + + // Drop the strong references created in StartDestroy. + if (mChildMessageManager || mRemoteBrowser) { + mOwnerContentStrong = nullptr; + if (mRemoteBrowser) { + mRemoteBrowser->CacheFrameLoader(nullptr); + } + if (mChildMessageManager) { + mChildMessageManager->CacheFrameLoader(nullptr); + } + } + + // Call TabParent::Destroy if we haven't already (in case of a crash). + if (mRemoteBrowser) { + mRemoteBrowser->SetOwnerElement(nullptr); + mRemoteBrowser->Destroy(); + mRemoteBrowser = nullptr; + } + + if (mMessageManager) { + mMessageManager->Disconnect(); + } + + if (mChildMessageManager) { + static_cast<nsInProcessTabChildGlobal*>(mChildMessageManager.get())->Disconnect(); + } + + mMessageManager = nullptr; + mChildMessageManager = nullptr; +} + +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 = GetCurrentRenderFrame()) { + rfp->OwnerContentChanged(aContent); + } +} + +bool +nsFrameLoader::OwnerIsMozBrowserOrAppFrame() +{ + nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwnerContent); + return browserFrame ? browserFrame->GetReallyIsBrowserOrApp() : false; +} + +// The xpcom getter version +NS_IMETHODIMP +nsFrameLoader::GetOwnerIsMozBrowserOrAppFrame(bool* aResult) +{ + *aResult = OwnerIsMozBrowserOrAppFrame(); + return NS_OK; +} + +bool +nsFrameLoader::OwnerIsAppFrame() +{ + nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwnerContent); + return browserFrame ? browserFrame->GetReallyIsApp() : false; +} + +bool +nsFrameLoader::OwnerIsMozBrowserFrame() +{ + return OwnerIsMozBrowserOrAppFrame() && !OwnerIsAppFrame(); +} + +bool +nsFrameLoader::OwnerIsIsolatedMozBrowserFrame() +{ + nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwnerContent); + if (!browserFrame) { + return false; + } + + if (!OwnerIsMozBrowserFrame()) { + return false; + } + + bool isolated = browserFrame->GetIsolated(); + if (isolated) { + return true; + } + + return false; +} + +void +nsFrameLoader::GetOwnerAppManifestURL(nsAString& aOut) +{ + aOut.Truncate(); + nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwnerContent); + if (browserFrame) { + browserFrame->GetAppManifestURL(aOut); + } +} + +already_AddRefed<mozIApplication> +nsFrameLoader::GetOwnApp() +{ + nsAutoString manifest; + GetOwnerAppManifestURL(manifest); + if (manifest.IsEmpty()) { + return nullptr; + } + + nsCOMPtr<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID); + NS_ENSURE_TRUE(appsService, nullptr); + + nsCOMPtr<mozIApplication> app; + appsService->GetAppByManifestURL(manifest, getter_AddRefs(app)); + + return app.forget(); +} + +already_AddRefed<mozIApplication> +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<nsIAppsService> appsService = do_GetService(APPS_SERVICE_CONTRACTID); + NS_ENSURE_TRUE(appsService, nullptr); + + nsCOMPtr<mozIApplication> 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_IsContentProcess() && + !CompositorBridgeChild::ChildProcessHasCompositorBridge()) { + return false; + } + + if (XRE_IsContentProcess() && + !(PR_GetEnv("MOZ_NESTED_OOP_TABS") || + Preferences::GetBool("dom.ipc.tabs.nested.enabled", false))) { + return false; + } + + // If we're an <iframe mozbrowser> and we don't have a "remote" attribute, + // fall back to the default. + if (OwnerIsMozBrowserOrAppFrame() && + !mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::Remote)) { + + return Preferences::GetBool("dom.ipc.browser_frames.oop_by_default", false); + } + + // Otherwise, we're remote if we have "remote=true" and we're either a + // browser frame or a XUL element. + return (OwnerIsMozBrowserOrAppFrame() || + mOwnerContent->GetNameSpaceID() == kNameSpaceID_XUL) && + mOwnerContent->AttrValueIs(kNameSpaceID_None, + nsGkAtoms::Remote, + nsGkAtoms::_true, + eCaseMatters); +} + +bool +nsFrameLoader::IsRemoteFrame() +{ + if (mRemoteFrame) { + MOZ_ASSERT(!mDocShell, "Found a remote frame with a DocShell"); + return true; + } + return false; +} + +nsresult +nsFrameLoader::MaybeCreateDocShell() +{ + if (mDocShell) { + return NS_OK; + } + if (IsRemoteFrame()) { + return NS_OK; + } + NS_ENSURE_STATE(!mDestroyCalled); + + // Get our parent docshell off the document of mOwnerContent + // XXXbz this is such a total hack.... We really need to have a + // better setup for doing this. + nsIDocument* doc = mOwnerContent->OwnerDoc(); + + MOZ_RELEASE_ASSERT(!doc->IsResourceDoc(), "We shouldn't even exist"); + + if (!(doc->IsStaticDocument() || mOwnerContent->IsInComposedDoc())) { + return NS_ERROR_UNEXPECTED; + } + + if (!doc->IsActive()) { + // Don't allow subframe loads in non-active documents. + // (See bug 610571 comment 5.) + return NS_ERROR_NOT_AVAILABLE; + } + + nsCOMPtr<nsIDocShell> docShell = doc->GetDocShell(); + nsCOMPtr<nsIWebNavigation> parentAsWebNav = do_QueryInterface(docShell); + NS_ENSURE_STATE(parentAsWebNav); + + // Create the docshell... + mDocShell = do_CreateInstance("@mozilla.org/docshell;1"); + NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE); + + if (mIsPrerendered) { + nsresult rv = mDocShell->SetIsPrerendered(); + NS_ENSURE_SUCCESS(rv,rv); + } + + if (!mNetworkCreated) { + if (mDocShell) { + mDocShell->SetCreatedDynamically(true); + } + } + + // Get the frame name and tell the docshell about it. + NS_ENSURE_TRUE(mDocShell, NS_ERROR_FAILURE); + nsAutoString frameName; + + int32_t namespaceID = mOwnerContent->GetNameSpaceID(); + if (namespaceID == kNameSpaceID_XHTML && !mOwnerContent->IsInHTMLDocument()) { + mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::id, frameName); + } else { + mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::name, frameName); + // XXX if no NAME then use ID, after a transition period this will be + // changed so that XUL only uses ID too (bug 254284). + if (frameName.IsEmpty() && namespaceID == kNameSpaceID_XUL) { + mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::id, frameName); + } + } + + if (!frameName.IsEmpty()) { + mDocShell->SetName(frameName); + } + + // Inform our docShell that it has a new child. + // Note: This logic duplicates a lot of logic in + // nsSubDocumentFrame::AttributeChanged. We should fix that. + + int32_t parentType = docShell->ItemType(); + + // XXXbz why is this in content code, exactly? We should handle + // this some other way..... Not sure how yet. + nsCOMPtr<nsIDocShellTreeOwner> parentTreeOwner; + docShell->GetTreeOwner(getter_AddRefs(parentTreeOwner)); + NS_ENSURE_STATE(parentTreeOwner); + mIsTopLevelContent = + AddTreeItemToTreeOwner(mDocShell, parentTreeOwner, parentType, docShell); + + // Make sure all shells have links back to the content element + // in the nearest enclosing chrome shell. + nsCOMPtr<nsIDOMEventTarget> chromeEventHandler; + + if (parentType == nsIDocShellTreeItem::typeChrome) { + // Our parent shell is a chrome shell. It is therefore our nearest + // enclosing chrome shell. + + chromeEventHandler = do_QueryInterface(mOwnerContent); + NS_ASSERTION(chromeEventHandler, + "This mContent should implement this."); + } else { + // Our parent shell is a content shell. Get the chrome event + // handler from it and use that for our shell as well. + + docShell->GetChromeEventHandler(getter_AddRefs(chromeEventHandler)); + } + + mDocShell->SetChromeEventHandler(chromeEventHandler); + + // This is nasty, this code (the mDocShell->GetWindow() below) + // *must* come *after* the above call to + // mDocShell->SetChromeEventHandler() for the global window to get + // the right chrome event handler. + + // Tell the window about the frame that hosts it. + nsCOMPtr<Element> frame_element = mOwnerContent; + NS_ASSERTION(frame_element, "frame loader owner element not a DOM element!"); + + nsCOMPtr<nsPIDOMWindowOuter> win_private(mDocShell->GetWindow()); + nsCOMPtr<nsIBaseWindow> base_win(do_QueryInterface(mDocShell)); + if (win_private) { + win_private->SetFrameElementInternal(frame_element); + + // Set the opener window if we have one provided here + if (mOpener) { + win_private->SetOpenerWindow(mOpener, true); + mOpener = nullptr; + } + } + + // This is kinda whacky, this call doesn't really create anything, + // but it must be called to make sure things are properly + // initialized. + if (NS_FAILED(base_win->Create()) || !win_private) { + // Do not call Destroy() here. See bug 472312. + NS_WARNING("Something wrong when creating the docshell for a frameloader!"); + return NS_ERROR_FAILURE; + } + + if (mIsTopLevelContent && + mOwnerContent->IsXULElement(nsGkAtoms::browser) && + !mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::disablehistory)) { + nsresult rv; + nsCOMPtr<nsISHistory> sessionHistory = + do_CreateInstance(NS_SHISTORY_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(mDocShell)); + webNav->SetSessionHistory(sessionHistory); + + + if (GroupedSHistory::GroupedHistoryEnabled()) { + mPartialSessionHistory = new PartialSHistory(this); + nsCOMPtr<nsISHistoryListener> listener(do_QueryInterface(mPartialSessionHistory)); + nsCOMPtr<nsIPartialSHistoryListener> partialListener(do_QueryInterface(mPartialSessionHistory)); + sessionHistory->AddSHistoryListener(listener); + sessionHistory->SetPartialSHistoryListener(partialListener); + } + } + + DocShellOriginAttributes attrs; + if (docShell->ItemType() == mDocShell->ItemType()) { + attrs = nsDocShell::Cast(docShell)->GetOriginAttributes(); + } + + // Inherit origin attributes from parent document if + // 1. It's in a content docshell. + // 2. its nodePrincipal is not a SystemPrincipal. + // 3. It's not a mozbrowser nor mozapp frame. + // + // For example, firstPartyDomain is computed from top-level document, it + // doesn't exist in the top-level docshell. + if (parentType == nsIDocShellTreeItem::typeContent && + !nsContentUtils::IsSystemPrincipal(doc->NodePrincipal()) && + !OwnerIsMozBrowserOrAppFrame()) { + PrincipalOriginAttributes poa = BasePrincipal::Cast(doc->NodePrincipal())->OriginAttributesRef(); + + // Assert on the firstPartyDomain from top-level docshell should be empty + if (mIsTopLevelContent) { + MOZ_ASSERT(attrs.mFirstPartyDomain.IsEmpty(), + "top-level docshell shouldn't have firstPartyDomain attribute."); + } + + // So far we want to make sure InheritFromDocToChildDocShell doesn't override + // any other origin attribute than firstPartyDomain. + MOZ_ASSERT(attrs.mAppId == poa.mAppId, + "docshell and document should have the same appId attribute."); + MOZ_ASSERT(attrs.mUserContextId == poa.mUserContextId, + "docshell and document should have the same userContextId attribute."); + MOZ_ASSERT(attrs.mInIsolatedMozBrowser == poa.mInIsolatedMozBrowser, + "docshell and document should have the same inIsolatedMozBrowser attribute."); + MOZ_ASSERT(attrs.mPrivateBrowsingId == poa.mPrivateBrowsingId, + "docshell and document should have the same privateBrowsingId attribute."); + + attrs.InheritFromDocToChildDocShell(poa); + } + + if (OwnerIsAppFrame()) { + // You can't be both an app and a browser frame. + MOZ_ASSERT(!OwnerIsMozBrowserFrame()); + + nsCOMPtr<mozIApplication> ownApp = GetOwnApp(); + MOZ_ASSERT(ownApp); + uint32_t ownAppId = nsIScriptSecurityManager::NO_APP_ID; + if (ownApp) { + NS_ENSURE_SUCCESS(ownApp->GetLocalId(&ownAppId), NS_ERROR_FAILURE); + } + + attrs.mAppId = ownAppId; + mDocShell->SetFrameType(nsIDocShell::FRAME_TYPE_APP); + } + + if (OwnerIsMozBrowserFrame()) { + // You can't be both a browser and an app frame. + MOZ_ASSERT(!OwnerIsAppFrame()); + + nsCOMPtr<mozIApplication> containingApp = GetContainingApp(); + uint32_t containingAppId = nsIScriptSecurityManager::NO_APP_ID; + if (containingApp) { + NS_ENSURE_SUCCESS(containingApp->GetLocalId(&containingAppId), + NS_ERROR_FAILURE); + } + + attrs.mAppId = containingAppId; + attrs.mInIsolatedMozBrowser = OwnerIsIsolatedMozBrowserFrame(); + mDocShell->SetFrameType(nsIDocShell::FRAME_TYPE_BROWSER); + } + + // Apply sandbox flags even if our owner is not an iframe, as this copies + // flags from our owning content's owning document. + // Note: ApplySandboxFlags should be called after mDocShell->SetFrameType + // because we need to get the correct presentation URL in ApplySandboxFlags. + uint32_t sandboxFlags = 0; + HTMLIFrameElement* iframe = HTMLIFrameElement::FromContent(mOwnerContent); + if (iframe) { + sandboxFlags = iframe->GetSandboxFlags(); + } + ApplySandboxFlags(sandboxFlags); + + // Grab the userContextId from owner if XUL + nsresult rv = PopulateUserContextIdFromAttribute(attrs); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + bool isPrivate = false; + nsCOMPtr<nsILoadContext> parentContext = do_QueryInterface(docShell); + NS_ENSURE_STATE(parentContext); + + rv = parentContext->GetUsePrivateBrowsing(&isPrivate); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + attrs.SyncAttributesWithPrivateBrowsing(isPrivate); + + if (OwnerIsMozBrowserOrAppFrame()) { + // For inproc frames, set the docshell properties. + nsAutoString name; + if (mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::name, name)) { + docShell->SetName(name); + } + mDocShell->SetFullscreenAllowed( + mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::allowfullscreen) || + mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozallowfullscreen)); + bool isPrivate = mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::mozprivatebrowsing); + if (isPrivate) { + if (mDocShell->GetHasLoadedNonBlankURI()) { + nsContentUtils::ReportToConsoleNonLocalized( + NS_LITERAL_STRING("We should not switch to Private Browsing after loading a document."), + nsIScriptError::warningFlag, + NS_LITERAL_CSTRING("mozprivatebrowsing"), + nullptr); + } else { + // This handles the case where a frames private browsing is set by chrome flags + // and not inherited by its parent. + attrs.SyncAttributesWithPrivateBrowsing(isPrivate); + } + } + } + + nsDocShell::Cast(mDocShell)->SetOriginAttributes(attrs); + + ReallyLoadFrameScripts(); + InitializeBrowserAPI(); + + nsCOMPtr<nsIObserverService> os = services::GetObserverService(); + if (os) { + os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this), + "inprocess-browser-shown", nullptr); + } + + return NS_OK; +} + +void +nsFrameLoader::GetURL(nsString& aURI) +{ + aURI.Truncate(); + + if (mOwnerContent->IsHTMLElement(nsGkAtoms::object)) { + mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::data, aURI); + } else { + mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::src, aURI); + } +} + +nsresult +nsFrameLoader::CheckForRecursiveLoad(nsIURI* aURI) +{ + nsresult rv; + + MOZ_ASSERT(!IsRemoteFrame(), + "Shouldn't call CheckForRecursiveLoad on remote frames."); + + mDepthTooGreat = false; + rv = MaybeCreateDocShell(); + if (NS_FAILED(rv)) { + return rv; + } + NS_ASSERTION(mDocShell, + "MaybeCreateDocShell succeeded, but null mDocShell"); + if (!mDocShell) { + return NS_ERROR_FAILURE; + } + + // Check that we're still in the docshell tree. + nsCOMPtr<nsIDocShellTreeOwner> treeOwner; + mDocShell->GetTreeOwner(getter_AddRefs(treeOwner)); + NS_WARNING_ASSERTION(treeOwner, + "Trying to load a new url to a docshell without owner!"); + NS_ENSURE_STATE(treeOwner); + + if (mDocShell->ItemType() != nsIDocShellTreeItem::typeContent) { + // No need to do recursion-protection here XXXbz why not?? Do we really + // trust people not to screw up with non-content docshells? + return NS_OK; + } + + // Bug 8065: Don't exceed some maximum depth in content frames + // (MAX_DEPTH_CONTENT_FRAMES) + nsCOMPtr<nsIDocShellTreeItem> parentAsItem; + mDocShell->GetSameTypeParent(getter_AddRefs(parentAsItem)); + int32_t depth = 0; + while (parentAsItem) { + ++depth; + + if (depth >= MAX_DEPTH_CONTENT_FRAMES) { + mDepthTooGreat = true; + NS_WARNING("Too many nested content frames so giving up"); + + return NS_ERROR_UNEXPECTED; // Too deep, give up! (silently?) + } + + nsCOMPtr<nsIDocShellTreeItem> temp; + temp.swap(parentAsItem); + temp->GetSameTypeParent(getter_AddRefs(parentAsItem)); + } + + // Bug 136580: Check for recursive frame loading excluding about:srcdoc URIs. + // srcdoc URIs require their contents to be specified inline, so it isn't + // possible for undesirable recursion to occur without the aid of a + // non-srcdoc URI, which this method will block normally. + // Besides, URI is not enough to guarantee uniqueness of srcdoc documents. + nsAutoCString buffer; + rv = aURI->GetScheme(buffer); + if (NS_SUCCEEDED(rv) && buffer.EqualsLiteral("about")) { + rv = aURI->GetPath(buffer); + if (NS_SUCCEEDED(rv) && buffer.EqualsLiteral("srcdoc")) { + // Duplicates allowed up to depth limits + return NS_OK; + } + } + int32_t matchCount = 0; + mDocShell->GetSameTypeParent(getter_AddRefs(parentAsItem)); + while (parentAsItem) { + // Check the parent URI with the URI we're loading + nsCOMPtr<nsIWebNavigation> parentAsNav(do_QueryInterface(parentAsItem)); + if (parentAsNav) { + // Does the URI match the one we're about to load? + nsCOMPtr<nsIURI> parentURI; + parentAsNav->GetCurrentURI(getter_AddRefs(parentURI)); + if (parentURI) { + // Bug 98158/193011: We need to ignore data after the # + bool equal; + rv = aURI->EqualsExceptRef(parentURI, &equal); + NS_ENSURE_SUCCESS(rv, rv); + + if (equal) { + matchCount++; + if (matchCount >= MAX_SAME_URL_CONTENT_FRAMES) { + NS_WARNING("Too many nested content frames have the same url (recursion?) so giving up"); + return NS_ERROR_UNEXPECTED; + } + } + } + } + nsCOMPtr<nsIDocShellTreeItem> temp; + temp.swap(parentAsItem); + temp->GetSameTypeParent(getter_AddRefs(parentAsItem)); + } + + return NS_OK; +} + +nsresult +nsFrameLoader::GetWindowDimensions(nsIntRect& aRect) +{ + // Need to get outer window position here + nsIDocument* doc = mOwnerContent->GetComposedDoc(); + if (!doc) { + return NS_ERROR_FAILURE; + } + + MOZ_RELEASE_ASSERT(!doc->IsResourceDoc(), "We shouldn't even exist"); + + nsCOMPtr<nsPIDOMWindowOuter> win = doc->GetWindow(); + if (!win) { + return NS_ERROR_FAILURE; + } + + nsCOMPtr<nsIDocShellTreeItem> parentAsItem(win->GetDocShell()); + if (!parentAsItem) { + return NS_ERROR_FAILURE; + } + + nsCOMPtr<nsIDocShellTreeOwner> parentOwner; + if (NS_FAILED(parentAsItem->GetTreeOwner(getter_AddRefs(parentOwner))) || + !parentOwner) { + return NS_ERROR_FAILURE; + } + + nsCOMPtr<nsIBaseWindow> treeOwnerAsWin(do_GetInterface(parentOwner)); + treeOwnerAsWin->GetPosition(&aRect.x, &aRect.y); + treeOwnerAsWin->GetSize(&aRect.width, &aRect.height); + return NS_OK; +} + +NS_IMETHODIMP +nsFrameLoader::UpdatePositionAndSize(nsSubDocumentFrame *aIFrame) +{ + if (IsRemoteFrame()) { + if (mRemoteBrowser) { + ScreenIntSize size = aIFrame->GetSubdocumentSize(); + nsIntRect dimensions; + NS_ENSURE_SUCCESS(GetWindowDimensions(dimensions), NS_ERROR_FAILURE); + mLazySize = size; + mRemoteBrowser->UpdateDimensions(dimensions, size); + } + return NS_OK; + } + UpdateBaseWindowPositionAndSize(aIFrame); + return NS_OK; +} + +void +nsFrameLoader::UpdateBaseWindowPositionAndSize(nsSubDocumentFrame *aIFrame) +{ + nsCOMPtr<nsIDocShell> docShell; + GetDocShell(getter_AddRefs(docShell)); + nsCOMPtr<nsIBaseWindow> baseWindow(do_QueryInterface(docShell)); + + // resize the sub document + if (baseWindow) { + int32_t x = 0; + int32_t y = 0; + + nsWeakFrame weakFrame(aIFrame); + + baseWindow->GetPosition(&x, &y); + + if (!weakFrame.IsAlive()) { + // GetPosition() killed us + return; + } + + ScreenIntSize size = aIFrame->GetSubdocumentSize(); + mLazySize = size; + + baseWindow->SetPositionAndSize(x, y, size.width, size.height, + nsIBaseWindow::eDelayResize); + } +} + +NS_IMETHODIMP +nsFrameLoader::GetLazyWidth(uint32_t* aLazyWidth) +{ + *aLazyWidth = mLazySize.width; + + nsIFrame* frame = GetPrimaryFrameOfOwningContent(); + if (frame) { + *aLazyWidth = frame->PresContext()->DevPixelsToIntCSSPixels(*aLazyWidth); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsFrameLoader::GetLazyHeight(uint32_t* aLazyHeight) +{ + *aLazyHeight = mLazySize.height; + + nsIFrame* frame = GetPrimaryFrameOfOwningContent(); + if (frame) { + *aLazyHeight = frame->PresContext()->DevPixelsToIntCSSPixels(*aLazyHeight); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsFrameLoader::GetEventMode(uint32_t* aEventMode) +{ + *aEventMode = mEventMode; + return NS_OK; +} + +NS_IMETHODIMP +nsFrameLoader::SetEventMode(uint32_t aEventMode) +{ + mEventMode = aEventMode; + return NS_OK; +} + +NS_IMETHODIMP +nsFrameLoader::GetClipSubdocument(bool* aResult) +{ + *aResult = mClipSubdocument; + return NS_OK; +} + +NS_IMETHODIMP +nsFrameLoader::SetClipSubdocument(bool aClip) +{ + mClipSubdocument = aClip; + nsIFrame* frame = GetPrimaryFrameOfOwningContent(); + if (frame) { + frame->InvalidateFrame(); + frame->PresContext()->PresShell()-> + FrameNeedsReflow(frame, nsIPresShell::eResize, NS_FRAME_IS_DIRTY); + nsSubDocumentFrame* subdocFrame = do_QueryFrame(frame); + if (subdocFrame) { + nsIFrame* subdocRootFrame = subdocFrame->GetSubdocumentRootFrame(); + if (subdocRootFrame) { + nsIFrame* subdocRootScrollFrame = subdocRootFrame->PresContext()->PresShell()-> + GetRootScrollFrame(); + if (subdocRootScrollFrame) { + frame->PresContext()->PresShell()-> + FrameNeedsReflow(frame, nsIPresShell::eResize, NS_FRAME_IS_DIRTY); + } + } + } + } + return NS_OK; +} + +NS_IMETHODIMP +nsFrameLoader::GetClampScrollPosition(bool* aResult) +{ + *aResult = mClampScrollPosition; + return NS_OK; +} + +NS_IMETHODIMP +nsFrameLoader::SetClampScrollPosition(bool aClamp) +{ + mClampScrollPosition = aClamp; + + // When turning clamping on, make sure the current position is clamped. + if (aClamp) { + nsIFrame* frame = GetPrimaryFrameOfOwningContent(); + nsSubDocumentFrame* subdocFrame = do_QueryFrame(frame); + if (subdocFrame) { + nsIFrame* subdocRootFrame = subdocFrame->GetSubdocumentRootFrame(); + if (subdocRootFrame) { + nsIScrollableFrame* subdocRootScrollFrame = subdocRootFrame->PresContext()->PresShell()-> + GetRootScrollFrameAsScrollable(); + if (subdocRootScrollFrame) { + subdocRootScrollFrame->ScrollTo(subdocRootScrollFrame->GetScrollPosition(), nsIScrollableFrame::INSTANT); + } + } + } + } + return NS_OK; +} + +static +ContentParent* +GetContentParent(Element* aBrowser) +{ + nsCOMPtr<nsIBrowser> browser = do_QueryInterface(aBrowser); + if (!browser) { + return nullptr; + } + + nsCOMPtr<nsIDOMElement> related; + browser->GetRelatedBrowser(getter_AddRefs(related)); + + nsCOMPtr<nsIFrameLoaderOwner> otherOwner = do_QueryInterface(related); + if (!otherOwner) { + return nullptr; + } + + nsCOMPtr<nsIFrameLoader> otherLoader = otherOwner->GetFrameLoader(); + TabParent* tabParent = TabParent::GetFrom(otherLoader); + if (tabParent && + tabParent->Manager() && + tabParent->Manager()->IsContentParent()) { + return tabParent->Manager()->AsContentParent(); + } + + return nullptr; +} + +bool +nsFrameLoader::TryRemoteBrowser() +{ + NS_ASSERTION(!mRemoteBrowser, "TryRemoteBrowser called with a remote browser already?"); + + //XXXsmaug Per spec (2014/08/21) frameloader should not work in case the + // element isn't in document, only in shadow dom, but that will change + // https://www.w3.org/Bugs/Public/show_bug.cgi?id=26365#c0 + nsIDocument* doc = mOwnerContent->GetComposedDoc(); + if (!doc) { + return false; + } + + MOZ_RELEASE_ASSERT(!doc->IsResourceDoc(), "We shouldn't even exist"); + + if (!doc->IsActive()) { + // Don't allow subframe loads in non-active documents. + // (See bug 610571 comment 5.) + return false; + } + + nsCOMPtr<nsPIDOMWindowOuter> parentWin = doc->GetWindow(); + if (!parentWin) { + return false; + } + + nsCOMPtr<nsIDocShell> parentDocShell = parentWin->GetDocShell(); + if (!parentDocShell) { + return false; + } + + TabParent* openingTab = TabParent::GetFrom(parentDocShell->GetOpener()); + ContentParent* openerContentParent = nullptr; + + if (openingTab && + openingTab->Manager() && + openingTab->Manager()->IsContentParent()) { + openerContentParent = openingTab->Manager()->AsContentParent(); + } + + // <iframe mozbrowser> gets to skip these checks. + if (!OwnerIsMozBrowserOrAppFrame()) { + if (parentDocShell->ItemType() != nsIDocShellTreeItem::typeChrome) { + return false; + } + + if (!mOwnerContent->IsXULElement()) { + return false; + } + + nsAutoString value; + mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::type, value); + + if (!value.LowerCaseEqualsLiteral("content") && + !StringBeginsWith(value, NS_LITERAL_STRING("content-"), + nsCaseInsensitiveStringComparator())) { + return false; + } + + // Try to get the related content parent from our browser element. + openerContentParent = GetContentParent(mOwnerContent); + } + + uint32_t chromeFlags = 0; + nsCOMPtr<nsIDocShellTreeOwner> parentOwner; + if (NS_FAILED(parentDocShell->GetTreeOwner(getter_AddRefs(parentOwner))) || + !parentOwner) { + return false; + } + nsCOMPtr<nsIXULWindow> window(do_GetInterface(parentOwner)); + if (window && NS_FAILED(window->GetChromeFlags(&chromeFlags))) { + return false; + } + + PROFILER_LABEL("nsFrameLoader", "CreateRemoteBrowser", + js::ProfileEntry::Category::OTHER); + + MutableTabContext context; + nsresult rv = GetNewTabContext(&context); + NS_ENSURE_SUCCESS(rv, false); + + nsCOMPtr<Element> ownerElement = mOwnerContent; + mRemoteBrowser = ContentParent::CreateBrowserOrApp(context, ownerElement, + openerContentParent, mFreshProcess); + if (!mRemoteBrowser) { + return false; + } + + MaybeUpdatePrimaryTabParent(eTabParentChanged); + + mChildID = mRemoteBrowser->Manager()->ChildID(); + + nsCOMPtr<nsIDocShellTreeItem> rootItem; + parentDocShell->GetRootTreeItem(getter_AddRefs(rootItem)); + nsCOMPtr<nsPIDOMWindowOuter> rootWin = rootItem->GetWindow(); + nsCOMPtr<nsIDOMChromeWindow> rootChromeWin = do_QueryInterface(rootWin); + + if (rootChromeWin) { + nsCOMPtr<nsIBrowserDOMWindow> browserDOMWin; + rootChromeWin->GetBrowserDOMWindow(getter_AddRefs(browserDOMWin)); + mRemoteBrowser->SetBrowserDOMWindow(browserDOMWin); + } + + ReallyLoadFrameScripts(); + InitializeBrowserAPI(); + + return true; +} + +mozilla::dom::PBrowserParent* +nsFrameLoader::GetRemoteBrowser() const +{ + return mRemoteBrowser; +} + +RenderFrameParent* +nsFrameLoader::GetCurrentRenderFrame() const +{ + if (mRemoteBrowser) { + return mRemoteBrowser->GetRenderFrame(); + } + return nullptr; +} + +NS_IMETHODIMP +nsFrameLoader::ActivateRemoteFrame() { + if (mRemoteBrowser) { + mRemoteBrowser->Activate(); + return NS_OK; + } + return NS_ERROR_UNEXPECTED; +} + +NS_IMETHODIMP +nsFrameLoader::DeactivateRemoteFrame() { + if (mRemoteBrowser) { + mRemoteBrowser->Deactivate(); + return NS_OK; + } + return NS_ERROR_UNEXPECTED; +} + +NS_IMETHODIMP +nsFrameLoader::SendCrossProcessMouseEvent(const nsAString& aType, + float aX, + float aY, + int32_t aButton, + int32_t aClickCount, + int32_t aModifiers, + bool aIgnoreRootScrollFrame) +{ + if (mRemoteBrowser) { + mRemoteBrowser->SendMouseEvent(aType, aX, aY, aButton, + aClickCount, aModifiers, + aIgnoreRootScrollFrame); + return NS_OK; + } + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsFrameLoader::ActivateFrameEvent(const nsAString& aType, + bool aCapture) +{ + if (mRemoteBrowser) { + return mRemoteBrowser->SendActivateFrameEvent(nsString(aType), aCapture) ? + NS_OK : NS_ERROR_NOT_AVAILABLE; + } + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +nsFrameLoader::SendCrossProcessKeyEvent(const nsAString& aType, + int32_t aKeyCode, + int32_t aCharCode, + int32_t aModifiers, + bool aPreventDefault) +{ + if (mRemoteBrowser) { + mRemoteBrowser->SendKeyEvent(aType, aKeyCode, aCharCode, aModifiers, + aPreventDefault); + return NS_OK; + } + return NS_ERROR_FAILURE; +} + +nsresult +nsFrameLoader::CreateStaticClone(nsIFrameLoader* aDest) +{ + nsFrameLoader* dest = static_cast<nsFrameLoader*>(aDest); + dest->MaybeCreateDocShell(); + NS_ENSURE_STATE(dest->mDocShell); + + nsCOMPtr<nsIDocument> kungFuDeathGrip = dest->mDocShell->GetDocument(); + Unused << kungFuDeathGrip; + + nsCOMPtr<nsIContentViewer> viewer; + dest->mDocShell->GetContentViewer(getter_AddRefs(viewer)); + NS_ENSURE_STATE(viewer); + + nsCOMPtr<nsIDocShell> origDocShell; + GetDocShell(getter_AddRefs(origDocShell)); + NS_ENSURE_STATE(origDocShell); + + nsCOMPtr<nsIDocument> doc = origDocShell->GetDocument(); + NS_ENSURE_STATE(doc); + + nsCOMPtr<nsIDocument> clonedDoc = doc->CreateStaticClone(dest->mDocShell); + nsCOMPtr<nsIDOMDocument> clonedDOMDoc = do_QueryInterface(clonedDoc); + + viewer->SetDOMDocument(clonedDOMDoc); + return NS_OK; +} + +bool +nsFrameLoader::DoLoadMessageManagerScript(const nsAString& aURL, bool aRunInGlobalScope) +{ + auto* tabParent = TabParent::GetFrom(GetRemoteBrowser()); + if (tabParent) { + return tabParent->SendLoadRemoteScript(nsString(aURL), aRunInGlobalScope); + } + RefPtr<nsInProcessTabChildGlobal> tabChild = + static_cast<nsInProcessTabChildGlobal*>(GetTabChildGlobalAsEventTarget()); + if (tabChild) { + tabChild->LoadFrameScript(aURL, aRunInGlobalScope); + } + return true; +} + +class nsAsyncMessageToChild : public nsSameProcessAsyncMessageBase, + public Runnable +{ +public: + nsAsyncMessageToChild(JS::RootingContext* aRootingCx, + JS::Handle<JSObject*> aCpows, + nsFrameLoader* aFrameLoader) + : nsSameProcessAsyncMessageBase(aRootingCx, aCpows) + , mFrameLoader(aFrameLoader) + { + } + + NS_IMETHOD Run() override + { + nsInProcessTabChildGlobal* tabChild = + static_cast<nsInProcessTabChildGlobal*>(mFrameLoader->mChildMessageManager.get()); + // Since bug 1126089, messages can arrive even when the docShell is destroyed. + // Here we make sure that those messages are not delivered. + if (tabChild && tabChild->GetInnerManager() && mFrameLoader->GetExistingDocShell()) { + nsCOMPtr<nsIXPConnectJSObjectHolder> kungFuDeathGrip(tabChild->GetGlobal()); + ReceiveMessage(static_cast<EventTarget*>(tabChild), mFrameLoader, + tabChild->GetInnerManager()); + } + return NS_OK; + } + RefPtr<nsFrameLoader> mFrameLoader; +}; + +nsresult +nsFrameLoader::DoSendAsyncMessage(JSContext* aCx, + const nsAString& aMessage, + StructuredCloneData& aData, + JS::Handle<JSObject *> aCpows, + nsIPrincipal* aPrincipal) +{ + TabParent* tabParent = mRemoteBrowser; + if (tabParent) { + ClonedMessageData data; + nsIContentParent* cp = tabParent->Manager(); + if (!BuildClonedMessageDataForParent(cp, aData, data)) { + MOZ_CRASH(); + return NS_ERROR_DOM_DATA_CLONE_ERR; + } + InfallibleTArray<mozilla::jsipc::CpowEntry> cpows; + jsipc::CPOWManager* mgr = cp->GetCPOWManager(); + if (aCpows && (!mgr || !mgr->Wrap(aCx, aCpows, &cpows))) { + return NS_ERROR_UNEXPECTED; + } + if (tabParent->SendAsyncMessage(nsString(aMessage), cpows, + IPC::Principal(aPrincipal), data)) { + return NS_OK; + } else { + return NS_ERROR_UNEXPECTED; + } + } + + if (mChildMessageManager) { + JS::RootingContext* rcx = JS::RootingContext::get(aCx); + RefPtr<nsAsyncMessageToChild> ev = new nsAsyncMessageToChild(rcx, aCpows, this); + nsresult rv = ev->Init(aMessage, aData, aPrincipal); + if (NS_FAILED(rv)) { + return rv; + } + rv = NS_DispatchToCurrentThread(ev); + if (NS_FAILED(rv)) { + return rv; + } + return rv; + } + + // We don't have any targets to send our asynchronous message to. + return NS_ERROR_UNEXPECTED; +} + +bool +nsFrameLoader::CheckPermission(const nsAString& aPermission) +{ + return AssertAppProcessPermission(GetRemoteBrowser(), + NS_ConvertUTF16toUTF8(aPermission).get()); +} + +bool +nsFrameLoader::CheckManifestURL(const nsAString& aManifestURL) +{ + return AssertAppProcessManifestURL(GetRemoteBrowser(), + NS_ConvertUTF16toUTF8(aManifestURL).get()); +} + +bool +nsFrameLoader::CheckAppHasPermission(const nsAString& aPermission) +{ + return AssertAppHasPermission(GetRemoteBrowser(), + NS_ConvertUTF16toUTF8(aPermission).get()); +} + +NS_IMETHODIMP +nsFrameLoader::GetMessageManager(nsIMessageSender** aManager) +{ + EnsureMessageManager(); + if (mMessageManager) { + RefPtr<nsFrameMessageManager> mm(mMessageManager); + mm.forget(aManager); + return NS_OK; + } + return NS_OK; +} + +nsresult +nsFrameLoader::EnsureMessageManager() +{ + NS_ENSURE_STATE(mOwnerContent); + + if (mMessageManager) { + return NS_OK; + } + + if (!mIsTopLevelContent && + !OwnerIsMozBrowserOrAppFrame() && + !IsRemoteFrame() && + !(mOwnerContent->IsXULElement() && + mOwnerContent->AttrValueIs(kNameSpaceID_None, + nsGkAtoms::forcemessagemanager, + nsGkAtoms::_true, eCaseMatters))) { + return NS_OK; + } + + nsCOMPtr<nsIDOMChromeWindow> chromeWindow = + do_QueryInterface(GetOwnerDoc()->GetWindow()); + nsCOMPtr<nsIMessageBroadcaster> parentManager; + + if (chromeWindow) { + nsAutoString messagemanagergroup; + if (mOwnerContent->IsXULElement() && + mOwnerContent->GetAttr(kNameSpaceID_None, + nsGkAtoms::messagemanagergroup, + messagemanagergroup)) { + chromeWindow->GetGroupMessageManager(messagemanagergroup, getter_AddRefs(parentManager)); + } + + if (!parentManager) { + chromeWindow->GetMessageManager(getter_AddRefs(parentManager)); + } + } else { + parentManager = do_GetService("@mozilla.org/globalmessagemanager;1"); + } + + mMessageManager = new nsFrameMessageManager(nullptr, + static_cast<nsFrameMessageManager*>(parentManager.get()), + MM_CHROME); + if (!IsRemoteFrame()) { + nsresult rv = MaybeCreateDocShell(); + if (NS_FAILED(rv)) { + return rv; + } + NS_ASSERTION(mDocShell, + "MaybeCreateDocShell succeeded, but null mDocShell"); + if (!mDocShell) { + return NS_ERROR_FAILURE; + } + mChildMessageManager = + new nsInProcessTabChildGlobal(mDocShell, mOwnerContent, mMessageManager); + } + return NS_OK; +} + +nsresult +nsFrameLoader::ReallyLoadFrameScripts() +{ + nsresult rv = EnsureMessageManager(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + if (mMessageManager) { + mMessageManager->InitWithCallback(this); + } + return NS_OK; +} + +EventTarget* +nsFrameLoader::GetTabChildGlobalAsEventTarget() +{ + return static_cast<nsInProcessTabChildGlobal*>(mChildMessageManager.get()); +} + +NS_IMETHODIMP +nsFrameLoader::GetOwnerElement(nsIDOMElement **aElement) +{ + nsCOMPtr<nsIDOMElement> ownerElement = do_QueryInterface(mOwnerContent); + ownerElement.forget(aElement); + return NS_OK; +} + +NS_IMETHODIMP +nsFrameLoader::GetChildID(uint64_t* aChildID) +{ + *aChildID = mChildID; + return NS_OK; +} + +void +nsFrameLoader::SetRemoteBrowser(nsITabParent* aTabParent) +{ + MOZ_ASSERT(!mRemoteBrowser); + mRemoteFrame = true; + mRemoteBrowser = TabParent::GetFrom(aTabParent); + mChildID = mRemoteBrowser ? mRemoteBrowser->Manager()->ChildID() : 0; + MaybeUpdatePrimaryTabParent(eTabParentChanged); + ReallyLoadFrameScripts(); + InitializeBrowserAPI(); + ShowRemoteFrame(ScreenIntSize(0, 0)); +} + +void +nsFrameLoader::SetDetachedSubdocFrame(nsIFrame* aDetachedFrame, + nsIDocument* aContainerDoc) +{ + mDetachedSubdocFrame = aDetachedFrame; + mContainerDocWhileDetached = aContainerDoc; +} + +nsIFrame* +nsFrameLoader::GetDetachedSubdocFrame(nsIDocument** aContainerDoc) const +{ + NS_IF_ADDREF(*aContainerDoc = mContainerDocWhileDetached); + return mDetachedSubdocFrame.GetFrame(); +} + +void +nsFrameLoader::ApplySandboxFlags(uint32_t sandboxFlags) +{ + if (mDocShell) { + uint32_t parentSandboxFlags = mOwnerContent->OwnerDoc()->GetSandboxFlags(); + + // The child can only add restrictions, never remove them. + sandboxFlags |= parentSandboxFlags; + + // If this frame is a receiving browsing context, we should add + // sandboxed auxiliary navigation flag to sandboxFlags. See + // https://w3c.github.io/presentation-api/#creating-a-receiving-browsing-context + nsAutoString presentationURL; + nsContentUtils::GetPresentationURL(mDocShell, presentationURL); + if (!presentationURL.IsEmpty()) { + sandboxFlags |= SANDBOXED_AUXILIARY_NAVIGATION; + } + mDocShell->SetSandboxFlags(sandboxFlags); + } +} + +/* virtual */ void +nsFrameLoader::AttributeChanged(nsIDocument* aDocument, + mozilla::dom::Element* aElement, + int32_t aNameSpaceID, + nsIAtom* aAttribute, + int32_t aModType, + const nsAttrValue* aOldValue) +{ + MOZ_ASSERT(mObservingOwnerContent); + + if (aNameSpaceID != kNameSpaceID_None || aAttribute != TypeAttrName()) { + return; + } + + if (aElement != mOwnerContent) { + return; + } + + // Note: This logic duplicates a lot of logic in + // MaybeCreateDocshell. We should fix that. + + // Notify our enclosing chrome that our type has changed. We only do this + // if our parent is chrome, since in all other cases we're random content + // subframes and the treeowner shouldn't worry about us. + if (!mDocShell) { + MaybeUpdatePrimaryTabParent(eTabParentChanged); + return; + } + + nsCOMPtr<nsIDocShellTreeItem> parentItem; + mDocShell->GetParent(getter_AddRefs(parentItem)); + if (!parentItem) { + return; + } + + if (parentItem->ItemType() != nsIDocShellTreeItem::typeChrome) { + return; + } + + nsCOMPtr<nsIDocShellTreeOwner> parentTreeOwner; + parentItem->GetTreeOwner(getter_AddRefs(parentTreeOwner)); + if (!parentTreeOwner) { + return; + } + + nsAutoString value; + aElement->GetAttr(kNameSpaceID_None, TypeAttrName(), value); + + bool is_primary = value.LowerCaseEqualsLiteral("content-primary"); + +#ifdef MOZ_XUL + // when a content panel is no longer primary, hide any open popups it may have + if (!is_primary) { + nsXULPopupManager* pm = nsXULPopupManager::GetInstance(); + if (pm) + pm->HidePopupsInDocShell(mDocShell); + } +#endif + + parentTreeOwner->ContentShellRemoved(mDocShell); + if (value.LowerCaseEqualsLiteral("content") || + StringBeginsWith(value, NS_LITERAL_STRING("content-"), + nsCaseInsensitiveStringComparator())) { + bool is_targetable = is_primary || + value.LowerCaseEqualsLiteral("content-targetable"); + + parentTreeOwner->ContentShellAdded(mDocShell, is_primary, + is_targetable, value); + } +} + +/** + * Send the RequestNotifyAfterRemotePaint message to the current Tab. + */ +NS_IMETHODIMP +nsFrameLoader::RequestNotifyAfterRemotePaint() +{ + // If remote browsing (e10s), handle this with the TabParent. + if (mRemoteBrowser) { + Unused << mRemoteBrowser->SendRequestNotifyAfterRemotePaint(); + } + + return NS_OK; +} + +NS_IMETHODIMP +nsFrameLoader::RequestFrameLoaderClose() +{ + nsCOMPtr<nsIBrowser> browser = do_QueryInterface(mOwnerContent); + if (NS_WARN_IF(!browser)) { + // OwnerElement other than nsIBrowser is not supported yet. + return NS_ERROR_NOT_IMPLEMENTED; + } + + return browser->CloseBrowser(); +} + +NS_IMETHODIMP +nsFrameLoader::Print(uint64_t aOuterWindowID, + nsIPrintSettings* aPrintSettings, + nsIWebProgressListener* aProgressListener) +{ +#if defined(NS_PRINTING) + if (mRemoteBrowser) { + RefPtr<embedding::PrintingParent> printingParent = + mRemoteBrowser->Manager()->AsContentParent()->GetPrintingParent(); + + embedding::PrintData printData; + nsresult rv = printingParent->SerializeAndEnsureRemotePrintJob( + aPrintSettings, aProgressListener, nullptr, &printData); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + + bool success = mRemoteBrowser->SendPrint(aOuterWindowID, printData); + return success ? NS_OK : NS_ERROR_FAILURE; + } + + nsGlobalWindow* outerWindow = + nsGlobalWindow::GetOuterWindowWithId(aOuterWindowID); + if (NS_WARN_IF(!outerWindow)) { + return NS_ERROR_FAILURE; + } + + nsCOMPtr<nsIWebBrowserPrint> webBrowserPrint = + do_GetInterface(outerWindow->AsOuter()); + if (NS_WARN_IF(!webBrowserPrint)) { + return NS_ERROR_FAILURE; + } + + return webBrowserPrint->Print(aPrintSettings, aProgressListener); +#endif + return NS_OK; +} + +/* [infallible] */ NS_IMETHODIMP +nsFrameLoader::SetVisible(bool aVisible) +{ + if (mVisible == aVisible) { + return NS_OK; + } + + mVisible = aVisible; + nsCOMPtr<nsIObserverService> os = services::GetObserverService(); + if (os) { + os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this), + "frameloader-visible-changed", nullptr); + } + return NS_OK; +} + +/* [infallible] */ NS_IMETHODIMP +nsFrameLoader::GetVisible(bool* aVisible) +{ + *aVisible = mVisible; + return NS_OK; +} + +NS_IMETHODIMP +nsFrameLoader::GetTabParent(nsITabParent** aTabParent) +{ + nsCOMPtr<nsITabParent> tp = mRemoteBrowser; + tp.forget(aTabParent); + return NS_OK; +} + +NS_IMETHODIMP +nsFrameLoader::GetLoadContext(nsILoadContext** aLoadContext) +{ + nsCOMPtr<nsILoadContext> loadContext; + if (mRemoteBrowser) { + loadContext = mRemoteBrowser->GetLoadContext(); + } else { + nsCOMPtr<nsIDocShell> docShell; + GetDocShell(getter_AddRefs(docShell)); + loadContext = do_GetInterface(docShell); + } + loadContext.forget(aLoadContext); + return NS_OK; +} + +void +nsFrameLoader::InitializeBrowserAPI() +{ + if (!OwnerIsMozBrowserOrAppFrame()) { + return; + } + if (!IsRemoteFrame()) { + nsresult rv = EnsureMessageManager(); + if (NS_WARN_IF(NS_FAILED(rv))) { + return; + } + if (mMessageManager) { + mMessageManager->LoadFrameScript( + NS_LITERAL_STRING("chrome://global/content/BrowserElementChild.js"), + /* allowDelayedLoad = */ true, + /* aRunInGlobalScope */ true); + } + } + nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwnerContent); + if (browserFrame) { + browserFrame->InitializeBrowserAPI(); + } +} + +void +nsFrameLoader::DestroyBrowserFrameScripts() +{ + if (!OwnerIsMozBrowserOrAppFrame()) { + return; + } + nsCOMPtr<nsIMozBrowserFrame> browserFrame = do_QueryInterface(mOwnerContent); + if (browserFrame) { + browserFrame->DestroyBrowserFrameScripts(); + } +} + +NS_IMETHODIMP +nsFrameLoader::StartPersistence(uint64_t aOuterWindowID, + nsIWebBrowserPersistDocumentReceiver* aRecv) +{ + if (!aRecv) { + return NS_ERROR_INVALID_POINTER; + } + + if (mRemoteBrowser) { + return mRemoteBrowser->StartPersistence(aOuterWindowID, aRecv); + } + + nsCOMPtr<nsIDocument> rootDoc = + mDocShell ? mDocShell->GetDocument() : nullptr; + nsCOMPtr<nsIDocument> foundDoc; + if (aOuterWindowID) { + foundDoc = nsContentUtils::GetSubdocumentWithOuterWindowId(rootDoc, aOuterWindowID); + } else { + foundDoc = rootDoc; + } + + if (!foundDoc) { + aRecv->OnError(NS_ERROR_NO_CONTENT); + } else { + nsCOMPtr<nsIWebBrowserPersistDocument> pdoc = + new mozilla::WebBrowserPersistLocalDocument(foundDoc); + aRecv->OnDocumentReady(pdoc); + } + return NS_OK; +} + +void +nsFrameLoader::MaybeUpdatePrimaryTabParent(TabParentChange aChange) +{ + if (mRemoteBrowser && mOwnerContent) { + nsCOMPtr<nsIDocShell> docShell = mOwnerContent->OwnerDoc()->GetDocShell(); + if (!docShell) { + return; + } + + int32_t parentType = docShell->ItemType(); + if (parentType != nsIDocShellTreeItem::typeChrome) { + return; + } + + nsCOMPtr<nsIDocShellTreeOwner> parentTreeOwner; + docShell->GetTreeOwner(getter_AddRefs(parentTreeOwner)); + if (!parentTreeOwner) { + return; + } + + if (!mObservingOwnerContent) { + mOwnerContent->AddMutationObserver(this); + mObservingOwnerContent = true; + } + + parentTreeOwner->TabParentRemoved(mRemoteBrowser); + if (aChange == eTabParentChanged) { + bool isPrimary = + mOwnerContent->AttrValueIs(kNameSpaceID_None, + TypeAttrName(), + NS_LITERAL_STRING("content-primary"), + eIgnoreCase); + parentTreeOwner->TabParentAdded(mRemoteBrowser, isPrimary); + } + } +} + +nsresult +nsFrameLoader::GetNewTabContext(MutableTabContext* aTabContext, + nsIURI* aURI) +{ + nsCOMPtr<mozIApplication> ownApp = GetOwnApp(); + nsCOMPtr<mozIApplication> containingApp = GetContainingApp(); + DocShellOriginAttributes attrs; + attrs.mInIsolatedMozBrowser = OwnerIsIsolatedMozBrowserFrame(); + nsresult rv; + + // Get the AppId from ownApp + uint32_t appId = nsIScriptSecurityManager::NO_APP_ID; + if (ownApp) { + rv = ownApp->GetLocalId(&appId); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_STATE(appId != nsIScriptSecurityManager::NO_APP_ID); + } else if (containingApp) { + rv = containingApp->GetLocalId(&appId); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_STATE(appId != nsIScriptSecurityManager::NO_APP_ID); + } + attrs.mAppId = appId; + + // set the userContextId on the attrs before we pass them into + // the tab context + rv = PopulateUserContextIdFromAttribute(attrs); + NS_ENSURE_SUCCESS(rv, rv); + + nsAutoString presentationURLStr; + mOwnerContent->GetAttr(kNameSpaceID_None, + nsGkAtoms::mozpresentation, + presentationURLStr); + + nsCOMPtr<nsIDocShell> docShell = mOwnerContent->OwnerDoc()->GetDocShell(); + nsCOMPtr<nsILoadContext> parentContext = do_QueryInterface(docShell); + NS_ENSURE_STATE(parentContext); + + bool isPrivate = parentContext->UsePrivateBrowsing(); + attrs.SyncAttributesWithPrivateBrowsing(isPrivate); + + UIStateChangeType showAccelerators = UIStateChangeType_NoChange; + UIStateChangeType showFocusRings = UIStateChangeType_NoChange; + nsIDocument* doc = mOwnerContent->OwnerDoc(); + if (doc) { + nsCOMPtr<nsPIWindowRoot> root = nsContentUtils::GetWindowRoot(doc); + if (root) { + showAccelerators = + root->ShowAccelerators() ? UIStateChangeType_Set : UIStateChangeType_Clear; + showFocusRings = + root->ShowFocusRings() ? UIStateChangeType_Set : UIStateChangeType_Clear; + } + } + + bool tabContextUpdated = + aTabContext->SetTabContext(OwnerIsMozBrowserFrame(), + mIsPrerendered, + ownApp, + containingApp, + showAccelerators, + showFocusRings, + attrs, + presentationURLStr); + NS_ENSURE_STATE(tabContextUpdated); + + return NS_OK; +} + +nsresult +nsFrameLoader::PopulateUserContextIdFromAttribute(DocShellOriginAttributes& aAttr) +{ + if (aAttr.mUserContextId == + nsIScriptSecurityManager::DEFAULT_USER_CONTEXT_ID) { + // Grab the userContextId from owner if XUL + nsAutoString userContextIdStr; + int32_t namespaceID = mOwnerContent->GetNameSpaceID(); + if ((namespaceID == kNameSpaceID_XUL) && + mOwnerContent->GetAttr(kNameSpaceID_None, nsGkAtoms::usercontextid, + userContextIdStr) && + !userContextIdStr.IsEmpty()) { + nsresult rv; + aAttr.mUserContextId = userContextIdStr.ToInteger(&rv); + NS_ENSURE_SUCCESS(rv, rv); + } + } + + return NS_OK; +} + +nsIMessageSender* +nsFrameLoader::GetProcessMessageManager() const +{ + return mRemoteBrowser ? mRemoteBrowser->Manager()->GetMessageManager() + : nullptr; +}; |