<!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>[185928] 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/185928">185928</a></dd>
<dt>Author</dt> <dd>commit-queue@webkit.org</dd>
<dt>Date</dt> <dd>2015-06-24 15:17:42 -0700 (Wed, 24 Jun 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>Web Inspector: Show warning icon for invalid CSS properties and/or values
https://bugs.webkit.org/show_bug.cgi?id=145657

Patch by Devin Rousso &lt;drousso@apple.com&gt; on 2015-06-24
Reviewed by Timothy Hatcher.

* UserInterface/Models/CSSCompletions.js:
(WebInspector.CSSCompletions.prototype.getClosestPropertyName): Calculates the levenshtein distance between a given property and every existing property name.  Returns the property name with the smallest distance or, in the case of multiple properties having the same distance, the first property in alphabetical order.
(WebInspector.CSSCompletions.prototype.propertyRequiresWebkitPrefix): Retruns if the property name exists only with a '-webkit-' prefix.
(WebInspector.CSSCompletions):
* UserInterface/Views/CSSStyleDeclarationTextEditor.css:
(.css-style-text-editor &gt; .CodeMirror .CodeMirror-lines .invalid-warning-marker):
(.css-style-text-editor &gt; .CodeMirror .CodeMirror-lines .invalid-warning-marker.clickable:hover):
* UserInterface/Views/CSSStyleDeclarationTextEditor.js:
(WebInspector.CSSStyleDeclarationTextEditor.prototype._createTextMarkerForPropertyIfNeeded.duplicatePropertyExistsBelow): Determines if there exists a property below (visually) the given property that has the same name.
(WebInspector.CSSStyleDeclarationTextEditor.prototype._createTextMarkerForPropertyIfNeeded.generateInvalidMarker): Creates a warning icon marker at the given position with the given title.
(WebInspector.CSSStyleDeclarationTextEditor.prototype._createTextMarkerForPropertyIfNeeded.instancesOfProperty): Returns the number of properties in the rule that have the same name.
(WebInspector.CSSStyleDeclarationTextEditor.prototype._createTextMarkerForPropertyIfNeeded): A button with a warning icon is now added to the beginning of an invalid property.  If this button is clicked, the property is replaced with the closest matching property and the autocomplete menu is opened.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebInspectorUIChangeLog">trunk/Source/WebInspectorUI/ChangeLog</a></li>
<li><a href="#trunkSourceWebInspectorUILocalizationsenlprojlocalizedStringsjs">trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceBaseUtilitiesjs">trunk/Source/WebInspectorUI/UserInterface/Base/Utilities.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceModelsCSSCompletionsjs">trunk/Source/WebInspectorUI/UserInterface/Models/CSSCompletions.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceViewsCSSStyleDeclarationTextEditorcss">trunk/Source/WebInspectorUI/UserInterface/Views/CSSStyleDeclarationTextEditor.css</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceViewsCSSStyleDeclarationTextEditorjs">trunk/Source/WebInspectorUI/UserInterface/Views/CSSStyleDeclarationTextEditor.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebInspectorUIChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/ChangeLog (185927 => 185928)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/ChangeLog        2015-06-24 21:43:54 UTC (rev 185927)
+++ trunk/Source/WebInspectorUI/ChangeLog        2015-06-24 22:17:42 UTC (rev 185928)
</span><span class="lines">@@ -1,5 +1,25 @@
</span><span class="cx"> 2015-06-24  Devin Rousso  &lt;drousso@apple.com&gt;
</span><span class="cx"> 
</span><ins>+        Web Inspector: Show warning icon for invalid CSS properties and/or values
+        https://bugs.webkit.org/show_bug.cgi?id=145657
+
+        Reviewed by Timothy Hatcher.
+
+        * UserInterface/Models/CSSCompletions.js:
+        (WebInspector.CSSCompletions.prototype.getClosestPropertyName): Calculates the levenshtein distance between a given property and every existing property name.  Returns the property name with the smallest distance or, in the case of multiple properties having the same distance, the first property in alphabetical order.
+        (WebInspector.CSSCompletions.prototype.propertyRequiresWebkitPrefix): Retruns if the property name exists only with a '-webkit-' prefix.
+        (WebInspector.CSSCompletions):
+        * UserInterface/Views/CSSStyleDeclarationTextEditor.css:
+        (.css-style-text-editor &gt; .CodeMirror .CodeMirror-lines .invalid-warning-marker):
+        (.css-style-text-editor &gt; .CodeMirror .CodeMirror-lines .invalid-warning-marker.clickable:hover):
+        * UserInterface/Views/CSSStyleDeclarationTextEditor.js:
+        (WebInspector.CSSStyleDeclarationTextEditor.prototype._createTextMarkerForPropertyIfNeeded.duplicatePropertyExistsBelow): Determines if there exists a property below (visually) the given property that has the same name.
+        (WebInspector.CSSStyleDeclarationTextEditor.prototype._createTextMarkerForPropertyIfNeeded.generateInvalidMarker): Creates a warning icon marker at the given position with the given title.
+        (WebInspector.CSSStyleDeclarationTextEditor.prototype._createTextMarkerForPropertyIfNeeded.instancesOfProperty): Returns the number of properties in the rule that have the same name.
+        (WebInspector.CSSStyleDeclarationTextEditor.prototype._createTextMarkerForPropertyIfNeeded): A button with a warning icon is now added to the beginning of an invalid property.  If this button is clicked, the property is replaced with the closest matching property and the autocomplete menu is opened.
+
+2015-06-24  Devin Rousso  &lt;drousso@apple.com&gt;
+
</ins><span class="cx">         Web Inspector: Background of Computed Styles is missing
</span><span class="cx">         https://bugs.webkit.org/show_bug.cgi?id=146209
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebInspectorUILocalizationsenlprojlocalizedStringsjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js (185927 => 185928)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js        2015-06-24 21:43:54 UTC (rev 185927)
+++ trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js        2015-06-24 22:17:42 UTC (rev 185928)
</span><span class="lines">@@ -147,6 +147,7 @@
</span><span class="cx"> localizedStrings[&quot;Delete Node&quot;] = &quot;Delete Node&quot;;
</span><span class="cx"> localizedStrings[&quot;Detach into separate window&quot;] = &quot;Detach into separate window&quot;;
</span><span class="cx"> localizedStrings[&quot;Details&quot;] = &quot;Details&quot;;
</span><ins>+localizedStrings[&quot;Did you mean '%s'?\nClick to replace.&quot;] = &quot;Did you mean '%s'?\nClick to replace.&quot;;
</ins><span class="cx"> localizedStrings[&quot;Disable Breakpoint&quot;] = &quot;Disable Breakpoint&quot;;
</span><span class="cx"> localizedStrings[&quot;Disable Breakpoints&quot;] = &quot;Disable Breakpoints&quot;;
</span><span class="cx"> localizedStrings[&quot;Disable all breakpoints (%s)&quot;] = &quot;Disable all breakpoints (%s)&quot;;
</span><span class="lines">@@ -162,6 +163,7 @@
</span><span class="cx"> localizedStrings[&quot;Domain&quot;] = &quot;Domain&quot;;
</span><span class="cx"> localizedStrings[&quot;Done&quot;] = &quot;Done&quot;;
</span><span class="cx"> localizedStrings[&quot;Download Web Archive&quot;] = &quot;Download Web Archive&quot;;
</span><ins>+localizedStrings[&quot;Duplicate property '%s'.\nClick to delete this property.&quot;] = &quot;Duplicate property '%s'.\nClick to delete this property.&quot;;
</ins><span class="cx"> localizedStrings[&quot;Duration&quot;] = &quot;Duration&quot;;
</span><span class="cx"> localizedStrings[&quot;Dynamically calculated for the parent element&quot;] = &quot;Dynamically calculated for the parent element&quot;;
</span><span class="cx"> localizedStrings[&quot;Dynamically calculated for the selected element&quot;] = &quot;Dynamically calculated for the selected element&quot;;
</span><span class="lines">@@ -476,7 +478,12 @@
</span><span class="cx"> localizedStrings[&quot;Text&quot;] = &quot;Text&quot;;
</span><span class="cx"> localizedStrings[&quot;Text Node&quot;] = &quot;Text Node&quot;;
</span><span class="cx"> localizedStrings[&quot;Text Only&quot;] = &quot;Text Only&quot;;
</span><ins>+localizedStrings[&quot;The 'webkit' prefix is needed for this property.\nClick to insert a duplicate with the prefix.&quot;] = &quot;The 'webkit' prefix is needed for this property.\nClick to insert a duplicate with the prefix.&quot;;
+localizedStrings[&quot;The 'webkit' prefix is not necessary.\nClick to insert a duplicate without the prefix.&quot;] = &quot;The 'webkit' prefix is not necessary.\nClick to insert a duplicate without the prefix.&quot;;
+localizedStrings[&quot;The property '%s' is not supported.&quot;] = &quot;The property '%s' is not supported.&quot;;
+localizedStrings[&quot;The value '%s' is not supported for this property.\nClick to delete and open autocomplete.&quot;] = &quot;The value '%s' is not supported for this property.\nClick to delete and open autocomplete.&quot;;
</ins><span class="cx"> localizedStrings[&quot;The  %s \ntable is empty.&quot;] = &quot;The  %s \ntable is empty.&quot;;
</span><ins>+localizedStrings[&quot;This property needs a value.\nClick to open autocomplete.&quot;] = &quot;This property needs a value.\nClick to open autocomplete.&quot;;
</ins><span class="cx"> localizedStrings[&quot;Timeline Events&quot;] = &quot;Timeline Events&quot;;
</span><span class="cx"> localizedStrings[&quot;Timeline Recording %d&quot;] = &quot;Timeline Recording %d&quot;;
</span><span class="cx"> localizedStrings[&quot;Timelines&quot;] = &quot;Timelines&quot;;
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceBaseUtilitiesjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Base/Utilities.js (185927 => 185928)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Base/Utilities.js        2015-06-24 21:43:54 UTC (rev 185927)
+++ trunk/Source/WebInspectorUI/UserInterface/Base/Utilities.js        2015-06-24 22:17:42 UTC (rev 185928)
</span><span class="lines">@@ -764,6 +764,39 @@
</span><span class="cx">     }
</span><span class="cx"> });
</span><span class="cx"> 
</span><ins>+Object.defineProperty(String.prototype, &quot;levenshteinDistance&quot;,
+{
+    value: function(s)
+    {
+        var m = this.length;
+        var n = s.length;
+        var d = new Array(m + 1);
+
+        for (var i = 0; i &lt;= m; ++i) {
+            d[i] = new Array(n + 1);
+            d[i][0] = i;
+        }
+
+        for (var j = 0; j &lt;= n; ++j)
+            d[0][j] = j;
+
+        for (var j = 1; j &lt;= n; ++j) {
+            for (var i = 1; i &lt;= m; ++i) {
+                if (this[i - 1] === s[j - 1])
+                    d[i][j] = d[i - 1][j - 1];
+                else {
+                    var deletion = d[i - 1][j] + 1;
+                    var insertion = d[i][j - 1] + 1;
+                    var substitution = d[i - 1][j - 1] + 1;
+                    d[i][j] = Math.min(deletion, insertion, substitution);
+                }
+            }
+        }
+
+        return d[m][n];
+    }
+});
+
</ins><span class="cx"> Object.defineProperty(Number, &quot;constrain&quot;,
</span><span class="cx"> {
</span><span class="cx">     value: function(num, min, max)
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceModelsCSSCompletionsjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Models/CSSCompletions.js (185927 => 185928)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Models/CSSCompletions.js        2015-06-24 21:43:54 UTC (rev 185927)
+++ trunk/Source/WebInspectorUI/UserInterface/Models/CSSCompletions.js        2015-06-24 22:17:42 UTC (rev 185928)
</span><span class="lines">@@ -280,6 +280,27 @@
</span><span class="cx">     {
</span><span class="cx">         return this._values.includes(name);
</span><span class="cx">     }
</span><ins>+
+    propertyRequiresWebkitPrefix(name)
+    {
+        return this._values.includes(&quot;-webkit-&quot; + name) &amp;&amp; !this._values.includes(name);
+    }
+
+    getClosestPropertyName(name)
+    {
+        var bestMatches = [{distance: Infinity, name: null}];
+
+        for (var property of this._values) {
+            var distance = name.levenshteinDistance(property);
+
+            if (distance &lt; bestMatches[0].distance)
+                bestMatches = [{distance, name: property}];
+            else if (distance === bestMatches[0].distance)
+                bestMatches.push({distance, name: property});
+        }
+
+        return bestMatches.length &lt; 3 ? bestMatches[0].name : false;
+    }
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> WebInspector.CSSCompletions.cssNameCompletions = null;
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceViewsCSSStyleDeclarationTextEditorcss"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Views/CSSStyleDeclarationTextEditor.css (185927 => 185928)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Views/CSSStyleDeclarationTextEditor.css        2015-06-24 21:43:54 UTC (rev 185927)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/CSSStyleDeclarationTextEditor.css        2015-06-24 22:17:42 UTC (rev 185928)
</span><span class="lines">@@ -78,6 +78,22 @@
</span><span class="cx">     -webkit-text-stroke-color: rgba(255, 0, 0, 0.6);
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+.css-style-text-editor &gt; .CodeMirror .CodeMirror-lines .invalid-warning-marker {
+    position: relative;
+    top: 1px; 
+    width: 9px;
+    height: 9px;
+    margin: 0 2px 0 0;
+    padding: 0;
+    border: none;
+    background-color: transparent;
+    background-image: url(../Images/Warning.svg);
+}
+
+.css-style-text-editor &gt; .CodeMirror .CodeMirror-lines .invalid-warning-marker.clickable:hover {
+    -webkit-filter: brightness(0.9);
+}
+
</ins><span class="cx"> .computed .css-style-text-editor &gt; .CodeMirror .CodeMirror-lines .css-style-declaration-property.implicit,
</span><span class="cx"> .css-style-text-editor &gt; .CodeMirror .CodeMirror-lines .css-style-declaration-property.not-inherited {
</span><span class="cx">     opacity: 0.5;
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceViewsCSSStyleDeclarationTextEditorjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Views/CSSStyleDeclarationTextEditor.js (185927 => 185928)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Views/CSSStyleDeclarationTextEditor.js        2015-06-24 21:43:54 UTC (rev 185927)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/CSSStyleDeclarationTextEditor.js        2015-06-24 22:17:42 UTC (rev 185928)
</span><span class="lines">@@ -610,6 +610,20 @@
</span><span class="cx">             this._codeMirror.setUniqueBookmark(to, arrowElement);
</span><span class="cx">         }
</span><span class="cx"> 
</span><ins>+        function duplicatePropertyExistsBelow(cssProperty)
+        {
+            var propertyFound = false;
+
+            for (var property of this._style.properties) {
+                if (property === cssProperty)
+                    propertyFound = true;
+                else if (property.name === cssProperty.name &amp;&amp; propertyFound)
+                    return true;
+            }
+
+            return false;
+        }
+
</ins><span class="cx">         var propertyNameIsValid = false;
</span><span class="cx">         if (WebInspector.CSSCompletions.cssNameCompletions)
</span><span class="cx">             propertyNameIsValid = WebInspector.CSSCompletions.cssNameCompletions.isValidPropertyName(property.name);
</span><span class="lines">@@ -627,7 +641,7 @@
</span><span class="cx"> 
</span><span class="cx">         if (!property.valid &amp;&amp; property.hasOtherVendorNameOrKeyword())
</span><span class="cx">             classNames.push(&quot;other-vendor&quot;);
</span><del>-        else if (!property.valid &amp;&amp; !propertyNameIsValid)
</del><ins>+        else if (!property.valid &amp;&amp; (!propertyNameIsValid || duplicatePropertyExistsBelow.call(this, property)))
</ins><span class="cx">             classNames.push(&quot;invalid&quot;);
</span><span class="cx"> 
</span><span class="cx">         if (!property.enabled)
</span><span class="lines">@@ -658,16 +672,6 @@
</span><span class="cx"> 
</span><span class="cx">         this._removeCheckboxPlaceholder(from.line);
</span><span class="cx"> 
</span><del>-        if (!property.valid &amp;&amp; propertyNameIsValid &amp;&amp; !property.text.trim().endsWith(&quot;:&quot;)) {
-            // The property.text.trim().endsWith(&quot;:&quot;) is for the situation when a property only has a name and colon and the user leaves the value blank (it looks weird to have an invalid marker through just the colon).
-            // Creating the synthesizedText is necessary for if the user adds multiple spaces before the value, causing the markText to mark one of the spaces instead.
-            var synthesizedText = property.name + &quot;: &quot; + property.value + &quot;;&quot;;
-            var start = {line: from.line, ch: from.ch + synthesizedText.indexOf(property.value)};
-            var end = {line: to.line, ch: start.ch + property.value.length};
-
-            this._codeMirror.markText(start, end, {className: &quot;invalid&quot;});
-        }
-
</del><span class="cx">         if (property.__filterResultClassName &amp;&amp; property.__filterResultNeedlePosition) {
</span><span class="cx">             for (var needlePosition of property.__filterResultNeedlePosition.start) {
</span><span class="cx">                 var start = {line: from.line, ch: needlePosition};
</span><span class="lines">@@ -676,6 +680,133 @@
</span><span class="cx">                 this._codeMirror.markText(start, end, {className: property.__filterResultClassName});
</span><span class="cx">             }
</span><span class="cx">         }
</span><ins>+
+        if (property.hasOtherVendorNameOrKeyword() || property.text.trim().endsWith(&quot;:&quot;))
+            return;
+
+        var propertyHasUnnecessaryPrefix = property.name.startsWith(&quot;-webkit-&quot;) &amp;&amp; WebInspector.CSSCompletions.cssNameCompletions.isValidPropertyName(property.canonicalName);
+
+        function generateInvalidMarker(options)
+        {
+            var invalidMarker = document.createElement(&quot;button&quot;);
+            invalidMarker.className = &quot;invalid-warning-marker&quot;;
+            invalidMarker.title = options.title;
+
+            if (typeof options.correction === &quot;string&quot;) {
+                // Allow for blank strings
+                invalidMarker.classList.add(&quot;clickable&quot;);
+                invalidMarker.addEventListener(&quot;click&quot;, function() {
+                    this._codeMirror.replaceRange(options.correction, from, to);
+
+                    if (options.autocomplete) {
+                        this._codeMirror.setCursor(to);
+                        this.focus();
+                        this._completionController._completeAtCurrentPosition(true);
+                    }
+                }.bind(this));
+            }
+
+            this._codeMirror.setBookmark(options.position, invalidMarker);
+        }
+
+        function instancesOfProperty(propertyName)
+        {
+            var count = 0;
+
+            for (var property of this._style.properties) {
+                if (property.name === propertyName)
+                    ++count;
+            }
+
+            return count;
+        }
+
+        // Number of times this property name is listed in the rule.
+        var instances = instancesOfProperty.call(this, property.name);
+        var invalidMarkerInfo;
+
+        if (propertyHasUnnecessaryPrefix &amp;&amp; !instancesOfProperty.call(this, property.canonicalName)) {
+            // This property has a prefix and is valid without the prefix and the rule containing this property does not have the unprefixed version of the property.
+            generateInvalidMarker.call(this, {
+                position: from,
+                title: WebInspector.UIString(&quot;The 'webkit' prefix is not necessary.\nClick to insert a duplicate without the prefix.&quot;),
+                correction: property.text + &quot;\n&quot; + property.text.replace(&quot;-webkit-&quot;, &quot;&quot;),
+                autocomplete: false
+            });
+        } else if (instances &gt; 1) {
+            invalidMarkerInfo = {
+                position: from,
+                title: WebInspector.UIString(&quot;Duplicate property '%s'.\nClick to delete this property.&quot;).format(property.name),
+                correction: &quot;&quot;,
+                autocomplete: false
+            };
+        }
+
+        if (property.valid) {
+            if (invalidMarkerInfo)
+                generateInvalidMarker.call(this, invalidMarkerInfo);
+
+            return;
+        }
+
+        if (propertyNameIsValid) {
+            // The property's name is valid but its value is not (either it is not supported for this property or there is no value).
+            var start = {line: from.line, ch: from.ch + property.name.length + 2};
+            var end = {line: to.line, ch: start.ch + property.value.length};
+
+            this._codeMirror.markText(start, end, {className: &quot;invalid&quot;});
+
+            var valueReplacement = property.value.length ? WebInspector.UIString(&quot;The value '%s' is not supported for this property.\nClick to delete and open autocomplete.&quot;).format(property.value) : WebInspector.UIString(&quot;This property needs a value.\nClick to open autocomplete.&quot;);
+
+            invalidMarkerInfo = {
+                position: start,
+                title: valueReplacement,
+                correction: property.name + &quot;: &quot;,
+                autocomplete: true
+            };
+        } else if (!instancesOfProperty.call(this, &quot;-webkit-&quot; + property.name) &amp;&amp; WebInspector.CSSCompletions.cssNameCompletions.propertyRequiresWebkitPrefix(property.name)) {
+            // The property is valid and exists in the rule while its prefixed version does not.
+            invalidMarkerInfo = {
+                position: from,
+                title: WebInspector.UIString(&quot;The 'webkit' prefix is needed for this property.\nClick to insert a duplicate with the prefix.&quot;),
+                correction: &quot;-webkit-&quot; + property.text + &quot;\n&quot; + property.text,
+                autocomplete: false
+            };
+        } else if (!propertyHasUnnecessaryPrefix &amp;&amp; !WebInspector.CSSCompletions.cssNameCompletions.isValidPropertyName(&quot;-webkit-&quot; + property.name)) {
+            // The property either has no prefix and is invalid with a prefix or is invalid without a prefix.
+            var closestPropertyName = WebInspector.CSSCompletions.cssNameCompletions.getClosestPropertyName(property.name);
+
+            if (closestPropertyName) {
+                // The property name has less than 3 other properties that have the same Levenshtein distance.
+                invalidMarkerInfo = {
+                    position: from,
+                    title: WebInspector.UIString(&quot;Did you mean '%s'?\nClick to replace.&quot;).format(closestPropertyName),
+                    correction: property.text.replace(property.name, closestPropertyName),
+                    autocomplete: true
+                };
+            } else if (property.name.startsWith(&quot;-webkit-&quot;) &amp;&amp; (closestPropertyName = WebInspector.CSSCompletions.cssNameCompletions.getClosestPropertyName(property.canonicalName))) {
+                // The unprefixed property name has less than 3 other properties that have the same Levenshtein distance.
+                invalidMarkerInfo = {
+                    position: from,
+                    title: WebInspector.UIString(&quot;Did you mean '%s'?\nClick to replace.&quot;).format(&quot;-webkit-&quot; + closestPropertyName),
+                    correction: property.text.replace(property.canonicalName, closestPropertyName),
+                    autocomplete: true
+                };
+            } else {
+                // The property name is so vague or nonsensical that there are more than 3 other properties that have the same Levenshtein value.
+                invalidMarkerInfo = {
+                    position: from,
+                    title: WebInspector.UIString(&quot;The property '%s' is not supported.&quot;).format(property.name),
+                    correction: false,
+                    autocomplete: false
+                };
+            }
+        }
+
+        if (!invalidMarkerInfo)
+            return;
+
+        generateInvalidMarker.call(this, invalidMarkerInfo);
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     _clearTextMarkers(nonatomic, all)
</span></span></pre>
</div>
</div>

</body>
</html>