Bug 1238314: Part 1 - Track opener tabs separately from owner and selected tab. r?Gijs draft
authorKris Maglione <maglione.k@gmail.com>
Sat, 05 Aug 2017 16:19:36 -0700
changeset 641145 65673cf4e43f33f4c2eb36e80a2c56c883f82e61
parent 641036 c47cd9c6e8d2423b19bb8812978b4afe4ea0cf37
child 641146 52fa4d59801017906bb461b891be1c2657c8bd7d
child 641153 755ab4021e85dcf17c2b4ca85874bd791f9d621c
push id72450
push usermaglione.k@gmail.com
push dateSat, 05 Aug 2017 23:28:24 +0000
reviewersGijs
bugs1238314
milestone57.0a1
Bug 1238314: Part 1 - Track opener tabs separately from owner and selected tab. r?Gijs MozReview-Commit-ID: MOetgoeYcL
browser/base/content/tabbrowser.xml
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -77,18 +77,18 @@
         document.getAnonymousElementByAttribute(this, "anonid", "panelcontainer");
       </field>
       <field name="mStringBundle">
         document.getAnonymousElementByAttribute(this, "anonid", "tbstringbundle");
       </field>
       <field name="mCurrentTab">
         null
       </field>
-      <field name="_lastRelatedTab">
-        null
+      <field name="_lastRelatedTabMap">
+        new WeakMap();
       </field>
       <field name="mCurrentBrowser">
         null
       </field>
       <field name="mProgressListeners">
         []
       </field>
       <field name="mTabsProgressListeners">
@@ -1119,21 +1119,22 @@
             }
 
             var oldTab = this.mCurrentTab;
 
             // Preview mode should not reset the owner
             if (!this._previewMode && !oldTab.selected)
               oldTab.owner = null;
 
-            if (this._lastRelatedTab) {
-              if (!this._lastRelatedTab.selected)
-                this._lastRelatedTab.owner = null;
-              this._lastRelatedTab = null;
-            }
+            let lastRelatedTab = this._lastRelatedTabMap.get(oldTab);
+            if (lastRelatedTab) {
+              if (!lastRelatedTab.selected)
+                lastRelatedTab.owner = null;
+            }
+            this._lastRelatedTabMap = new WeakMap();
 
             var oldBrowser = this.mCurrentBrowser;
 
             if (!gMultiProcessBrowser) {
               oldBrowser.removeAttribute("primary");
               oldBrowser.docShellIsActive = false;
               newBrowser.setAttribute("primary", "true");
               newBrowser.docShellIsActive =
@@ -2458,18 +2459,41 @@
               aFocusUrlBar              = params.focusUrlBar;
               aName                     = params.name;
             }
 
             // if we're adding tabs, we're past interrupt mode, ditch the owner
             if (this.mCurrentTab.owner)
               this.mCurrentTab.owner = null;
 
+            // Find the tab that opened this one, if any. This is used for
+            // determining positioning, and inherited attributes such as the
+            // user context ID.
+            //
+            // If we're passed an explicit owner tab, that takes precedence,
+            // and we treat that as the opener. If not, and we have a browser
+            // opener (which is usually the top-level browser from a remote
+            // window.open() call), use that.
+            //
+            // Finally, if the tab is related to the current tab (e.g.,
+            // because it was opened by a link click), use the selected tab as
+            // the owner. If aReferrerURI is set, and we don't have an
+            // explicit relatedToCurrent arg, we assume that the tab is
+            // related to the current tab, since aReferrerURI is null or
+            // undefined if the tab is opened from an external application or
+            // bookmark (i.e. somewhere other than an existing tab).
+            let relatedToCurrent = aRelatedToCurrent == null ? !!aReferrerURI : aRelatedToCurrent;
+            let openerTab = (aOwner ||
+                             (aOpenerBrowser && this.getTabForBrowser(aOpenerBrowser)) ||
+                             (relatedToCurrent && this.selectedTab));
+
             var t = document.createElementNS(NS_XUL, "tab");
 
+            t.openerTab = openerTab;
+
             aURI = aURI || "about:blank";
             let aURIObject = null;
             try {
               aURIObject = Services.io.newURI(aURI);
             } catch (ex) { /* we'll try to fix up this URL later */ }
 
             let lazyBrowserURI;
             if (aCreateLazyBrowser && aURI != "about:blank") {
@@ -2489,19 +2513,18 @@
             }
 
             if (aIsPrerendered) {
               t.setAttribute("hidden", "true");
             }
 
             // Related tab inherits current tab's user context unless a different
             // usercontextid is specified
-            if (aUserContextId == null &&
-                (aRelatedToCurrent == null ? aReferrerURI : aRelatedToCurrent)) {
-              aUserContextId = this.mCurrentTab.getAttribute("usercontextid") || 0;
+            if (aUserContextId == null && openerTab) {
+              aUserContextId = openerTab.getAttribute("usercontextid") || 0;
             }
 
             if (aUserContextId) {
               t.setAttribute("usercontextid", aUserContextId);
               ContextualIdentityService.setTabStyle(t);
             }
 
             t.setAttribute("onerror", "this.removeAttribute('image');");
@@ -2664,31 +2687,29 @@
                   charset: aCharset,
                   postData: aPostData,
                 });
               } catch (ex) {
                 Cu.reportError(ex);
               }
             }
 
-            // Check if we're opening a tab related to the current tab and
-            // move it to after the current tab.
-            // aReferrerURI is null or undefined if the tab is opened from
-            // an external application or bookmark, i.e. somewhere other
-            // than the current tab.
-            if ((aRelatedToCurrent == null ? aReferrerURI : aRelatedToCurrent) &&
+            // If we're opening a tab related to the an existing tab, move it
+            // to a position after that tab.
+            if (openerTab &&
                 Services.prefs.getBoolPref("browser.tabs.insertRelatedAfterCurrent")) {
-              let newTabPos = (this._lastRelatedTab ||
-                               this.selectedTab)._tPos + 1;
-              if (this._lastRelatedTab)
-                this._lastRelatedTab.owner = null;
+
+              let lastRelatedTab = this._lastRelatedTabMap.get(openerTab);
+              let newTabPos = (lastRelatedTab || openerTab)._tPos + 1;
+              if (lastRelatedTab)
+                lastRelatedTab.owner = null;
               else
-                t.owner = this.selectedTab;
-              this.moveTabTo(t, newTabPos);
-              this._lastRelatedTab = t;
+                t.owner = openerTab;
+              this.moveTabTo(t, newTabPos, true);
+              this._lastRelatedTabMap.set(openerTab, t);
             }
 
             // This field is updated regardless if we actually animate
             // since it's important that we keep this count correct in all cases.
             this.tabAnimationsInProgress++;
 
             if (animate) {
               requestAnimationFrame(function() {
@@ -3075,17 +3096,17 @@
 
             if (this._windowIsClosing) {
               aCloseWindow = false;
               aNewTab = false;
             }
 
             this.tabAnimationsInProgress--;
 
-            this._lastRelatedTab = null;
+            this._lastRelatedTabMap = new WeakMap();
 
             // update the UI early for responsiveness
             aTab.collapsed = true;
             this._blurTab(aTab);
 
             this._removingTabs.splice(this._removingTabs.indexOf(aTab), 1);
 
             if (aCloseWindow) {
@@ -3725,31 +3746,34 @@
             return window.openDialog("chrome://browser/content/", "_blank", "chrome,all,dialog=no,non-remote", url);
           ]]>
         </body>
       </method>
 
       <method name="moveTabTo">
         <parameter name="aTab"/>
         <parameter name="aIndex"/>
+        <parameter name="aKeepRelatedTabs"/>
         <body>
         <![CDATA[
           var oldPosition = aTab._tPos;
           if (oldPosition == aIndex)
             return;
 
           // Don't allow mixing pinned and unpinned tabs.
           if (aTab.pinned)
             aIndex = Math.min(aIndex, this._numPinnedTabs - 1);
           else
             aIndex = Math.max(aIndex, this._numPinnedTabs);
           if (oldPosition == aIndex)
             return;
 
-          this._lastRelatedTab = null;
+          if (!aKeepRelatedTabs) {
+            this._lastRelatedTabMap = new WeakMap();
+          }
 
           let wasFocused = (document.activeElement == this.mCurrentTab);
 
           aIndex = aIndex < aTab._tPos ? aIndex : aIndex + 1;
 
           // invalidate cache
           this._visibleTabs = null;