Web Inspector: TabbedPane: measure tab widths in batches.
authorpfeldman@chromium.org <pfeldman@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 22 Aug 2012 06:58:30 +0000 (06:58 +0000)
committerpfeldman@chromium.org <pfeldman@chromium.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 22 Aug 2012 06:58:30 +0000 (06:58 +0000)
https://bugs.webkit.org/show_bug.cgi?id=94484

Reviewed by Vsevolod Vlasov.

Source/WebCore:

- Introduces global batch update schema
- Migrates Toolbar and TabbedPane to the new schema

* inspector/front-end/ScriptsPanel.js:
(WebInspector.ScriptsPanel):
* inspector/front-end/TabbedPane.js:
(WebInspector.TabbedPane.prototype.appendTab):
(WebInspector.TabbedPane.prototype._updateTabElements):
(WebInspector.TabbedPane.prototype._innerUpdateTabElements):
(WebInspector.TabbedPane.prototype._updateWidths):
(WebInspector.TabbedPane.prototype._measureWidths):
(WebInspector.TabbedPaneTab):
(WebInspector.TabbedPaneTab.prototype.width):
(WebInspector.TabbedPaneTab.prototype.setWidth):
* inspector/front-end/Toolbar.js:
(WebInspector.Toolbar):
(WebInspector.Toolbar.prototype._updateDropdownButtonAndHideDropdown):
(WebInspector.Toolbar.prototype._innerUpdateDropdownButtonAndHideDropdown):
* inspector/front-end/UIUtils.js:
(WebInspector.startBatchUpdate):
(WebInspector.invokeOnceAfterBatchUpdate.get if):
(WebInspector.invokeOnceAfterBatchUpdate):
* inspector/front-end/inspector.js:
* inspector/front-end/utilities.js:

LayoutTests:

* inspector/start-end-batch-update-expected.txt: Added.
* inspector/start-end-batch-update.html: Added.
* inspector/tabbed-pane-tabs-to-show.html:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@126268 268f45cc-cd09-0410-ab3c-d52691b4dbfc

LayoutTests/ChangeLog
LayoutTests/inspector/tabbed-pane-tabs-to-show.html
Source/WebCore/ChangeLog
Source/WebCore/inspector/front-end/ScriptsPanel.js
Source/WebCore/inspector/front-end/TabbedPane.js
Source/WebCore/inspector/front-end/Toolbar.js
Source/WebCore/inspector/front-end/UIUtils.js
Source/WebCore/inspector/front-end/inspector.js
Source/WebCore/inspector/front-end/utilities.js

index bd01766..42d1609 100644 (file)
@@ -1,3 +1,14 @@
+2012-08-21  Pavel Feldman  <pfeldman@chromium.org>
+
+        Web Inspector: TabbedPane: measure tab widths in batches.
+        https://bugs.webkit.org/show_bug.cgi?id=94484
+
+        Reviewed by Vsevolod Vlasov.
+
+        * inspector/start-end-batch-update-expected.txt: Added.
+        * inspector/start-end-batch-update.html: Added.
+        * inspector/tabbed-pane-tabs-to-show.html:
+
 2012-08-21  Dominic Cooney  <dominicc@chromium.org>
 
         [Chromium] Unreviewed gardening.
index bb6d32e..4d2bee7 100644 (file)
@@ -10,7 +10,7 @@ var test = function()
         {
             return title;
         }
-        return { width: width, title: title, toString: toString };
+        return { width: function() { return width; }, title: title, toString: toString };
     }
 
     var dropDownButtonMeasuredWidth = 10;
index 851a277..9f52cb4 100644 (file)
@@ -1,3 +1,35 @@
+2012-08-21  Pavel Feldman  <pfeldman@chromium.org>
+
+        Web Inspector: TabbedPane: measure tab widths in batches.
+        https://bugs.webkit.org/show_bug.cgi?id=94484
+
+        Reviewed by Vsevolod Vlasov.
+
+        - Introduces global batch update schema
+        - Migrates Toolbar and TabbedPane to the new schema
+
+        * inspector/front-end/ScriptsPanel.js:
+        (WebInspector.ScriptsPanel):
+        * inspector/front-end/TabbedPane.js:
+        (WebInspector.TabbedPane.prototype.appendTab):
+        (WebInspector.TabbedPane.prototype._updateTabElements):
+        (WebInspector.TabbedPane.prototype._innerUpdateTabElements):
+        (WebInspector.TabbedPane.prototype._updateWidths):
+        (WebInspector.TabbedPane.prototype._measureWidths):
+        (WebInspector.TabbedPaneTab):
+        (WebInspector.TabbedPaneTab.prototype.width):
+        (WebInspector.TabbedPaneTab.prototype.setWidth):
+        * inspector/front-end/Toolbar.js:
+        (WebInspector.Toolbar):
+        (WebInspector.Toolbar.prototype._updateDropdownButtonAndHideDropdown):
+        (WebInspector.Toolbar.prototype._innerUpdateDropdownButtonAndHideDropdown):
+        * inspector/front-end/UIUtils.js:
+        (WebInspector.startBatchUpdate):
+        (WebInspector.invokeOnceAfterBatchUpdate.get if):
+        (WebInspector.invokeOnceAfterBatchUpdate):
+        * inspector/front-end/inspector.js:
+        * inspector/front-end/utilities.js:
+
 2012-08-21  Kentaro Hara  <haraken@chromium.org>
 
         [V8] Move String related code in V8Binding to a separate file
index 4a7cc22..1f688d6 100644 (file)
@@ -185,9 +185,11 @@ WebInspector.ScriptsPanel = function(workspaceForTest)
     WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.ExecutionLineChanged, this._executionLineChanged, this);
     WebInspector.debuggerModel.addEventListener(WebInspector.DebuggerModel.Events.BreakpointsActiveStateChanged, this._breakpointsActiveStateChanged, this);
 
+    WebInspector.startBatchUpdate();
     var uiSourceCodes = this._workspace.uiSourceCodes();
     for (var i = 0; i < uiSourceCodes.length; ++i)
         this._addUISourceCode(uiSourceCodes[i]);
+    WebInspector.endBatchUpdate();
 
     this._workspace.addEventListener(WebInspector.UISourceCodeProvider.Events.UISourceCodeAdded, this._uiSourceCodeAdded, this);
     this._workspace.addEventListener(WebInspector.UISourceCodeProvider.Events.UISourceCodeReplaced, this._uiSourceCodeReplaced, this);
index a279a1e..3e8e8ab 100644 (file)
@@ -101,7 +101,7 @@ WebInspector.TabbedPane.prototype = {
      */
     appendTab: function(id, tabTitle, view, tabTooltip, userGesture)
     {
-        var tab = new WebInspector.TabbedPaneTab(this, this._tabsElement, id, tabTitle, this._closeableTabs, view, tabTooltip);
+        var tab = new WebInspector.TabbedPaneTab(this, id, tabTitle, this._closeableTabs, view, tabTooltip);
         this._tabsById[id] = tab;
 
         this._tabs.push(tab);
@@ -254,6 +254,11 @@ WebInspector.TabbedPane.prototype = {
 
     _updateTabElements: function()
     {
+        WebInspector.invokeOnceAfterBatchUpdate(this, this._innerUpdateTabElements);
+    },
+
+    _innerUpdateTabElements: function()
+    {
         if (!this.isShowing())
             return;
 
@@ -265,9 +270,7 @@ WebInspector.TabbedPane.prototype = {
         if (!this._measuredDropDownButtonWidth)
             this._measureDropDownButton();
 
-        if (this._shrinkableTabs)
-            this._updateWidths();
-        
+        this._updateWidths();
         this._updateTabsDropDown();
     },
 
@@ -374,16 +377,44 @@ WebInspector.TabbedPane.prototype = {
 
     _updateWidths: function()
     {
-        var measuredWidths = [];
-        for (var tabId in this._tabs)
-            measuredWidths.push(this._tabs[tabId].measuredWidth);
-        
-        var maxWidth = this._calculateMaxWidth(measuredWidths, this._totalWidth());
-        
+        var measuredWidths = this._measureWidths();
+        var maxWidth = this._shrinkableTabs ? this._calculateMaxWidth(measuredWidths.slice(), this._totalWidth()) : Number.MAX_VALUE;
+
+        var i = 0;
+        for (var tabId in this._tabs) {
+            var tab = this._tabs[tabId];
+            tab.setWidth(Math.min(maxWidth, measuredWidths[i++]));
+        }
+    },
+
+    _measureWidths: function()
+    {
+        // Add all elements to measure into this._tabsElement
+        var measuringTabElements = [];
         for (var tabId in this._tabs) {
             var tab = this._tabs[tabId];
-            tab.width = Math.min(tab.measuredWidth, maxWidth);
+            if (typeof tab._measuredWidth === "number")
+                continue;
+            var measuringTabElement = tab._createTabElement(true);
+            measuringTabElement.__tab = tab;
+            measuringTabElements.push(measuringTabElement);
+            this._tabsElement.appendChild(measuringTabElement);
         }
+
+        // Perform measurement
+        for (var i = 0; i < measuringTabElements.length; ++i)
+            measuringTabElements[i].__tab._measuredWidth = measuringTabElements[i].getBoundingClientRect().width;
+
+        // Nuke elements from the UI
+        for (var i = 0; i < measuringTabElements.length; ++i)
+            measuringTabElements[i].parentElement.removeChild(measuringTabElements[i]);
+
+        // Combine the results.
+        var measuredWidths = [];
+        for (var tabId in this._tabs)
+            measuredWidths.push(this._tabs[tabId]._measuredWidth);
+
+        return measuredWidths;
     },
 
     /**
@@ -429,7 +460,7 @@ WebInspector.TabbedPane.prototype = {
 
         var totalTabsWidth = 0;
         for (var i = 0; i < tabsHistory.length; ++i) {
-            totalTabsWidth += tabsHistory[i].width;
+            totalTabsWidth += tabsHistory[i].width();
             var minimalRequiredWidth = totalTabsWidth;
             if (i !== tabsHistory.length - 1)
                 minimalRequiredWidth += measuredDropDownButtonWidth;
@@ -510,18 +541,16 @@ WebInspector.TabbedPane.prototype.__proto__ = WebInspector.View.prototype;
 /**
  * @constructor
  * @param {WebInspector.TabbedPane} tabbedPane
- * @param {Element} measureElement
  * @param {string} id
  * @param {string} title
  * @param {boolean} closeable
  * @param {WebInspector.View} view
  * @param {string=} tooltip
  */
-WebInspector.TabbedPaneTab = function(tabbedPane, measureElement, id, title, closeable, view, tooltip)
+WebInspector.TabbedPaneTab = function(tabbedPane, id, title, closeable, view, tooltip)
 {
     this._closeable = closeable;
     this._tabbedPane = tabbedPane;
-    this._measureElement = measureElement;
     this._id = id;
     this._title = title;
     this._tooltip = tooltip;
@@ -599,24 +628,15 @@ WebInspector.TabbedPaneTab.prototype = {
     /**
      * @return {number}
      */
-    get measuredWidth()
+    width: function()
     {
-        if (typeof(this._measuredWidth) !== "undefined")
-            return this._measuredWidth;
-        
-        this._measure();
-        return this._measuredWidth;
+        return this._width;
     },
 
     /**
-     * @return {number}
+     * @param {number} width
      */
-    get width()
-    {
-        return this._width || this.measuredWidth;
-    },
-
-    set width(width)
+    setWidth: function(width)
     {
         this.tabElement.style.width = width + "px";
         this._width = width;
@@ -657,14 +677,6 @@ WebInspector.TabbedPaneTab.prototype = {
         return tabElement;
     },
 
-    _measure: function()
-    {
-        var measuringTabElement = this._createTabElement(true);
-        this._measureElement.appendChild(measuringTabElement);
-        this._measuredWidth = measuringTabElement.getBoundingClientRect().width;
-        this._measureElement.removeChild(measuringTabElement);
-    },
-
     /**
      * @param {Event} event
      */
index 9a5e4a2..9b8995c 100644 (file)
@@ -42,20 +42,9 @@ WebInspector.Toolbar = function()
 
     document.getElementById("close-button-left").addEventListener("click", this._onClose, true);
     document.getElementById("close-button-right").addEventListener("click", this._onClose, true);
-    this._coalescingLevel = 0;
 }
 
 WebInspector.Toolbar.prototype = {
-    /**
-     * @param {boolean} enabled
-     */
-    setCoalescingUpdate: function(enabled)
-    {
-        this._coalescingLevel += enabled ? 1 : -1;
-        if (!this._coalescingLevel)
-            this._updateDropdownButtonAndHideDropdown();
-    },
-
     resize: function()
     {
         this._updateDropdownButtonAndHideDropdown();
@@ -179,8 +168,11 @@ WebInspector.Toolbar.prototype = {
 
     _updateDropdownButtonAndHideDropdown: function()
     {
-        if (this._coalescingLevel)
-            return;
+        WebInspector.invokeOnceAfterBatchUpdate(this, this._innerUpdateDropdownButtonAndHideDropdown);
+    },
+
+    _innerUpdateDropdownButtonAndHideDropdown: function()
+    {
         this._setDropdownVisible(false);
 
         var toolbar = document.getElementById("toolbar");
index 44ceb41..9af5070 100644 (file)
@@ -1109,6 +1109,51 @@ WebInspector.populateHrefContextMenu = function(contextMenu, contextNode, event)
     return true;
 }
 
+WebInspector._coalescingLevel = 0;
+
+WebInspector.startBatchUpdate = function()
+{
+    if (!WebInspector._coalescingLevel)
+        WebInspector._postUpdateHandlers = new Map();
+    WebInspector._coalescingLevel++;
+}
+
+WebInspector.endBatchUpdate = function()
+{
+    if (--WebInspector._coalescingLevel)
+        return;
+
+    var handlers = WebInspector._postUpdateHandlers;
+    delete WebInspector._postUpdateHandlers;
+
+    var keys = handlers.keys();
+    for (var i = 0; i < keys.length; ++i) {
+        var object = keys[i];
+        var methods = handlers.get(object).keys();
+        for (var j = 0; j < methods.length; ++j)
+            methods[j].call(object);
+    }
+}
+
+/**
+ * @param {Object} object
+ * @param {function()} method
+ */
+WebInspector.invokeOnceAfterBatchUpdate = function(object, method)
+{
+    if (!WebInspector._coalescingLevel) {
+        method.call(object);
+        return;
+    }
+    
+    var methods = WebInspector._postUpdateHandlers.get(object);
+    if (!methods) {
+        methods = new Map();
+        WebInspector._postUpdateHandlers.put(object, methods);
+    }
+    methods.put(method);
+}
+
 ;(function() {
 
 function windowLoaded()
index b3319c1..5160cec 100644 (file)
@@ -527,11 +527,11 @@ WebInspector._doLoadedDoneWithCapabilities = function()
     WebInspector._installDockToRight();
 
     this.toolbar = new WebInspector.Toolbar();
-    this.toolbar.setCoalescingUpdate(true);
+    WebInspector.startBatchUpdate();
     var panelDescriptors = this._panelDescriptors();
     for (var i = 0; i < panelDescriptors.length; ++i)
         WebInspector.inspectorView.addPanel(panelDescriptors[i]);
-    this.toolbar.setCoalescingUpdate(false);
+    WebInspector.endBatchUpdate();
 
     this.addMainEventListeners(document);
     WebInspector.registerLinkifierPlugin(this._profilesLinkifier.bind(this));
index d9fa794..bba9a95 100644 (file)
@@ -690,42 +690,57 @@ Map.prototype = {
             objectIdentifier = ++Map._lastObjectIdentifier;
             key.__identifier = objectIdentifier;
         }
-        this._map[objectIdentifier] = value;
+        this._map[objectIdentifier] = [key, value];
     },
     
     /**
      * @param {Object} key
-     * @return {Object} value
      */
     remove: function(key)
     {
         var result = this._map[key.__identifier];
         delete this._map[key.__identifier];
-        return result;
+        return result ? result[1] : undefined;
     },
-    
+
+    /**
+     * @return {Array.<Object>}
+     */
+    keys: function()
+    {
+        return this._list(0);
+    },
+
     values: function()
     {
+        return this._list(1);
+    },
+
+    /**
+     * @param {number} index
+     */
+    _list: function(index)
+    {
         var result = [];
         for (var objectIdentifier in this._map)
-            result.push(this._map[objectIdentifier]);
+            result.push(this._map[objectIdentifier][index]);
         return result;
     },
-    
+
     /**
      * @param {Object} key
      */
     get: function(key)
     {
-        return this._map[key.__identifier];
+        var entry = this._map[key.__identifier];
+        return entry ? entry[1] : undefined;
     },
     
     clear: function()
     {
         this._map = {};
     }
-};
-
+}
 /**
  * @param {string} url
  * @param {boolean=} async