Web Inspector: Computed panel: allow to expand properties to show list of overridden...
authornvasilyev@apple.com <nvasilyev@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 28 Nov 2018 00:28:06 +0000 (00:28 +0000)
committernvasilyev@apple.com <nvasilyev@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 28 Nov 2018 00:28:06 +0000 (00:28 +0000)
https://bugs.webkit.org/show_bug.cgi?id=191984

Reviewed by Devin Rousso.

Introduce the new experimental Computed Style Cascades.

Each property now can expand to show a list of overridden values, their corresponding
selectors, and source locations.

* Localizations/en.lproj/localizedStrings.js:
* UserInterface/Base/Setting.js:
* UserInterface/Main.html:
* UserInterface/Models/DOMNodeStyles.js:
(WI.DOMNodeStyles.prototype.get uniqueOrderedStyles):
Move `uniqueOrderedStyles` function unmodified from SpreadsheetRulesStyleDetailsPanel so it can be used by ComputedStyleDetailsPanel, too.

* UserInterface/Views/ComputedStyleDetailsPanel.css:
(.computed-with-traces .computed-style-properties):
(.computed-with-traces .details-section.computed-style-properties:not(.collapsed) > :matches(.header, .content)):
(.computed-with-traces .details-section.computed-style-properties > .content):
(.computed-with-traces .computed-style-properties .property .go-to-arrow):

* UserInterface/Views/ComputedStyleDetailsPanel.js:
(WI.ComputedStyleDetailsPanel.prototype.refresh):
(WI.ComputedStyleDetailsPanel.prototype.applyFilter):
(WI.ComputedStyleDetailsPanel.prototype.focusFirstSection):
(WI.ComputedStyleDetailsPanel.prototype.initialLayout):
(WI.ComputedStyleDetailsPanel.prototype._computePropertyTraces):
(WI.ComputedStyleDetailsPanel.prototype._computedStyleShowAllCheckboxValueChanged):
(WI.ComputedStyleDetailsPanel.prototype._handlePropertiesSectionCollapsedStateChanged):
(WI.ComputedStyleDetailsPanel.prototype._handleEditorFilterApplied):
(WI.ComputedStyleDetailsPanel):
Use the new Computed section (WI.ComputedStyleSection) only when it's enabled in the experimental settings.
Otherwise, use the current Computed section.

* UserInterface/Views/ComputedStyleDetailsSidebarPanel.js:
(WI.ComputedStyleDetailsSidebarPanel):

* UserInterface/Views/ComputedStyleSection.css: Added.
(.computed-style-section):
(.computed-style-section .computed-property-item):
(.computed-style-section .computed-property-item.expanded):
(.computed-style-section .computed-property-item.expanded + .computed-property-item):
(.computed-style-section .computed-property-item .disclosure-button):
(.computed-style-section .computed-property-item .property-traces):
(.computed-style-section .computed-property-item.expanded .property-traces):
(.computed-style-section .computed-property-item.expanded .disclosure-button):
(.computed-style-section .computed-property-item .property-trace-item):
(.computed-style-section .computed-property-item .property-trace-item .property.overridden .value):
(.computed-style-section .property-trace-item-left,):
(.computed-style-section .property-trace-item-right):
(.computed-style-section .computed-property-item .property-trace-item .value):
(.computed-style-section .property .value):
(.computed-style-section .computed-property-item .property):
(.computed-style-section .computed-property-item .property .name):
(.computed-style-section .computed-property-item .property-trace-item .selector):
(.computed-style-section .computed-property-item .origin):
(.computed-style-section .computed-property-item .go-to-link):
(.computed-style-section .property-trace-item .property .name,):
(.computed-style-section .property-trace-item .property .value + span):
(.computed-style-properties.details-section > .content,):

* UserInterface/Views/ComputedStyleSection.js: Added.
(WI.ComputedStyleSection):
(WI.ComputedStyleSection.prototype.get style):
(WI.ComputedStyleSection.prototype.set style):
(WI.ComputedStyleSection.prototype.get styleTraces):
(WI.ComputedStyleSection.prototype.set styleTraces):
(WI.ComputedStyleSection.prototype.set showsImplicitProperties):
(WI.ComputedStyleSection.prototype.set alwaysShowPropertyNames):
(WI.ComputedStyleSection.prototype.set propertyVisibilityMode):
(WI.ComputedStyleSection.prototype.set hideFilterNonMatchingProperties):
(WI.ComputedStyleSection.prototype.get propertiesToRender):
(WI.ComputedStyleSection.prototype.layout):
(WI.ComputedStyleSection.prototype.detached):
(WI.ComputedStyleSection.prototype.hidden):
(WI.ComputedStyleSection.prototype.applyFilter):
(WI.ComputedStyleSection.prototype.spreadsheetStylePropertyShowProperty):
(WI.ComputedStyleSection.prototype._createTrace):
(WI.ComputedStyleSection.prototype._handlePropertiesChanged):

* UserInterface/Views/ExpandableView.js: Added.
(WI.ExpandableView):
(WI.ExpandableView.prototype.get element):
(WI.ExpandableView.prototype._onDisclosureButtonClick):
(WI.ExpandableView.prototype._update):

* UserInterface/Views/SettingsTabContentView.js:
(WI.SettingsTabContentView.prototype._createExperimentalSettingsView):

* UserInterface/Views/SpreadsheetCSSStyleDeclarationSection.js:
(WI.SpreadsheetCSSStyleDeclarationSection.prototype.initialLayout):
(WI.SpreadsheetCSSStyleDeclarationSection.prototype.layout):
Replace `_renderOrigin` with WI.StyleOriginView so it could be used by WI.ComputedStyleSection.

* UserInterface/Views/SpreadsheetRulesStyleDetailsPanel.js:
(WI.SpreadsheetRulesStyleDetailsPanel.prototype.layout):

* UserInterface/Views/SpreadsheetStyleProperty.js:
(WI.SpreadsheetStyleProperty.prototype.update):
(WI.SpreadsheetStyleProperty.prototype._isEditable):
(WI.SpreadsheetStyleProperty.prototype._createInlineSwatch):
Introduce `readOnly` option so WI.SpreadsheetStyleProperty could be used by WI.ComputedStyleSection.

* UserInterface/Views/StyleOriginView.js: Added.
(WI.StyleOriginView):

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

16 files changed:
Source/WebInspectorUI/ChangeLog
Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js
Source/WebInspectorUI/UserInterface/Base/Setting.js
Source/WebInspectorUI/UserInterface/Main.html
Source/WebInspectorUI/UserInterface/Models/DOMNodeStyles.js
Source/WebInspectorUI/UserInterface/Views/ComputedStyleDetailsPanel.css
Source/WebInspectorUI/UserInterface/Views/ComputedStyleDetailsPanel.js
Source/WebInspectorUI/UserInterface/Views/ComputedStyleDetailsSidebarPanel.js
Source/WebInspectorUI/UserInterface/Views/ComputedStyleSection.css [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Views/ComputedStyleSection.js [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Views/ExpandableView.js [new file with mode: 0644]
Source/WebInspectorUI/UserInterface/Views/SettingsTabContentView.js
Source/WebInspectorUI/UserInterface/Views/SpreadsheetCSSStyleDeclarationSection.js
Source/WebInspectorUI/UserInterface/Views/SpreadsheetRulesStyleDetailsPanel.js
Source/WebInspectorUI/UserInterface/Views/SpreadsheetStyleProperty.js
Source/WebInspectorUI/UserInterface/Views/StyleOriginView.js [new file with mode: 0644]

index ef923bd..0d72720 100644 (file)
@@ -1,3 +1,113 @@
+2018-11-27  Nikita Vasilyev  <nvasilyev@apple.com>
+
+        Web Inspector: Computed panel: allow to expand properties to show list of overridden values
+        https://bugs.webkit.org/show_bug.cgi?id=191984
+
+        Reviewed by Devin Rousso.
+
+        Introduce the new experimental Computed Style Cascades.
+
+        Each property now can expand to show a list of overridden values, their corresponding
+        selectors, and source locations.
+
+        * Localizations/en.lproj/localizedStrings.js:
+        * UserInterface/Base/Setting.js:
+        * UserInterface/Main.html:
+        * UserInterface/Models/DOMNodeStyles.js:
+        (WI.DOMNodeStyles.prototype.get uniqueOrderedStyles):
+        Move `uniqueOrderedStyles` function unmodified from SpreadsheetRulesStyleDetailsPanel so it can be used by ComputedStyleDetailsPanel, too.
+
+        * UserInterface/Views/ComputedStyleDetailsPanel.css:
+        (.computed-with-traces .computed-style-properties):
+        (.computed-with-traces .details-section.computed-style-properties:not(.collapsed) > :matches(.header, .content)):
+        (.computed-with-traces .details-section.computed-style-properties > .content):
+        (.computed-with-traces .computed-style-properties .property .go-to-arrow):
+
+        * UserInterface/Views/ComputedStyleDetailsPanel.js:
+        (WI.ComputedStyleDetailsPanel.prototype.refresh):
+        (WI.ComputedStyleDetailsPanel.prototype.applyFilter):
+        (WI.ComputedStyleDetailsPanel.prototype.focusFirstSection):
+        (WI.ComputedStyleDetailsPanel.prototype.initialLayout):
+        (WI.ComputedStyleDetailsPanel.prototype._computePropertyTraces):
+        (WI.ComputedStyleDetailsPanel.prototype._computedStyleShowAllCheckboxValueChanged):
+        (WI.ComputedStyleDetailsPanel.prototype._handlePropertiesSectionCollapsedStateChanged):
+        (WI.ComputedStyleDetailsPanel.prototype._handleEditorFilterApplied):
+        (WI.ComputedStyleDetailsPanel):
+        Use the new Computed section (WI.ComputedStyleSection) only when it's enabled in the experimental settings.
+        Otherwise, use the current Computed section.
+
+        * UserInterface/Views/ComputedStyleDetailsSidebarPanel.js:
+        (WI.ComputedStyleDetailsSidebarPanel):
+
+        * UserInterface/Views/ComputedStyleSection.css: Added.
+        (.computed-style-section):
+        (.computed-style-section .computed-property-item):
+        (.computed-style-section .computed-property-item.expanded):
+        (.computed-style-section .computed-property-item.expanded + .computed-property-item):
+        (.computed-style-section .computed-property-item .disclosure-button):
+        (.computed-style-section .computed-property-item .property-traces):
+        (.computed-style-section .computed-property-item.expanded .property-traces):
+        (.computed-style-section .computed-property-item.expanded .disclosure-button):
+        (.computed-style-section .computed-property-item .property-trace-item):
+        (.computed-style-section .computed-property-item .property-trace-item .property.overridden .value):
+        (.computed-style-section .property-trace-item-left,):
+        (.computed-style-section .property-trace-item-right):
+        (.computed-style-section .computed-property-item .property-trace-item .value):
+        (.computed-style-section .property .value):
+        (.computed-style-section .computed-property-item .property):
+        (.computed-style-section .computed-property-item .property .name):
+        (.computed-style-section .computed-property-item .property-trace-item .selector):
+        (.computed-style-section .computed-property-item .origin):
+        (.computed-style-section .computed-property-item .go-to-link):
+        (.computed-style-section .property-trace-item .property .name,):
+        (.computed-style-section .property-trace-item .property .value + span):
+        (.computed-style-properties.details-section > .content,):
+
+        * UserInterface/Views/ComputedStyleSection.js: Added.
+        (WI.ComputedStyleSection):
+        (WI.ComputedStyleSection.prototype.get style):
+        (WI.ComputedStyleSection.prototype.set style):
+        (WI.ComputedStyleSection.prototype.get styleTraces):
+        (WI.ComputedStyleSection.prototype.set styleTraces):
+        (WI.ComputedStyleSection.prototype.set showsImplicitProperties):
+        (WI.ComputedStyleSection.prototype.set alwaysShowPropertyNames):
+        (WI.ComputedStyleSection.prototype.set propertyVisibilityMode):
+        (WI.ComputedStyleSection.prototype.set hideFilterNonMatchingProperties):
+        (WI.ComputedStyleSection.prototype.get propertiesToRender):
+        (WI.ComputedStyleSection.prototype.layout):
+        (WI.ComputedStyleSection.prototype.detached):
+        (WI.ComputedStyleSection.prototype.hidden):
+        (WI.ComputedStyleSection.prototype.applyFilter):
+        (WI.ComputedStyleSection.prototype.spreadsheetStylePropertyShowProperty):
+        (WI.ComputedStyleSection.prototype._createTrace):
+        (WI.ComputedStyleSection.prototype._handlePropertiesChanged):
+
+        * UserInterface/Views/ExpandableView.js: Added.
+        (WI.ExpandableView):
+        (WI.ExpandableView.prototype.get element):
+        (WI.ExpandableView.prototype._onDisclosureButtonClick):
+        (WI.ExpandableView.prototype._update):
+
+        * UserInterface/Views/SettingsTabContentView.js:
+        (WI.SettingsTabContentView.prototype._createExperimentalSettingsView):
+
+        * UserInterface/Views/SpreadsheetCSSStyleDeclarationSection.js:
+        (WI.SpreadsheetCSSStyleDeclarationSection.prototype.initialLayout):
+        (WI.SpreadsheetCSSStyleDeclarationSection.prototype.layout):
+        Replace `_renderOrigin` with WI.StyleOriginView so it could be used by WI.ComputedStyleSection.
+
+        * UserInterface/Views/SpreadsheetRulesStyleDetailsPanel.js:
+        (WI.SpreadsheetRulesStyleDetailsPanel.prototype.layout):
+
+        * UserInterface/Views/SpreadsheetStyleProperty.js:
+        (WI.SpreadsheetStyleProperty.prototype.update):
+        (WI.SpreadsheetStyleProperty.prototype._isEditable):
+        (WI.SpreadsheetStyleProperty.prototype._createInlineSwatch):
+        Introduce `readOnly` option so WI.SpreadsheetStyleProperty could be used by WI.ComputedStyleSection.
+
+        * UserInterface/Views/StyleOriginView.js: Added.
+        (WI.StyleOriginView):
+
 2018-11-27  Timothy Hatcher  <timothy@apple.com>
 
         Web Inspector: Add support for forcing color scheme appearance in DOM tree.
index 6d917a3..178fb36 100644 (file)
@@ -351,6 +351,7 @@ localizedStrings["Elements"] = "Elements";
 localizedStrings["Enable Audit Tab"] = "Enable Audit Tab";
 localizedStrings["Enable Breakpoint"] = "Enable Breakpoint";
 localizedStrings["Enable Breakpoints"] = "Enable Breakpoints";
+localizedStrings["Enable Computed Style Cascades"] = "Enable Computed Style Cascades";
 localizedStrings["Enable Event Listener"] = "Enable Event Listener";
 localizedStrings["Enable Layers Tab"] = "Enable Layers Tab";
 localizedStrings["Enable New Tab Bar"] = "Enable New Tab Bar";
index b5d964d..96bcea1 100644 (file)
@@ -130,6 +130,7 @@ WI.settings = {
 
     // Experimental
     experimentalEnableMultiplePropertiesSelection: new WI.Setting("experimental-enable-multiple-properties-selection", false),
+    experimentalEnableComputedStyleCascades: new WI.Setting("experimental-enable-computed-style-cascades", false),
     experimentalEnableLayersTab: new WI.Setting("experimental-enable-layers-tab", false),
     experimentalEnableNewTabBar: new WI.Setting("experimental-enable-new-tab-bar", false),
     experimentalEnableAuditTab: new WI.Setting("experimental-enable-audit-tab", false),
index 5d01e39..5636599 100644 (file)
@@ -62,6 +62,7 @@
     <link rel="stylesheet" href="Views/ColorWheel.css">
     <link rel="stylesheet" href="Views/CompletionSuggestionsView.css">
     <link rel="stylesheet" href="Views/ComputedStyleDetailsPanel.css">
+    <link rel="stylesheet" href="Views/ComputedStyleSection.css">
     <link rel="stylesheet" href="Views/ConsoleDrawer.css">
     <link rel="stylesheet" href="Views/ConsoleMessageView.css">
     <link rel="stylesheet" href="Views/ConsolePrompt.css">
     <script src="Views/ColorWheel.js"></script>
     <script src="Views/CompletionSuggestionsView.js"></script>
     <script src="Views/ComputedStyleDetailsPanel.js"></script>
+    <script src="Views/ComputedStyleSection.js"></script>
     <script src="Views/ConsoleDrawer.js"></script>
     <script src="Views/ConsoleGroup.js"></script>
     <script src="Views/ConsolePrompt.js"></script>
     <script src="Views/EventBreakpointPopover.js"></script>
     <script src="Views/EventBreakpointTreeElement.js"></script>
     <script src="Views/EventListenerSectionGroup.js"></script>
+    <script src="Views/ExpandableView.js"></script>
     <script src="Views/FilterBar.js"></script>
     <script src="Views/FilterBarButton.js"></script>
     <script src="Views/FilterBarNavigationItem.js"></script>
     <script src="Views/SpreadsheetSelectorField.js"></script>
     <script src="Views/SpreadsheetStyleProperty.js"></script>
     <script src="Views/SpreadsheetTextField.js"></script>
+    <script src="Views/StyleOriginView.js"></script>
 
     <script src="Views/SpringEditor.js"></script>
     <script src="Views/SVGImageResourceClusterContentView.js"></script>
index 8626e32..f44b056 100644 (file)
@@ -368,6 +368,31 @@ WI.DOMNodeStyles = class DOMNodeStyles extends WI.Object
         return this._orderedStyles;
     }
 
+    get uniqueOrderedStyles()
+    {
+        let uniqueStyles = [];
+
+        for (let style of this._orderedStyles) {
+            let rule = style.ownerRule;
+            if (!rule) {
+                uniqueStyles.push(style);
+                continue;
+            }
+
+            let found = false;
+            for (let existingStyle of uniqueStyles) {
+                if (rule.isEqualTo(existingStyle.ownerRule)) {
+                    found = true;
+                    break;
+                }
+            }
+            if (!found)
+                uniqueStyles.push(style);
+        }
+
+        return uniqueStyles;
+    }
+
     effectivePropertyForName(name)
     {
         let property = this._propertyNameToEffectivePropertyMap[name];
index f56fe2b..3d9a61b 100644 (file)
 .computed-style-properties .property:hover .go-to-arrow {
     display: initial;
 }
+
+.computed-with-traces .computed-style-properties {
+    --disclosure-button-size: 15px;
+}
+
+.computed-with-traces .details-section.computed-style-properties:not(.collapsed) > :matches(.header, .content) {
+    background-color: hsl(0, 0%, 97%);
+}
+
+.computed-with-traces .details-section.computed-style-properties > .content {
+    font: 12px -webkit-system-font, sans-serif;
+}
+
+.computed-with-traces .computed-style-properties .property .go-to-arrow {
+    width: var(--disclosure-button-size);
+    height: var(--disclosure-button-size);
+}
index 67d3034..fa6903f 100644 (file)
@@ -45,8 +45,10 @@ WI.ComputedStyleDetailsPanel = class ComputedStyleDetailsPanel extends WI.StyleD
             return;
         }
 
-        this._propertiesTextEditor.style = this.nodeStyles.computedStyle;
-        this._propertiesSection.element.classList.toggle("hidden", !this._propertiesTextEditor.propertiesToRender.length);
+        if (WI.settings.experimentalEnableComputedStyleCascades.value)
+            this._computedStyleSection.styleTraces = this._computePropertyTraces(this.nodeStyles.uniqueOrderedStyles);
+
+        this._computedStyleSection.style = this.nodeStyles.computedStyle;
 
         this._variablesTextEditor.style = this.nodeStyles.computedStyle;
         this._variablesSection.element.classList.toggle("hidden", !this._variablesTextEditor.propertiesToRender.length);
@@ -66,7 +68,7 @@ WI.ComputedStyleDetailsPanel = class ComputedStyleDetailsPanel extends WI.StyleD
         if (!this.didInitialLayout)
             return;
 
-        this._propertiesTextEditor.applyFilter(filterText);
+        this._computedStyleSection.applyFilter(filterText);
         this._variablesTextEditor.applyFilter(filterText);
     }
 
@@ -80,7 +82,7 @@ WI.ComputedStyleDetailsPanel = class ComputedStyleDetailsPanel extends WI.StyleD
 
     focusFirstSection()
     {
-        this._propertiesTextEditor.focus();
+        this._computedStyleSection.focus();
     }
 
     focusLastSection()
@@ -101,22 +103,29 @@ WI.ComputedStyleDetailsPanel = class ComputedStyleDetailsPanel extends WI.StyleD
         this._computedStyleShowAllCheckbox.addEventListener("change", this._computedStyleShowAllCheckboxValueChanged.bind(this));
         computedStyleShowAllLabel.appendChild(this._computedStyleShowAllCheckbox);
 
-        this._propertiesTextEditor = new WI.SpreadsheetCSSStyleDeclarationEditor(this);
-        this._propertiesTextEditor.propertyVisibilityMode = WI.SpreadsheetCSSStyleDeclarationEditor.PropertyVisibilityMode.HideVariables;
-        this._propertiesTextEditor.showsImplicitProperties = this._computedStyleShowAllSetting.value;
-        this._propertiesTextEditor.alwaysShowPropertyNames = ["display", "width", "height"];
-        this._propertiesTextEditor.hideFilterNonMatchingProperties = true;
-        this._propertiesTextEditor.sortPropertiesByName = true;
-        this._propertiesTextEditor.addEventListener(WI.SpreadsheetCSSStyleDeclarationEditor.Event.FilterApplied, this._handleEditorFilterApplied, this);
+        if (WI.settings.experimentalEnableComputedStyleCascades.value) {
+            this._computedStyleSection = new WI.ComputedStyleSection(this);
+            this._computedStyleSection.propertyVisibilityMode = WI.ComputedStyleSection.PropertyVisibilityMode.HideVariables;
+            this._computedStyleSection.addEventListener(WI.ComputedStyleSection.Event.FilterApplied, this._handleEditorFilterApplied, this);
+        } else {
+            this._computedStyleSection = new WI.SpreadsheetCSSStyleDeclarationEditor(this);
+            this._computedStyleSection.propertyVisibilityMode = WI.SpreadsheetCSSStyleDeclarationEditor.PropertyVisibilityMode.HideVariables;
+            this._computedStyleSection.sortPropertiesByName = true;
+            this._computedStyleSection.addEventListener(WI.SpreadsheetCSSStyleDeclarationEditor.Event.FilterApplied, this._handleEditorFilterApplied, this);
+        }
+
+        this._computedStyleSection.showsImplicitProperties = this._computedStyleShowAllSetting.value;
+        this._computedStyleSection.alwaysShowPropertyNames = ["display", "width", "height"];
+        this._computedStyleSection.hideFilterNonMatchingProperties = true;
 
         let propertiesRow = new WI.DetailsSectionRow;
         let propertiesGroup = new WI.DetailsSectionGroup([propertiesRow]);
         this._propertiesSection = new WI.DetailsSection("computed-style-properties", WI.UIString("Properties"), [propertiesGroup], computedStyleShowAllLabel);
         this._propertiesSection.addEventListener(WI.DetailsSection.Event.CollapsedStateChanged, this._handlePropertiesSectionCollapsedStateChanged, this);
 
-        this.addSubview(this._propertiesTextEditor);
+        this.addSubview(this._computedStyleSection);
 
-        propertiesRow.element.appendChild(this._propertiesTextEditor.element);
+        propertiesRow.element.appendChild(this._computedStyleSection.element);
 
         this._variablesTextEditor = new WI.SpreadsheetCSSStyleDeclarationEditor(this);
         this._variablesTextEditor.propertyVisibilityMode = WI.SpreadsheetCSSStyleDeclarationEditor.PropertyVisibilityMode.HideNonVariables;
@@ -153,17 +162,34 @@ WI.ComputedStyleDetailsPanel = class ComputedStyleDetailsPanel extends WI.StyleD
 
     // Private
 
+    _computePropertyTraces(orderedDeclarations)
+    {
+        let result = new Map();
+        for (let rule of orderedDeclarations) {
+            for (let property of rule.allProperties) {
+                let properties = result.get(property.name);
+                if (!properties) {
+                    properties = [];
+                    result.set(property.name, properties);
+                }
+                properties.push(property);
+            }
+        }
+
+        return result;
+    }
+
     _computedStyleShowAllCheckboxValueChanged(event)
     {
         let checked = this._computedStyleShowAllCheckbox.checked;
         this._computedStyleShowAllSetting.value = checked;
-        this._propertiesTextEditor.showsImplicitProperties = checked;
+        this._computedStyleSection.showsImplicitProperties = checked;
     }
 
     _handlePropertiesSectionCollapsedStateChanged(event)
     {
         if (event && event.data && !event.data.collapsed)
-            this._propertiesTextEditor.needsLayout();
+            this._computedStyleSection.needsLayout();
     }
 
     _handleVariablesSectionCollapsedStateChanged(event)
@@ -175,7 +201,7 @@ WI.ComputedStyleDetailsPanel = class ComputedStyleDetailsPanel extends WI.StyleD
     _handleEditorFilterApplied(event)
     {
         let section = null;
-        if (event.target === this._propertiesTextEditor)
+        if (event.target === this._computedStyleSection)
             section = this._propertiesSection;
         else if (event.target === this._variablesTextEditor)
             section = this._variablesSection;
index f53a169..9010ceb 100644 (file)
@@ -28,5 +28,8 @@ WI.ComputedStyleDetailsSidebarPanel = class ComputedStyleDetailsSidebarPanel ext
     constructor()
     {
         super("style-computed", WI.UIString("Computed"), WI.ComputedStyleDetailsPanel);
+
+        if (WI.settings.experimentalEnableComputedStyleCascades.value)
+            this.element.classList.add("computed-with-traces");
     }
 };
diff --git a/Source/WebInspectorUI/UserInterface/Views/ComputedStyleSection.css b/Source/WebInspectorUI/UserInterface/Views/ComputedStyleSection.css
new file mode 100644 (file)
index 0000000..80c7964
--- /dev/null
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2018 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+.computed-style-section {
+    padding-bottom: 3px;
+    color: hsl(0, 0%, 70%);
+    -webkit-user-select: text;
+}
+
+.computed-style-section .computed-property-item {
+    box-sizing: border-box;
+    max-width: 100%;
+    min-height: var(--disclosure-button-size);
+    padding-left: calc(var(--disclosure-button-size) + 6px);
+    padding-right: var(--css-declaration-horizontal-padding);
+    border-top: 0.5px solid transparent;
+    overflow: hidden;
+    text-overflow: ellipsis;
+}
+
+.computed-style-section .computed-property-item.expanded {
+    padding-bottom: 2px;
+    background-color: var(--background-color);
+    border-top-color: hsla(0, 0%, 0%, 0.2);
+}
+
+.computed-style-section .computed-property-item.expanded + .computed-property-item {
+    border-top-color: hsla(0, 0%, 0%, 0.2);
+}
+
+.computed-style-section .computed-property-item .disclosure-button {
+    display: inline-block;
+    width: var(--disclosure-button-size);
+    height: var(--disclosure-button-size);
+    margin-left: calc(-1 * var(--disclosure-button-size));
+    border: 0;
+    background-color: transparent;
+    background-image: url(../Images/DisclosureTriangles.svg#closed-normal);
+    background-repeat: no-repeat;
+    background-position: center;
+    background-size: 13px 13px;
+    -webkit-appearance: none;
+}
+
+.computed-style-section .computed-property-item .property-traces {
+    display: none;
+    width: 100%;
+}
+
+.computed-style-section .computed-property-item.expanded .property-traces {
+    display: table;
+}
+
+.computed-style-section .computed-property-item.expanded .disclosure-button {
+    background-image: url(../Images/DisclosureTriangles.svg#open-normal);
+}
+
+.computed-style-section .computed-property-item .property-trace-item {
+    display: table-row;
+}
+
+.computed-style-section .computed-property-item .property-trace-item .property.overridden .value {
+    text-decoration: line-through;
+    text-decoration-color: hsla(0, 100%, 50%, 0.7);
+}
+
+.computed-style-section .property-trace-item-left,
+.computed-style-section .property-trace-item-right {
+    display: table-cell;
+}
+
+.computed-style-section .property-trace-item-right {
+    text-align: right;
+}
+
+.computed-style-section .computed-property-item .property-trace-item .value {
+    min-width: 4em;
+    padding-right: 0.6em;
+    color: var(--text-color);
+}
+
+.computed-style-section .property .value {
+    color: var(--text-color);
+}
+
+.computed-style-section .computed-property-item .property {
+    display: inline-block;
+    white-space: nowrap;
+    overflow: hidden;
+    max-width: 100%;
+    text-overflow: ellipsis;
+}
+
+.computed-style-section .computed-property-item .property .name {
+    color: var(--syntax-highlight-boolean-color);
+}
+
+.computed-style-section .computed-property-item .property-trace-item .selector {
+    white-space: nowrap;
+    color: hsl(0, 0%, 50%);
+}
+
+.computed-style-section .computed-property-item .origin {
+    font: 11px sans-serif;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+}
+
+.computed-style-section .computed-property-item .go-to-link {
+    color: hsl(0, 0%, 50%);
+}
+
+.computed-style-section .property-trace-item .property .name,
+.computed-style-section .property-trace-item .property .name + span {
+    visibility: hidden;
+}
+
+.computed-style-section .property-trace-item .property .value + span {
+    display: none;
+}
+
+.computed-style-properties.details-section > .content,
+.computed-style-properties.details-section > .content > .group {
+    display: block;
+}
diff --git a/Source/WebInspectorUI/UserInterface/Views/ComputedStyleSection.js b/Source/WebInspectorUI/UserInterface/Views/ComputedStyleSection.js
new file mode 100644 (file)
index 0000000..7d18f48
--- /dev/null
@@ -0,0 +1,273 @@
+/*
+ * Copyright (C) 2018 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WI.ComputedStyleSection = class ComputedStyleSection extends WI.View
+{
+    constructor(delegate)
+    {
+        super();
+
+        this.element.classList.add(WI.ComputedStyleSection.StyleClassName);
+        this.element.dir = "ltr";
+
+        this._delegate = delegate;
+        this._style = null;
+        this._styleTraces = [];
+        this._propertyViews = [];
+
+        this._showsImplicitProperties = false;
+        this._alwaysShowPropertyNames = new Set;
+        this._propertyVisibilityMode = WI.ComputedStyleSection.PropertyVisibilityMode.ShowAll;
+        this._hideFilterNonMatchingProperties = false;
+        this._filterText = null;
+    }
+
+    // Public
+
+    get style()
+    {
+        return this._style;
+    }
+
+    set style(style)
+    {
+        if (this._style === style)
+            return;
+
+        if (this._style)
+            this._style.removeEventListener(WI.CSSStyleDeclaration.Event.PropertiesChanged, this._handlePropertiesChanged, this);
+
+        this._style = style || null;
+
+        if (this._style)
+            this._style.addEventListener(WI.CSSStyleDeclaration.Event.PropertiesChanged, this._handlePropertiesChanged, this);
+
+        this.needsLayout();
+    }
+
+    get styleTraces()
+    {
+        return this._styleTraces;
+    }
+
+    set styleTraces(styleTraces)
+    {
+        if (Array.shallowEqual(this._styleTraces, styleTraces))
+            return;
+
+        this._styleTraces = styleTraces || null;
+        this.needsLayout();
+    }
+
+    set showsImplicitProperties(value)
+    {
+        if (value === this._showsImplicitProperties)
+            return;
+
+        this._showsImplicitProperties = value;
+
+        this.needsLayout();
+    }
+
+    set alwaysShowPropertyNames(propertyNames)
+    {
+        this._alwaysShowPropertyNames = new Set(propertyNames);
+
+        this.needsLayout();
+    }
+
+    set propertyVisibilityMode(propertyVisibilityMode)
+    {
+        if (this._propertyVisibilityMode === propertyVisibilityMode)
+            return;
+
+        this._propertyVisibilityMode = propertyVisibilityMode;
+
+        this.needsLayout();
+    }
+
+    set hideFilterNonMatchingProperties(value)
+    {
+        if (value === this._hideFilterNonMatchingProperties)
+            return;
+
+        this._hideFilterNonMatchingProperties = value;
+
+        this.needsLayout();
+    }
+
+    get propertiesToRender()
+    {
+        let properties = [];
+        if (!this._style)
+            return properties;
+
+        if (this._style._styleSheetTextRange)
+            properties = this._style.allVisibleProperties;
+        else
+            properties = this._style.allProperties;
+
+        properties.sort((a, b) => a.name.extendedLocaleCompare(b.name));
+
+        return properties.filter((property) => {
+            if (!property.variable && this._propertyVisibilityMode === WI.ComputedStyleSection.PropertyVisibilityMode.HideNonVariables)
+                return false;
+
+            if (property.variable && this._propertyVisibilityMode === WI.ComputedStyleSection.PropertyVisibilityMode.HideVariables)
+                return false;
+
+            return !property.implicit || this._showsImplicitProperties || this._alwaysShowPropertyNames.has(property.canonicalName);
+        });
+    }
+
+    layout()
+    {
+        super.layout();
+
+        this.element.removeChildren();
+
+        this._propertyViews = [];
+        let properties = this.propertiesToRender;
+
+        for (let i = 0; i < properties.length; i++) {
+            let property = properties[i];
+            let propertyView = new WI.SpreadsheetStyleProperty(this, property);
+
+            if (this._filterText) {
+                let matchesFilter = propertyView.applyFilter(this._filterText);
+                if (!matchesFilter)
+                    continue;
+            }
+
+            propertyView.index = i;
+            this._propertyViews.push(propertyView);
+
+            let propertyTrace = this._styleTraces ? this._styleTraces.get(property.name) : null;
+            let traceElement = null;
+            if (propertyTrace && propertyTrace.length > 0)
+                traceElement = this._createTrace(propertyTrace);
+
+            let expandableView = new WI.ExpandableView(property.name, propertyView.element, traceElement);
+            expandableView.element.classList.add("computed-property-item");
+            this.element.append(expandableView.element);
+        }
+    }
+
+    detached()
+    {
+        super.detached();
+
+        for (let propertyView of this._propertyViews)
+            propertyView.detached();
+    }
+
+    hidden()
+    {
+        for (let propertyView of this._propertyViews)
+            propertyView.hidden();
+    }
+
+    applyFilter(filterText)
+    {
+        this._filterText = filterText;
+
+        if (!this.didInitialLayout)
+            return;
+
+        this.needsLayout();
+    }
+
+    // SpreadsheetStyleProperty delegate
+
+    spreadsheetStylePropertyShowProperty(propertyView, property)
+    {
+        if (this._delegate.spreadsheetCSSStyleDeclarationEditorShowProperty)
+            this._delegate.spreadsheetCSSStyleDeclarationEditorShowProperty(this, property);
+    }
+
+    // Private
+
+    _createTrace(propertyTrace)
+    {
+        let traceElement = document.createElement("div");
+        traceElement.className = "property-traces";
+
+        for (let property of propertyTrace) {
+            let traceItemElement = document.createElement("div");
+            traceItemElement.className = "property-trace-item";
+
+            let leftElement = document.createElement("div");
+            leftElement.className = "property-trace-item-left";
+
+            let rightElement = document.createElement("div");
+            rightElement.className = "property-trace-item-right";
+
+            traceItemElement.append(leftElement, rightElement);
+
+            let propertyView = new WI.SpreadsheetStyleProperty(this, property, {readOnly: true});
+
+            let selectorText = "";
+            let ownerStyle = property.ownerStyle;
+            if (ownerStyle) {
+                let ownerRule = ownerStyle.ownerRule;
+                if (ownerRule)
+                    selectorText = ownerRule.selectorText;
+            }
+            let selectorElement = document.createElement("span");
+            selectorElement.textContent = selectorText.truncateMiddle(24);
+            selectorElement.className = "selector";
+
+            leftElement.append(propertyView.element, selectorElement);
+
+            if (property.ownerStyle) {
+                let styleOriginView = new WI.StyleOriginView();
+                styleOriginView.update(property.ownerStyle);
+                rightElement.append(styleOriginView.element);
+            }
+
+            traceElement.append(traceItemElement);
+        }
+
+        return traceElement;
+    }
+
+    _handlePropertiesChanged(event)
+    {
+        this.needsLayout();
+    }
+
+};
+
+WI.ComputedStyleSection.Event = {
+    FilterApplied: "computed-style-section-filter-applied",
+};
+
+WI.ComputedStyleSection.StyleClassName = "computed-style-section";
+
+WI.ComputedStyleSection.PropertyVisibilityMode = {
+    ShowAll: Symbol("variable-visibility-show-all"),
+    HideVariables: Symbol("variable-visibility-hide-variables"),
+    HideNonVariables: Symbol("variable-visibility-hide-non-variables"),
+};
diff --git a/Source/WebInspectorUI/UserInterface/Views/ExpandableView.js b/Source/WebInspectorUI/UserInterface/Views/ExpandableView.js
new file mode 100644 (file)
index 0000000..39daf3e
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2018 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WI.ExpandableView = class ExpandableView
+{
+    constructor(key, titleElement, childElement)
+    {
+        this._element = document.createElement("div");
+
+        if (childElement) {
+            let disclosureButton = document.createElement("button");
+            disclosureButton.classList.add("disclosure-button");
+            disclosureButton.addEventListener("click", this._onDisclosureButtonClick.bind(this));
+            this._element.append(disclosureButton);
+        }
+
+        this._element.append(titleElement);
+        this._expandedSetting = new WI.Setting("expanded-" + key, false);
+
+        if (childElement) {
+            this._element.append(childElement);
+            this._element.classList.toggle("expanded", this._expandedSetting.value);
+        }
+    }
+
+    // Public
+
+    get element()
+    {
+        return this._element;
+    }
+
+    // Private
+
+    _onDisclosureButtonClick(event)
+    {
+        let shouldExpand = !this._expandedSetting.value;
+        this._update(shouldExpand);
+    }
+
+    _update(shouldExpand)
+    {
+        this._element.classList.toggle("expanded", shouldExpand);
+        this._expandedSetting.value = shouldExpand;
+    }
+};
index decaee8..b9c8e9e 100644 (file)
@@ -243,7 +243,9 @@ WI.SettingsTabContentView = class SettingsTabContentView extends WI.TabContentVi
         let experimentalSettingsView = new WI.SettingsView("experimental", WI.UIString("Experimental"));
 
         if (window.CSSAgent) {
-            experimentalSettingsView.addSetting(WI.UIString("Styles Sidebar:"), WI.settings.experimentalEnableMultiplePropertiesSelection, WI.UIString("Enable Selection of Multiple Properties"));
+            let group = experimentalSettingsView.addGroup(WI.UIString("Styles Sidebar:"));
+            group.addSetting(WI.settings.experimentalEnableMultiplePropertiesSelection, WI.UIString("Enable Selection of Multiple Properties"));
+            group.addSetting(WI.settings.experimentalEnableComputedStyleCascades, WI.UIString("Enable Computed Style Cascades"));
             experimentalSettingsView.addSeparator();
         }
 
@@ -285,6 +287,7 @@ WI.SettingsTabContentView = class SettingsTabContentView extends WI.TabContentVi
         }
 
         listenForChange(WI.settings.experimentalEnableMultiplePropertiesSelection);
+        listenForChange(WI.settings.experimentalEnableComputedStyleCascades);
         listenForChange(WI.settings.experimentalEnableLayersTab);
         listenForChange(WI.settings.experimentalEnableAuditTab);
         listenForChange(WI.settings.experimentalEnableNewTabBar);
index adf3500..c4b35f5 100644 (file)
@@ -69,9 +69,8 @@ WI.SpreadsheetCSSStyleDeclarationSection = class SpreadsheetCSSStyleDeclarationS
         this._headerElement = document.createElement("div");
         this._headerElement.classList.add("header");
 
-        this._originElement = document.createElement("span");
-        this._originElement.classList.add("origin");
-        this._headerElement.append(this._originElement);
+        this._styleOriginView = new WI.StyleOriginView();
+        this._headerElement.append(this._styleOriginView.element);
 
         this._selectorElement = document.createElement("span");
         this._selectorElement.classList.add("selector");
@@ -122,7 +121,7 @@ WI.SpreadsheetCSSStyleDeclarationSection = class SpreadsheetCSSStyleDeclarationS
     {
         super.layout();
 
-        this._renderOrigin();
+        this._styleOriginView.update(this._style);
         this._renderSelector();
 
         if (this._shouldFocusSelectorElement)
@@ -326,71 +325,6 @@ WI.SpreadsheetCSSStyleDeclarationSection = class SpreadsheetCSSStyleDeclarationS
             this.applyFilter(this._filterText);
     }
 
-    _renderOrigin()
-    {
-        this._originElement.removeChildren();
-
-        switch (this._style.type) {
-        case WI.CSSStyleDeclaration.Type.Rule:
-            console.assert(this._style.ownerRule);
-
-            if (this._style.ownerRule.sourceCodeLocation) {
-                let options = {
-                    dontFloat: true,
-                    ignoreNetworkTab: true,
-                    ignoreSearchTab: true,
-                };
-
-                if (this._style.ownerStyleSheet.isInspectorStyleSheet()) {
-                    options.nameStyle = WI.SourceCodeLocation.NameStyle.None;
-                    options.prefix = WI.UIString("Inspector Style Sheet") + ":";
-                }
-
-                let sourceCodeLink = WI.createSourceCodeLocationLink(this._style.ownerRule.sourceCodeLocation, options);
-                this._originElement.appendChild(sourceCodeLink);
-            } else {
-                let originString = "";
-
-                switch (this._style.ownerRule.type) {
-                case WI.CSSStyleSheet.Type.Author:
-                    originString = WI.UIString("Author Stylesheet");
-                    break;
-
-                case WI.CSSStyleSheet.Type.User:
-                    originString = WI.UIString("User Stylesheet");
-                    break;
-
-                case WI.CSSStyleSheet.Type.UserAgent:
-                    originString = WI.UIString("User Agent Stylesheet");
-                    break;
-
-                case WI.CSSStyleSheet.Type.Inspector:
-                    originString = WI.UIString("Web Inspector");
-                    break;
-                }
-
-                console.assert(originString);
-                if (originString)
-                    this._originElement.append(originString);
-
-                if (!this._style.editable) {
-                    let styleTitle = "";
-                    if (this._style.ownerRule && this._style.ownerRule.type === WI.CSSStyleSheet.Type.UserAgent)
-                        styleTitle = WI.UIString("User Agent Stylesheet");
-                    else
-                        styleTitle = WI.UIString("Style rule");
-
-                    this._originElement.title = WI.UIString("%s cannot be modified").format(styleTitle);
-                }
-            }
-            break;
-
-        case WI.CSSStyleDeclaration.Type.Attribute:
-            this._originElement.append(WI.UIString("HTML Attributes"));
-            break;
-        }
-    }
-
     _createMediaHeader()
     {
         let mediaList = this._style.mediaList;
index e0f5ca8..d8f0a5a 100644 (file)
@@ -215,30 +215,6 @@ WI.SpreadsheetRulesStyleDetailsPanel = class SpreadsheetRulesStyleDetailsPanel e
         this._headerMap.clear();
         this._sections = [];
 
-        let uniqueOrderedStyles = (orderedStyles) => {
-            let uniqueStyles = [];
-
-            for (let style of orderedStyles) {
-                let rule = style.ownerRule;
-                if (!rule) {
-                    uniqueStyles.push(style);
-                    continue;
-                }
-
-                let found = false;
-                for (let existingStyle of uniqueStyles) {
-                    if (rule.isEqualTo(existingStyle.ownerRule)) {
-                        found = true;
-                        break;
-                    }
-                }
-                if (!found)
-                    uniqueStyles.push(style);
-            }
-
-            return uniqueStyles;
-        };
-
         let createHeader = (text, node) => {
             let header = this.element.appendChild(document.createElement("h2"));
             header.classList.add("section-header");
@@ -270,7 +246,7 @@ WI.SpreadsheetRulesStyleDetailsPanel = class SpreadsheetRulesStyleDetailsPanel e
             previousStyle = style;
         };
 
-        for (let style of uniqueOrderedStyles(this.nodeStyles.orderedStyles)) {
+        for (let style of this.nodeStyles.uniqueOrderedStyles) {
             if (style.inherited && (!previousStyle || previousStyle.node !== style.node))
                 createHeader(WI.UIString("Inherited From"), style.node);
 
@@ -283,7 +259,7 @@ WI.SpreadsheetRulesStyleDetailsPanel = class SpreadsheetRulesStyleDetailsPanel e
             for (let pseudoNodeStyle of pseudoNodeStyles) {
                 createHeader(WI.UIString("Pseudo Element"), pseudoNodeStyle.node);
 
-                for (let style of uniqueOrderedStyles(pseudoNodeStyle.orderedStyles))
+                for (let style of pseudoNodeStyle.uniqueOrderedStyles)
                     createSection(style);
             }
         });
index 91b49d4..c086f32 100644 (file)
@@ -25,7 +25,7 @@
 
 WI.SpreadsheetStyleProperty = class SpreadsheetStyleProperty extends WI.Object
 {
-    constructor(delegate, property)
+    constructor(delegate, property, options = {})
     {
         super();
 
@@ -33,6 +33,7 @@ WI.SpreadsheetStyleProperty = class SpreadsheetStyleProperty extends WI.Object
 
         this._delegate = delegate || null;
         this._property = property;
+        this._readOnly = options.readOnly || false;
         this._element = document.createElement("div");
 
         this._contentElement = null;
@@ -42,7 +43,10 @@ WI.SpreadsheetStyleProperty = class SpreadsheetStyleProperty extends WI.Object
         this._nameTextField = null;
         this._valueTextField = null;
 
-        this._property.__propertyView = this;
+        if (!this._readOnly) {
+            // This is only needed to navigate from Computed to the corresponding property in the Styles panel.
+            this._property.__propertyView = this;
+        }
 
         this._selected = false;
         this._hasInvalidVariableValue = false;
@@ -51,7 +55,7 @@ WI.SpreadsheetStyleProperty = class SpreadsheetStyleProperty extends WI.Object
         property.addEventListener(WI.CSSProperty.Event.OverriddenStatusChanged, this.updateStatus, this);
         property.addEventListener(WI.CSSProperty.Event.Changed, this.updateStatus, this);
 
-        if (WI.settings.experimentalEnableMultiplePropertiesSelection.value && this._property.editable) {
+        if (WI.settings.experimentalEnableMultiplePropertiesSelection.value && this._isEditable()) {
             this._element.tabIndex = -1;
 
             this._element.addEventListener("blur", (event) => {
@@ -136,7 +140,7 @@ WI.SpreadsheetStyleProperty = class SpreadsheetStyleProperty extends WI.Object
     {
         this.element.removeChildren();
 
-        if (this._property.editable) {
+        if (this._isEditable()) {
             this._checkboxElement = this.element.appendChild(document.createElement("input"));
             this._checkboxElement.classList.add("property-toggle");
             this._checkboxElement.type = "checkbox";
@@ -167,7 +171,7 @@ WI.SpreadsheetStyleProperty = class SpreadsheetStyleProperty extends WI.Object
         this._valueElement.classList.add("value");
         this._renderValue(this._property.rawValue);
 
-        if (this._property.editable && this._property.enabled) {
+        if (this._isEditable() && this._property.enabled) {
             this._nameElement.tabIndex = 0;
             this._nameElement.addEventListener("beforeinput", this._handleNameBeforeInput.bind(this));
             this._nameElement.addEventListener("paste", this._handleNamePaste.bind(this));
@@ -180,7 +184,7 @@ WI.SpreadsheetStyleProperty = class SpreadsheetStyleProperty extends WI.Object
             this._valueTextField = new WI.SpreadsheetTextField(this, this._valueElement, this._valueCompletionDataProvider.bind(this));
         }
 
-        if (this._property.editable) {
+        if (this._isEditable()) {
             this._setupJumpToSymbol(this._nameElement);
             this._setupJumpToSymbol(this._valueElement);
         }
@@ -379,6 +383,11 @@ WI.SpreadsheetStyleProperty = class SpreadsheetStyleProperty extends WI.Object
 
     // Private
 
+    _isEditable()
+    {
+        return !this._readOnly && this._property.editable;
+    }
+
     _renderValue(value)
     {
         this._hasInvalidVariableValue = false;
@@ -434,7 +443,7 @@ WI.SpreadsheetStyleProperty = class SpreadsheetStyleProperty extends WI.Object
         let innerElement = document.createElement("span");
         innerElement.textContent = text;
 
-        let readOnly = !this._property.editable;
+        let readOnly = !this._isEditable();
         let swatch = new WI.InlineSwatch(type, valueObject, readOnly);
 
         swatch.addEventListener(WI.InlineSwatch.Event.ValueChanged, (event) => {
diff --git a/Source/WebInspectorUI/UserInterface/Views/StyleOriginView.js b/Source/WebInspectorUI/UserInterface/Views/StyleOriginView.js
new file mode 100644 (file)
index 0000000..f98f63c
--- /dev/null
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2018 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+WI.StyleOriginView = class StyleOriginView
+{
+    constructor()
+    {
+        this.element = document.createElement("span");
+        this.element.className = "origin";
+    }
+
+    // Public
+
+    update(style)
+    {
+        this.element.removeChildren();
+        this.element.removeAttribute("title");
+
+        switch (style.type) {
+        case WI.CSSStyleDeclaration.Type.Rule:
+            console.assert(style.ownerRule);
+
+            if (style.ownerRule.sourceCodeLocation) {
+                let options = {
+                    dontFloat: true,
+                    ignoreNetworkTab: true,
+                    ignoreSearchTab: true,
+                };
+
+                if (style.ownerStyleSheet.isInspectorStyleSheet()) {
+                    options.nameStyle = WI.SourceCodeLocation.NameStyle.None;
+                    options.prefix = WI.UIString("Inspector Style Sheet") + ":";
+                }
+
+                let sourceCodeLink = WI.createSourceCodeLocationLink(style.ownerRule.sourceCodeLocation, options);
+                this.element.appendChild(sourceCodeLink);
+            } else {
+                let originString = "";
+
+                switch (style.ownerRule.type) {
+                case WI.CSSStyleSheet.Type.Author:
+                    originString = WI.UIString("Author Stylesheet");
+                    break;
+
+                case WI.CSSStyleSheet.Type.User:
+                    originString = WI.UIString("User Stylesheet");
+                    break;
+
+                case WI.CSSStyleSheet.Type.UserAgent:
+                    originString = WI.UIString("User Agent Stylesheet");
+                    break;
+
+                case WI.CSSStyleSheet.Type.Inspector:
+                    originString = WI.UIString("Web Inspector");
+                    break;
+                }
+
+                console.assert(originString);
+                if (originString)
+                    this.element.append(originString);
+
+                if (!style.editable) {
+                    let styleTitle = "";
+                    if (style.ownerRule && style.ownerRule.type === WI.CSSStyleSheet.Type.UserAgent)
+                        styleTitle = WI.UIString("User Agent Stylesheet");
+                    else
+                        styleTitle = WI.UIString("Style rule");
+
+                    this.element.title = WI.UIString("%s cannot be modified").format(styleTitle);
+                }
+            }
+            break;
+
+        case WI.CSSStyleDeclaration.Type.Attribute:
+            this.element.append(WI.UIString("HTML Attributes"));
+            break;
+        }
+    }
+};