<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[237659] trunk/Source/WebInspectorUI</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/237659">237659</a></dd>
<dt>Author</dt> <dd>nvasilyev@apple.com</dd>
<dt>Date</dt> <dd>2018-10-31 15:52:11 -0700 (Wed, 31 Oct 2018)</dd>
</dl>

<h3>Log Message</h3>
<pre>Web Inspector: Styles: implement copying and deletion of multiple properties
https://bugs.webkit.org/show_bug.cgi?id=191037
<rdar://problem/45650078>

Reviewed by Brian Burg.

This patch should only work with "Enable Selection of Multiple Properties" checked. It shouldn't introduce any
changes when this setting is unchecked.

Mousedown on a property (1) and moving the mouse cursor to another property (2) should select properties 1, 2, and
all properties between them until mouseup is fired.

Once selected:
- Pressing Command-C should copy the selected properties.
- Pressing Delete should remove the properties.

* UserInterface/Models/CSSProperty.js:
(WI.CSSProperty.prototype.get formattedText):
* UserInterface/Views/SpreadsheetCSSStyleDeclarationEditor.css:
(.spreadsheet-style-declaration-editor):
(.multiple-properties-selection .spreadsheet-style-declaration-editor .property):
(.multiple-properties-selection .spreadsheet-style-declaration-editor :matches(.name, .value):not(.editing)):
(.multiple-properties-selection .spreadsheet-style-declaration-editor .property.selected):
(.multiple-properties-selection .spreadsheet-style-declaration-editor .property.selected:focus):
(@media (prefers-dark-interface)):

* UserInterface/Views/SpreadsheetCSSStyleDeclarationEditor.js:
(WI.SpreadsheetCSSStyleDeclarationEditor):
(WI.SpreadsheetCSSStyleDeclarationEditor.prototype.initialLayout):
(WI.SpreadsheetCSSStyleDeclarationEditor.prototype.selectProperties):
(WI.SpreadsheetCSSStyleDeclarationEditor.prototype.deselectProperties):
(WI.SpreadsheetCSSStyleDeclarationEditor.prototype.spreadsheetStylePropertyBlur):
(WI.SpreadsheetCSSStyleDeclarationEditor.prototype.spreadsheetStylePropertyMouseEnter):
(WI.SpreadsheetCSSStyleDeclarationEditor.prototype.spreadsheetStylePropertyMouseLeave):
(WI.SpreadsheetCSSStyleDeclarationEditor.prototype.spreadsheetStylePropertyCopy):
(WI.SpreadsheetCSSStyleDeclarationEditor.prototype._handleKeyDown):
(WI.SpreadsheetCSSStyleDeclarationEditor.prototype._hasSelectedProperties):
Property selection is defined as two numbers: anchorIndex and focusIndex.
The property with focusIndex is actually focused. The focus outline is replaced by a more subtle left blue border.

* UserInterface/Views/SpreadsheetCSSStyleDeclarationSection.css:
(.spreadsheet-css-declaration.selecting,):
* UserInterface/Views/SpreadsheetCSSStyleDeclarationSection.js:
(WI.SpreadsheetCSSStyleDeclarationSection):
(WI.SpreadsheetCSSStyleDeclarationSection.prototype.spreadsheetCSSStyleDeclarationEditorPropertyBlur):
(WI.SpreadsheetCSSStyleDeclarationSection.prototype.spreadsheetCSSStyleDeclarationEditorPropertyMouseEnter):
(WI.SpreadsheetCSSStyleDeclarationSection.prototype.spreadsheetCSSStyleDeclarationEditorPropertyMouseLeave):
(WI.SpreadsheetCSSStyleDeclarationSection.prototype._handleMouseDown):
(WI.SpreadsheetCSSStyleDeclarationSection.prototype._handleWindowMouseUp):
(WI.SpreadsheetCSSStyleDeclarationSection.prototype._handleClick):
* UserInterface/Views/SpreadsheetStyleProperty.js:
(WI.SpreadsheetStyleProperty):
Implement copying the same way it's done for DataGrid: by adding copyHandler property to the focused element.
Add `tabIndex=-1` so the property element can be focused.

(WI.SpreadsheetStyleProperty.prototype.get property):
(WI.SpreadsheetStyleProperty.prototype.get selected):
(WI.SpreadsheetStyleProperty.prototype.set selected):
(WI.SpreadsheetStyleProperty.prototype.remove):
(WI.SpreadsheetStyleProperty.prototype.updateStatus):
(WI.SpreadsheetStyleProperty.prototype.handleCopyEvent):
(WI.SpreadsheetStyleProperty.prototype.spreadsheetTextFieldDidCommit):
(WI.SpreadsheetStyleProperty.prototype.spreadsheetTextFieldDidBlur):
(WI.SpreadsheetStyleProperty.prototype._handleNamePaste):</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebInspectorUIChangeLog">trunk/Source/WebInspectorUI/ChangeLog</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceModelsCSSPropertyjs">trunk/Source/WebInspectorUI/UserInterface/Models/CSSProperty.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceViewsSpreadsheetCSSStyleDeclarationEditorcss">trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetCSSStyleDeclarationEditor.css</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceViewsSpreadsheetCSSStyleDeclarationEditorjs">trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetCSSStyleDeclarationEditor.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceViewsSpreadsheetCSSStyleDeclarationSectioncss">trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetCSSStyleDeclarationSection.css</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceViewsSpreadsheetCSSStyleDeclarationSectionjs">trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetCSSStyleDeclarationSection.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceViewsSpreadsheetStylePropertyjs">trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetStyleProperty.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebInspectorUIChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/ChangeLog (237658 => 237659)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/ChangeLog    2018-10-31 22:44:27 UTC (rev 237658)
+++ trunk/Source/WebInspectorUI/ChangeLog       2018-10-31 22:52:11 UTC (rev 237659)
</span><span class="lines">@@ -1,3 +1,70 @@
</span><ins>+2018-10-31  Nikita Vasilyev  <nvasilyev@apple.com>
+
+        Web Inspector: Styles: implement copying and deletion of multiple properties
+        https://bugs.webkit.org/show_bug.cgi?id=191037
+        <rdar://problem/45650078>
+
+        Reviewed by Brian Burg.
+
+        This patch should only work with "Enable Selection of Multiple Properties" checked. It shouldn't introduce any
+        changes when this setting is unchecked.
+
+        Mousedown on a property (1) and moving the mouse cursor to another property (2) should select properties 1, 2, and
+        all properties between them until mouseup is fired.
+
+        Once selected:
+        - Pressing Command-C should copy the selected properties.
+        - Pressing Delete should remove the properties.
+
+        * UserInterface/Models/CSSProperty.js:
+        (WI.CSSProperty.prototype.get formattedText):
+        * UserInterface/Views/SpreadsheetCSSStyleDeclarationEditor.css:
+        (.spreadsheet-style-declaration-editor):
+        (.multiple-properties-selection .spreadsheet-style-declaration-editor .property):
+        (.multiple-properties-selection .spreadsheet-style-declaration-editor :matches(.name, .value):not(.editing)):
+        (.multiple-properties-selection .spreadsheet-style-declaration-editor .property.selected):
+        (.multiple-properties-selection .spreadsheet-style-declaration-editor .property.selected:focus):
+        (@media (prefers-dark-interface)):
+
+        * UserInterface/Views/SpreadsheetCSSStyleDeclarationEditor.js:
+        (WI.SpreadsheetCSSStyleDeclarationEditor):
+        (WI.SpreadsheetCSSStyleDeclarationEditor.prototype.initialLayout):
+        (WI.SpreadsheetCSSStyleDeclarationEditor.prototype.selectProperties):
+        (WI.SpreadsheetCSSStyleDeclarationEditor.prototype.deselectProperties):
+        (WI.SpreadsheetCSSStyleDeclarationEditor.prototype.spreadsheetStylePropertyBlur):
+        (WI.SpreadsheetCSSStyleDeclarationEditor.prototype.spreadsheetStylePropertyMouseEnter):
+        (WI.SpreadsheetCSSStyleDeclarationEditor.prototype.spreadsheetStylePropertyMouseLeave):
+        (WI.SpreadsheetCSSStyleDeclarationEditor.prototype.spreadsheetStylePropertyCopy):
+        (WI.SpreadsheetCSSStyleDeclarationEditor.prototype._handleKeyDown):
+        (WI.SpreadsheetCSSStyleDeclarationEditor.prototype._hasSelectedProperties):
+        Property selection is defined as two numbers: anchorIndex and focusIndex.
+        The property with focusIndex is actually focused. The focus outline is replaced by a more subtle left blue border.
+
+        * UserInterface/Views/SpreadsheetCSSStyleDeclarationSection.css:
+        (.spreadsheet-css-declaration.selecting,):
+        * UserInterface/Views/SpreadsheetCSSStyleDeclarationSection.js:
+        (WI.SpreadsheetCSSStyleDeclarationSection):
+        (WI.SpreadsheetCSSStyleDeclarationSection.prototype.spreadsheetCSSStyleDeclarationEditorPropertyBlur):
+        (WI.SpreadsheetCSSStyleDeclarationSection.prototype.spreadsheetCSSStyleDeclarationEditorPropertyMouseEnter):
+        (WI.SpreadsheetCSSStyleDeclarationSection.prototype.spreadsheetCSSStyleDeclarationEditorPropertyMouseLeave):
+        (WI.SpreadsheetCSSStyleDeclarationSection.prototype._handleMouseDown):
+        (WI.SpreadsheetCSSStyleDeclarationSection.prototype._handleWindowMouseUp):
+        (WI.SpreadsheetCSSStyleDeclarationSection.prototype._handleClick):
+        * UserInterface/Views/SpreadsheetStyleProperty.js:
+        (WI.SpreadsheetStyleProperty):
+        Implement copying the same way it's done for DataGrid: by adding copyHandler property to the focused element.
+        Add `tabIndex=-1` so the property element can be focused.
+
+        (WI.SpreadsheetStyleProperty.prototype.get property):
+        (WI.SpreadsheetStyleProperty.prototype.get selected):
+        (WI.SpreadsheetStyleProperty.prototype.set selected):
+        (WI.SpreadsheetStyleProperty.prototype.remove):
+        (WI.SpreadsheetStyleProperty.prototype.updateStatus):
+        (WI.SpreadsheetStyleProperty.prototype.handleCopyEvent):
+        (WI.SpreadsheetStyleProperty.prototype.spreadsheetTextFieldDidCommit):
+        (WI.SpreadsheetStyleProperty.prototype.spreadsheetTextFieldDidBlur):
+        (WI.SpreadsheetStyleProperty.prototype._handleNamePaste):
+
</ins><span class="cx"> 2018-10-31  Devin Rousso  <drousso@apple.com>
</span><span class="cx"> 
</span><span class="cx">         Web Inspector: Audit: attempt to re-link DOM nodes for imported results
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceModelsCSSPropertyjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Models/CSSProperty.js (237658 => 237659)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Models/CSSProperty.js  2018-10-31 22:44:27 UTC (rev 237658)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/CSSProperty.js     2018-10-31 22:52:11 UTC (rev 237659)
</span><span class="lines">@@ -164,6 +164,14 @@
</span><span class="cx">         this._text = newText;
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    get formattedText()
+    {
+        if (!this._name)
+            return "";
+
+        return `${this._name}: ${this._rawValue};`;
+    }
+
</ins><span class="cx">     get name()
</span><span class="cx">     {
</span><span class="cx">         return this._name;
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceViewsSpreadsheetCSSStyleDeclarationEditorcss"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetCSSStyleDeclarationEditor.css (237658 => 237659)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetCSSStyleDeclarationEditor.css 2018-10-31 22:44:27 UTC (rev 237658)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetCSSStyleDeclarationEditor.css    2018-10-31 22:52:11 UTC (rev 237659)
</span><span class="lines">@@ -30,6 +30,9 @@
</span><span class="cx">     font-size: 11px;
</span><span class="cx">     color: hsl(0, 0%, 70%);
</span><span class="cx">     -webkit-user-select: text;
</span><ins>+
+    --background-color-selected: hsl(210, 98%, 93%);
+    --border-color-selected: hsl(225, 91%, 70%);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> .spreadsheet-style-declaration-editor .property {
</span><span class="lines">@@ -37,6 +40,11 @@
</span><span class="cx">     padding-left: calc(var(--css-declaration-horizontal-padding) + 17px);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+.multiple-properties-selection .spreadsheet-style-declaration-editor .property {
+    border-left: 1px solid transparent;
+    outline: none;
+}
+
</ins><span class="cx"> .spreadsheet-style-declaration-editor .property:not(.disabled) .name {
</span><span class="cx">     color: var(--syntax-highlight-boolean-color);
</span><span class="cx"> }
</span><span class="lines">@@ -45,6 +53,10 @@
</span><span class="cx">     color: var(--text-color);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+.multiple-properties-selection .spreadsheet-style-declaration-editor :matches(.name, .value):not(.editing) {
+    outline: none;
+}
+
</ins><span class="cx"> .spreadsheet-style-declaration-editor :matches(.name, .value).editing {
</span><span class="cx">     outline: 1px solid white !important;
</span><span class="cx">     box-shadow: 0 1px 2px 1px hsla(0, 0%, 0%, 0.6);
</span><span class="lines">@@ -124,6 +136,14 @@
</span><span class="cx">     -webkit-clip-path: polygon(0% 50%, 6px 0%, 100% 0%, 100% 100%, 6px 100%);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+.multiple-properties-selection .spreadsheet-style-declaration-editor .property.selected {
+    background-color: var(--background-color-selected);
+}
+
+.multiple-properties-selection .spreadsheet-style-declaration-editor .property.selected:focus {
+    border-left-color: var(--border-color-selected);
+}
+
</ins><span class="cx"> .spreadsheet-style-declaration-editor .property:matches(.implicit, .not-inherited) .content > * {
</span><span class="cx">     opacity: 0.5;
</span><span class="cx"> }
</span><span class="lines">@@ -160,6 +180,11 @@
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> @media (prefers-dark-interface) {
</span><ins>+    .spreadsheet-style-declaration-editor {
+        --background-color-selected: hsl(230, 51%, 36%);
+        --border-color-selected: hsl(216, 98%, 67%);
+    }
+
</ins><span class="cx">     .spreadsheet-style-declaration-editor :matches(.name, .value).editing {
</span><span class="cx">         outline-color: var(--background-color-secondary) !important;
</span><span class="cx">     }
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceViewsSpreadsheetCSSStyleDeclarationEditorjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetCSSStyleDeclarationEditor.js (237658 => 237659)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetCSSStyleDeclarationEditor.js  2018-10-31 22:44:27 UTC (rev 237658)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetCSSStyleDeclarationEditor.js     2018-10-31 22:52:11 UTC (rev 237659)
</span><span class="lines">@@ -47,6 +47,9 @@
</span><span class="cx">         this._propertyPendingStartEditing = null;
</span><span class="cx">         this._pendingAddBlankPropertyIndexOffset = NaN;
</span><span class="cx">         this._filterText = null;
</span><ins>+
+        this._anchorIndex = NaN;
+        this._focusIndex = NaN;
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     // Public
</span><span class="lines">@@ -64,6 +67,9 @@
</span><span class="cx"> 
</span><span class="cx">             this.focused = false;
</span><span class="cx">         }, true);
</span><ins>+
+        if (WI.settings.experimentalEnableMultiplePropertiesSelection.value)
+            this.element.addEventListener("keydown", this._handleKeyDown.bind(this));
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     layout()
</span><span class="lines">@@ -307,6 +313,43 @@
</span><span class="cx">         this.needsLayout();
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    selectProperties(anchorIndex, focusIndex)
+    {
+        console.assert(anchorIndex < this._propertyViews.length, `anchorIndex (${anchorIndex}) is greater than the last property index (${this._propertyViews.length})`);
+        console.assert(focusIndex < this._propertyViews.length, `focusIndex (${focusIndex}) is greater than the last property index (${this._propertyViews.length})`);
+
+        if (isNaN(anchorIndex) || isNaN(focusIndex)) {
+            console.error(`Nothing to select. anchorIndex (${anchorIndex}) and focusIndex (${focusIndex}) must be numbers.`);
+            this.deselectProperties();
+            return;
+        }
+
+        this._anchorIndex = anchorIndex;
+        this._focusIndex = focusIndex;
+
+        let startIndex = Math.min(anchorIndex, focusIndex);
+        let endIndex = Math.max(anchorIndex, focusIndex);
+
+        for (let i = 0; i < this._propertyViews.length; ++i) {
+            let propertyView = this._propertyViews[i];
+            let isSelected = i >= startIndex && i <= endIndex;
+            propertyView.selected = isSelected;
+        }
+
+        let property = this._propertyViews[focusIndex];
+        property.element.focus();
+    }
+
+    deselectProperties()
+    {
+        for (let propertyView  of this._propertyViews)
+            propertyView.selected = false;
+
+        this._focused = false;
+        this._anchorIndex = NaN;
+        this._focusIndex = NaN;
+    }
+
</ins><span class="cx">     applyFilter(filterText)
</span><span class="cx">     {
</span><span class="cx">         this._filterText = filterText;
</span><span class="lines">@@ -330,6 +373,24 @@
</span><span class="cx"> 
</span><span class="cx">     // SpreadsheetStyleProperty delegate
</span><span class="cx"> 
</span><ins>+    spreadsheetStylePropertyBlur(event, property)
+    {
+        if (this._suppressBlur)
+            return;
+
+        this._delegate.spreadsheetCSSStyleDeclarationEditorPropertyBlur(event, property);
+    }
+
+    spreadsheetStylePropertyMouseEnter(event, property)
+    {
+        this._delegate.spreadsheetCSSStyleDeclarationEditorPropertyMouseEnter(event, property);
+    }
+
+    spreadsheetStylePropertyMouseLeave(event, property)
+    {
+        this._delegate.spreadsheetCSSStyleDeclarationEditorPropertyMouseLeave(event, property);
+    }
+
</ins><span class="cx">     spreadsheetStylePropertyFocusMoved(propertyView, {direction, willRemoveProperty})
</span><span class="cx">     {
</span><span class="cx">         this._updatePropertiesStatus();
</span><span class="lines">@@ -384,6 +445,23 @@
</span><span class="cx">         this._pendingAddBlankPropertyIndexOffset = this._propertyViews.length - index;
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    spreadsheetStylePropertyCopy(event)
+    {
+        if (!this._hasSelectedProperties())
+            return;
+
+        let formattedProperties = [];
+        let startIndex = Math.min(this._anchorIndex, this._focusIndex);
+        let endIndex = Math.max(this._anchorIndex, this._focusIndex);
+        for (let i = startIndex; i <= endIndex; ++i) {
+            let propertyView = this._propertyViews[i];
+            formattedProperties.push(propertyView.property.formattedText);
+        }
+
+        event.clipboardData.setData("text/plain", formattedProperties.join("\n"));
+        event.stop();
+    }
+
</ins><span class="cx">     spreadsheetStylePropertyRemoved(propertyView)
</span><span class="cx">     {
</span><span class="cx">         this._propertyViews.remove(propertyView);
</span><span class="lines">@@ -412,6 +490,53 @@
</span><span class="cx"> 
</span><span class="cx">     // Private
</span><span class="cx"> 
</span><ins>+    _handleKeyDown(event)
+    {
+        if (event.key === "ArrowUp" || event.key === "ArrowDown") {
+            let delta = event.key === "ArrowUp" ? -1 : 1;
+            let focusIndex = Number.constrain(this._focusIndex + delta, 0, this._propertyViews.length - 1);
+
+            // Blur event deselects all properties.
+            this._suppressBlur = true;
+            this.selectProperties(focusIndex, focusIndex);
+            this._suppressBlur = false;
+
+            event.stop();
+        } else if (event.key === "Backspace") {
+            if (!this._hasSelectedProperties())
+                return;
+
+            let startIndex = Math.min(this._anchorIndex, this._focusIndex);
+            let endIndex = Math.max(this._anchorIndex, this._focusIndex);
+
+            let propertyIndexToSelect = NaN;
+            if (endIndex + 1 !== this._propertyViews.length)
+                propertyIndexToSelect = startIndex;
+            else if (startIndex > 0)
+                propertyIndexToSelect = startIndex - 1;
+
+            this.deselectProperties();
+
+            for (let i = endIndex; i >= startIndex; i--)
+                this._propertyViews[i].remove();
+
+            if (!isNaN(propertyIndexToSelect)) {
+                this._suppressBlur = true;
+                this.selectProperties(propertyIndexToSelect, propertyIndexToSelect);
+                this._suppressBlur = false;
+            }
+
+            event.stop();
+
+        } else if (event.key === "Esc")
+            this.deselectProperties();
+    }
+
+    _hasSelectedProperties()
+    {
+        return !isNaN(this._anchorIndex) && !isNaN(this._focusIndex);
+    }
+
</ins><span class="cx">     _editablePropertyAfter(propertyIndex)
</span><span class="cx">     {
</span><span class="cx">         for (let index = propertyIndex + 1; index < this._propertyViews.length; index++) {
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceViewsSpreadsheetCSSStyleDeclarationSectioncss"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetCSSStyleDeclarationSection.css (237658 => 237659)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetCSSStyleDeclarationSection.css        2018-10-31 22:44:27 UTC (rev 237658)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetCSSStyleDeclarationSection.css   2018-10-31 22:52:11 UTC (rev 237659)
</span><span class="lines">@@ -34,6 +34,11 @@
</span><span class="cx">     -webkit-user-select: text;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+.spreadsheet-css-declaration.selecting,
+.spreadsheet-css-declaration.selecting .spreadsheet-style-declaration-editor {
+    -webkit-user-select: none;
+}
+
</ins><span class="cx"> .spreadsheet-css-declaration :matches(.header, .header-media) {
</span><span class="cx">     padding: 0 var(--css-declaration-horizontal-padding);
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceViewsSpreadsheetCSSStyleDeclarationSectionjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetCSSStyleDeclarationSection.js (237658 => 237659)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetCSSStyleDeclarationSection.js 2018-10-31 22:44:27 UTC (rev 237658)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetCSSStyleDeclarationSection.js    2018-10-31 22:52:11 UTC (rev 237659)
</span><span class="lines">@@ -37,6 +37,9 @@
</span><span class="cx"> 
</span><span class="cx">         super(element);
</span><span class="cx"> 
</span><ins>+        if (WI.settings.experimentalEnableMultiplePropertiesSelection.value)
+            element.classList.add("multiple-properties-selection");
+
</ins><span class="cx">         this._delegate = delegate || null;
</span><span class="cx">         this._style = style;
</span><span class="cx">         this._propertiesEditor = null;
</span><span class="lines">@@ -45,6 +48,10 @@
</span><span class="cx">         this._filterText = null;
</span><span class="cx">         this._shouldFocusSelectorElement = false;
</span><span class="cx">         this._wasEditing = false;
</span><ins>+
+        this._isMousePressed = true;
+        this._mouseDownIndex = NaN;
+        this._startedSelection = false;
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     // Public
</span><span class="lines">@@ -208,6 +215,26 @@
</span><span class="cx">             this._delegate.spreadsheetCSSStyleDeclarationSectionStartEditingAdjacentRule(this, delta);
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    spreadsheetCSSStyleDeclarationEditorPropertyBlur(event, property)
+    {
+        if (!this._startedSelection)
+            this._propertiesEditor.deselectProperties();
+    }
+
+    spreadsheetCSSStyleDeclarationEditorPropertyMouseEnter(event, property)
+    {
+        if (this._isMousePressed && this._startedSelection) {
+            let index = parseInt(property.element.dataset.propertyIndex);
+            this._propertiesEditor.selectProperties(this._mouseDownIndex, index);
+        }
+    }
+
+    spreadsheetCSSStyleDeclarationEditorPropertyMouseLeave(event, property)
+    {
+        if (this._isMousePressed)
+            this._startedSelection = true;
+    }
+
</ins><span class="cx">     applyFilter(filterText)
</span><span class="cx">     {
</span><span class="cx">         this._filterText = filterText;
</span><span class="lines">@@ -435,11 +462,50 @@
</span><span class="cx">     _handleMouseDown(event)
</span><span class="cx">     {
</span><span class="cx">         this._wasEditing = this._propertiesEditor.editing || document.activeElement === this._selectorElement;
</span><ins>+
+        if (!WI.settings.experimentalEnableMultiplePropertiesSelection.value)
+            return;
+
+        let propertyElement = event.target.closest(".property");
+        if (!propertyElement)
+            return;
+
+        this._isMousePressed = true;
+        this._startedSelection = false;
+
+        // Disable text selection on mousemove.
+        event.preventDefault();
+
+        // Canceling mousedown event prevents blur event from firing on the previously focused element.
+        if (this._wasEditing && document.activeElement)
+            document.activeElement.blur();
+
+        window.addEventListener("mouseup", this._handleWindowMouseUp.bind(this), {capture: true, once: true});
+
+        let propertyIndex = parseInt(propertyElement.dataset.propertyIndex);
+        this._propertiesEditor.deselectProperties();
+        this._mouseDownIndex = propertyIndex;
+
+        this._element.classList.add("selecting");
</ins><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    _handleWindowMouseUp(event)
+    {
+        if (this._startedSelection) {
+            // Don't start editing name/value if there's selection.
+            event.stop();
+            this._startedSelection = false;
+        }
+
+        this._isMousePressed = false;
+        this._mouseDownIndex = NaN;
+
+        this._element.classList.remove("selecting");
+    }
+
</ins><span class="cx">     _handleClick(event)
</span><span class="cx">     {
</span><del>-        if (this._wasEditing)
</del><ins>+        if (this._wasEditing || this._startedSelection)
</ins><span class="cx">             return;
</span><span class="cx"> 
</span><span class="cx">         if (window.getSelection().type === "Range")
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceViewsSpreadsheetStylePropertyjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetStyleProperty.js (237658 => 237659)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetStyleProperty.js      2018-10-31 22:44:27 UTC (rev 237658)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetStyleProperty.js 2018-10-31 22:52:11 UTC (rev 237659)
</span><span class="lines">@@ -44,16 +44,36 @@
</span><span class="cx"> 
</span><span class="cx">         this._property.__propertyView = this;
</span><span class="cx"> 
</span><ins>+        this._selected = false;
</ins><span class="cx">         this._hasInvalidVariableValue = false;
</span><span class="cx"> 
</span><span class="cx">         this._update();
</span><span class="cx">         property.addEventListener(WI.CSSProperty.Event.OverriddenStatusChanged, this.updateStatus, this);
</span><span class="cx">         property.addEventListener(WI.CSSProperty.Event.Changed, this.updateStatus, this);
</span><ins>+
+        if (WI.settings.experimentalEnableMultiplePropertiesSelection.value && this._property.editable) {
+            this._element.tabIndex = -1;
+
+            this._element.addEventListener("blur", (event) => {
+                this._delegate.spreadsheetStylePropertyBlur(event, this);
+            });
+
+            this._element.addEventListener("mouseenter", (event) => {
+                this._delegate.spreadsheetStylePropertyMouseEnter(event, this);
+            });
+
+            this._element.addEventListener("mouseleave", (event) => {
+                this._delegate.spreadsheetStylePropertyMouseLeave(event, this);
+            });
+
+            this._element.copyHandler = this;
+        }
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     // Public
</span><span class="cx"> 
</span><span class="cx">     get element() { return this._element; }
</span><ins>+    get property() { return this._property; }
</ins><span class="cx">     get nameTextField() { return this._nameTextField; }
</span><span class="cx">     get valueTextField() { return this._valueTextField; }
</span><span class="cx">     get enabled() { return this._property.enabled; }
</span><span class="lines">@@ -63,6 +83,20 @@
</span><span class="cx">         this._element.dataset.propertyIndex = index;
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    get selected()
+    {
+        return this._selected;
+    }
+
+    set selected(value)
+    {
+        if (value === this._selected)
+            return;
+
+        this._selected = value;
+        this.updateStatus();
+    }
+
</ins><span class="cx">     detached()
</span><span class="cx">     {
</span><span class="cx">         this._property.__propertyView = null;
</span><span class="lines">@@ -87,6 +121,21 @@
</span><span class="cx">         this._element.classList.add("highlighted");
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    remove(replacement = null)
+    {
+        this.element.remove();
+
+        if (replacement)
+            this._property.replaceWithText(replacement);
+        else
+            this._property.remove();
+
+        this.detached();
+
+        if (this._delegate && typeof this._delegate.spreadsheetStylePropertyRemoved === "function")
+            this._delegate.spreadsheetStylePropertyRemoved(this);
+    }
+
</ins><span class="cx">     updateStatus()
</span><span class="cx">     {
</span><span class="cx">         let duplicatePropertyExistsBelow = (cssProperty) => {
</span><span class="lines">@@ -140,6 +189,9 @@
</span><span class="cx">         if (!this._property.enabled)
</span><span class="cx">             classNames.push("disabled");
</span><span class="cx"> 
</span><ins>+        if (this._selected)
+            classNames.push("selected");
+
</ins><span class="cx">         this._element.className = classNames.join(" ");
</span><span class="cx">         this._element.title = elementTitle;
</span><span class="cx">     }
</span><span class="lines">@@ -157,23 +209,13 @@
</span><span class="cx">         return matches;
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    // Private
-
-    _remove(replacement = "")
</del><ins>+    handleCopyEvent(event)
</ins><span class="cx">     {
</span><del>-        this.element.remove();
</del><ins>+        this._delegate.spreadsheetStylePropertyCopy(event, this);
+    }
</ins><span class="cx"> 
</span><del>-        if (replacement)
-            this._property.replaceWithText(replacement);
-        else
-            this._property.remove();
</del><ins>+    // Private
</ins><span class="cx"> 
</span><del>-        this.detached();
-
-        if (this._delegate && typeof this._delegate.spreadsheetStylePropertyRemoved === "function")
-            this._delegate.spreadsheetStylePropertyRemoved(this);
-    }
-
</del><span class="cx">     _update()
</span><span class="cx">     {
</span><span class="cx">         this.element.removeChildren();
</span><span class="lines">@@ -315,7 +357,7 @@
</span><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         if (willRemoveProperty)
</span><del>-            this._remove();
</del><ins>+            this.remove();
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     spreadsheetTextFieldDidBlur(textField, event)
</span><span class="lines">@@ -322,7 +364,7 @@
</span><span class="cx">     {
</span><span class="cx">         let focusedOutsideThisProperty = event.relatedTarget !== this._nameElement && event.relatedTarget !== this._valueElement;
</span><span class="cx">         if (focusedOutsideThisProperty && (!this._nameTextField.value.trim() || !this._valueTextField.value.trim())) {
</span><del>-            this._remove();
</del><ins>+            this.remove();
</ins><span class="cx">             return;
</span><span class="cx">         }
</span><span class="cx"> 
</span><span class="lines">@@ -617,7 +659,7 @@
</span><span class="cx"> 
</span><span class="cx">         event.preventDefault();
</span><span class="cx"> 
</span><del>-        this._remove(text);
</del><ins>+        this.remove(text);
</ins><span class="cx"> 
</span><span class="cx">         if (this._delegate.spreadsheetStylePropertyAddBlankPropertySoon) {
</span><span class="cx">             this._delegate.spreadsheetStylePropertyAddBlankPropertySoon(this, {
</span></span></pre>
</div>
</div>

</body>
</html>