Bug 625269 - Reduce some unnecessary resizing of Items when resizing the window [r=ian, a=dietrich]
authorMichael Yoshitaka Erlewine <mitcho@mitcho.com>
Fri, 21 Jan 2011 10:21:30 -0500
changeset 61112 ddd42df9beb7d5ed80180f52cd9ba1507f71e4da
parent 61111 8f8108abcff2be98d287d9b934d33cff428bbba3
child 61113 84ed248b728db45455ebc54179dbb4d314a689cb
push idunknown
push userunknown
push dateunknown
reviewersian, dietrich
bugs625269
milestone2.0b10pre
Bug 625269 - Reduce some unnecessary resizing of Items when resizing the window [r=ian, a=dietrich]
browser/base/content/tabview/groupitems.js
browser/base/content/tabview/tabitems.js
browser/base/content/tabview/ui.js
browser/base/content/test/tabview/Makefile.in
browser/base/content/test/tabview/browser_tabview_bug625269.js
--- a/browser/base/content/tabview/groupitems.js
+++ b/browser/base/content/tabview/groupitems.js
@@ -486,16 +486,18 @@ GroupItem.prototype = Utils.extend(new I
 
       this.$titlebar.animate(titlebarCSS, {
         duration: 350
       });
     }
 
     this.adjustTitleSize();
 
+    UI.clearShouldResizeItems();
+
     this._updateDebugBounds();
     this.setTrenches(rect);
 
     this.save();
   },
 
   // ----------
   // Function: setZ
--- a/browser/base/content/tabview/tabitems.js
+++ b/browser/base/content/tabview/tabitems.js
@@ -491,16 +491,18 @@ TabItem.prototype = Utils.extend(new Ite
          "padding-bottom": pad + "px",
          "border-color": "rgba(0,0,0,"+ alphaRange.scale(proportion) +")",
         });
       }
 
       this._hasBeenDrawn = true;
     }
 
+    UI.clearShouldResizeItems();
+
     this._updateDebugBounds();
     rect = this.getBounds(); // ensure that it's a <Rect>
 
     if (!Utils.isRect(this.bounds))
       Utils.trace('TabItem.setBounds: this.bounds is not a real rectangle!', this.bounds);
 
     if (!this.parent && this.tab.parentNode != null)
       this.setTrenches(rect);
--- a/browser/base/content/tabview/ui.js
+++ b/browser/base/content/tabview/ui.js
@@ -1199,26 +1199,31 @@ let UI = {
   // Update the TabView UI contents in response to a window size change.
   // Won't do anything if it doesn't deem the resize necessary.
   // Parameters:
   //   force - true to update even when "unnecessary"; default false
   _resize: function UI__resize(force) {
     if (!this._pageBounds)
       return;
 
-    // If TabView isn't focused and is not showing, don't perform a resize.
-    // This resize really slows things down.
+    // Here are reasons why we *won't* resize:
+    // 1. Panorama isn't visible (in which case we will resize when we do display)
+    // 2. the screen dimensions haven't changed
+    // 3. everything on the screen fits and nothing feels cramped
     if (!force && !this.isTabViewVisible())
       return;
 
-    var oldPageBounds = new Rect(this._pageBounds);
-    var newPageBounds = Items.getPageBounds();
+    let oldPageBounds = new Rect(this._pageBounds);
+    let newPageBounds = Items.getPageBounds();
     if (newPageBounds.equals(oldPageBounds))
       return;
 
+    if (!this.shouldResizeItems())
+      return;
+
     var items = Items.getTopLevelItems();
 
     // compute itemBounds: the union of all the top-level items' bounds.
     var itemBounds = new Rect(this._pageBounds);
     // We start with pageBounds so that we respect the empty space the user
     // has left on the page.
     itemBounds.width = 1;
     itemBounds.height = 1;
@@ -1276,16 +1281,68 @@ let UI = {
     pairs.forEach(function(pair) {
       pair.item.setBounds(pair.bounds, true);
       pair.item.snap();
     });
 
     this._pageBounds = Items.getPageBounds();
     this._save();
   },
+  
+  // ----------
+  // Function: shouldResizeItems
+  // Returns whether we should resize the items on the screen, based on whether
+  // the top-level items fit in the screen or not and whether they feel
+  // "cramped" or not.
+  // These computations may be done using cached values. The cache can be
+  // cleared with UI.clearShouldResizeItems().
+  shouldResizeItems: function UI_shouldResizeItems() {
+
+    let newPageBounds = Items.getPageBounds();
+    
+    // If we don't have cached cached values...
+    if (this._minimalRect === undefined || this._feelsCramped === undefined) {
+
+      // Loop through every top-level Item for two operations:
+      // 1. check if it is feeling "cramped" due to squishing (a technical term),
+      // 2. union its bounds with the minimalRect
+      let feelsCramped = false;
+      let minimalRect = new Rect(0, 0, 1, 1);
+      
+      Items.getTopLevelItems()
+        .forEach(function UI_shouldResizeItems_checkItem(item) {
+          let bounds = new Rect(item.getBounds());
+          feelsCramped = feelsCramped || (item.userSize &&
+            (item.userSize.x > bounds.width || item.userSize.y > bounds.height));
+          bounds.inset(-Trenches.defaultRadius, -Trenches.defaultRadius);
+          minimalRect = minimalRect.union(bounds);
+        });
+      
+      // ensure the minimalRect extends to, but not beyond, the origin
+      minimalRect.left = 0;
+      minimalRect.top  = 0;
+  
+      this._minimalRect = minimalRect;
+      this._feelsCramped = feelsCramped;
+    }
+
+    return this._minimalRect.width > newPageBounds.width ||
+      this._minimalRect.height > newPageBounds.height ||
+      this._feelsCramped;
+  },
+  
+  // ----------
+  // Function: clearShouldResizeItems
+  // Clear the cache of whether we should resize the items on the Panorama
+  // screen, forcing a recomputation on the next UI.shouldResizeItems()
+  // call.
+  clearShouldResizeItems: function UI_clearShouldResizeItems() {
+    delete this._minimalRect;
+    delete this._feelsCramped;
+  },
 
   // ----------
   // Function: exit
   // Exits TabView UI.
   exit: function UI_exit() {
     let self = this;
     let zoomedIn = false;
 
--- a/browser/base/content/test/tabview/Makefile.in
+++ b/browser/base/content/test/tabview/Makefile.in
@@ -80,16 +80,17 @@ include $(topsrcdir)/config/rules.mk
                  browser_tabview_bug608158.js \
                  browser_tabview_bug610242.js \
                  browser_tabview_bug616967.js \
                  browser_tabview_bug618828.js \
                  browser_tabview_bug619937.js \
                  browser_tabview_bug622835.js \
                  browser_tabview_bug624265.js \
                  browser_tabview_bug624953.js \
+                 browser_tabview_bug625269.js \
                  browser_tabview_dragdrop.js \
                  browser_tabview_exit_button.js \
                  browser_tabview_expander.js \
                  browser_tabview_group.js \
                  browser_tabview_launch.js \
                  browser_tabview_multiwindow_search.js \
                  browser_tabview_orphaned_tabs.js \
                  browser_tabview_privatebrowsing.js \
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/tabview/browser_tabview_bug625269.js
@@ -0,0 +1,111 @@
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is bug 625269 test.
+ *
+ * The Initial Developer of the Original Code is
+ * Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2010
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ * Michael Yoshitaka Erlewine <mitcho@mitcho.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+function test() {
+  waitForExplicitFinish();
+
+  newWindowWithTabView(onTabViewWindowLoaded);
+}
+
+function onTabViewWindowLoaded(win) {
+  win.removeEventListener("tabviewshown", onTabViewWindowLoaded, false);
+
+  ok(win.TabView.isVisible(), "Tab View is visible");
+
+  let contentWindow = win.document.getElementById("tab-view").contentWindow;
+  let [originalTab] = win.gBrowser.visibleTabs;
+
+  let currentGroup = contentWindow.GroupItems.getActiveGroupItem();
+
+  currentGroup.setSize(600, 600, true);
+  currentGroup.setUserSize();
+
+  let down1 = function down1(resized) {
+    checkResized(currentGroup, 50, 50, false, "A little bigger", up1, contentWindow, win);
+  };
+  
+  let up1 = function up1(resized) {
+    checkResized(currentGroup, -400, -400, true, "Much smaller", down2, contentWindow, win);    
+  }
+
+  let down2 = function down2(resized) {
+    checkResized(currentGroup, 400, 400, undefined,
+      "Bigger after much smaller: TODO (bug 625668): the group should be resized!",
+      up2, contentWindow, win);
+  };
+  
+  let up2 = function up2(resized) {
+    checkResized(currentGroup, -400, -400, undefined,
+      "Much smaller: TODO (bug 625668): the group should be resized!",
+      down3, contentWindow, win);    
+  }
+
+  let down3 = function down3(resized) {
+    // reset the usersize of the group, so this should clear the "cramped" feeling.
+    currentGroup.setSize(100,100,true);
+    currentGroup.setUserSize();
+    checkResized(currentGroup, 400, 400, false,
+      "After clearing the cramp",
+      up3, contentWindow, win);
+  };
+  
+  let up3 = function up3(resized) {
+    win.close();
+    finish();
+  }
+
+  // start by making it a little smaller.
+  checkResized(currentGroup, -50, -50, false, "A little smaller", down1, contentWindow, win);
+}
+
+function simulateResizeBy(xDiff, yDiff, win) {
+  win = win || window;
+
+  win.resizeBy(xDiff, yDiff);
+}
+
+function checkResized(item, xDiff, yDiff, expectResized, note, callback, contentWindow, win) {
+  let originalBounds = new contentWindow.Rect(item.getBounds());
+  simulateResizeBy(xDiff, yDiff, win);
+
+  let newBounds = item.getBounds();
+  let resized = !newBounds.equals(originalBounds);
+  if (expectResized !== undefined)
+    is(resized, expectResized, note + ": The group should " + 
+      (expectResized ? "" : "not ") + "be resized");
+  callback(resized);
+}