Bug 1209621 - Add a way to get the TabParent for the content-primary tab, r=mconley
authorOlli Pettay <Olli.Pettay@helsinki.fi>
Thu, 01 Oct 2015 20:06:51 +0300
changeset 299012 a473909f575931f67e836ae17dd739f480275e85
parent 299011 65257cf4c5f8f534cc03cc5e090515a35b314183
child 299013 4d2c675a88302e9796334b53181260bce9fb1635
push id5392
push userraliiev@mozilla.com
push dateMon, 14 Dec 2015 20:08:23 +0000
treeherdermozilla-beta@16ce8562a975 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmconley
bugs1209621
milestone44.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1209621 - Add a way to get the TabParent for the content-primary tab, r=mconley
docshell/base/nsIDocShellTreeOwner.idl
dom/base/nsFrameLoader.cpp
dom/base/nsFrameLoader.h
dom/base/test/chrome/chrome.ini
dom/base/test/chrome/file_bug1209621.xul
dom/base/test/chrome/test_bug1209621.xul
embedding/browser/nsDocShellTreeOwner.cpp
embedding/browser/nsDocShellTreeOwner.h
xpfe/appshell/nsChromeTreeOwner.cpp
xpfe/appshell/nsContentTreeOwner.cpp
xpfe/appshell/nsIXULWindow.idl
xpfe/appshell/nsXULWindow.cpp
xpfe/appshell/nsXULWindow.h
--- a/docshell/base/nsIDocShellTreeOwner.idl
+++ b/docshell/base/nsIDocShellTreeOwner.idl
@@ -6,18 +6,19 @@
 
 #include "nsISupports.idl"
 
 /**
  * The nsIDocShellTreeOwner
  */
 
 interface nsIDocShellTreeItem;
+interface nsITabParent;
 
-[scriptable, uuid(05b5b240-1f61-11e4-8c21-0800200c9a66)]
+[scriptable, uuid(0e3dc4b1-4cea-4a37-af71-79f0afd07574)]
 interface nsIDocShellTreeOwner : nsISupports
 {
 	/*
 	Return the child DocShellTreeItem with the specified name.
 	name - This is the name of the item that is trying to be found.
 	aRequestor - This is the docshellTreeItem that is requesting the find.  This
 	parameter is used to identify when the child is asking its parent to find
 	a child with the specific name.  The parent uses this parameter to ensure
@@ -59,16 +60,25 @@ interface nsIDocShellTreeOwner : nsISupp
 	 */
 	void contentShellRemoved(in nsIDocShellTreeItem aContentShell);
 
 	/*
 	Returns the Primary Content Shell
 	*/
 	readonly attribute nsIDocShellTreeItem primaryContentShell;
 
+	void tabParentAdded(in nsITabParent aTab, in boolean aPrimary);
+	void tabParentRemoved(in nsITabParent aTab);
+
+	/*
+	In multiprocess case we may not have primaryContentShell but
+	primaryTabParent.
+	 */
+	readonly attribute nsITabParent primaryTabParent;
+
 	/*
 	Tells the tree owner to size its window or parent window in such a way
 	that the shell passed along will be the size specified.
 	*/
 	void sizeShellTo(in nsIDocShellTreeItem shell, in long cx, in long cy);
 
 	/*
 	Sets the persistence of different attributes of the window.
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -951,22 +951,28 @@ nsFrameLoader::SwapWithOtherRemoteLoader
   nsPIDOMWindow* newWin = ourDoc->GetWindow();
   if (newWin) {
     nsRefPtr<nsIWidget> newParent = ((nsGlobalWindow*)newWin)->GetMainWidget();
     for (uint32_t idx = 0; idx < plugins.Length(); ++idx) {
       static_cast<mozilla::plugins::PluginWidgetParent*>(plugins[idx])->SetParent(newParent);
     }
   }
 
+  MaybeUpdatePrimaryTabParent(eTabParentRemoved);
+  aOther->MaybeUpdatePrimaryTabParent(eTabParentRemoved);
+
   SetOwnerContent(otherContent);
   aOther->SetOwnerContent(ourContent);
 
   mRemoteBrowser->SetOwnerElement(otherContent);
   aOther->mRemoteBrowser->SetOwnerElement(ourContent);
 
+  MaybeUpdatePrimaryTabParent(eTabParentChanged);
+  aOther->MaybeUpdatePrimaryTabParent(eTabParentChanged);
+
   nsRefPtr<nsFrameMessageManager> ourMessageManager = mMessageManager;
   nsRefPtr<nsFrameMessageManager> otherMessageManager = aOther->mMessageManager;
   // Swap and setup things in parent message managers.
   if (mMessageManager) {
     mMessageManager->SetCallback(aOther);
   }
   if (aOther->mMessageManager) {
     aOther->mMessageManager->SetCallback(this);
@@ -1399,17 +1405,17 @@ nsFrameLoader::StartDestroy()
   }
 
   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();
     }
@@ -2278,16 +2284,18 @@ nsFrameLoader::TryRemoteBrowser()
   NS_ENSURE_TRUE(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<nsIDOMWindow> rootWin = rootItem->GetWindow();
   nsCOMPtr<nsIDOMChromeWindow> rootChromeWin = do_QueryInterface(rootWin);
 
   if (rootChromeWin) {
@@ -2630,16 +2638,17 @@ nsFrameLoader::GetChildID(uint64_t* aChi
 
 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)
 {
@@ -2653,25 +2662,29 @@ nsFrameLoader::SwapRemoteBrowser(nsITabP
   }
   if (!OwnerIsBrowserOrAppFrame()) {
     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);
   }
@@ -2713,34 +2726,33 @@ nsFrameLoader::ApplySandboxFlags(uint32_
 nsFrameLoader::AttributeChanged(nsIDocument* aDocument,
                                 mozilla::dom::Element* aElement,
                                 int32_t      aNameSpaceID,
                                 nsIAtom*     aAttribute,
                                 int32_t      aModType,
                                 const nsAttrValue* aOldValue)
 {
   MOZ_ASSERT(mObservingOwnerContent);
-  // TODO: Implement ContentShellAdded for remote browsers (bug 658304)
-  MOZ_ASSERT(!mRemoteBrowser);
 
   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;
   }
@@ -2991,8 +3003,45 @@ nsFrameLoader::StartPersistence(uint64_t
     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);
+    }
+  }
+}
--- a/dom/base/nsFrameLoader.h
+++ b/dom/base/nsFrameLoader.h
@@ -312,16 +312,22 @@ private:
   }
 
   // Update the permission manager's app-id refcount based on mOwnerContent's
   // own-or-containing-app.
   void ResetPermissionManagerStatus();
 
   void InitializeBrowserAPI();
 
+  enum TabParentChange {
+    eTabParentRemoved,
+    eTabParentChanged
+  };
+  void MaybeUpdatePrimaryTabParent(TabParentChange aChange);
+
   nsCOMPtr<nsIDocShell> mDocShell;
   nsCOMPtr<nsIURI> mURIToLoad;
   mozilla::dom::Element* mOwnerContent; // WEAK
 
   // After the frameloader has been removed from the DOM but before all of the
   // messages from the frame have been received, we keep a strong reference to
   // our <browser> element.
   nsRefPtr<mozilla::dom::Element> mOwnerContentStrong;
--- a/dom/base/test/chrome/chrome.ini
+++ b/dom/base/test/chrome/chrome.ini
@@ -12,16 +12,17 @@ support-files =
   file_bug616841.xul
   file_bug816340.xul
   file_bug990812-1.xul
   file_bug990812-2.xul
   file_bug990812-3.xul
   file_bug990812-4.xul
   file_bug990812-5.xul
   file_bug1139964.xul
+  file_bug1209621.xul
   fileconstructor_file.png
   frame_bug814638.xul
   frame_registerElement_content.html
   registerElement_ep.js
   host_bug814638.xul
   window_nsITextInputProcessor.xul
   title_window.xul
 
@@ -57,16 +58,17 @@ skip-if = buildapp == 'mulet'
 [test_bug780529.xul]
 [test_bug800386.xul]
 [test_bug814638.xul]
 [test_bug816340.xul]
 [test_bug914381.html]
 [test_bug990812.xul]
 [test_bug1063837.xul]
 [test_bug1139964.xul]
+[test_bug1209621.xul]
 [test_cpows.xul]
 skip-if = buildapp == 'mulet'
 [test_document_register.xul]
 [test_mutationobserver_anonymous.html]
 [test_registerElement_content.xul]
 [test_registerElement_ep.xul]
 [test_domparsing.xul]
 [test_fileconstructor.xul]
new file mode 100644
--- /dev/null
+++ b/dom/base/test/chrome/file_bug1209621.xul
@@ -0,0 +1,79 @@
+<?xml version="1.0"?>
+<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
+                 type="text/css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1209621
+-->
+<window title="Mozilla Bug 1209621"
+  xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
+  onload="run()">
+  <label value="Mozilla Bug 1209621"/>
+  <!-- test code goes here -->
+  <script type="application/javascript"><![CDATA[
+  var Cc = Components.classes;
+  var Ci = Components.interfaces;
+  var Cr = Components.results;
+  var Cu = Components.utils;
+  function ok(cond, msg) {
+    opener.wrappedJSObject.ok(cond, msg);
+  }
+
+  function is(actual, expected, msg) {
+    opener.wrappedJSObject.is(actual, expected, msg);
+  }
+
+  function run() {
+    var docshell = window.getInterface(Ci.nsIDocShell);
+    ok(docshell, "Active window should have a DocShell");
+    var treeOwner = docshell.treeOwner;
+    ok(treeOwner, "Active docshell should have a TreeOwner!");
+
+    is(treeOwner.primaryContentShell, null,
+       "There shouldn't be primaryContentShell because no browser has type=content-primary.");
+    is(treeOwner.primaryTabParent, null,
+       "There shouldn't be primaryTabParent because no remote browser has type=content-primary.");
+
+    var ip = document.getElementById("inprocess");
+    var remote = document.getElementById("remote");
+    var remote2 = document.getElementById("remote2");
+
+    ip.setAttribute("type", "content-primary");
+    ok(ip.docShell, "non-remote browser should have a DocShell.");
+    is(treeOwner.primaryContentShell, ip.docShell,
+       "content-primary browser should be the primaryContentShell.");
+    is(treeOwner.primaryTabParent, null,
+       "There shouldn't be primaryTabParent because no remote browser has type=content-primary.");
+
+    ip.setAttribute("type", "content");
+    remote.setAttribute("type", "content-primary");
+    is(treeOwner.primaryContentShell, null,
+       "There shouldn't be primaryContentShell because no browser has type=content-primary.");
+    var tp = remote.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader.tabParent;
+    ok(tp, "Remote browsers should have a TabParent.");
+    is(treeOwner.primaryTabParent, tp,
+       "content-primary remote browser should be the primaryTabParent.");
+
+    remote.setAttribute("type", "content");
+    is(treeOwner.primaryContentShell, null,
+       "There shouldn't be primaryContentShell because no browser has type=content-primary.");
+    is(treeOwner.primaryTabParent, null,
+       "There shouldn't be primaryTabParent because no remote browser has type=content-primary.");
+
+    remote2.setAttribute("type", "content-primary");
+    var tp2 = remote2.QueryInterface(Ci.nsIFrameLoaderOwner).frameLoader.tabParent;
+    ok(tp2, "Remote browsers should have a TabParent.");
+    is(treeOwner.primaryTabParent, tp2,
+       "content-primary remote browser should be the primaryTabParent.");
+    is(treeOwner.primaryContentShell, null,
+       "There shouldn't be primaryContentShell because no browser has type=content-primary.");
+
+    opener.setTimeout("done()", 0);
+    window.close();
+  }
+
+  ]]></script>
+  <browser type="content" src="about:blank" id="inprocess"/>
+  <browser type="content" remote="true" src="about:blank" id="remote"/>
+  <browser type="content" remote="true" src="about:blank" id="remote2"/>
+</window>
new file mode 100644
--- /dev/null
+++ b/dom/base/test/chrome/test_bug1209621.xul
@@ -0,0 +1,34 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=1209621
+-->
+<window title="Mozilla Bug 1209621"
+        xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
+
+  <!-- test results are displayed in the html:body -->
+  <body xmlns="http://www.w3.org/1999/xhtml">
+  <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1209621"
+     target="_blank">Mozilla Bug 1209621</a>
+  </body>
+
+  <!-- test code goes here -->
+  <script type="application/javascript">
+  <![CDATA[
+  /** Test for Bug 1209621 **/
+
+  SimpleTest.waitForExplicitFinish();
+
+  function done() {
+    SimpleTest.finish();
+  }
+
+  addLoadEvent(function() {
+    window.open("file_bug1209621.xul", "", "chrome");
+  });
+
+  ]]>
+  </script>
+</window>
--- a/embedding/browser/nsDocShellTreeOwner.cpp
+++ b/embedding/browser/nsDocShellTreeOwner.cpp
@@ -49,16 +49,17 @@
 #include "nsIDOMHTMLElement.h"
 #include "nsIPresShell.h"
 #include "nsPIDOMWindow.h"
 #include "nsPIWindowRoot.h"
 #include "nsIDOMWindowCollection.h"
 #include "nsIWindowWatcher.h"
 #include "nsPIWindowWatcher.h"
 #include "nsIPrompt.h"
+#include "nsITabParent.h"
 #include "nsRect.h"
 #include "nsIWebBrowserChromeFocus.h"
 #include "nsIContent.h"
 #include "imgIContainer.h"
 #include "nsContextMenuInfo.h"
 #include "nsPresContext.h"
 #include "nsViewManager.h"
 #include "nsView.h"
@@ -331,16 +332,17 @@ nsDocShellTreeOwner::ContentShellAdded(n
                                        const nsAString& aID)
 {
   if (mTreeOwner)
     return mTreeOwner->ContentShellAdded(aContentShell, aPrimary, aTargetable,
                                          aID);
 
   if (aPrimary) {
     mPrimaryContentShell = aContentShell;
+    mPrimaryTabParent = nullptr;
   }
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDocShellTreeOwner::ContentShellRemoved(nsIDocShellTreeItem* aContentShell)
 {
   if (mTreeOwner) {
@@ -359,23 +361,69 @@ nsDocShellTreeOwner::GetPrimaryContentSh
 {
   NS_ENSURE_ARG_POINTER(aShell);
 
   if (mTreeOwner) {
     return mTreeOwner->GetPrimaryContentShell(aShell);
   }
 
   nsCOMPtr<nsIDocShellTreeItem> shell;
-  shell = mPrimaryContentShell ? mPrimaryContentShell : mWebBrowser->mDocShell;
+  if (!mPrimaryTabParent) {
+    shell =
+      mPrimaryContentShell ? mPrimaryContentShell : mWebBrowser->mDocShell;
+  }
   shell.forget(aShell);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsDocShellTreeOwner::TabParentAdded(nsITabParent* aTab, bool aPrimary)
+{
+  if (mTreeOwner) {
+    return mTreeOwner->TabParentAdded(aTab, aPrimary);
+  }
+
+  if (aPrimary) {
+    mPrimaryTabParent = aTab;
+    mPrimaryContentShell = nullptr;
+  } else if (mPrimaryTabParent == aTab) {
+    mPrimaryTabParent = nullptr;
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShellTreeOwner::TabParentRemoved(nsITabParent* aTab)
+{
+  if (mTreeOwner) {
+    return mTreeOwner->TabParentRemoved(aTab);
+  }
+
+  if (aTab == mPrimaryTabParent) {
+    mPrimaryTabParent = nullptr;
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDocShellTreeOwner::GetPrimaryTabParent(nsITabParent** aTab)
+{
+  if (mTreeOwner) {
+    return mTreeOwner->GetPrimaryTabParent(aTab);
+  }
+
+  nsCOMPtr<nsITabParent> tab = mPrimaryTabParent;
+  tab.forget(aTab);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsDocShellTreeOwner::SizeShellTo(nsIDocShellTreeItem* aShellItem,
                                  int32_t aCX, int32_t aCY)
 {
   nsCOMPtr<nsIWebBrowserChrome> webBrowserChrome = GetWebBrowserChrome();
 
   NS_ENSURE_STATE(mTreeOwner || webBrowserChrome);
 
   if (mTreeOwner) {
--- a/embedding/browser/nsDocShellTreeOwner.h
+++ b/embedding/browser/nsDocShellTreeOwner.h
@@ -125,16 +125,17 @@ protected:
   // the objects that listen for chrome events like context menus and tooltips.
   // They are separate objects to avoid circular references between |this|
   // and the DOM.
   nsRefPtr<ChromeTooltipListener> mChromeTooltipListener;
   nsRefPtr<ChromeContextMenuListener> mChromeContextMenuListener;
 
   nsCOMPtr<nsIPrompt> mPrompter;
   nsCOMPtr<nsIAuthPrompt> mAuthPrompter;
+  nsCOMPtr<nsITabParent> mPrimaryTabParent;
 };
 
 
 // The class that listens to the chrome events and tells the embedding chrome to
 // show tooltips, as appropriate. Handles registering itself with the DOM with
 // AddChromeListeners() and removing itself with RemoveChromeListeners().
 class ChromeTooltipListener final : public nsIDOMEventListener
 {
--- a/xpfe/appshell/nsChromeTreeOwner.cpp
+++ b/xpfe/appshell/nsChromeTreeOwner.cpp
@@ -245,16 +245,37 @@ nsChromeTreeOwner::ContentShellRemoved(n
 }
 
 NS_IMETHODIMP nsChromeTreeOwner::GetPrimaryContentShell(nsIDocShellTreeItem** aShell)
 {
    NS_ENSURE_STATE(mXULWindow);
    return mXULWindow->GetPrimaryContentShell(aShell);
 }
 
+NS_IMETHODIMP
+nsChromeTreeOwner::TabParentAdded(nsITabParent* aTab, bool aPrimary)
+{
+  NS_ENSURE_STATE(mXULWindow);
+  return mXULWindow->TabParentAdded(aTab, aPrimary);
+}
+
+NS_IMETHODIMP
+nsChromeTreeOwner::TabParentRemoved(nsITabParent* aTab)
+{
+  NS_ENSURE_STATE(mXULWindow);
+  return mXULWindow->TabParentRemoved(aTab);
+}
+
+NS_IMETHODIMP
+nsChromeTreeOwner::GetPrimaryTabParent(nsITabParent** aTab)
+{
+  NS_ENSURE_STATE(mXULWindow);
+  return mXULWindow->GetPrimaryTabParent(aTab);
+}
+
 NS_IMETHODIMP nsChromeTreeOwner::SizeShellTo(nsIDocShellTreeItem* aShellItem,
    int32_t aCX, int32_t aCY)
 {
    NS_ENSURE_STATE(mXULWindow);
    return mXULWindow->SizeShellTo(aShellItem, aCX, aCY);
 }
 
 NS_IMETHODIMP
--- a/xpfe/appshell/nsContentTreeOwner.cpp
+++ b/xpfe/appshell/nsContentTreeOwner.cpp
@@ -307,16 +307,37 @@ nsContentTreeOwner::ContentShellRemoved(
 
 NS_IMETHODIMP
 nsContentTreeOwner::GetPrimaryContentShell(nsIDocShellTreeItem** aShell)
 {
    NS_ENSURE_STATE(mXULWindow);
    return mXULWindow->GetPrimaryContentShell(aShell);
 }
 
+NS_IMETHODIMP
+nsContentTreeOwner::TabParentAdded(nsITabParent* aTab, bool aPrimary)
+{
+  NS_ENSURE_STATE(mXULWindow);
+  return mXULWindow->TabParentAdded(aTab, aPrimary);
+}
+
+NS_IMETHODIMP
+nsContentTreeOwner::TabParentRemoved(nsITabParent* aTab)
+{
+  NS_ENSURE_STATE(mXULWindow);
+  return mXULWindow->TabParentRemoved(aTab);
+}
+
+NS_IMETHODIMP
+nsContentTreeOwner::GetPrimaryTabParent(nsITabParent** aTab)
+{
+  NS_ENSURE_STATE(mXULWindow);
+  return mXULWindow->GetPrimaryTabParent(aTab);
+}
+
 NS_IMETHODIMP nsContentTreeOwner::SizeShellTo(nsIDocShellTreeItem* aShellItem,
    int32_t aCX, int32_t aCY)
 {
    NS_ENSURE_STATE(mXULWindow);
    return mXULWindow->SizeShellTo(aShellItem, aCX, aCY);
 }
 
 NS_IMETHODIMP
--- a/xpfe/appshell/nsIXULWindow.idl
+++ b/xpfe/appshell/nsIXULWindow.idl
@@ -13,17 +13,17 @@
  * notification through the global observer service.
  */
 
 interface nsIDocShell;
 interface nsIDocShellTreeItem;
 interface nsIXULBrowserWindow;
 interface nsITabParent;
 
-[scriptable, uuid(a68a40b9-f7df-47ff-a874-2af3df7eb888)]
+[scriptable, uuid(d6d7a014-e28d-4c9d-8727-1cf6d870619b)]
 interface nsIXULWindow : nsISupports
 {
   /**
    * The docshell owning the XUL for this window.
    */
   readonly attribute nsIDocShell docShell;
 
   /**
@@ -37,16 +37,25 @@ interface nsIXULWindow : nsISupports
    * Note that this is a docshell tree item and therefore can not be assured of
    * what object it is. It could be an editor, a docshell, or a browser object.
    * Or down the road any other object that supports being a DocShellTreeItem
    * Query accordingly to determine the capabilities.
    */
   readonly attribute nsIDocShellTreeItem primaryContentShell;
 
   /**
+   * In multiprocess case we may not have primaryContentShell but
+   * primaryTabParent.
+   */
+  readonly attribute nsITabParent primaryTabParent;
+
+  void tabParentAdded(in nsITabParent aTab, in boolean aPrimary);
+  void tabParentRemoved(in nsITabParent aTab);
+
+  /**
    * The content shell specified by the supplied id.
    *
    * Note that this is a docshell tree item and therefore can not be assured of
    * what object it is.  It could be an editor, a docshell, or a browser object.
    * Or down the road any other object that supports being a DocShellTreeItem
    * Query accordingly to determine the capabilities.
    */
   nsIDocShellTreeItem getContentShellById(in wstring ID);
--- a/xpfe/appshell/nsXULWindow.cpp
+++ b/xpfe/appshell/nsXULWindow.cpp
@@ -314,16 +314,47 @@ NS_IMETHODIMP nsXULWindow::GetIntrinsica
 NS_IMETHODIMP nsXULWindow::GetPrimaryContentShell(nsIDocShellTreeItem** 
    aDocShellTreeItem)
 {
   NS_ENSURE_ARG_POINTER(aDocShellTreeItem);
   NS_IF_ADDREF(*aDocShellTreeItem = mPrimaryContentShell);
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsXULWindow::TabParentAdded(nsITabParent* aTab, bool aPrimary)
+{
+  if (aPrimary) {
+    mPrimaryTabParent = aTab;
+    mPrimaryContentShell = nullptr;
+  } else if (mPrimaryTabParent == aTab) {
+    mPrimaryTabParent = nullptr;
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXULWindow::TabParentRemoved(nsITabParent* aTab)
+{
+  if (aTab == mPrimaryTabParent) {
+    mPrimaryTabParent = nullptr;
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsXULWindow::GetPrimaryTabParent(nsITabParent** aTab)
+{
+  nsCOMPtr<nsITabParent> tab = mPrimaryTabParent;
+  tab.forget(aTab);
+  return NS_OK;
+}
+
 NS_IMETHODIMP nsXULWindow::GetContentShellById(const char16_t* aID, 
    nsIDocShellTreeItem** aDocShellTreeItem)
 {
   NS_ENSURE_ARG_POINTER(aDocShellTreeItem);
   *aDocShellTreeItem = nullptr;
 
   uint32_t count = mContentShells.Length();
   for (uint32_t i = 0; i < count; i++) {
@@ -1560,16 +1591,17 @@ nsresult nsXULWindow::ContentShellAdded(
     mContentShells.AppendElement(shellInfo);
   }
     
   // Set the default content tree owner
   if (aPrimary) {
     NS_ENSURE_SUCCESS(EnsurePrimaryContentTreeOwner(), NS_ERROR_FAILURE);
     aContentShell->SetTreeOwner(mPrimaryContentTreeOwner);
     mPrimaryContentShell = aContentShell;
+    mPrimaryTabParent = nullptr;
   }
   else {
     NS_ENSURE_SUCCESS(EnsureContentTreeOwner(), NS_ERROR_FAILURE);
     aContentShell->SetTreeOwner(mContentTreeOwner);
     if (mPrimaryContentShell == aContentShell)
       mPrimaryContentShell = nullptr;
   }
 
--- a/xpfe/appshell/nsXULWindow.h
+++ b/xpfe/appshell/nsXULWindow.h
@@ -152,16 +152,18 @@ protected:
    uint32_t                mContextFlags;
    uint32_t                mPersistentAttributesDirty; // persistentAttributes
    uint32_t                mPersistentAttributesMask;
    uint32_t                mChromeFlags;
    nsString                mTitle;
    nsIntRect               mOpenerScreenRect; // the screen rect of the opener
 
    nsCOMArray<nsIWeakReference> mTargetableShells; // targetable shells only
+
+   nsCOMPtr<nsITabParent> mPrimaryTabParent;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsXULWindow, NS_XULWINDOW_IMPL_CID)
 
 // nsContentShellInfo
 // Used to map shell IDs to nsIDocShellTreeItems.
 
 class nsContentShellInfo