Bug 1350642 - Keep the tabCountResizable property in sync; r?dao draft
authorHaik Aftandilian <haftandilian@mozilla.com>
Tue, 06 Mar 2018 17:41:44 -0800
changeset 803835 54a02c00a08e221284de80537ba29596939bfaf5
parent 803834 99b5df7d6ee9210671d3bb6cebe1d8036f5cbf8f
push id112204
push userbmo:mrbkap@mozilla.com
push dateMon, 04 Jun 2018 21:39:33 +0000
reviewersdao
bugs1350642
milestone62.0a1
Bug 1350642 - Keep the tabCountResizable property in sync; r?dao MozReview-Commit-ID: F1o37qVId5L
browser/base/content/browser.js
browser/base/content/tab-content.js
browser/base/content/tabbrowser.js
dom/base/nsGlobalWindowOuter.cpp
dom/ipc/ContentChild.cpp
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/DOMTypes.ipdlh
dom/ipc/TabChild.cpp
dom/tests/mochitest/general/test_resizeby.html
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1407,16 +1407,20 @@ var gBrowserInit = {
 
   _delayedStartup() {
     let { TelemetryTimestamps } =
       ChromeUtils.import("resource://gre/modules/TelemetryTimestamps.jsm", {});
     TelemetryTimestamps.add("delayedStartupStarted");
 
     this._cancelDelayedStartup();
 
+    gBrowser.initialBrowser
+            .messageManager
+            .sendAsyncMessage("Browser:HasSiblings", false);
+
     // We need to set the OfflineApps message listeners up before we
     // load homepages, which might need them.
     OfflineApps.init();
 
     // This pageshow listener needs to be registered before we may call
     // swapBrowsersAndCloseOther() to receive pageshow events fired by that.
     window.messageManager.addMessageListener("PageVisibility:Show", function(message) {
       if (message.target == gBrowser.selectedBrowser) {
--- a/browser/base/content/tab-content.js
+++ b/browser/base/content/tab-content.js
@@ -60,16 +60,24 @@ addMessageListener("Browser:HideSessionR
   let doc = content.document;
   let container;
   if (doc.documentURI.toLowerCase() == "about:home" &&
       (container = doc.getElementById("sessionRestoreContainer"))) {
     container.hidden = true;
   }
 });
 
+if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
+  addMessageListener("Browser:HasSiblings", function(message) {
+    let tabChild = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
+                           .getInterface(Ci.nsITabChild);
+    let hasSiblings = message.data;
+    tabChild.hasSiblings = hasSiblings;
+  });
+}
 
 // XXX(nika): Should we try to call this in the parent process instead?
 addMessageListener("Browser:Reload", function(message) {
   /* First, we'll try to use the session history object to reload so
    * that framesets are handled properly. If we're in a special
    * window (such as view-source) that has no session history, fall
    * back on using the web navigation's reload method.
    */
--- a/browser/base/content/tabbrowser.js
+++ b/browser/base/content/tabbrowser.js
@@ -1703,16 +1703,20 @@ window._gBrowser = {
     if (this.isFindBarInitialized(tab)) {
       this.getCachedFindBar(tab).browser = aBrowser;
     }
 
     evt = document.createEvent("Events");
     evt.initEvent("TabRemotenessChange", true, false);
     tab.dispatchEvent(evt);
 
+    tab.linkedBrowser
+       .messageManager
+       .sendAsyncMessage("Browser:HasSiblings", this.tabs.length > 1);
+
     return true;
   },
 
   updateBrowserRemotenessByURL(aBrowser, aURL, aOptions) {
     aOptions = aOptions || {};
 
     if (!gMultiProcessBrowser)
       return this.updateBrowserRemoteness(aBrowser, false);
@@ -2441,17 +2445,39 @@ window._gBrowser = {
         this._tabFilters.delete(t);
         this._tabListeners.delete(t);
         let notificationbox = this.getNotificationBox(t.linkedBrowser);
         notificationbox.remove();
       }
       throw e;
     }
 
-    // Hack to ensure that the about:newtab, and about:welcome favicon is loaded
+    // Update TabChild hasSiblings properties
+    if (!aCreateLazyBrowser) {
+      // If we transitioned from one tab to two tabs, we need to set
+      // hasSiblings=false on both the existing tab and the new tab.
+      // Tell the original tab it now has siblings
+      if (this.tabs.length == 2) {
+        // Don't assume any ordering of the tabs array.
+        for (let tab of this.tabs) {
+          if (tab !== t) {
+            tab.linkedBrowser
+               .messageManager
+               .sendAsyncMessage("Browser:HasSiblings", true);
+            break;
+          }
+        }
+      }
+
+      t.linkedBrowser
+       .messageManager
+       .sendAsyncMessage("Browser:HasSiblings", this.tabs.length > 1);
+    }
+
+    // Hack to ensure that the about:newtab favicon is loaded
     // instantaneously, to avoid flickering and improve perceived performance.
     if (aURI == "about:newtab" || aURI == "about:home" || aURI == "about:welcome") {
       this.setIcon(t, "chrome://branding/content/icon32.png");
     } else if (aURI == "about:privatebrowsing") {
       this.setIcon(t, "chrome://browser/skin/privatebrowsing/favicon.svg");
     }
 
     // Dispatch a new tab notification.  We do this once we're
@@ -2843,16 +2869,23 @@ window._gBrowser = {
 
     // We're committed to closing the tab now.
     // Dispatch a notification.
     // We dispatch it before any teardown so that event listeners can
     // inspect the tab that's about to close.
     var evt = new CustomEvent("TabClose", { bubbles: true, detail: { adoptedBy: aAdoptedByTab } });
     aTab.dispatchEvent(evt);
 
+    if (this.tabs.length == 2) {
+      // We're closing one of our two open tabs, inform the other tab that its
+      // sibling is going away.
+      window.messageManager
+            .broadcastAsyncMessage("Browser:HasSiblings", false);
+    }
+
     if (aTab.linkedPanel) {
       if (!aAdoptedByTab && !gMultiProcessBrowser) {
         // Prevent this tab from showing further dialogs, since we're closing it
         var windowUtils = browser.contentWindow.QueryInterface(Ci.nsIInterfaceRequestor)
                                                .getInterface(Ci.nsIDOMWindowUtils);
         windowUtils.disableDialogs();
       }
 
--- a/dom/base/nsGlobalWindowOuter.cpp
+++ b/dom/base/nsGlobalWindowOuter.cpp
@@ -4507,17 +4507,17 @@ nsGlobalWindowOuter::CanMoveResizeWindow
     if (XRE_IsContentProcess()) {
       nsCOMPtr<nsIDocShell> docShell = GetDocShell();
       if (docShell) {
         nsCOMPtr<nsITabChild> child = docShell->GetTabChild();
         bool hasSiblings = true;
         if (child &&
             NS_SUCCEEDED(child->GetHasSiblings(&hasSiblings)) &&
             hasSiblings) {
-          child->SendGetTabCount(&itemCount);
+          return false;
         }
       }
     } else {
       nsCOMPtr<nsIDocShellTreeOwner> treeOwner = GetTreeOwner();
       uint32_t itemCount = 0;
       if (treeOwner && NS_SUCCEEDED(treeOwner->GetTabCount(&itemCount)) &&
           itemCount > 1) {
         return false;
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -969,16 +969,17 @@ ContentChild::ProvideWindowCommon(TabChi
     *aWindowIsNew = info.windowOpened();
     nsTArray<FrameScriptInfo> frameScripts(info.frameScripts());
     nsCString urlToLoad = info.urlToLoad();
     TextureFactoryIdentifier textureFactoryIdentifier = info.textureFactoryIdentifier();
     layers::LayersId layersId = info.layersId();
     CompositorOptions compositorOptions = info.compositorOptions();
     uint32_t maxTouchPoints = info.maxTouchPoints();
     DimensionInfo dimensionInfo = info.dimensions();
+    bool hasSiblings = info.hasSiblings();
 
     // Once this function exits, we should try to exit the nested event loop.
     ready = true;
 
     // NOTE: We have to handle this immediately in the resolve callback in order
     // to make sure that we don't process any more IPC messages before returning
     // from ProvideWindowCommon.
 
@@ -1011,16 +1012,17 @@ ContentChild::ProvideWindowCommon(TabChi
       showInfo = ShowInfo(EmptyString(), false,
                           context->UsePrivateBrowsing(), true, false,
                           aTabOpener->WebWidget()->GetDPI(),
                           aTabOpener->WebWidget()->RoundsWidgetCoordinatesTo(),
                           aTabOpener->WebWidget()->GetDefaultScale().scale);
     }
 
     newChild->SetMaxTouchPoints(maxTouchPoints);
+    newChild->SetHasSiblings(hasSiblings);
 
     // Set the opener window for this window before we start loading the document
     // inside of it. We have to do this before loading the remote scripts, because
     // they can poke at the document and cause the nsDocument to be created before
     // the openerwindow
     nsCOMPtr<mozIDOMWindowProxy> windowProxy = do_GetInterface(newChild->WebNavigation());
     if (!aForceNoOpener && windowProxy && aParent) {
       nsPIDOMWindowOuter* outer = nsPIDOMWindowOuter::From(windowProxy);
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -4659,16 +4659,17 @@ ContentParent::CommonCreateWindow(PBrows
                                   const nsCString& aFeatures,
                                   const nsCString& aBaseURI,
                                   const float& aFullZoom,
                                   uint64_t aNextTabParentId,
                                   const nsString& aName,
                                   nsresult& aResult,
                                   nsCOMPtr<nsITabParent>& aNewTabParent,
                                   bool* aWindowIsNew,
+                                  int32_t& aOpenLocation,
                                   nsIPrincipal* aTriggeringPrincipal,
                                   uint32_t aReferrerPolicy,
                                   bool aLoadURI)
 
 {
   // The content process should never be in charge of computing whether or
   // not a window should be private or remote - the parent will do that.
   const uint32_t badFlags = nsIWebBrowserChrome::CHROME_PRIVATE_WINDOW |
@@ -4715,32 +4716,32 @@ ContentParent::CommonCreateWindow(PBrows
     }
 
     nsCOMPtr<nsIDOMChromeWindow> rootChromeWin = do_QueryInterface(outerWin);
     if (rootChromeWin) {
       rootChromeWin->GetBrowserDOMWindow(getter_AddRefs(browserDOMWin));
     }
   }
 
-  int32_t openLocation = nsWindowWatcher::GetWindowOpenLocation(
+  aOpenLocation = nsWindowWatcher::GetWindowOpenLocation(
     outerWin, aChromeFlags, aCalledFromJS, aPositionSpecified, aSizeSpecified);
 
-  MOZ_ASSERT(openLocation == nsIBrowserDOMWindow::OPEN_NEWTAB ||
-             openLocation == nsIBrowserDOMWindow::OPEN_NEWWINDOW);
+  MOZ_ASSERT(aOpenLocation == nsIBrowserDOMWindow::OPEN_NEWTAB ||
+             aOpenLocation == nsIBrowserDOMWindow::OPEN_NEWWINDOW);
 
   // Read the origin attributes for the tab from the opener tabParent.
   OriginAttributes openerOriginAttributes;
   if (thisTabParent) {
     nsCOMPtr<nsILoadContext> loadContext = thisTabParent->GetLoadContext();
     loadContext->GetOriginAttributes(openerOriginAttributes);
   } else if (Preferences::GetBool("browser.privatebrowsing.autostart")) {
     openerOriginAttributes.mPrivateBrowsingId = 1;
   }
 
-  if (openLocation == nsIBrowserDOMWindow::OPEN_NEWTAB) {
+  if (aOpenLocation == nsIBrowserDOMWindow::OPEN_NEWTAB) {
     if (NS_WARN_IF(!browserDOMWin)) {
       aResult = NS_ERROR_ABORT;
       return IPC_OK();
     }
 
     nsCOMPtr<nsIFrameLoaderOwner> opener = do_QueryInterface(frame);
 
     nsCOMPtr<nsIOpenURIInFrameParams> params =
@@ -4748,23 +4749,23 @@ ContentParent::CommonCreateWindow(PBrows
     params->SetReferrer(NS_ConvertUTF8toUTF16(aBaseURI));
     MOZ_ASSERT(aTriggeringPrincipal, "need a valid triggeringPrincipal");
     params->SetTriggeringPrincipal(aTriggeringPrincipal);
     params->SetReferrerPolicy(aReferrerPolicy);
 
     nsCOMPtr<nsIFrameLoaderOwner> frameLoaderOwner;
     if (aLoadURI) {
       aResult = browserDOMWin->OpenURIInFrame(aURIToLoad,
-                                              params, openLocation,
+                                              params, aOpenLocation,
                                               nsIBrowserDOMWindow::OPEN_NEW,
                                               aNextTabParentId, aName,
                                               getter_AddRefs(frameLoaderOwner));
     } else {
       aResult = browserDOMWin->CreateContentWindowInFrame(aURIToLoad,
-                                              params, openLocation,
+                                              params, aOpenLocation,
                                               nsIBrowserDOMWindow::OPEN_NEW,
                                               aNextTabParentId, aName,
                                               getter_AddRefs(frameLoaderOwner));
     }
     if (NS_SUCCEEDED(aResult) && frameLoaderOwner) {
       RefPtr<nsFrameLoader> frameLoader = frameLoaderOwner->GetFrameLoader();
       if (frameLoader) {
         aNewTabParent = frameLoader->GetTabParent();
@@ -4773,23 +4774,23 @@ ContentParent::CommonCreateWindow(PBrows
         // frameloader to display will be correct (instead of falling back to a 10x10
         // default), we force layout if necessary to get the most up-to-date dimensions.
         // See bug 1358712 for details.
         frameLoader->ForceLayoutIfNecessary();
       }
     } else if (NS_SUCCEEDED(aResult) && !frameLoaderOwner) {
       // Fall through to the normal window opening code path when there is no
       // window which we can open a new tab in.
-      openLocation = nsIBrowserDOMWindow::OPEN_NEWWINDOW;
+      aOpenLocation = nsIBrowserDOMWindow::OPEN_NEWWINDOW;
     } else {
       *aWindowIsNew = false;
     }
 
     // If we didn't retarget our window open into a new window, we should return now.
-    if (openLocation != nsIBrowserDOMWindow::OPEN_NEWWINDOW) {
+    if (aOpenLocation != nsIBrowserDOMWindow::OPEN_NEWWINDOW) {
       return IPC_OK();
     }
   }
 
   nsCOMPtr<nsPIWindowWatcher> pwwatch =
     do_GetService(NS_WINDOWWATCHER_CONTRACTID, &aResult);
   if (NS_WARN_IF(NS_FAILED(aResult))) {
     return IPC_OK();
@@ -4881,16 +4882,17 @@ ContentParent::RecvCreateWindow(PBrowser
 {
   nsresult rv = NS_OK;
   CreatedWindowInfo cwi;
 
   // We always expect to open a new window here. If we don't, it's an error.
   cwi.windowOpened() = true;
   cwi.layersId() = LayersId{0};
   cwi.maxTouchPoints() = 0;
+  cwi.hasSiblings() = false;
 
   // Make sure to resolve the resolver when this function exits, even if we
   // failed to generate a valid response.
   auto resolveOnExit = MakeScopeExit([&] {
     // Copy over the nsresult, and then resolve.
     cwi.rv() = rv;
     aResolve(cwi);
   });
@@ -4913,22 +4915,23 @@ ContentParent::RecvCreateWindow(PBrowser
 
   TabParent::AutoUseNewTab aunt(newTab, &cwi.urlToLoad());
   const uint64_t nextTabParentId = ++sNextTabParentId;
   sNextTabParents.Put(nextTabParentId, newTab);
 
   const nsCOMPtr<nsIURI> uriToLoad = DeserializeURI(aURIToLoad);
 
   nsCOMPtr<nsITabParent> newRemoteTab;
+  int32_t openLocation = nsIBrowserDOMWindow::OPEN_NEWWINDOW;
   mozilla::ipc::IPCResult ipcResult =
     CommonCreateWindow(aThisTab, /* aSetOpener = */ true, aChromeFlags,
                        aCalledFromJS, aPositionSpecified, aSizeSpecified,
                        uriToLoad, aFeatures, aBaseURI, aFullZoom,
                        nextTabParentId, VoidString(), rv,
-                       newRemoteTab, &cwi.windowOpened(),
+                       newRemoteTab, &cwi.windowOpened(), openLocation,
                        aTriggeringPrincipal, aReferrerPolicy,
                        /* aLoadUri = */ false);
   if (!ipcResult) {
     return ipcResult;
   }
 
   if (NS_WARN_IF(NS_FAILED(rv)) || !newRemoteTab) {
     return IPC_OK();
@@ -4949,16 +4952,18 @@ ContentParent::RecvCreateWindow(PBrowser
   cwi.compositorOptions() = rfp->GetCompositorOptions();
 
   nsCOMPtr<nsIWidget> widget = newTab->GetWidget();
   if (widget) {
     cwi.maxTouchPoints() = widget->GetMaxTouchPoints();
     cwi.dimensions() = newTab->GetDimensionInfo();
   }
 
+  cwi.hasSiblings() = (openLocation == nsIBrowserDOMWindow::OPEN_NEWTAB);
+
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 ContentParent::RecvCreateWindowInDifferentProcess(
   PBrowserParent* aThisTab,
   const uint32_t& aChromeFlags,
   const bool& aCalledFromJS,
@@ -4970,24 +4975,26 @@ ContentParent::RecvCreateWindowInDiffere
   const float& aFullZoom,
   const nsString& aName,
   const IPC::Principal& aTriggeringPrincipal,
   const uint32_t& aReferrerPolicy)
 {
   nsCOMPtr<nsITabParent> newRemoteTab;
   bool windowIsNew;
   nsCOMPtr<nsIURI> uriToLoad = DeserializeURI(aURIToLoad);
+  int32_t openLocation = nsIBrowserDOMWindow::OPEN_NEWWINDOW;
   nsresult rv;
   mozilla::ipc::IPCResult ipcResult =
     CommonCreateWindow(aThisTab, /* aSetOpener = */ false, aChromeFlags,
                        aCalledFromJS, aPositionSpecified, aSizeSpecified,
                        uriToLoad, aFeatures, aBaseURI, aFullZoom,
                        /* aNextTabParentId = */ 0, aName, rv,
-                       newRemoteTab, &windowIsNew, aTriggeringPrincipal,
-                       aReferrerPolicy, /* aLoadUri = */ true);
+                       newRemoteTab, &windowIsNew, openLocation,
+                       aTriggeringPrincipal, aReferrerPolicy,
+                       /* aLoadUri = */ true);
   if (!ipcResult) {
     return ipcResult;
   }
 
   if (NS_FAILED(rv)) {
     NS_WARNING("Call to CommonCreateWindow failed.");
   }
 
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -722,16 +722,17 @@ private:
                      const nsCString& aFeatures,
                      const nsCString& aBaseURI,
                      const float& aFullZoom,
                      uint64_t aNextTabParentId,
                      const nsString& aName,
                      nsresult& aResult,
                      nsCOMPtr<nsITabParent>& aNewTabParent,
                      bool* aWindowIsNew,
+                     int32_t& aOpenLocation,
                      nsIPrincipal* aTriggeringPrincipal,
                      uint32_t aReferrerPolicy,
                      bool aLoadUri);
 
   FORWARD_SHMEM_ALLOCATOR_TO(PContentParent)
 
   explicit ContentParent(int32_t aPluginID)
     : ContentParent(nullptr, EmptyString(), aPluginID)
--- a/dom/ipc/DOMTypes.ipdlh
+++ b/dom/ipc/DOMTypes.ipdlh
@@ -120,16 +120,17 @@ struct CreatedWindowInfo
   bool windowOpened;
   FrameScriptInfo[] frameScripts;
   nsCString urlToLoad;
   TextureFactoryIdentifier textureFactoryIdentifier;
   LayersId layersId;
   CompositorOptions compositorOptions;
   uint32_t maxTouchPoints;
   DimensionInfo dimensions;
+  bool hasSiblings;
 };
 
 
 /**
  * PerformanceInfo is used to pass performance info stored
  * in WorkerPrivate & DocGroup instances
  *
  * Each (host, pid, wid, pwid) is unique to a given DocGroup or
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -408,17 +408,17 @@ TabChild::TabChild(nsIContentChild* aMan
   , mDidFakeShow(false)
   , mNotified(false)
   , mTriedBrowserInit(false)
   , mOrientation(eScreenOrientation_PortraitPrimary)
   , mIgnoreKeyPressEvent(false)
   , mHasValidInnerSize(false)
   , mDestroyed(false)
   , mUniqueId(aTabId)
-  , mHasSiblings(true)
+  , mHasSiblings(false)
   , mIsTransparent(false)
   , mIPCOpen(false)
   , mParentIsActive(false)
   , mDidSetRealShowInfo(false)
   , mDidLoadURLInit(false)
   , mAwaitingLA(false)
   , mSkipKeyPress(false)
   , mLayerObserverEpoch(1)
@@ -3481,16 +3481,30 @@ TabChild::BeforeUnloadRemoved()
 }
 
 mozilla::dom::TabGroup*
 TabChild::TabGroup()
 {
   return mTabGroup;
 }
 
+nsresult
+TabChild::GetHasSiblings(bool* aHasSiblings)
+{
+  *aHasSiblings = mHasSiblings;
+  return NS_OK;
+}
+
+nsresult
+TabChild::SetHasSiblings(bool aHasSiblings)
+{
+  mHasSiblings = aHasSiblings;
+  return NS_OK;
+}
+
 TabChildGlobal::TabChildGlobal(TabChild* aTabChild)
 : ContentFrameMessageManager(new nsFrameMessageManager(aTabChild)),
   mTabChild(aTabChild)
 {
 }
 
 TabChildGlobal::~TabChildGlobal()
 {
--- a/dom/tests/mochitest/general/test_resizeby.html
+++ b/dom/tests/mochitest/general/test_resizeby.html
@@ -24,18 +24,19 @@
     await SimpleTest.promiseFocus();
 
     let clicky = document.querySelector("#clicky");
 
     let popupPromise = new Promise(resolve => {
       let linkclick = () => {
         clicky.removeEventListener('click', linkclick);
         let popup = window.open("about:blank", "_blank", "width=500,height=500");
-        is(popup.innerHeight, 500);
-        is(popup.innerWidth, 500);
+        is(popup.innerHeight, 500, "starting width is 500");
+        is(popup.innerWidth, 500, "starting height in 500");
+
         popup.resizeBy(50, 0);
 
         // We resized synchronously. If this happened, we sometimes won't fire
         // an resize event, so we resolve immediately.
         if (popup.innerWidth == 550) {
           resolve(popup);
         } else {
           let popupresize = () => {
@@ -47,15 +48,15 @@
       };
 
       clicky.addEventListener('click', linkclick);
     });
 
     synthesizeMouseAtCenter(clicky, {}, window);
 
     let popup = await popupPromise;
-    is(popup.innerHeight, 500);
-    is(popup.innerWidth, 550);
+    is(popup.innerHeight, 500, "ending height is 500");
+    is(popup.innerWidth, 550, "ending width is 550");
     popup.close();
   });
 </script>
 </body>
 </html>