Bug 1241837 - Use proxy for browsers property in tabbrowser instead of explicit array. r=dao
☠☠ backed out by 3a53eb9e6554 ☠ ☠
authorAllasso Travesser <allassopraise@gmail.com>
Wed, 03 Feb 2016 16:05:56 +0100
changeset 319166 97c27a348f0963da5bce6b667612e7ba2d209078
parent 319165 6826dfd0e85b29050e45273d9bfdb27dc2bc1128
child 319167 6599b14546f1a62e7089e9b8b404799db54b5da0
push id5913
push userjlund@mozilla.com
push dateMon, 25 Apr 2016 16:57:49 +0000
treeherdermozilla-beta@dcaf0a6fa115 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdao
bugs1241837
milestone47.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 1241837 - Use proxy for browsers property in tabbrowser instead of explicit array. r=dao
browser/base/content/tabbrowser.xml
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -1807,18 +1807,17 @@
                           Services.prefs.getBoolPref("browser.tabs.animate");
             if (!animate) {
               t.setAttribute("fadein", "true");
               setTimeout(function (tabContainer) {
                 tabContainer._handleNewTab(t);
               }, 0, this.tabContainer);
             }
 
-            // invalidate caches
-            this._browsers = null;
+            // invalidate cache
             this._visibleTabs = null;
 
             this.tabContainer.appendChild(t);
 
             // If this new tab is owned by another, assert that relationship
             if (aOwner)
               t.owner = aOwner;
 
@@ -2333,20 +2332,16 @@
             // destroyed until the document goes away.  So we force a
             // cleanup ourselves.
             // This has to happen before we remove the child so that the
             // XBL implementation of nsIObserver still works.
             browser.destroy();
 
             var wasPinned = aTab.pinned;
 
-            // Invalidate browsers cache, as the tab is removed from the
-            // tab container.
-            this._browsers = null;
-
             // Remove the tab ...
             this.tabContainer.removeChild(aTab);
 
             // ... and fix up the _tPos properties immediately.
             for (let i = aTab._tPos; i < this.tabs.length; i++)
               this.tabs[i]._tPos = i;
 
             if (!this._windowIsClosing) {
@@ -2814,25 +2809,42 @@
           ]]>
         </setter>
       </property>
 
       <property name="selectedBrowser"
                 onget="return this.mCurrentBrowser;"
                 readonly="true"/>
 
-      <property name="browsers" readonly="true">
-       <getter>
-          <![CDATA[
-            return this._browsers ||
-                   (this._browsers = Array.map(this.tabs, tab => tab.linkedBrowser));
-          ]]>
-        </getter>
-      </property>
-      <field name="_browsers">null</field>
+      <field name="browsers" readonly="true">
+        <![CDATA[
+          // This defines a proxy which allows us to access browsers by
+          // index without actually creating a full array of browsers.
+          new Proxy([], {
+            has: (target, name) => {
+              if (typeof name == "string" && Number.isInteger(parseInt(name))) {
+                return (name in this.tabs);
+              }
+              return false;
+            },
+            get: (target, name) => {
+              if (name == "length") {
+                return this.tabs.length;
+              }
+              if (typeof name == "string" && Number.isInteger(parseInt(name))) {
+                if (!(name in this.tabs)) {
+                  return undefined;
+                }
+                return this.tabs[name].linkedBrowser;
+              }
+              return target[name];
+            }
+          });
+        ]]>
+      </field>
 
       <!-- Moves a tab to a new browser window, unless it's already the only tab
            in the current window, in which case this will do nothing. -->
       <method name="replaceTabWithWindow">
         <parameter name="aTab"/>
         <parameter name="aOptions"/>
         <body>
           <![CDATA[
@@ -2883,18 +2895,17 @@
           this._lastRelatedTab = null;
 
           let wasFocused = (document.activeElement == this.mCurrentTab);
 
           aIndex = aIndex < aTab._tPos ? aIndex: aIndex+1;
           this.mCurrentTab._logicallySelected = false;
           this.mCurrentTab._visuallySelected = false;
 
-          // invalidate caches
-          this._browsers = null;
+          // invalidate cache
           this._visibleTabs = null;
 
           // use .item() instead of [] because dragging to the end of the strip goes out of
           // bounds: .item() returns null (so it acts like appendChild), but [] throws
           this.tabContainer.insertBefore(aTab, this.tabs.item(aIndex));
 
           for (let i = 0; i < this.tabs.length; i++) {
             this.tabs[i]._tPos = i;