forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			3429 lines
		
	
	
	
		
			107 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			3429 lines
		
	
	
	
		
			107 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- 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 "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)
 | |
| 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)
 | |
| {
 | |
|   mRemoteFrame = ShouldUseRemoteProcess();
 | |
|   MOZ_ASSERT(!mRemoteFrame || !aOpener,
 | |
|              "Cannot pass aOpener for a remote frame!");
 | |
| }
 | |
| 
 | |
| 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::SwitchProcessAndLoadURI(nsIURI* aURI, const nsACString& aPackageId)
 | |
| {
 | |
|   RefPtr<TabParent> tp = nullptr;
 | |
| 
 | |
|   MutableTabContext context;
 | |
|   nsresult rv = GetNewTabContext(&context, aURI, aPackageId);
 | |
|   NS_ENSURE_SUCCESS(rv, rv);
 | |
| 
 | |
|   nsCOMPtr<Element> ownerElement = mOwnerContent;
 | |
|   tp = ContentParent::CreateBrowserOrApp(context, ownerElement, nullptr);
 | |
|   if (!tp) {
 | |
|     return NS_ERROR_FAILURE;
 | |
|   }
 | |
|   mRemoteBrowserShown = false;
 | |
| 
 | |
|   rv = SwapRemoteBrowser(tp);
 | |
|   if (NS_WARN_IF(NS_FAILED(rv))) {
 | |
|     return rv;
 | |
|   }
 | |
|   LoadURI(aURI);
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| 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;
 | |
| }
 | |
| 
 | |
| 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->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);
 | |
|   }
 | |
| 
 | |
|   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);
 | |
|   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));
 | |
| }
 | |
| 
 | |
| nsresult
 | |
| nsFrameLoader::SwapRemoteBrowser(nsITabParent* aTabParent)
 | |
| {
 | |
|   RefPtr<TabParent> newParent = TabParent::GetFrom(aTabParent);
 | |
|   if (!newParent || !mRemoteBrowser) {
 | |
|     return NS_ERROR_DOM_INVALID_STATE_ERR;
 | |
|   }
 | |
|   if (!IsRemoteFrame()) {
 | |
|     NS_WARNING("Switching from in-process to out-of-process is not supported.");
 | |
|     return NS_ERROR_NOT_IMPLEMENTED;
 | |
|   }
 | |
|   if (!OwnerIsMozBrowserOrAppFrame()) {
 | |
|     NS_WARNING("Switching process for non-mozbrowser/app frame is not supported.");
 | |
|     return NS_ERROR_NOT_IMPLEMENTED;
 | |
|   }
 | |
|   if (newParent == mRemoteBrowser) {
 | |
|     return NS_OK;
 | |
|   }
 | |
| 
 | |
|   MaybeUpdatePrimaryTabParent(eTabParentRemoved);
 | |
|   mRemoteBrowser->CacheFrameLoader(nullptr);
 | |
|   mRemoteBrowser->SetOwnerElement(nullptr);
 | |
|   mRemoteBrowser->Detach();
 | |
|   mRemoteBrowser->Destroy();
 | |
| 
 | |
|   mRemoteBrowser = newParent;
 | |
|   mRemoteBrowser->Attach(this);
 | |
|   mChildID = mRemoteBrowser->Manager()->ChildID();
 | |
| 
 | |
|   MaybeUpdatePrimaryTabParent(eTabParentChanged);
 | |
| 
 | |
|   // Force the new remote frame manager to load pending scripts
 | |
|   mMessageManager->LoadPendingScripts();
 | |
| 
 | |
|   nsCOMPtr<nsIObserverService> os = services::GetObserverService();
 | |
|   if (os) {
 | |
|     os->NotifyObservers(NS_ISUPPORTS_CAST(nsIFrameLoader*, this),
 | |
|                         "remote-browser-swapped", nullptr);
 | |
|   }
 | |
|   if (!mRemoteBrowserShown) {
 | |
|     ShowRemoteFrame(ScreenIntSize(0, 0));
 | |
|   }
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| 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::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,
 | |
|                                 const nsACString& aPackageId)
 | |
| {
 | |
|   nsCOMPtr<mozIApplication> ownApp = GetOwnApp();
 | |
|   nsCOMPtr<mozIApplication> containingApp = GetContainingApp();
 | |
|   DocShellOriginAttributes attrs;
 | |
|   attrs.mInIsolatedMozBrowser = OwnerIsIsolatedMozBrowserFrame();
 | |
| 
 | |
|   nsCString signedPkgOrigin;
 | |
|   if (!aPackageId.IsEmpty()) {
 | |
|     // Only when aPackageId is not empty would signed package origin
 | |
|     // be meaningful.
 | |
|     nsPrincipal::GetOriginForURI(aURI, signedPkgOrigin);
 | |
|   }
 | |
| 
 | |
|   // Get the AppId from ownApp
 | |
|   uint32_t appId = nsIScriptSecurityManager::NO_APP_ID;
 | |
|   if (ownApp) {
 | |
|     nsresult rv = ownApp->GetLocalId(&appId);
 | |
|     NS_ENSURE_SUCCESS(rv, rv);
 | |
|     NS_ENSURE_STATE(appId != nsIScriptSecurityManager::NO_APP_ID);
 | |
|   } else if (containingApp) {
 | |
|     nsresult rv = containingApp->GetLocalId(&appId);
 | |
|     NS_ENSURE_SUCCESS(rv, rv);
 | |
|     NS_ENSURE_STATE(appId != nsIScriptSecurityManager::NO_APP_ID);
 | |
|   }
 | |
|   attrs.mAppId = appId;
 | |
| 
 | |
|   // Populate packageId to signedPkg.
 | |
|   attrs.mSignedPkg = NS_ConvertUTF8toUTF16(aPackageId);
 | |
| 
 | |
|   // set the userContextId on the attrs before we pass them into
 | |
|   // the tab context
 | |
|   nsAutoString userContextIdStr;
 | |
|   if (mOwnerContent->HasAttr(kNameSpaceID_None, nsGkAtoms::usercontextid)) {
 | |
|     mOwnerContent->GetAttr(kNameSpaceID_None,
 | |
|                            nsGkAtoms::usercontextid,
 | |
|                            userContextIdStr);
 | |
|   }
 | |
|   if (!userContextIdStr.IsEmpty()) {
 | |
|     nsresult err;
 | |
|     uint32_t userContextId = userContextIdStr.ToInteger(&err);
 | |
|     NS_ENSURE_SUCCESS(err, err);
 | |
|     attrs.mUserContextId = userContextId;
 | |
|   }
 | |
| 
 | |
|   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,
 | |
|                                signedPkgOrigin,
 | |
|                                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;
 | |
| };
 | 
