Title: [201245] trunk/Source/WebInspectorUI
Revision
201245
Author
[email protected]
Date
2016-05-21 12:21:35 -0700 (Sat, 21 May 2016)

Log Message

Web Inspector: Creating the CSSStyleDetailsSidebarPanel takes about 50ms (20%) of main load
https://bugs.webkit.org/show_bug.cgi?id=156707
<rdar://problem/25780404>

Reviewed by Timothy Hatcher.

This patch adds new View concepts, `initialLayout` and `widthDidChange`,
making it possible for hidden views to postpone the creation of their
UI subtree until they are shown for the first time.

Sidebar panels get this performance improvement by virtue of SidebarPanel
and StyleDetailsPanel, which trigger a layout when shown. This can be
removed once <https://webkit.org/b/150741> is fixed, and this is done
automatically by View.

* UserInterface/Views/CSSStyleDeclarationTextEditor.js:
(WebInspector.CSSStyleDeclarationTextEditor):
Should subclass View.
(WebInspector.CSSStyleDeclarationTextEditor.prototype.layout):
(WebInspector.CSSStyleDeclarationTextEditor.prototype.get element): Deleted.
Handled in View base class.
(WebInspector.CSSStyleDeclarationTextEditor.prototype.updateLayout): Deleted.
Relocate to `layout` override, ignore unused parameter `force`.

* UserInterface/Views/CSSStyleDetailsSidebarPanel.js:
(WebInspector.CSSStyleDetailsSidebarPanel):
Create the minimum required initial state and UI elements. Relocate
anything that can be lazy loaded to `initialLayout`.

(WebInspector.CSSStyleDetailsSidebarPanel.prototype.initialLayout):
(WebInspector.CSSStyleDetailsSidebarPanel.prototype.sizeDidChange):
(WebInspector.CSSStyleDetailsSidebarPanel.prototype.widthDidChange): Deleted.

* UserInterface/Views/ComputedStyleDetailsPanel.js:
(WebInspector.ComputedStyleDetailsPanel):
Relocate anything that can be lazy loaded to `initialLayout`.
(WebInspector.ComputedStyleDetailsPanel.prototype.initialLayout):
(WebInspector.ComputedStyleDetailsPanel.prototype.shown): Deleted.
(WebInspector.ComputedStyleDetailsPanel.prototype.widthDidChange): Deleted.
Handled in View base class.

* UserInterface/Views/DataGrid.js:
(WebInspector.DataGrid.prototype.layout):
Resize logic can be safely moved to `sizeDidChange`, since columns are
always initialized when the width changes.
(WebInspector.DataGrid.prototype.sizeDidChange):
Reposition headers, scrollbars.
(WebInspector.DataGrid.prototype._updateHeaderAndScrollbar):
Broke out header repositioning, which needs to be called whenever
column widths are initialized or the view size changes.

* UserInterface/Views/NavigationBar.js:
(WebInspector.NavigationBar.prototype.layout):

* UserInterface/Views/RulesStyleDetailsPanel.js:
(WebInspector.RulesStyleDetailsPanel.prototype.sizeDidChange):
(WebInspector.RulesStyleDetailsPanel.prototype.widthDidChange): Deleted.

* UserInterface/Views/Sidebar.js:
(WebInspector.Sidebar.prototype._recalculateWidth):
Width changes need to be coordinated by the View base class, since the
initial layout must have occurred before handling a width change.
Force a layout with a resize layout reason.

* UserInterface/Views/SidebarPanel.js:
(WebInspector.SidebarPanel.prototype.get displayName):
Drive-by style fix: add getter so that CSSStyleDetailsSidebarPanel
doesn't have to read the private property directly.

(WebInspector.SidebarPanel.prototype.shown):
Force a layout whenever the panel is shown.
(WebInspector.SidebarPanel.prototype.sizeDidChange):
(WebInspector.SidebarPanel):
(WebInspector.SidebarPanel.prototype.widthDidChange): Deleted.

* UserInterface/Views/StyleDetailsPanel.js:
(WebInspector.StyleDetailsPanel.prototype.shown):
Schedule a layout when shown. A forced layout isn't necessary.
Unlike SidebarPanels, the initial state of style panels doesn't depend
on its layout, and can be safely initialized by the next rAF.

(WebInspector.StyleDetailsPanel.prototype.hidden):
Cancel a pending layout if the panel is hidden before the next AF.
(WebInspector.StyleDetailsPanel.prototype.widthDidChange): Deleted.
Not needed, defined in View base class.

* UserInterface/Views/TimelineOverview.js:
(WebInspector.TimelineOverview.prototype.sizeDidChange):
(WebInspector.TimelineOverview.prototype.layout):
Moved resize logic to `sizeDidChange`.

* UserInterface/Views/TimelineRuler.js:
(WebInspector.TimelineRuler.prototype.sizeDidChange):
(WebInspector.TimelineRuler.prototype.layout):
Moved resize logic to `sizeDidChange`.

* UserInterface/Views/View.js:
(WebInspector.View):
(WebInspector.View.prototype.updateLayout):
(WebInspector.View.prototype.cancelLayout):
Allow a pending layout to be canceled. Useful when a view with a
pending layout is hidden before the layout occurs.

(WebInspector.View.prototype.get layoutReason):
Protected getter for subclasses that need to check the layout reason
outside `sizeDidChange`.

(WebInspector.View.prototype.initialLayout):
Subclass hook to create UI subtree the first time a layout occurs.
Called only once during the lifetime of the View.

(WebInspector.View.prototype.layout):
Drive-by comment fix.
(WebInspector.View.prototype.sizeDidChange):
New layout cycle hook for subclasses.
(WebInspector.View.prototype._layoutSubtree):
Do an initial layout the first time layout is called.
Call the `sizeDidChange` hook so that subclasses can update state
which depends on size/position before doing layout.

* UserInterface/Views/VisualStyleDetailsPanel.js:
(WebInspector.VisualStyleDetailsPanel):
Create the minimum required initial state and UI elements. Relocate
anything that can be lazy loaded to `initialLayout`.

(WebInspector.VisualStyleDetailsPanel.prototype.refresh):
No changes, shifting line numbers confused the diff.
(WebInspector.VisualStyleDetailsPanel.prototype.initialLayout):
(WebInspector.VisualStyleDetailsPanel.prototype.sizeDidChange):
(WebInspector.VisualStyleDetailsPanel.prototype.widthDidChange): Deleted.

Modified Paths

Diff

Modified: trunk/Source/WebInspectorUI/ChangeLog (201244 => 201245)


--- trunk/Source/WebInspectorUI/ChangeLog	2016-05-21 19:21:31 UTC (rev 201244)
+++ trunk/Source/WebInspectorUI/ChangeLog	2016-05-21 19:21:35 UTC (rev 201245)
@@ -1,5 +1,138 @@
 2016-05-21  Matt Baker  <[email protected]>
 
+        Web Inspector: Creating the CSSStyleDetailsSidebarPanel takes about 50ms (20%) of main load
+        https://bugs.webkit.org/show_bug.cgi?id=156707
+        <rdar://problem/25780404>
+
+        Reviewed by Timothy Hatcher.
+
+        This patch adds new View concepts, `initialLayout` and `widthDidChange`,
+        making it possible for hidden views to postpone the creation of their
+        UI subtree until they are shown for the first time.
+
+        Sidebar panels get this performance improvement by virtue of SidebarPanel
+        and StyleDetailsPanel, which trigger a layout when shown. This can be
+        removed once <https://webkit.org/b/150741> is fixed, and this is done
+        automatically by View.
+
+        * UserInterface/Views/CSSStyleDeclarationTextEditor.js:
+        (WebInspector.CSSStyleDeclarationTextEditor):
+        Should subclass View.
+        (WebInspector.CSSStyleDeclarationTextEditor.prototype.layout):
+        (WebInspector.CSSStyleDeclarationTextEditor.prototype.get element): Deleted.
+        Handled in View base class.
+        (WebInspector.CSSStyleDeclarationTextEditor.prototype.updateLayout): Deleted.
+        Relocate to `layout` override, ignore unused parameter `force`.
+
+        * UserInterface/Views/CSSStyleDetailsSidebarPanel.js:
+        (WebInspector.CSSStyleDetailsSidebarPanel):
+        Create the minimum required initial state and UI elements. Relocate
+        anything that can be lazy loaded to `initialLayout`.
+
+        (WebInspector.CSSStyleDetailsSidebarPanel.prototype.initialLayout):
+        (WebInspector.CSSStyleDetailsSidebarPanel.prototype.sizeDidChange):
+        (WebInspector.CSSStyleDetailsSidebarPanel.prototype.widthDidChange): Deleted.
+
+        * UserInterface/Views/ComputedStyleDetailsPanel.js:
+        (WebInspector.ComputedStyleDetailsPanel):
+        Relocate anything that can be lazy loaded to `initialLayout`.
+        (WebInspector.ComputedStyleDetailsPanel.prototype.initialLayout):
+        (WebInspector.ComputedStyleDetailsPanel.prototype.shown): Deleted.
+        (WebInspector.ComputedStyleDetailsPanel.prototype.widthDidChange): Deleted.
+        Handled in View base class.
+
+        * UserInterface/Views/DataGrid.js:
+        (WebInspector.DataGrid.prototype.layout):
+        Resize logic can be safely moved to `sizeDidChange`, since columns are
+        always initialized when the width changes.
+        (WebInspector.DataGrid.prototype.sizeDidChange):
+        Reposition headers, scrollbars.
+        (WebInspector.DataGrid.prototype._updateHeaderAndScrollbar):
+        Broke out header repositioning, which needs to be called whenever
+        column widths are initialized or the view size changes.
+
+        * UserInterface/Views/NavigationBar.js:
+        (WebInspector.NavigationBar.prototype.layout):
+
+        * UserInterface/Views/RulesStyleDetailsPanel.js:
+        (WebInspector.RulesStyleDetailsPanel.prototype.sizeDidChange):
+        (WebInspector.RulesStyleDetailsPanel.prototype.widthDidChange): Deleted.
+
+        * UserInterface/Views/Sidebar.js:
+        (WebInspector.Sidebar.prototype._recalculateWidth):
+        Width changes need to be coordinated by the View base class, since the
+        initial layout must have occurred before handling a width change.
+        Force a layout with a resize layout reason.
+
+        * UserInterface/Views/SidebarPanel.js:
+        (WebInspector.SidebarPanel.prototype.get displayName):
+        Drive-by style fix: add getter so that CSSStyleDetailsSidebarPanel
+        doesn't have to read the private property directly.
+
+        (WebInspector.SidebarPanel.prototype.shown):
+        Force a layout whenever the panel is shown.
+        (WebInspector.SidebarPanel.prototype.sizeDidChange):
+        (WebInspector.SidebarPanel):
+        (WebInspector.SidebarPanel.prototype.widthDidChange): Deleted.
+
+        * UserInterface/Views/StyleDetailsPanel.js:
+        (WebInspector.StyleDetailsPanel.prototype.shown):
+        Schedule a layout when shown. A forced layout isn't necessary.
+        Unlike SidebarPanels, the initial state of style panels doesn't depend
+        on its layout, and can be safely initialized by the next rAF.
+
+        (WebInspector.StyleDetailsPanel.prototype.hidden):
+        Cancel a pending layout if the panel is hidden before the next AF.
+        (WebInspector.StyleDetailsPanel.prototype.widthDidChange): Deleted.
+        Not needed, defined in View base class.
+
+        * UserInterface/Views/TimelineOverview.js:
+        (WebInspector.TimelineOverview.prototype.sizeDidChange):
+        (WebInspector.TimelineOverview.prototype.layout):
+        Moved resize logic to `sizeDidChange`.
+
+        * UserInterface/Views/TimelineRuler.js:
+        (WebInspector.TimelineRuler.prototype.sizeDidChange):
+        (WebInspector.TimelineRuler.prototype.layout):
+        Moved resize logic to `sizeDidChange`.
+
+        * UserInterface/Views/View.js:
+        (WebInspector.View):
+        (WebInspector.View.prototype.updateLayout):
+        (WebInspector.View.prototype.cancelLayout):
+        Allow a pending layout to be canceled. Useful when a view with a
+        pending layout is hidden before the layout occurs.
+
+        (WebInspector.View.prototype.get layoutReason):
+        Protected getter for subclasses that need to check the layout reason
+        outside `sizeDidChange`.
+
+        (WebInspector.View.prototype.initialLayout):
+        Subclass hook to create UI subtree the first time a layout occurs.
+        Called only once during the lifetime of the View.
+
+        (WebInspector.View.prototype.layout):
+        Drive-by comment fix.
+        (WebInspector.View.prototype.sizeDidChange):
+        New layout cycle hook for subclasses.
+        (WebInspector.View.prototype._layoutSubtree):
+        Do an initial layout the first time layout is called.
+        Call the `sizeDidChange` hook so that subclasses can update state
+        which depends on size/position before doing layout.
+
+        * UserInterface/Views/VisualStyleDetailsPanel.js:
+        (WebInspector.VisualStyleDetailsPanel):
+        Create the minimum required initial state and UI elements. Relocate
+        anything that can be lazy loaded to `initialLayout`.
+
+        (WebInspector.VisualStyleDetailsPanel.prototype.refresh):
+        No changes, shifting line numbers confused the diff.
+        (WebInspector.VisualStyleDetailsPanel.prototype.initialLayout):
+        (WebInspector.VisualStyleDetailsPanel.prototype.sizeDidChange):
+        (WebInspector.VisualStyleDetailsPanel.prototype.widthDidChange): Deleted.
+
+2016-05-21  Matt Baker  <[email protected]>
+
         Assertion Failed: StyleDetailsPanel.markAsNeedsRefresh() called with null domNode
         https://bugs.webkit.org/show_bug.cgi?id=157955
         <rdar://problem/26398943>

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/CSSStyleDeclarationTextEditor.js (201244 => 201245)


--- trunk/Source/WebInspectorUI/UserInterface/Views/CSSStyleDeclarationTextEditor.js	2016-05-21 19:21:31 UTC (rev 201244)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/CSSStyleDeclarationTextEditor.js	2016-05-21 19:21:35 UTC (rev 201245)
@@ -24,17 +24,16 @@
  * THE POSSIBILITY OF SUCH DAMAGE.
  */
 
-WebInspector.CSSStyleDeclarationTextEditor = class CSSStyleDeclarationTextEditor extends WebInspector.Object
+WebInspector.CSSStyleDeclarationTextEditor = class CSSStyleDeclarationTextEditor extends WebInspector.View
 {
-    constructor(delegate, style, element)
+    constructor(delegate, style)
     {
         super();
 
-        this._element = element || document.createElement("div");
-        this._element.classList.add(WebInspector.CSSStyleDeclarationTextEditor.StyleClassName);
-        this._element.classList.add(WebInspector.SyntaxHighlightedStyleClassName);
-        this._element.addEventListener("mousedown", this._handleMouseDown.bind(this));
-        this._element.addEventListener("mouseup", this._handleMouseUp.bind(this));
+        this.element.classList.add(WebInspector.CSSStyleDeclarationTextEditor.StyleClassName);
+        this.element.classList.add(WebInspector.SyntaxHighlightedStyleClassName);
+        this.element.addEventListener("mousedown", this._handleMouseDown.bind(this));
+        this.element.addEventListener("mouseup", this._handleMouseUp.bind(this));
 
         this._mouseDownCursorPosition = null;
 
@@ -93,11 +92,6 @@
 
     // Public
 
-    get element()
-    {
-        return this._element;
-    }
-
     get delegate()
     {
         return this._delegate;
@@ -194,11 +188,6 @@
         this._resetContent();
     }
 
-    updateLayout(force)
-    {
-        this._codeMirror.refresh();
-    }
-
     highlightProperty(property)
     {
         function propertiesMatch(cssProperty)
@@ -400,6 +389,11 @@
             this._propertiesChanged();
     }
 
+    layout()
+    {
+        this._codeMirror.refresh();
+    }
+
     // Private
 
     _textAtCursorIsComment(codeMirror, cursor)

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/CSSStyleDetailsSidebarPanel.js (201244 => 201245)


--- trunk/Source/WebInspectorUI/UserInterface/Views/CSSStyleDetailsSidebarPanel.js	2016-05-21 19:21:31 UTC (rev 201244)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/CSSStyleDetailsSidebarPanel.js	2016-05-21 19:21:35 UTC (rev 201245)
@@ -30,100 +30,21 @@
         super("css-style", WebInspector.UIString("Styles"), WebInspector.UIString("Style"), null, true);
 
         this._selectedPanel = null;
-
-        this._forcedPseudoClassCheckboxes = {};
-
-        if (WebInspector.cssStyleManager.canForcePseudoClasses()) {
-            this._forcedPseudoClassContainer = document.createElement("div");
-            this._forcedPseudoClassContainer.className = "pseudo-classes";
-
-            let groupElement = null;
-
-            WebInspector.CSSStyleManager.ForceablePseudoClasses.forEach(function(pseudoClass) {
-                // We don't localize the label since it is a CSS pseudo-class from the CSS standard.
-                let label = pseudoClass.capitalize();
-
-                let labelElement = document.createElement("label");
-
-                let checkboxElement = document.createElement("input");
-                checkboxElement.addEventListener("change", this._forcedPseudoClassCheckboxChanged.bind(this, pseudoClass));
-                checkboxElement.type = "checkbox";
-
-                this._forcedPseudoClassCheckboxes[pseudoClass] = checkboxElement;
-
-                labelElement.appendChild(checkboxElement);
-                labelElement.append(label);
-
-                if (!groupElement || groupElement.children.length === 2) {
-                    groupElement = document.createElement("div");
-                    groupElement.className = "group";
-                    this._forcedPseudoClassContainer.appendChild(groupElement);
-                }
-
-                groupElement.appendChild(labelElement);
-            }, this);
-
-            this.contentView.element.appendChild(this._forcedPseudoClassContainer);
-        }
-
         this._computedStyleDetailsPanel = new WebInspector.ComputedStyleDetailsPanel(this);
         this._rulesStyleDetailsPanel = new WebInspector.RulesStyleDetailsPanel(this);
         this._visualStyleDetailsPanel = new WebInspector.VisualStyleDetailsPanel(this);
 
-        this._computedStyleDetailsPanel.addEventListener(WebInspector.StyleDetailsPanel.Event.Refreshed, this._filterDidChange, this);
-        this._rulesStyleDetailsPanel.addEventListener(WebInspector.StyleDetailsPanel.Event.Refreshed, this._filterDidChange, this);
-
         this._panels = [this._computedStyleDetailsPanel, this._rulesStyleDetailsPanel, this._visualStyleDetailsPanel];
         this._panelNavigationInfo = [this._computedStyleDetailsPanel.navigationInfo, this._rulesStyleDetailsPanel.navigationInfo, this._visualStyleDetailsPanel.navigationInfo];
 
         this._lastSelectedSectionSetting = new WebInspector.Setting("last-selected-style-details-panel", this._rulesStyleDetailsPanel.navigationInfo.identifier);
 
-        let selectedPanel = this._panelMatchingIdentifier(this._lastSelectedSectionSetting.value);
-        if (!selectedPanel)
-            selectedPanel = this._rulesStyleDetailsPanel;
+        this._initiallySelectedPanel = this._panelMatchingIdentifier(this._lastSelectedSectionSetting.value) || this._rulesStyleDetailsPanel;
 
-        this._switchPanels(selectedPanel);
-
-        this._navigationItem = new WebInspector.ScopeRadioButtonNavigationItem(this._identifier, this._displayName, this._panelNavigationInfo, selectedPanel.navigationInfo);
+        this._navigationItem = new WebInspector.ScopeRadioButtonNavigationItem(this.identifier, this.displayName, this._panelNavigationInfo, this._initiallySelectedPanel.navigationInfo);
         this._navigationItem.addEventListener(WebInspector.ScopeRadioButtonNavigationItem.Event.SelectedItemChanged, this._handleSelectedItemChanged, this);
 
-        let optionsContainer = this.element.createChild("div", "options-container");
-
-        let newRuleButton = optionsContainer.createChild("img", "new-rule");
-        newRuleButton.title = WebInspector.UIString("New Rule");
-        newRuleButton.addEventListener("click", this._newRuleButtonClicked.bind(this));
-
-        this._filterBar = new WebInspector.FilterBar;
-        this._filterBar.placeholder = WebInspector.UIString("Filter Styles");
-        this._filterBar.addEventListener(WebInspector.FilterBar.Event.FilterDidChange, this._filterDidChange, this);
-        optionsContainer.appendChild(this._filterBar.element);
-
-        this._classToggleButton = optionsContainer.createChild("button", "toggle-class-toggle");
-        this._classToggleButton.textContent = WebInspector.UIString("Classes");
-        this._classToggleButton.title = WebInspector.UIString("Toggle Classes");
-        this._classToggleButton.addEventListener("click", this._classToggleButtonClicked.bind(this));
-
-        this._classListContainer = this.element.createChild("div", "class-list-container");
-        this._classListContainer.hidden = true;
-
-        this._addClassContainer = this._classListContainer.createChild("div", "new-class");
-        this._addClassContainer.title = WebInspector.UIString("Add a Class");
-        this._addClassContainer.addEventListener("click", this._addClassContainerClicked.bind(this));
-
-        let addClassCheckbox = this._addClassContainer.createChild("input");
-        addClassCheckbox.type = "checkbox";
-        addClassCheckbox.checked = true;
-
-        let addClassIcon = useSVGSymbol("Images/Plus13.svg", "add-class-icon");
-        this._addClassContainer.appendChild(addClassIcon);
-
-        this._addClassInput = this._addClassContainer.createChild("input", "class-name-input");
-        this._addClassInput.setAttribute("placeholder", WebInspector.UIString("Enter Class Name"));
-        this._addClassInput.addEventListener("keypress", this._addClassInputKeyPressed.bind(this));
-        this._addClassInput.addEventListener("blur", this._addClassInputBlur.bind(this));
-
-        WebInspector.cssStyleManager.addEventListener(WebInspector.CSSStyleManager.Event.StyleSheetAdded, this.refresh, this);
-        WebInspector.cssStyleManager.addEventListener(WebInspector.CSSStyleManager.Event.StyleSheetRemoved, this.refresh, this);
+        this._forcedPseudoClassCheckboxes = {};
     }
 
     // Public
@@ -170,16 +91,6 @@
         this._selectedPanel.markAsNeedsRefresh(this.domNode);
     }
 
-    widthDidChange()
-    {
-        super.widthDidChange();
-
-        this._updateNoForcedPseudoClassesScrollOffset();
-
-        if (this._selectedPanel)
-            this._selectedPanel.widthDidChange();
-    }
-
     computedStyleDetailsPanelShowProperty(property)
     {
         this._rulesStyleDetailsPanel.scrollToSectionAndHighlightProperty(property);
@@ -210,6 +121,98 @@
         effectiveDOMNode.removeEventListener(null, null, this);
     }
 
+    initialLayout()
+    {
+        if (WebInspector.cssStyleManager.canForcePseudoClasses()) {
+            this._forcedPseudoClassContainer = document.createElement("div");
+            this._forcedPseudoClassContainer.className = "pseudo-classes";
+
+            let groupElement = null;
+
+            WebInspector.CSSStyleManager.ForceablePseudoClasses.forEach(function(pseudoClass) {
+                // We don't localize the label since it is a CSS pseudo-class from the CSS standard.
+                let label = pseudoClass.capitalize();
+
+                let labelElement = document.createElement("label");
+
+                let checkboxElement = document.createElement("input");
+                checkboxElement.addEventListener("change", this._forcedPseudoClassCheckboxChanged.bind(this, pseudoClass));
+                checkboxElement.type = "checkbox";
+
+                this._forcedPseudoClassCheckboxes[pseudoClass] = checkboxElement;
+
+                labelElement.appendChild(checkboxElement);
+                labelElement.append(label);
+
+                if (!groupElement || groupElement.children.length === 2) {
+                    groupElement = document.createElement("div");
+                    groupElement.className = "group";
+                    this._forcedPseudoClassContainer.appendChild(groupElement);
+                }
+
+                groupElement.appendChild(labelElement);
+            }, this);
+
+            this.contentView.element.appendChild(this._forcedPseudoClassContainer);
+        }
+
+        this._computedStyleDetailsPanel.addEventListener(WebInspector.StyleDetailsPanel.Event.Refreshed, this._filterDidChange, this);
+        this._rulesStyleDetailsPanel.addEventListener(WebInspector.StyleDetailsPanel.Event.Refreshed, this._filterDidChange, this);
+
+        console.assert(this._initiallySelectedPanel, "Should have an initially selected panel.");
+
+        this._switchPanels(this._initiallySelectedPanel);
+        this._initiallySelectedPanel = null;
+
+        let optionsContainer = this.element.createChild("div", "options-container");
+
+        let newRuleButton = optionsContainer.createChild("img", "new-rule");
+        newRuleButton.title = WebInspector.UIString("New Rule");
+        newRuleButton.addEventListener("click", this._newRuleButtonClicked.bind(this));
+
+        this._filterBar = new WebInspector.FilterBar;
+        this._filterBar.placeholder = WebInspector.UIString("Filter Styles");
+        this._filterBar.addEventListener(WebInspector.FilterBar.Event.FilterDidChange, this._filterDidChange, this);
+        optionsContainer.appendChild(this._filterBar.element);
+
+        this._classToggleButton = optionsContainer.createChild("button", "toggle-class-toggle");
+        this._classToggleButton.textContent = WebInspector.UIString("Classes");
+        this._classToggleButton.title = WebInspector.UIString("Toggle Classes");
+        this._classToggleButton.addEventListener("click", this._classToggleButtonClicked.bind(this));
+
+        this._classListContainer = this.element.createChild("div", "class-list-container");
+        this._classListContainer.hidden = true;
+
+        this._addClassContainer = this._classListContainer.createChild("div", "new-class");
+        this._addClassContainer.title = WebInspector.UIString("Add a Class");
+        this._addClassContainer.addEventListener("click", this._addClassContainerClicked.bind(this));
+
+        let addClassCheckbox = this._addClassContainer.createChild("input");
+        addClassCheckbox.type = "checkbox";
+        addClassCheckbox.checked = true;
+
+        let addClassIcon = useSVGSymbol("Images/Plus13.svg", "add-class-icon");
+        this._addClassContainer.appendChild(addClassIcon);
+
+        this._addClassInput = this._addClassContainer.createChild("input", "class-name-input");
+        this._addClassInput.setAttribute("placeholder", WebInspector.UIString("Enter Class Name"));
+        this._addClassInput.addEventListener("keypress", this._addClassInputKeyPressed.bind(this));
+        this._addClassInput.addEventListener("blur", this._addClassInputBlur.bind(this));
+
+        WebInspector.cssStyleManager.addEventListener(WebInspector.CSSStyleManager.Event.StyleSheetAdded, this.refresh, this);
+        WebInspector.cssStyleManager.addEventListener(WebInspector.CSSStyleManager.Event.StyleSheetRemoved, this.refresh, this);
+    }
+
+    sizeDidChange()
+    {
+        super.sizeDidChange();
+
+        this._updateNoForcedPseudoClassesScrollOffset();
+
+        if (this._selectedPanel)
+            this._selectedPanel.sizeDidChange();
+    }
+
     // Private
 
     get _initialScrollOffset()
@@ -458,7 +461,7 @@
     }
 };
 
-WebInspector.CSSStyleDetailsSidebarPanel.NoForcedPseudoClassesScrollOffset = 30; // Default height of the forced pseudo classes container. Updated in widthDidChange.
+WebInspector.CSSStyleDetailsSidebarPanel.NoForcedPseudoClassesScrollOffset = 30; // Default height of the forced pseudo classes container. Updated in sizeDidChange.
 WebInspector.CSSStyleDetailsSidebarPanel.FilterInProgressClassName = "filter-in-progress";
 WebInspector.CSSStyleDetailsSidebarPanel.FilterMatchingSectionHasLabelClassName = "filter-section-has-label";
 WebInspector.CSSStyleDetailsSidebarPanel.FilterMatchSectionClassName = "filter-matching";

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/ComputedStyleDetailsPanel.js (201244 => 201245)


--- trunk/Source/WebInspectorUI/UserInterface/Views/ComputedStyleDetailsPanel.js	2016-05-21 19:21:31 UTC (rev 201244)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/ComputedStyleDetailsPanel.js	2016-05-21 19:21:35 UTC (rev 201245)
@@ -30,72 +30,6 @@
         super(delegate, WebInspector.ComputedStyleDetailsPanel.StyleClassName, "computed", WebInspector.UIString("Styles \u2014 Computed"));
 
         this._computedStyleShowAllSetting = new WebInspector.Setting("computed-style-show-all", false);
-
-        var computedStyleShowAllLabel = document.createElement("label");
-        computedStyleShowAllLabel.textContent = WebInspector.UIString("Show All");
-
-        this._computedStyleShowAllCheckbox = document.createElement("input");
-        this._computedStyleShowAllCheckbox.type = "checkbox";
-        this._computedStyleShowAllCheckbox.checked = this._computedStyleShowAllSetting.value;
-        this._computedStyleShowAllCheckbox.addEventListener("change", this._computedStyleShowAllCheckboxValueChanged.bind(this));
-        computedStyleShowAllLabel.appendChild(this._computedStyleShowAllCheckbox);
-
-        this._propertiesTextEditor = new WebInspector.CSSStyleDeclarationTextEditor(this);
-        this._propertiesTextEditor.showsImplicitProperties = this._computedStyleShowAllSetting.value;
-        this._propertiesTextEditor.alwaysShowPropertyNames = ["display", "width", "height"];
-        this._propertiesTextEditor.sortProperties = true;
-
-        var propertiesRow = new WebInspector.DetailsSectionRow;
-        var propertiesGroup = new WebInspector.DetailsSectionGroup([propertiesRow]);
-        var propertiesSection = new WebInspector.DetailsSection("computed-style-properties", WebInspector.UIString("Properties"), [propertiesGroup], computedStyleShowAllLabel);
-        propertiesSection.addEventListener(WebInspector.DetailsSection.Event.CollapsedStateChanged, this._handleCollapsedStateChanged, this);
-
-        propertiesRow.element.appendChild(this._propertiesTextEditor.element);
-
-        // Region flow name is used to display the "flow-from" property of the Region Containers.
-        this._regionFlowFragment = document.createElement("span");
-        this._regionFlowFragment.appendChild(document.createElement("img")).className = "icon";
-        this._regionFlowNameLabelValue = this._regionFlowFragment.appendChild(document.createElement("span"));
-
-        var goToRegionFlowButton = this._regionFlowFragment.appendChild(WebInspector.createGoToArrowButton());
-        goToRegionFlowButton.addEventListener("click", this._goToRegionFlowArrowWasClicked.bind(this));
-
-        this._regionFlowNameRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Region Flow"));
-        this._regionFlowNameRow.element.classList.add("content-flow-link");
-
-        // Content flow name is used to display the "flow-into" property of the Content nodes.
-        this._contentFlowFragment = document.createElement("span");
-        this._contentFlowFragment.appendChild(document.createElement("img")).className = "icon";
-        this._contentFlowNameLabelValue = this._contentFlowFragment.appendChild(document.createElement("span"));
-
-        var goToContentFlowButton = this._contentFlowFragment.appendChild(WebInspector.createGoToArrowButton());
-        goToContentFlowButton.addEventListener("click", this._goToContentFlowArrowWasClicked.bind(this));
-
-        this._contentFlowNameRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Content Flow"));
-        this._contentFlowNameRow.element.classList.add("content-flow-link");
-
-        var flowNamesGroup = new WebInspector.DetailsSectionGroup([this._regionFlowNameRow, this._contentFlowNameRow]);
-        this._flowNamesSection = new WebInspector.DetailsSection("content-flow", WebInspector.UIString("Flows"), [flowNamesGroup]);
-
-        this._containerRegionsDataGrid = new WebInspector.DOMTreeDataGrid;
-        this._containerRegionsDataGrid.element.classList.add("no-header");
-
-        var containerRegionsRow = new WebInspector.DetailsSectionDataGridRow(this._containerRegionsDataGrid);
-        var containerRegionsGroup = new WebInspector.DetailsSectionGroup([containerRegionsRow]);
-        this._containerRegionsFlowSection = new WebInspector.DetailsSection("container-regions", WebInspector.UIString("Container Regions"), [containerRegionsGroup]);
-
-        this.element.appendChild(propertiesSection.element);
-        this.element.appendChild(this._flowNamesSection.element);
-        this.element.appendChild(this._containerRegionsFlowSection.element);
-
-        this._resetFlowDetails();
-
-        this._boxModelDiagramRow = new WebInspector.BoxModelDetailsSectionRow;
-
-        var boxModelGroup = new WebInspector.DetailsSectionGroup([this._boxModelDiagramRow]);
-        var boxModelSection = new WebInspector.DetailsSection("style-box-model", WebInspector.UIString("Box Model"), [boxModelGroup]);
-
-        this.element.appendChild(boxModelSection.element);
         
         this.cssStyleDeclarationTextEditorShouldAddPropertyGoToArrows = true;
     }
@@ -207,16 +141,75 @@
 
     // Protected
 
-    shown()
+    initialLayout()
     {
-        super.shown();
+        let computedStyleShowAllLabel = document.createElement("label");
+        computedStyleShowAllLabel.textContent = WebInspector.UIString("Show All");
 
-        this._propertiesTextEditor.updateLayout();
-    }
+        this._computedStyleShowAllCheckbox = document.createElement("input");
+        this._computedStyleShowAllCheckbox.type = "checkbox";
+        this._computedStyleShowAllCheckbox.checked = this._computedStyleShowAllSetting.value;
+        this._computedStyleShowAllCheckbox.addEventListener("change", this._computedStyleShowAllCheckboxValueChanged.bind(this));
+        computedStyleShowAllLabel.appendChild(this._computedStyleShowAllCheckbox);
 
-    widthDidChange()
-    {
-        this._propertiesTextEditor.updateLayout();
+        this._propertiesTextEditor = new WebInspector.CSSStyleDeclarationTextEditor(this);
+        this._propertiesTextEditor.showsImplicitProperties = this._computedStyleShowAllSetting.value;
+        this._propertiesTextEditor.alwaysShowPropertyNames = ["display", "width", "height"];
+        this._propertiesTextEditor.sortProperties = true;
+
+        let propertiesRow = new WebInspector.DetailsSectionRow;
+        let propertiesGroup = new WebInspector.DetailsSectionGroup([propertiesRow]);
+        let propertiesSection = new WebInspector.DetailsSection("computed-style-properties", WebInspector.UIString("Properties"), [propertiesGroup], computedStyleShowAllLabel);
+        propertiesSection.addEventListener(WebInspector.DetailsSection.Event.CollapsedStateChanged, this._handleCollapsedStateChanged, this);
+
+        this.addSubview(this._propertiesTextEditor);
+
+        propertiesRow.element.appendChild(this._propertiesTextEditor.element);
+
+        // Region flow name is used to display the "flow-from" property of the Region Containers.
+        this._regionFlowFragment = document.createElement("span");
+        this._regionFlowFragment.appendChild(document.createElement("img")).className = "icon";
+        this._regionFlowNameLabelValue = this._regionFlowFragment.appendChild(document.createElement("span"));
+
+        let goToRegionFlowButton = this._regionFlowFragment.appendChild(WebInspector.createGoToArrowButton());
+        goToRegionFlowButton.addEventListener("click", this._goToRegionFlowArrowWasClicked.bind(this));
+
+        this._regionFlowNameRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Region Flow"));
+        this._regionFlowNameRow.element.classList.add("content-flow-link");
+
+        // Content flow name is used to display the "flow-into" property of the Content nodes.
+        this._contentFlowFragment = document.createElement("span");
+        this._contentFlowFragment.appendChild(document.createElement("img")).className = "icon";
+        this._contentFlowNameLabelValue = this._contentFlowFragment.appendChild(document.createElement("span"));
+
+        let goToContentFlowButton = this._contentFlowFragment.appendChild(WebInspector.createGoToArrowButton());
+        goToContentFlowButton.addEventListener("click", this._goToContentFlowArrowWasClicked.bind(this));
+
+        this._contentFlowNameRow = new WebInspector.DetailsSectionSimpleRow(WebInspector.UIString("Content Flow"));
+        this._contentFlowNameRow.element.classList.add("content-flow-link");
+
+        let flowNamesGroup = new WebInspector.DetailsSectionGroup([this._regionFlowNameRow, this._contentFlowNameRow]);
+        this._flowNamesSection = new WebInspector.DetailsSection("content-flow", WebInspector.UIString("Flows"), [flowNamesGroup]);
+
+        this._containerRegionsDataGrid = new WebInspector.DOMTreeDataGrid;
+        this._containerRegionsDataGrid.element.classList.add("no-header");
+
+        let containerRegionsRow = new WebInspector.DetailsSectionDataGridRow(this._containerRegionsDataGrid);
+        let containerRegionsGroup = new WebInspector.DetailsSectionGroup([containerRegionsRow]);
+        this._containerRegionsFlowSection = new WebInspector.DetailsSection("container-regions", WebInspector.UIString("Container Regions"), [containerRegionsGroup]);
+
+        this.element.appendChild(propertiesSection.element);
+        this.element.appendChild(this._flowNamesSection.element);
+        this.element.appendChild(this._containerRegionsFlowSection.element);
+
+        this._resetFlowDetails();
+
+        this._boxModelDiagramRow = new WebInspector.BoxModelDetailsSectionRow;
+
+        let boxModelGroup = new WebInspector.DetailsSectionGroup([this._boxModelDiagramRow]);
+        let boxModelSection = new WebInspector.DetailsSection("style-box-model", WebInspector.UIString("Box Model"), [boxModelGroup]);
+
+        this.element.appendChild(boxModelSection.element);
     }
 
     // Private

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/DataGrid.js (201244 => 201245)


--- trunk/Source/WebInspectorUI/UserInterface/Views/DataGrid.js	2016-05-21 19:21:31 UTC (rev 201244)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/DataGrid.js	2016-05-21 19:21:35 UTC (rev 201245)
@@ -792,54 +792,58 @@
     //
     // If this function is not called after the DataGrid is attached to its
     // parent element, then the DataGrid's columns will not be resizable.
-    layout(layoutReason)
+    layout()
     {
-        let firstUpdate = false;
-
         // Do not attempt to use offsets if we're not attached to the document tree yet.
         if (!this._columnWidthsInitialized && this.element.offsetWidth) {
             // Give all the columns initial widths now so that during a resize,
             // when the two columns that get resized get a percent value for
             // their widths, all the other columns already have percent values
             // for their widths.
-            var headerTableColumnElements = this._headerTableColumnGroupElement.children;
-            var tableWidth = this._dataTableElement.offsetWidth;
-            var numColumns = headerTableColumnElements.length;
-            var cells = this._headerTableBodyElement.rows[0].cells;
+            let headerTableColumnElements = this._headerTableColumnGroupElement.children;
+            let tableWidth = this._dataTableElement.offsetWidth;
+            let numColumns = headerTableColumnElements.length;
+            let cells = this._headerTableBodyElement.rows[0].cells;
 
             // Calculate widths.
-            var columnWidths = [];
-            for (var i = 0; i < numColumns; ++i) {
-                var headerCellElement = cells[i];
+            let columnWidths = [];
+            for (let i = 0; i < numColumns; ++i) {
+                let headerCellElement = cells[i];
                 if (this._isColumnVisible(headerCellElement.columnIdentifier)) {
-                    var columnWidth = headerCellElement.offsetWidth;
-                    var percentWidth = ((columnWidth / tableWidth) * 100) + "%";
+                    let columnWidth = headerCellElement.offsetWidth;
+                    let percentWidth = ((columnWidth / tableWidth) * 100) + "%";
                     columnWidths.push(percentWidth);
                 } else
                     columnWidths.push(0);
             }
 
             // Apply widths.
-            for (var i = 0; i < numColumns; i++) {
+            for (let i = 0; i < numColumns; i++) {
                 let percentWidth = columnWidths[i];
                 this._headerTableColumnGroupElement.children[i].style.width = percentWidth;
                 this._dataTableColumnGroupElement.children[i].style.width = percentWidth;
             }
 
             this._columnWidthsInitialized = true;
-            firstUpdate = true;
+            this._updateHeaderAndScrollbar();
         }
 
-        if (layoutReason === WebInspector.View.LayoutReason.Resize || firstUpdate) {
-            this._positionResizerElements();
-            this._positionHeaderViews();
-            this._updateScrollbarPadding();
+        this._updateVisibleRows();
+    }
 
-            this._cachedScrollTop = NaN;
-            this._cachedScrollableOffsetHeight = NaN;
-        }
+    sizeDidChange()
+    {
+        this._updateHeaderAndScrollbar();
+    }
 
-        this._updateVisibleRows();
+    _updateHeaderAndScrollbar()
+    {
+        this._positionResizerElements();
+        this._positionHeaderViews();
+        this._updateScrollbarPadding();
+
+        this._cachedScrollTop = NaN;
+        this._cachedScrollableOffsetHeight = NaN;
     }
 
     columnWidthsMap()

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/NavigationBar.js (201244 => 201245)


--- trunk/Source/WebInspectorUI/UserInterface/Views/NavigationBar.js	2016-05-21 19:21:31 UTC (rev 201244)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/NavigationBar.js	2016-05-21 19:21:35 UTC (rev 201245)
@@ -168,9 +168,9 @@
         super.needsLayout();
     }
 
-    layout(layoutReason)
+    layout()
     {
-        if (layoutReason !== WebInspector.View.LayoutReason.Resize && !this._forceLayout)
+        if (this.layoutReason !== WebInspector.View.LayoutReason.Resize && !this._forceLayout)
             return;
 
         this._forceLayout = false;

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/RulesStyleDetailsPanel.js (201244 => 201245)


--- trunk/Source/WebInspectorUI/UserInterface/Views/RulesStyleDetailsPanel.js	2016-05-21 19:21:31 UTC (rev 201244)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/RulesStyleDetailsPanel.js	2016-05-21 19:21:35 UTC (rev 201245)
@@ -452,7 +452,7 @@
             delete this._sections[i].style.__rulesSection;
     }
 
-    widthDidChange()
+    sizeDidChange()
     {
         for (var i = 0; i < this._sections.length; ++i)
             this._sections[i].updateLayout();

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/Sidebar.js (201244 => 201245)


--- trunk/Source/WebInspectorUI/UserInterface/Views/Sidebar.js	2016-05-21 19:21:31 UTC (rev 201244)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/Sidebar.js	2016-05-21 19:21:35 UTC (rev 201245)
@@ -273,7 +273,7 @@
             this._navigationBar.needsLayout();
 
         if (!this.collapsed && this._selectedSidebarPanel)
-            this._selectedSidebarPanel.widthDidChange();
+            this._selectedSidebarPanel.updateLayout(WebInspector.View.LayoutReason.Resize);
 
         this.dispatchEventToListeners(WebInspector.Sidebar.Event.WidthDidChange);
     }

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/SidebarPanel.js (201244 => 201245)


--- trunk/Source/WebInspectorUI/UserInterface/Views/SidebarPanel.js	2016-05-21 19:21:31 UTC (rev 201244)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/SidebarPanel.js	2016-05-21 19:21:35 UTC (rev 201245)
@@ -58,6 +58,11 @@
         return this._contentView;
     }
 
+    get displayName()
+    {
+        return this._displayName;
+    }
+
     get visible()
     {
         return this.selected && this.parentSidebar && !this.parentSidebar.collapsed;
@@ -129,6 +134,9 @@
     {
         this._contentView.element.scrollTop = this._savedScrollPosition;
 
+        // FIXME: remove once <https://webkit.org/b/150741> is fixed.
+        this.updateLayout();
+
         // Implemented by subclasses.
     }
 
@@ -139,17 +147,19 @@
         // Implemented by subclasses.
     }
 
-    widthDidChange()
+    visibilityDidChange()
     {
+        // Implemented by subclasses.
+    }
+
+    // Protected
+
+    sizeDidChange()
+    {
         let width = this.element.realOffsetWidth;
         if (width && width !== this._widthSetting.value)
             this._widthSetting.value = width;
 
         // Implemented by subclasses.
     }
-
-    visibilityDidChange()
-    {
-        // Implemented by subclasses.
-    }
 };

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/StyleDetailsPanel.js (201244 => 201245)


--- trunk/Source/WebInspectorUI/UserInterface/Views/StyleDetailsPanel.js	2016-05-21 19:21:31 UTC (rev 201244)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/StyleDetailsPanel.js	2016-05-21 19:21:35 UTC (rev 201245)
@@ -60,16 +60,16 @@
         this._visible = true;
 
         this._refreshNodeStyles();
+
+        // FIXME: remove once <https://webkit.org/b/150741> is fixed.
+        this.needsLayout();
     }
 
     hidden()
     {
         this._visible = false;
-    }
 
-    widthDidChange()
-    {
-        // Implemented by subclasses.
+        this.cancelLayout();
     }
 
     markAsNeedsRefresh(domNode)

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/TimelineOverview.js (201244 => 201245)


--- trunk/Source/WebInspectorUI/UserInterface/Views/TimelineOverview.js	2016-05-21 19:21:31 UTC (rev 201244)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/TimelineOverview.js	2016-05-21 19:21:35 UTC (rev 201245)
@@ -429,11 +429,8 @@
         return this._timelineRuler;
     }
 
-    layout(layoutReason)
+    layout()
     {
-        if (layoutReason === WebInspector.View.LayoutReason.Resize)
-            this._cachedScrollContainerWidth = NaN;
-
         let startTime = this._startTime;
         let endTime = this._endTime;
         let currentTime = this._currentTime;
@@ -488,6 +485,11 @@
         }
     }
 
+    sizeDidChange()
+    {
+        this._cachedScrollContainerWidth = NaN;
+    }
+
     // Private
 
     _updateElementWidth(element, newWidth)

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/TimelineRuler.js (201244 => 201245)


--- trunk/Source/WebInspectorUI/UserInterface/Views/TimelineRuler.js	2016-05-21 19:21:31 UTC (rev 201244)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/TimelineRuler.js	2016-05-21 19:21:35 UTC (rev 201245)
@@ -415,11 +415,8 @@
 
     // Protected
 
-    layout(layoutReason)
+    layout()
     {
-        if (layoutReason === WebInspector.View.LayoutReason.Resize)
-            this._cachedClientWidth = this.element.clientWidth;
-
         let visibleWidth = this._recalculate();
         if (visibleWidth <= 0)
             return;
@@ -532,6 +529,11 @@
         this._updateSelection(visibleWidth, duration);
     }
 
+    sizeDidChange()
+    {
+        this._cachedClientWidth = this.element.clientWidth;
+    }
+
     // Private
 
     _needsMarkerLayout()

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/View.js (201244 => 201245)


--- trunk/Source/WebInspectorUI/UserInterface/Views/View.js	2016-05-21 19:21:31 UTC (rev 201244)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/View.js	2016-05-21 19:21:35 UTC (rev 201245)
@@ -38,6 +38,7 @@
         this._needsLayoutWhenAttachedToRoot = false;
         this._isAttachedToRoot = false;
         this._layoutReason = null;
+        this._didInitialLayout = false;
     }
 
     // Static
@@ -140,7 +141,7 @@
 
     updateLayout(layoutReason)
     {
-        WebInspector.View._cancelScheduledLayoutForView(this);
+        this.cancelLayout();
 
         this._setLayoutReason(layoutReason);
         this._layoutSubtree();
@@ -164,8 +165,15 @@
         WebInspector.View._scheduleLayoutForView(this);
     }
 
+    cancelLayout()
+    {
+        WebInspector.View._cancelScheduledLayoutForView(this);
+    }
+
     // Protected
 
+    get layoutReason() { return this._layoutReason; }
+
     didMoveToWindow(isAttachedToRoot)
     {
         this._isAttachedToRoot = isAttachedToRoot;
@@ -200,13 +208,30 @@
         }
     }
 
+    initialLayout()
+    {
+        // Implemented by subclasses.
+
+        // Called once when the view is shown for the first time.
+        // Views with complex DOM subtrees should create UI elements in
+        // initialLayout rather than at construction time.
+    }
+
     layout()
     {
-        // Overridden by subclasses.
+        // Implemented by subclasses.
+
         // Not responsible for recursing to child views.
         // Should not be called directly; use updateLayout() instead.
     }
 
+    sizeDidChange()
+    {
+        // Implemented by subclasses.
+
+        // Called after initialLayout, and before layout.
+    }
+
     // Private
 
     _layoutSubtree()
@@ -214,8 +239,16 @@
         this._dirty = false;
         this._dirtyDescendantsCount = 0;
 
-        this.layout(this._layoutReason);
+        if (!this._didInitialLayout) {
+            this.initialLayout();
+            this._didInitialLayout = true;
+        }
 
+        if (this._layoutReason === WebInspector.View.LayoutReason.Resize)
+            this.sizeDidChange();
+
+        this.layout();
+
         for (let view of this._subviews) {
             view._setLayoutReason(this._layoutReason);
             view._layoutSubtree();

Modified: trunk/Source/WebInspectorUI/UserInterface/Views/VisualStyleDetailsPanel.js (201244 => 201245)


--- trunk/Source/WebInspectorUI/UserInterface/Views/VisualStyleDetailsPanel.js	2016-05-21 19:21:31 UTC (rev 201244)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/VisualStyleDetailsPanel.js	2016-05-21 19:21:35 UTC (rev 201245)
@@ -54,7 +54,24 @@
             basic: ["%"].concat(this._units.defaultsSansPercent.basic),
             advanced: this._units.defaultsSansPercent.advanced
         };
+    }
 
+    // Public
+
+    refresh(significantChange)
+    {
+        if (significantChange)
+            this._selectorSection.update(this._nodeStyles);
+        else
+            this._updateSections();
+
+        super.refresh();
+    }
+
+    // Protected
+
+    initialLayout()
+    {
         // Selector Section
         this._selectorSection = new WebInspector.VisualStyleSelectorSection(this);
         this._selectorSection.addEventListener(WebInspector.VisualStyleSelectorSection.Event.SelectorChanged, this._updateSections, this);
@@ -102,22 +119,10 @@
         this.element.appendChild(this._sections.effects.element);
     }
 
-    // Public
-
-    refresh(significantChange)
+    sizeDidChange()
     {
-        if (significantChange)
-            this._selectorSection.update(this._nodeStyles);
-        else
-            this._updateSections();
+        super.sizeDidChange();
 
-        super.refresh();
-    }
-
-    widthDidChange()
-    {
-        super.widthDidChange();
-
         let sidebarWidth = this.element.realOffsetWidth;
         for (let key in this._groups) {
             let group = this._groups[key];
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to