<!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>[245991] trunk</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/245991">245991</a></dd>
<dt>Author</dt> <dd>nvasilyev@apple.com</dd>
<dt>Date</dt> <dd>2019-05-31 15:53:40 -0700 (Fri, 31 May 2019)</dd>
</dl>

<h3>Log Message</h3>
<pre>Web Inspector: CSS Changes: modifications aren't shared for rules that match multiple elements
https://bugs.webkit.org/show_bug.cgi?id=195264
<rdar://problem/48550023>

Reviewed by Devin Rousso.

Source/WebInspectorUI:

This patch fixes several cases when the diff was incorrect.

1. Perform diff based on CSSProperty content (name, value, and enabled property) instead
   of strict equality of CSSProperty instances.

2. Copy all initial CSSProperty instances of CSSStyleDeclaration on 1st edit.
   This removes the need to update `properties` on every single edit.

3. Do full diff to display modified property markers (green background) in Rules panel.
   This fixes a few cases when the markers were inaccurate. E.g. a newly added property
   matches removed property - no need to show the green background.

* UserInterface/Base/Utilities.js:
(Array.diffArrays):
Allow repeating items in the arrays.

* UserInterface/Controllers/CSSManager.js:
(WI.CSSManager.prototype.getModifiedStyle):
(WI.CSSManager.prototype.removeModifiedStyle):
* UserInterface/Models/CSSProperty.js:
(WI.CSSProperty):
(WI.CSSProperty.prototype.get modified):
(WI.CSSProperty.prototype.set modified):
(WI.CSSProperty.prototype.equals):
(WI.CSSProperty.prototype.clone):
(WI.CSSProperty.prototype._updateOwnerStyleText):
(WI.CSSProperty.prototype._markModified):
* UserInterface/Models/CSSStyleDeclaration.js:
(WI.CSSStyleDeclaration.prototype.markModified):
(WI.CSSStyleDeclaration.prototype.updatePropertiesModifiedState):
* UserInterface/Views/ChangesDetailsSidebarPanel.js:
(WI.ChangesDetailsSidebarPanel.prototype._createRuleElement):
* UserInterface/Views/SpreadsheetCSSStyleDeclarationEditor.js:
(WI.SpreadsheetCSSStyleDeclarationEditor.prototype.layout):
* UserInterface/Views/SpreadsheetStyleProperty.js:

LayoutTests:

Test arrays with repeating items for Array.diffArrays.

* inspector/unit-tests/array-utilities-expected.txt:
* inspector/unit-tests/array-utilities.html:</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkLayoutTestsinspectorunittestsarrayutilitiesexpectedtxt">trunk/LayoutTests/inspector/unit-tests/array-utilities-expected.txt</a></li>
<li><a href="#trunkLayoutTestsinspectorunittestsarrayutilitieshtml">trunk/LayoutTests/inspector/unit-tests/array-utilities.html</a></li>
<li><a href="#trunkSourceWebInspectorUIChangeLog">trunk/Source/WebInspectorUI/ChangeLog</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceBaseUtilitiesjs">trunk/Source/WebInspectorUI/UserInterface/Base/Utilities.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceControllersCSSManagerjs">trunk/Source/WebInspectorUI/UserInterface/Controllers/CSSManager.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceModelsCSSPropertyjs">trunk/Source/WebInspectorUI/UserInterface/Models/CSSProperty.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceModelsCSSStyleDeclarationjs">trunk/Source/WebInspectorUI/UserInterface/Models/CSSStyleDeclaration.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceViewsChangesDetailsSidebarPaneljs">trunk/Source/WebInspectorUI/UserInterface/Views/ChangesDetailsSidebarPanel.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceViewsSpreadsheetCSSStyleDeclarationEditorjs">trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetCSSStyleDeclarationEditor.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="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (245990 => 245991)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog      2019-05-31 22:30:55 UTC (rev 245990)
+++ trunk/LayoutTests/ChangeLog 2019-05-31 22:53:40 UTC (rev 245991)
</span><span class="lines">@@ -1,3 +1,16 @@
</span><ins>+2019-05-31  Nikita Vasilyev  <nvasilyev@apple.com>
+
+        Web Inspector: CSS Changes: modifications aren't shared for rules that match multiple elements
+        https://bugs.webkit.org/show_bug.cgi?id=195264
+        <rdar://problem/48550023>
+
+        Reviewed by Devin Rousso.
+
+        Test arrays with repeating items for Array.diffArrays.
+
+        * inspector/unit-tests/array-utilities-expected.txt:
+        * inspector/unit-tests/array-utilities.html:
+
</ins><span class="cx"> 2019-05-31  Ryosuke Niwa  <rniwa@webkit.org>
</span><span class="cx"> 
</span><span class="cx">         iOS: Main frame should be scrollable when pinch zoomed or software keyboard is up
</span></span></pre></div>
<a id="trunkLayoutTestsinspectorunittestsarrayutilitiesexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/inspector/unit-tests/array-utilities-expected.txt (245990 => 245991)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/unit-tests/array-utilities-expected.txt      2019-05-31 22:30:55 UTC (rev 245990)
+++ trunk/LayoutTests/inspector/unit-tests/array-utilities-expected.txt 2019-05-31 22:53:40 UTC (rev 245991)
</span><span class="lines">@@ -81,9 +81,19 @@
</span><span class="cx"> ["b","a"], ["a"] => [["b",-1],["a",0]]
</span><span class="cx"> ["b","a"], ["a","c"] => [["b",-1],["a",0],["c",1]]
</span><span class="cx"> ["b","a"], ["a","c"] => [["b",-1],["a",0],["c",1]]
</span><del>-["b","a"], ["a","b"] => [["a",0],["b",0]]
</del><ins>+["b","a"], ["a","b"] => [["a",1],["b",0],["a",-1]]
</ins><span class="cx"> ["a","b","c"], ["a","d","c"] => [["a",0],["b",-1],["d",1],["c",0]]
</span><ins>+["a","b","c"], ["c","b","a"] => [["c",1],["b",1],["a",0],["b",-1],["c",-1]]
</ins><span class="cx"> 
</span><ins>+Repeating items:
+["a"], ["a","a"] => [["a",0],["a",1]]
+["a","a"], ["a"] => [["a",0],["a",-1]]
+["a","a"], ["a","a"] => [["a",0],["a",0]]
+["b","a","b"], ["a","b","a"] => [["a",1],["b",0],["a",0],["b",-1]]
+["a","b","b","c"], ["c","b","b","b","a"] => [["a",-1],["c",1],["b",0],["b",0],["c",-1],["b",1],["a",1]]
+["a","b","b","b","c"], ["c","b","b","a"] => [["a",-1],["c",1],["b",0],["b",0],["b",-1],["c",-1],["a",1]]
+["a","a","b","b","c","c"], ["b","b","c","c","a","a"] => [["a",-1],["a",-1],["b",0],["b",0],["c",0],["c",0],["a",1],["a",1]]
+
</ins><span class="cx"> -- Running test case: Array.prototype.lastValue
</span><span class="cx"> PASS: lastValue of a nonempty array should be the last value.
</span><span class="cx"> PASS: lastValue of an empty array should be undefined.
</span></span></pre></div>
<a id="trunkLayoutTestsinspectorunittestsarrayutilitieshtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/inspector/unit-tests/array-utilities.html (245990 => 245991)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/inspector/unit-tests/array-utilities.html      2019-05-31 22:30:55 UTC (rev 245990)
+++ trunk/LayoutTests/inspector/unit-tests/array-utilities.html 2019-05-31 22:53:40 UTC (rev 245991)
</span><span class="lines">@@ -175,7 +175,17 @@
</span><span class="cx">             diff(["b", "a"], ["a", "c"]);
</span><span class="cx">             diff(["b", "a"], ["a", "b"]);
</span><span class="cx">             diff(["a", "b", "c"], ["a", "d", "c"]);
</span><ins>+            diff(["a", "b", "c"], ["c", "b", "a"]);
</ins><span class="cx"> 
</span><ins>+            InspectorTest.log("\nRepeating items:");
+            diff(["a"], ["a", "a"]);
+            diff(["a", "a"], ["a"]);
+            diff(["a", "a"], ["a", "a"]);
+            diff(["b", "a", "b"], ["a", "b", "a"]);
+            diff(["a", "b", "b", "c"], ["c", "b", "b", "b", "a"]);
+            diff(["a", "b", "b", "b", "c"], ["c", "b", "b", "a"]);
+            diff(["a", "a", "b", "b", "c", "c"], ["b", "b", "c", "c", "a", "a"]);
+
</ins><span class="cx">             return true;
</span><span class="cx">         }
</span><span class="cx">     });
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/ChangeLog (245990 => 245991)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/ChangeLog    2019-05-31 22:30:55 UTC (rev 245990)
+++ trunk/Source/WebInspectorUI/ChangeLog       2019-05-31 22:53:40 UTC (rev 245991)
</span><span class="lines">@@ -1,3 +1,47 @@
</span><ins>+2019-05-31  Nikita Vasilyev  <nvasilyev@apple.com>
+
+        Web Inspector: CSS Changes: modifications aren't shared for rules that match multiple elements
+        https://bugs.webkit.org/show_bug.cgi?id=195264
+        <rdar://problem/48550023>
+
+        Reviewed by Devin Rousso.
+
+        This patch fixes several cases when the diff was incorrect.
+
+        1. Perform diff based on CSSProperty content (name, value, and enabled property) instead
+           of strict equality of CSSProperty instances.
+
+        2. Copy all initial CSSProperty instances of CSSStyleDeclaration on 1st edit.
+           This removes the need to update `properties` on every single edit.
+
+        3. Do full diff to display modified property markers (green background) in Rules panel.
+           This fixes a few cases when the markers were inaccurate. E.g. a newly added property
+           matches removed property - no need to show the green background.
+
+        * UserInterface/Base/Utilities.js:
+        (Array.diffArrays):
+        Allow repeating items in the arrays.
+
+        * UserInterface/Controllers/CSSManager.js:
+        (WI.CSSManager.prototype.getModifiedStyle):
+        (WI.CSSManager.prototype.removeModifiedStyle):
+        * UserInterface/Models/CSSProperty.js:
+        (WI.CSSProperty):
+        (WI.CSSProperty.prototype.get modified):
+        (WI.CSSProperty.prototype.set modified):
+        (WI.CSSProperty.prototype.equals):
+        (WI.CSSProperty.prototype.clone):
+        (WI.CSSProperty.prototype._updateOwnerStyleText):
+        (WI.CSSProperty.prototype._markModified):
+        * UserInterface/Models/CSSStyleDeclaration.js:
+        (WI.CSSStyleDeclaration.prototype.markModified):
+        (WI.CSSStyleDeclaration.prototype.updatePropertiesModifiedState):
+        * UserInterface/Views/ChangesDetailsSidebarPanel.js:
+        (WI.ChangesDetailsSidebarPanel.prototype._createRuleElement):
+        * UserInterface/Views/SpreadsheetCSSStyleDeclarationEditor.js:
+        (WI.SpreadsheetCSSStyleDeclarationEditor.prototype.layout):
+        * UserInterface/Views/SpreadsheetStyleProperty.js:
+
</ins><span class="cx"> 2019-05-31  Devin Rousso  <drousso@apple.com>
</span><span class="cx"> 
</span><span class="cx">         Web Inspector: Timelines: CPU: gray (?) and (x) should be white
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceBaseUtilitiesjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Base/Utilities.js (245990 => 245991)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Base/Utilities.js      2019-05-31 22:30:55 UTC (rev 245990)
+++ trunk/Source/WebInspectorUI/UserInterface/Base/Utilities.js 2019-05-31 22:53:40 UTC (rev 245991)
</span><span class="lines">@@ -522,62 +522,86 @@
</span><span class="cx"> 
</span><span class="cx"> Object.defineProperty(Array, "diffArrays",
</span><span class="cx"> {
</span><del>-    value(initialArray, currentArray, onEach)
</del><ins>+    value(initialArray, currentArray, onEach, comparator)
</ins><span class="cx">     {
</span><del>-        let initialSet = new Set(initialArray);
-        let currentSet = new Set(currentArray);
-        let indexInitial = 0;
-        let indexCurrent = 0;
-        let deltaInitial = 0;
-        let deltaCurrent = 0;
</del><ins>+        "use strict";
</ins><span class="cx"> 
</span><del>-        let i = 0;
-        while (true) {
-            if (indexInitial >= initialArray.length || indexCurrent >= currentArray.length)
-                break;
</del><ins>+        function defaultComparator(initial, current) {
+            return initial === current;
+        }
+        comparator = comparator || defaultComparator;
</ins><span class="cx"> 
</span><del>-            let initial = initialArray[indexInitial];
-            let current = currentArray[indexCurrent];
</del><ins>+        // Find the shortest prefix of matching items in both arrays.
+        //
+        //    initialArray = ["a", "b", "b", "c"]
+        //    currentArray = ["c", "b", "b", "a"]
+        //    findShortestEdit() // [1, 1]
+        //
+        function findShortestEdit() {
+            let deletionCount = initialArray.length;
+            let additionCount = currentArray.length;
+            let editCount = deletionCount + additionCount;
+            for (let i = 0; i < initialArray.length; ++i) {
+                if (i > editCount) {
+                    // Break since any possible edits at this point are going to be longer than the one already found.
+                    break;
+                }
</ins><span class="cx"> 
</span><del>-            if (initial === current)
-                onEach(current, 0);
-            else if (currentSet.has(initial)) {
-                if (initialSet.has(current)) {
-                    // Moved.
-                    onEach(current, 0);
-                } else {
-                    // Added.
-                    onEach(current, 1);
-                    --i;
-                    ++deltaCurrent;
</del><ins>+                for (let j = 0; j < currentArray.length; ++j) {
+                    let newEditCount = i + j;
+                    if (newEditCount > editCount) {
+                        // Break since any possible edits at this point are going to be longer than the one already found.
+                        break;
+                    }
+
+                    if (comparator(initialArray[i], currentArray[j])) {
+                        // A candidate for the shortest edit found.
+                        if (newEditCount < editCount) {
+                            editCount = newEditCount;
+                            deletionCount = i;
+                            additionCount = j;
+                        }
+                        break;
+                    }
</ins><span class="cx">                 }
</span><del>-            } else {
-                // Removed.
-                onEach(initial, -1);
-                if (!initialSet.has(current)) {
-                    // Added.
-                    onEach(current, 1);
-                } else {
-                    --i;
-                    ++deltaInitial;
-                }
</del><span class="cx">             }
</span><ins>+            return [deletionCount, additionCount];
+        }
</ins><span class="cx"> 
</span><del>-            ++i;
-            indexInitial = i + deltaInitial;
-            indexCurrent = i + deltaCurrent;
</del><ins>+        function commonPrefixLength(listA, listB) {
+            let shorterListLength = Math.min(listA.length, listB.length);
+            let i = 0;
+            while (i < shorterListLength) {
+                if (!comparator(listA[i], listB[i]))
+                    break;
+                ++i;
+            }
+            return i;
</ins><span class="cx">         }
</span><span class="cx"> 
</span><del>-        for (let i = indexInitial; i < initialArray.length; ++i) {
-            // Removed.
-            onEach(initialArray[i], -1);
</del><ins>+        function fireOnEach(count, diffAction, array) {
+            for (let i = 0; i < count; ++i)
+                onEach(array[i], diffAction);
</ins><span class="cx">         }
</span><span class="cx"> 
</span><del>-        for (let i = indexCurrent; i < currentArray.length; ++i) {
-            // Added.
-            onEach(currentArray[i], 1);
</del><ins>+        while (initialArray.length || currentArray.length) {
+            // Remove common prefix.
+            let prefixLength = commonPrefixLength(initialArray, currentArray);
+            if (prefixLength) {
+                fireOnEach(prefixLength, 0, currentArray);
+                initialArray = initialArray.slice(prefixLength);
+                currentArray = currentArray.slice(prefixLength);
+            }
+
+            if (!initialArray.length && !currentArray.length)
+                break;
+
+            let [deletionCount, additionCount] = findShortestEdit();
+            fireOnEach(deletionCount, -1, initialArray);
+            fireOnEach(additionCount, 1, currentArray);
+            initialArray = initialArray.slice(deletionCount);
+            currentArray = currentArray.slice(additionCount);
</ins><span class="cx">         }
</span><del>-
</del><span class="cx">     }
</span><span class="cx"> });
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceControllersCSSManagerjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Controllers/CSSManager.js (245990 => 245991)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Controllers/CSSManager.js      2019-05-31 22:30:55 UTC (rev 245990)
+++ trunk/Source/WebInspectorUI/UserInterface/Controllers/CSSManager.js 2019-05-31 22:53:40 UTC (rev 245991)
</span><span class="lines">@@ -431,6 +431,16 @@
</span><span class="cx">         this._modifiedStyles.set(style.stringId, style);
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    getModifiedStyle(style)
+    {
+        return this._modifiedStyles.get(style.stringId);
+    }
+
+    removeModifiedStyle(style)
+    {
+        this._modifiedStyles.delete(style.stringId);
+    }
+
</ins><span class="cx">     // Protected
</span><span class="cx"> 
</span><span class="cx">     mediaQueryResultChanged()
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceModelsCSSPropertyjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Models/CSSProperty.js (245990 => 245991)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Models/CSSProperty.js  2019-05-31 22:30:55 UTC (rev 245990)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/CSSProperty.js     2019-05-31 22:53:40 UTC (rev 245991)
</span><span class="lines">@@ -33,6 +33,7 @@
</span><span class="cx">         this._index = index;
</span><span class="cx">         this._overridingProperty = null;
</span><span class="cx">         this._initialState = null;
</span><ins>+        this._modified = false;
</ins><span class="cx"> 
</span><span class="cx">         this.update(text, name, value, priority, enabled, overridden, implicit, anonymous, valid, styleSheetTextRange, true);
</span><span class="cx">     }
</span><span class="lines">@@ -184,9 +185,18 @@
</span><span class="cx"> 
</span><span class="cx">     get modified()
</span><span class="cx">     {
</span><del>-        return !!this._initialState;
</del><ins>+        return this._modified;
</ins><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    set modified(value)
+    {
+        if (this._modified === value)
+            return;
+
+        this._modified = value;
+        this.dispatchEventToListeners(WI.CSSProperty.Event.ModifiedChanged);
+    }
+
</ins><span class="cx">     get name()
</span><span class="cx">     {
</span><span class="cx">         return this._name;
</span><span class="lines">@@ -377,6 +387,37 @@
</span><span class="cx">         return this._hasOtherVendorNameOrKeyword;
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    equals(property)
+    {
+        if (property === this)
+            return true;
+
+        if (!property)
+            return false;
+
+        return this._name === property.name && this._rawValue === property.rawValue && this._enabled === property.enabled;
+    }
+
+    clone()
+    {
+        let cssProperty = new WI.CSSProperty(
+            this._index,
+            this._text,
+            this._name,
+            this._rawValue,
+            this._priority,
+            this._enabled,
+            this._overridden,
+            this._implicit,
+            this._anonymous,
+            this._valid,
+            this._styleSheetTextRange);
+
+        cssProperty.ownerStyle = this._ownerStyle;
+
+        return cssProperty;
+    }
+
</ins><span class="cx">     // Private
</span><span class="cx"> 
</span><span class="cx">     _updateStyleText(forceRemove = false)
</span><span class="lines">@@ -393,8 +434,6 @@
</span><span class="cx"> 
</span><span class="cx">     _updateOwnerStyleText(oldText, newText, forceRemove = false)
</span><span class="cx">     {
</span><del>-        console.assert(this.modified, "CSSProperty was modified without saving initial state.");
-
</del><span class="cx">         if (oldText === newText) {
</span><span class="cx">             if (forceRemove) {
</span><span class="cx">                 const lineDelta = 0;
</span><span class="lines">@@ -437,6 +476,7 @@
</span><span class="cx"> 
</span><span class="cx">         let propertyWasRemoved = !newText;
</span><span class="cx">         this._ownerStyle.shiftPropertiesAfter(this, lineDelta, columnDelta, propertyWasRemoved);
</span><ins>+        this._ownerStyle.updatePropertiesModifiedState();
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     _prependSemicolonIfNeeded()
</span><span class="lines">@@ -456,30 +496,13 @@
</span><span class="cx"> 
</span><span class="cx">     _markModified()
</span><span class="cx">     {
</span><del>-        if (this.modified)
-            return;
-
-        this._initialState = new WI.CSSProperty(
-            this._index,
-            this._text,
-            this._name,
-            this._rawValue,
-            this._priority,
-            this._enabled,
-            this._overridden,
-            this._implicit,
-            this._anonymous,
-            this._valid,
-            this._styleSheetTextRange);
-
-        if (this._ownerStyle) {
</del><ins>+        if (this._ownerStyle)
</ins><span class="cx">             this._ownerStyle.markModified();
</span><del>-            this._initialState.ownerStyle = this._ownerStyle.initialState;
-        }
</del><span class="cx">     }
</span><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> WI.CSSProperty.Event = {
</span><span class="cx">     Changed: "css-property-changed",
</span><ins>+    ModifiedChanged: "css-property-modified-changed",
</ins><span class="cx">     OverriddenStatusChanged: "css-property-overridden-status-changed"
</span><span class="cx"> };
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceModelsCSSStyleDeclarationjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Models/CSSStyleDeclaration.js (245990 => 245991)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Models/CSSStyleDeclaration.js  2019-05-31 22:30:55 UTC (rev 245990)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/CSSStyleDeclaration.js     2019-05-31 22:53:40 UTC (rev 245991)
</span><span class="lines">@@ -365,9 +365,11 @@
</span><span class="cx"> 
</span><span class="cx">     markModified()
</span><span class="cx">     {
</span><del>-        let properties = this._initialState ? this._initialState.properties : this._properties;
</del><ins>+        if (!this._initialState) {
+            let visibleProperties = this.visibleProperties.map((property) => {
+                return property.clone();
+            });
</ins><span class="cx"> 
</span><del>-        if (!this._initialState) {
</del><span class="cx">             this._initialState = new WI.CSSStyleDeclaration(
</span><span class="cx">                 this._nodeStyles,
</span><span class="cx">                 this._ownerStyleSheet,
</span><span class="lines">@@ -376,12 +378,10 @@
</span><span class="cx">                 this._node,
</span><span class="cx">                 this._inherited,
</span><span class="cx">                 this._text,
</span><del>-                [], // Passing CSS properties here would change their ownerStyle.
</del><ins>+                visibleProperties,
</ins><span class="cx">                 this._styleSheetTextRange);
</span><span class="cx">         }
</span><span class="cx"> 
</span><del>-        this._initialState.properties = properties.map((property) => { return property.initialState || property });
-
</del><span class="cx">         WI.cssManager.addModifiedStyle(this);
</span><span class="cx">     }
</span><span class="cx"> 
</span><span class="lines">@@ -417,6 +417,36 @@
</span><span class="cx">         this._visibleProperties = null;
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    updatePropertiesModifiedState()
+    {
+        if (!this._initialState)
+            return;
+
+        if (this._type === WI.CSSStyleDeclaration.Type.Computed)
+            return;
+
+        let initialCSSProperties = this._initialState.visibleProperties;
+        let cssProperties = this.visibleProperties;
+
+        let hasModified = false;
+
+        function onEach(cssProperty, action) {
+            if (action !== 0)
+                hasModified = true;
+
+            cssProperty.modified = action === 1;
+        }
+
+        function comparator(a, b) {
+            return a.equals(b);
+        }
+
+        Array.diffArrays(initialCSSProperties, cssProperties, onEach, comparator);
+
+        if (!hasModified)
+            WI.cssManager.removeModifiedStyle(this);
+    }
+
</ins><span class="cx">     // Protected
</span><span class="cx"> 
</span><span class="cx">     get nodeStyles()
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceViewsChangesDetailsSidebarPaneljs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Views/ChangesDetailsSidebarPanel.js (245990 => 245991)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Views/ChangesDetailsSidebarPanel.js    2019-05-31 22:30:55 UTC (rev 245990)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/ChangesDetailsSidebarPanel.js       2019-05-31 22:53:40 UTC (rev 245991)
</span><span class="lines">@@ -149,28 +149,28 @@
</span><span class="cx"> 
</span><span class="cx">         selectorLineElement.append(" {\n");
</span><span class="cx"> 
</span><del>-        let appendProperty = (cssProperty, className) => {
</del><ins>+        function onEach(cssProperty, action) {
+            let className = "";
+            if (action === 1)
+                className = "added";
+            else if (action === -1)
+                className = "removed";
+            else
+                className = "unchanged";
+
</ins><span class="cx">             let propertyLineElement = ruleElement.appendChild(document.createElement("div"));
</span><span class="cx">             propertyLineElement.classList.add("css-property-line", className);
</span><del>-            let stylePropertyView = new WI.SpreadsheetStyleProperty(null, cssProperty, {readOnly: true});
</del><ins>+
+            const delegate = null;
+            let stylePropertyView = new WI.SpreadsheetStyleProperty(delegate, cssProperty, {readOnly: true});
</ins><span class="cx">             propertyLineElement.append(WI.indentString(), stylePropertyView.element, "\n");
</span><del>-        };
</del><ins>+        }
</ins><span class="cx"> 
</span><del>-        let initialCSSProperties = style.initialState.visibleProperties;
-        let cssProperties = style.visibleProperties;
</del><ins>+        function comparator(a, b) {
+            return a.equals(b);
+        }
</ins><span class="cx"> 
</span><del>-        Array.diffArrays(initialCSSProperties, cssProperties, (cssProperty, action) => {
-            if (action === 0) {
-                if (cssProperty.modified) {
-                    appendProperty(cssProperty.initialState, "removed");
-                    appendProperty(cssProperty, "added");
-                } else
-                    appendProperty(cssProperty, "unchanged");
-            } else if (action === 1)
-                appendProperty(cssProperty, "added");
-            else if (action === -1)
-                appendProperty(cssProperty, "removed");
-        });
</del><ins>+        Array.diffArrays(style.initialState.visibleProperties, style.visibleProperties, onEach, comparator);
</ins><span class="cx"> 
</span><span class="cx">         let closeBraceElement = document.createElement("span");
</span><span class="cx">         closeBraceElement.className = "close-brace";
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceViewsSpreadsheetCSSStyleDeclarationEditorjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetCSSStyleDeclarationEditor.js (245990 => 245991)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetCSSStyleDeclarationEditor.js  2019-05-31 22:30:55 UTC (rev 245990)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetCSSStyleDeclarationEditor.js     2019-05-31 22:53:40 UTC (rev 245991)
</span><span class="lines">@@ -82,6 +82,9 @@
</span><span class="cx"> 
</span><span class="cx">         this.element.removeChildren();
</span><span class="cx"> 
</span><ins>+        if (this._style)
+            this._style.updatePropertiesModifiedState();
+
</ins><span class="cx">         let properties = this.propertiesToRender;
</span><span class="cx">         this.element.classList.toggle("no-properties", !properties.length);
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceViewsSpreadsheetStylePropertyjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetStyleProperty.js (245990 => 245991)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetStyleProperty.js      2019-05-31 22:30:55 UTC (rev 245990)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/SpreadsheetStyleProperty.js 2019-05-31 22:53:40 UTC (rev 245991)
</span><span class="lines">@@ -53,6 +53,7 @@
</span><span class="cx"> 
</span><span class="cx">         if (!this._readOnly) {
</span><span class="cx">             this._element.tabIndex = -1;
</span><ins>+            property.addEventListener(WI.CSSProperty.Event.ModifiedChanged, this.updateStatus, this);
</ins><span class="cx"> 
</span><span class="cx">             this._element.addEventListener("blur", (event) => {
</span><span class="cx">                 // Keep selection after tabbing out of Web Inspector window and back.
</span></span></pre>
</div>
</div>

</body>
</html>