<!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>[184000] 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/184000">184000</a></dd>
<dt>Author</dt> <dd>commit-queue@webkit.org</dd>
<dt>Date</dt> <dd>2015-05-08 11:37:49 -0700 (Fri, 08 May 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>Web Inspector: Styles sidebar editing with incomplete property looks poor in UI
https://bugs.webkit.org/show_bug.cgi?id=141692

Patch by Tobias Reiss &lt;tobi+webkit@basecode.de&gt; on 2015-05-08
Reviewed by Timothy Hatcher.

Add &quot;css-rule&quot; Formatter that breaks CSS declarations into multiple lines,
keeps comments and invalid styles and adds whitespace.

* Tools/PrettyPrinting/css-rule-tests/*.css: Added.
Add test cases.

* Tools/PrettyPrinting/index.html:
Enable Test setup to be able to run &quot;css-rule&quot; Formatter tests.

* UserInterface/Controllers/Formatter.js:
(Formatter.prototype._handleToken):
* UserInterface/Controllers/FormatterContentBuilder.js:
(FormatterContentBuilder.prototype.removeLastNewline):
(FormatterContentBuilder.prototype.removeLastWhitespace):
(FormatterContentBuilder.prototype._popFormattedContent):
(FormatterContentBuilder.prototype._popNewLine): Deleted.
* UserInterface/Views/CSSStyleDeclarationTextEditor.js:
(WebInspector.CSSStyleDeclarationTextEditor.prototype._formattedContentFromEditor):
(WebInspector.CSSStyleDeclarationTextEditor.prototype._resetContent.update.set this):
(WebInspector.CSSStyleDeclarationTextEditor.prototype._resetContent.update.get this):
(WebInspector.CSSStyleDeclarationTextEditor.prototype._resetContent.update):
(WebInspector.CSSStyleDeclarationTextEditor.prototype._resetContent):
(WebInspector.CSSStyleDeclarationTextEditor.prototype._resetContent.update.countNewLineCharacters): Deleted.
(WebInspector.CSSStyleDeclarationTextEditor.prototype._resetContent.update.else): Deleted.
* UserInterface/Views/CodeMirrorFormatters.js:</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceWebInspectorUIChangeLog">trunk/Source/WebInspectorUI/ChangeLog</a></li>
<li><a href="#trunkSourceWebInspectorUIToolsPrettyPrintingindexhtml">trunk/Source/WebInspectorUI/Tools/PrettyPrinting/index.html</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceControllersFormatterjs">trunk/Source/WebInspectorUI/UserInterface/Controllers/Formatter.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceControllersFormatterContentBuilderjs">trunk/Source/WebInspectorUI/UserInterface/Controllers/FormatterContentBuilder.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceViewsCSSStyleDeclarationTextEditorjs">trunk/Source/WebInspectorUI/UserInterface/Views/CSSStyleDeclarationTextEditor.js</a></li>
<li><a href="#trunkSourceWebInspectorUIUserInterfaceViewsCodeMirrorFormattersjs">trunk/Source/WebInspectorUI/UserInterface/Views/CodeMirrorFormatters.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceWebInspectorUIChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/ChangeLog (183999 => 184000)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/ChangeLog        2015-05-08 18:20:59 UTC (rev 183999)
+++ trunk/Source/WebInspectorUI/ChangeLog        2015-05-08 18:37:49 UTC (rev 184000)
</span><span class="lines">@@ -1,3 +1,36 @@
</span><ins>+2015-05-08  Tobias Reiss  &lt;tobi+webkit@basecode.de&gt;
+
+        Web Inspector: Styles sidebar editing with incomplete property looks poor in UI
+        https://bugs.webkit.org/show_bug.cgi?id=141692
+
+        Reviewed by Timothy Hatcher.
+
+        Add &quot;css-rule&quot; Formatter that breaks CSS declarations into multiple lines,
+        keeps comments and invalid styles and adds whitespace.
+
+        * Tools/PrettyPrinting/css-rule-tests/*.css: Added.
+        Add test cases.
+
+        * Tools/PrettyPrinting/index.html:
+        Enable Test setup to be able to run &quot;css-rule&quot; Formatter tests.
+
+        * UserInterface/Controllers/Formatter.js:
+        (Formatter.prototype._handleToken):
+        * UserInterface/Controllers/FormatterContentBuilder.js:
+        (FormatterContentBuilder.prototype.removeLastNewline):
+        (FormatterContentBuilder.prototype.removeLastWhitespace):
+        (FormatterContentBuilder.prototype._popFormattedContent):
+        (FormatterContentBuilder.prototype._popNewLine): Deleted.
+        * UserInterface/Views/CSSStyleDeclarationTextEditor.js:
+        (WebInspector.CSSStyleDeclarationTextEditor.prototype._formattedContentFromEditor):
+        (WebInspector.CSSStyleDeclarationTextEditor.prototype._resetContent.update.set this):
+        (WebInspector.CSSStyleDeclarationTextEditor.prototype._resetContent.update.get this):
+        (WebInspector.CSSStyleDeclarationTextEditor.prototype._resetContent.update):
+        (WebInspector.CSSStyleDeclarationTextEditor.prototype._resetContent):
+        (WebInspector.CSSStyleDeclarationTextEditor.prototype._resetContent.update.countNewLineCharacters): Deleted.
+        (WebInspector.CSSStyleDeclarationTextEditor.prototype._resetContent.update.else): Deleted.
+        * UserInterface/Views/CodeMirrorFormatters.js:
+
</ins><span class="cx"> 2015-05-07  Joseph Pecoraro  &lt;pecoraro@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Web Inspector: Expanding Object with only __proto__ looks poor should have a label
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIToolsPrettyPrintingindexhtml"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/Tools/PrettyPrinting/index.html (183999 => 184000)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/Tools/PrettyPrinting/index.html        2015-05-08 18:20:59 UTC (rev 183999)
+++ trunk/Source/WebInspectorUI/Tools/PrettyPrinting/index.html        2015-05-08 18:37:49 UTC (rev 184000)
</span><span class="lines">@@ -8,6 +8,8 @@
</span><span class="cx">     &lt;script src=&quot;../../UserInterface/External/CodeMirror/codemirror.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;../../UserInterface/External/CodeMirror/javascript.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;../../UserInterface/External/CodeMirror/css.js&quot;&gt;&lt;/script&gt;
</span><ins>+    &lt;script src=&quot;../../UserInterface/Base/WebInspector.js&quot;&gt;&lt;/script&gt;
+    &lt;script src=&quot;../../UserInterface/Views/CodeMirrorAdditions.js&quot;&gt;&lt;/script&gt;
</ins><span class="cx">     &lt;script src=&quot;../../UserInterface/Controllers/Formatter.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;FormatterDebug.js&quot;&gt;&lt;/script&gt;
</span><span class="cx">     &lt;script src=&quot;../../UserInterface/Controllers/FormatterContentBuilder.js&quot;&gt;&lt;/script&gt;
</span><span class="lines">@@ -21,6 +23,7 @@
</span><span class="cx">     &lt;select id=&quot;mode&quot;&gt;
</span><span class="cx">         &lt;option selected value=&quot;text/javascript&quot;&gt;JavaScript&lt;/option&gt;
</span><span class="cx">         &lt;option value=&quot;text/css&quot;&gt;CSS&lt;/option&gt;
</span><ins>+        &lt;option value=&quot;css-rule&quot;&gt;CSS-Rule&lt;/option&gt;
</ins><span class="cx">     &lt;/select&gt;
</span><span class="cx">     &lt;button id=&quot;populate&quot;&gt;Populate&lt;/button&gt;
</span><span class="cx">     &lt;button id=&quot;run-tests&quot;&gt;Run Tests&lt;/button&gt;
</span><span class="lines">@@ -230,6 +233,8 @@
</span><span class="cx"> 
</span><span class="cx">         if (modePicker.value === &quot;text/javascript&quot;)
</span><span class="cx">             runJavaScriptTests(completedCallback);
</span><ins>+        else if (modePicker.value === &quot;css-rule&quot;)
+            runCssRuleTests(completedCallback);
</ins><span class="cx">         else
</span><span class="cx">             runCSSTests(completedCallback);
</span><span class="cx">     }
</span><span class="lines">@@ -240,6 +245,34 @@
</span><span class="cx">             &quot;js-tests/switch-case-default.js&quot;,
</span><span class="cx">         ]);
</span><span class="cx">     }
</span><ins>+    function runCssRuleTests(callback) {
+        _runTests(callback, [
+            &quot;css-rule-tests/invalid-property-is-not-removed.css&quot;,
+            &quot;css-rule-tests/remove-whitespace-before-colon.css&quot;,
+            &quot;css-rule-tests/remove-whitespace-before-semicolon.css&quot;,
+            &quot;css-rule-tests/remove-whitespace-before-property.css&quot;,
+            &quot;css-rule-tests/remove-whitespace-before-prefixed-property.css&quot;,
+            &quot;css-rule-tests/remove-whitespace-before-invalid-property.css&quot;,
+            &quot;css-rule-tests/remove-whitespace-before-comment.css&quot;,
+            &quot;css-rule-tests/split-comment-followed-by-property.css&quot;,
+            &quot;css-rule-tests/split-comment-followed-by-prefixed-property.css&quot;,
+            &quot;css-rule-tests/split-comment-followed-by-invalid-property.css&quot;,
+            &quot;css-rule-tests/split-comment-followed-by-comment.css&quot;,
+            &quot;css-rule-tests/split-property-followed-by-property.css&quot;,
+            &quot;css-rule-tests/split-property-followed-by-prefixed-property.css&quot;,
+            &quot;css-rule-tests/split-property-followed-by-invalid-property.css&quot;,
+            &quot;css-rule-tests/split-property-followed-by-comment.css&quot;,
+            &quot;css-rule-tests/split-invalid-property-followed-by-property.css&quot;,
+            &quot;css-rule-tests/split-invalid-property-followed-by-prefixed-property.css&quot;,
+            &quot;css-rule-tests/split-invalid-property-followed-by-invalid-property.css&quot;,
+            &quot;css-rule-tests/split-invalid-property-followed-by-comment.css&quot;,
+            &quot;css-rule-tests/split-property-without-semicolon-followed-by-comment-and-property.css&quot;,
+            &quot;css-rule-tests/add-whitespace-after-colon.css&quot;,
+            &quot;css-rule-tests/add-whitespace-after-comma.css&quot;,
+            &quot;css-rule-tests/do-not-append-semicolon.css&quot;,
+            &quot;css-rule-tests/keep-prefixed-value.css&quot;,
+        ]);
+    }
</ins><span class="cx">     function runCSSTests(callback) {
</span><span class="cx">         _runTests(callback, [
</span><span class="cx">             &quot;css-tests/basic.css&quot;,
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceControllersFormatterjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Controllers/Formatter.js (183999 => 184000)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Controllers/Formatter.js        2015-05-08 18:20:59 UTC (rev 183999)
+++ trunk/Source/WebInspectorUI/UserInterface/Controllers/Formatter.js        2015-05-08 18:37:49 UTC (rev 184000)
</span><span class="lines">@@ -89,7 +89,7 @@
</span><span class="cx">         if (startOfNewLine)
</span><span class="cx">             this._builder.appendNewline();
</span><span class="cx"> 
</span><del>-        // Whitespace. Collapse to a single space.
</del><ins>+        // Whitespace. Remove all spaces or collapse to a single space.
</ins><span class="cx">         if (isWhiteSpace) {
</span><span class="cx">             this._builder.appendSpace();
</span><span class="cx">             return;
</span><span class="lines">@@ -101,6 +101,10 @@
</span><span class="cx">         if (mode.modifyStateForTokenPre)
</span><span class="cx">             mode.modifyStateForTokenPre(this._lastToken, this._lastContent, token, state, content, isComment);
</span><span class="cx"> 
</span><ins>+        // Should we remove the last whitespace?
+        if (this._builder.lastTokenWasWhitespace &amp;&amp; mode.removeLastWhitespace(this._lastToken, this._lastContent, token, state, content, isComment))
+            this._builder.removeLastWhitespace();
+
</ins><span class="cx">         // Should we remove the last newline?
</span><span class="cx">         if (this._builder.lastTokenWasNewline &amp;&amp; mode.removeLastNewline(this._lastToken, this._lastContent, token, state, content, isComment, firstTokenOnLine))
</span><span class="cx">             this._builder.removeLastNewline();
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceControllersFormatterContentBuilderjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Controllers/FormatterContentBuilder.js (183999 => 184000)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Controllers/FormatterContentBuilder.js        2015-05-08 18:20:59 UTC (rev 183999)
+++ trunk/Source/WebInspectorUI/UserInterface/Controllers/FormatterContentBuilder.js        2015-05-08 18:37:49 UTC (rev 184000)
</span><span class="lines">@@ -1,5 +1,6 @@
</span><span class="cx"> /*
</span><span class="cx">  * Copyright (C) 2013 Apple Inc. All rights reserved.
</span><ins>+ * Copyright (C) 2015 Tobias Reiss &lt;tobi+webkit@basecode.de&gt;
</ins><span class="cx">  *
</span><span class="cx">  * Redistribution and use in source and binary forms, with or without
</span><span class="cx">  * modification, are permitted provided that the following conditions
</span><span class="lines">@@ -137,13 +138,27 @@
</span><span class="cx">         console.assert(this.lastTokenWasNewline);
</span><span class="cx">         console.assert(this._formattedContent.lastValue === &quot;\n&quot;);
</span><span class="cx">         if (this.lastTokenWasNewline) {
</span><del>-            this._popNewLine();
</del><ins>+            this._popFormattedContent();
+            this._formattedLineEndings.pop();
</ins><span class="cx">             this._startOfLine = false;
</span><span class="cx">             this.lastTokenWasNewline = false;
</span><span class="cx">             this.lastTokenWasWhitespace = false;
</span><span class="cx">         }
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    removeLastWhitespace()
+    {
+        console.assert(this.lastTokenWasWhitespace);
+        console.assert(this._formattedContent.lastValue === &quot; &quot;);
+        if (this.lastTokenWasWhitespace) {
+            this._popFormattedContent();
+            // No need to worry about `_startOfLine` and `lastTokenWasNewline`
+            // because `appendSpace` takes care of not adding whitespace
+            // to the beginning of a line.
+            this.lastTokenWasWhitespace = false;
+        }
+    }
+
</ins><span class="cx">     indent()
</span><span class="cx">     {
</span><span class="cx">         ++this._indent;
</span><span class="lines">@@ -170,11 +185,10 @@
</span><span class="cx"> 
</span><span class="cx">     // Private
</span><span class="cx"> 
</span><del>-    _popNewLine()
</del><ins>+    _popFormattedContent()
</ins><span class="cx">     {
</span><span class="cx">         var removed = this._formattedContent.pop();
</span><span class="cx">         this._formattedContentLength -= removed.length;
</span><del>-        this._formattedLineEndings.pop();
</del><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx">     _append(str)
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceViewsCSSStyleDeclarationTextEditorjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Views/CSSStyleDeclarationTextEditor.js (183999 => 184000)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Views/CSSStyleDeclarationTextEditor.js        2015-05-08 18:20:59 UTC (rev 183999)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/CSSStyleDeclarationTextEditor.js        2015-05-08 18:37:49 UTC (rev 184000)
</span><span class="lines">@@ -1,5 +1,6 @@
</span><span class="cx"> /*
</span><span class="cx">  * Copyright (C) 2013 Apple Inc. All rights reserved.
</span><ins>+ * Copyright (C) 2015 Tobias Reiss &lt;tobi+webkit@basecode.de&gt;
</ins><span class="cx">  *
</span><span class="cx">  * Redistribution and use in source and binary forms, with or without
</span><span class="cx">  * modification, are permitted provided that the following conditions
</span><span class="lines">@@ -776,6 +777,20 @@
</span><span class="cx">         }
</span><span class="cx">     }
</span><span class="cx"> 
</span><ins>+    _formattedContentFromEditor()
+    {
+        var mapping = {original: [0], formatted: [0]};
+        // FIXME: &lt;rdar://problem/10593948&gt; Provide a way to change the tab width in the Web Inspector
+        var indentString = &quot;    &quot;;
+        var builder = new FormatterContentBuilder(mapping, [], [], 0, 0, indentString);
+        var formatter = new Formatter(this._codeMirror, builder);
+        var start = {line: 0, ch: 0};
+        var end = {line: this._codeMirror.lineCount() - 1};
+        formatter.format(start, end);
+
+        return builder.formattedContent.trim();
+    }
+
</ins><span class="cx">     _resetContent()
</span><span class="cx">     {
</span><span class="cx">         if (this._commitChangesTimeout) {
</span><span class="lines">@@ -816,115 +831,63 @@
</span><span class="cx">             // Remember the cursor position/selection.
</span><span class="cx">             var selectionAnchor = this._codeMirror.getCursor(&quot;anchor&quot;);
</span><span class="cx">             var selectionHead = this._codeMirror.getCursor(&quot;head&quot;);
</span><ins>+            var isEditorReadOnly = this._codeMirror.getOption(&quot;readOnly&quot;);
+            var styleText = this._style.text.trim();
+            var findWhitespace = /\s+/g;
</ins><span class="cx"> 
</span><del>-            function countNewLineCharacters(text)
-            {
-                var matches = text.match(/\n/g);
-                return matches ? matches.length : 0;
</del><ins>+            // Only format non-empty styles. Keep in mind that styleText is always empty
+            // for &quot;readOnly&quot; Editors. But prepare Checkbox placeholders in any case.
+            // Because that will indent the cursor when the User starts typing.
+            if (!styleText &amp;&amp; !isEditorReadOnly) {
+                this._markLinesWithCheckboxPlaceholder();
+                return;
</ins><span class="cx">             }
</span><span class="cx"> 
</span><del>-            var styleText = this._style.text;
</del><ins>+            // Set non-optimized, valid and invalid styles in preparation for the Formatter.
+            // Set empty string in case of readonly styles.
+            this._codeMirror.setValue(styleText);
</ins><span class="cx"> 
</span><del>-            // Pretty print the content if there are more properties than there are lines.
-            // This could be an option exposed to the user; however, it is almost always
-            // desired in this case.
-
-            if (styleText &amp;&amp; this._style.visibleProperties.length &lt;= countNewLineCharacters(styleText.trim()) + 1) {
-                // This style has formatted text content, so use it for a high-fidelity experience.
-
-                var prefixWhitespaceMatch = styleText.match(/^[ \t]*\n/);
-                this._prefixWhitespace = prefixWhitespaceMatch ? prefixWhitespaceMatch[0] : &quot;&quot;;
-
-                var suffixWhitespaceMatch = styleText.match(/\n[ \t]*$/);
-                this._suffixWhitespace = suffixWhitespaceMatch ? suffixWhitespaceMatch[0] : &quot;&quot;;
-
-                this._codeMirror.setValue(styleText);
-
-                if (this._prefixWhitespace)
-                    this._codeMirror.replaceRange(&quot;&quot;, {line: 0, ch: 0}, {line: 1, ch: 0});
-
-                if (this._suffixWhitespace) {
-                    var lineCount = this._codeMirror.lineCount();
-                    this._codeMirror.replaceRange(&quot;&quot;, {line: lineCount - 2}, {line: lineCount - 1});
-                }
-
-                this._linePrefixWhitespace = &quot;&quot;;
-
-                var linesToStrip = [];
-
-                // Remember the whitespace so it can be restored on commit.
-                var lineCount = this._codeMirror.lineCount();
-                for (var i = 0; i &lt; lineCount; ++i) {
-                    var lineContent = this._codeMirror.getLine(i);
-                    var prefixWhitespaceMatch = lineContent.match(/^\s+/);
-
-                    // If there is no prefix whitespace (except for empty lines) then the prefix
-                    // whitespace of all other lines will be retained as is. Update markers and return.
-                    if (!prefixWhitespaceMatch) {
-                        if (!lineContent)
-                            continue;
-                        this._linePrefixWhitespace = &quot;&quot;;
-                        this._updateTextMarkers(true);
-                        return;
-                    }
-
-                    linesToStrip.push(i);
-
-                    // Only remember the shortest whitespace so we don't loose any of the
-                    // original author's whitespace if their indentation lengths differed.
-                    // Using the shortest also makes the adjustment work in _updateTextMarkers.
-
-                    // FIXME: This messes up if there is a mix of spaces and tabs. A tab
-                    // is treated the same as a space when prefix whitespace is omitted,
-                    // so if the shortest prefixed whitespace is, say, two tab characters,
-                    // lines that begin with four spaces will only have a two space indent.
-                    if (!this._linePrefixWhitespace || prefixWhitespaceMatch[0].length &lt; this._linePrefixWhitespace.length)
-                        this._linePrefixWhitespace = prefixWhitespaceMatch[0];
-                }
-
-                // Strip the whitespace from the beginning of each line.
-                for (var i = 0; i &lt; linesToStrip.length; ++i) {
-                    var lineNumber = linesToStrip[i];
-                    var from = {line: lineNumber, ch: 0};
-                    var to = {line: lineNumber, ch: this._linePrefixWhitespace.length};
-                    this._codeMirror.replaceRange(&quot;&quot;, from, to);
-                }
-
-                // Update all the text markers.
-                this._updateTextMarkers(true);
-            } else {
-                // This style does not have text content or it is minified, so we want to synthesize the text content.
-
-                this._prefixWhitespace = &quot;&quot;;
-                this._suffixWhitespace = &quot;&quot;;
-                this._linePrefixWhitespace = &quot;&quot;;
-
-                this._codeMirror.setValue(&quot;&quot;);
-
</del><ins>+            if (isEditorReadOnly) {
</ins><span class="cx">                 var lineNumber = 0;
</span><del>-
-                // Iterate only visible properties if we have original style text. That way we known we only synthesize
-                // what was originaly in the style text.
-                this._iterateOverProperties(styleText ? true : false, function(property) {
-                    // Some property text can have line breaks, so consider that in the ranges below.
-                    var propertyText = property.synthesizedText;
-                    var propertyLineCount = countNewLineCharacters(propertyText);
-
</del><ins>+                this._iterateOverProperties(false, function(property) {
</ins><span class="cx">                     var from = {line: lineNumber, ch: 0};
</span><del>-                    var to = {line: lineNumber + propertyLineCount};
-
-                    this._codeMirror.replaceRange((lineNumber ? &quot;\n&quot; : &quot;&quot;) + propertyText, from);
</del><ins>+                    var to = {line: lineNumber};
+                    // Readonly properties are pretty printed by `synthesizedText` and not the Formatter.
+                    this._codeMirror.replaceRange((lineNumber ? &quot;\n&quot; : &quot;&quot;) + property.synthesizedText, from);
</ins><span class="cx">                     this._createTextMarkerForPropertyIfNeeded(from, to, property);
</span><del>-
-                    lineNumber += propertyLineCount + 1;
</del><ins>+                    lineNumber++;
</ins><span class="cx">                 });
</span><span class="cx"> 
</span><del>-                // Look for colors and make swatches.
-                this._createColorSwatches(true);
</del><ins>+                return;
</ins><span class="cx">             }
</span><span class="cx"> 
</span><del>-            this._markLinesWithCheckboxPlaceholder();
</del><ins>+            // Now the Formatter pretty prints the styles.
+            this._codeMirror.setValue(this._formattedContentFromEditor());
</ins><span class="cx"> 
</span><ins>+            // We need to workaround the fact that...
+            // 1) `this._style.properties` only holds valid CSSProperty instances but not
+            // comments and invalid properties like `color;`.
+            // 2) `_createTextMarkerForPropertyIfNeeded` relies on CSSProperty instances.
+            var cssPropertiesMap = new Map();
+            this._iterateOverProperties(false, function(cssProperty) {
+                cssPropertiesMap.set(cssProperty.text.replace(findWhitespace, &quot;&quot;), cssProperty);
+            });
+
+            // Go through the Editor line by line and create TextMarker when a
+            // CSSProperty instance for that property exists. If not, then don't create a TextMarker.
+            this._codeMirror.eachLine(function(lineHandler) {
+                var lineNumber = lineHandler.lineNo();
+                var lineContentSansWhitespace = lineHandler.text.replace(findWhitespace, &quot;&quot;);
+                if (cssPropertiesMap.has(lineContentSansWhitespace)) {
+                    var from = {line: lineNumber, ch: 0};
+                    var to = {line: lineNumber};
+                    this._createTextMarkerForPropertyIfNeeded(from, to, cssPropertiesMap.get(lineContentSansWhitespace));
+                }
+            }.bind(this));
+
+            // Look for colors and make swatches.
+            this._createColorSwatches(true);
+
</ins><span class="cx">             // Restore the cursor position/selection.
</span><span class="cx">             this._codeMirror.setSelection(selectionAnchor, selectionHead);
</span><span class="cx"> 
</span><span class="lines">@@ -934,6 +897,8 @@
</span><span class="cx"> 
</span><span class="cx">             // Mark the editor as clean (unedited state).
</span><span class="cx">             this._codeMirror.markClean();
</span><ins>+
+            this._markLinesWithCheckboxPlaceholder();
</ins><span class="cx">         }
</span><span class="cx"> 
</span><span class="cx">         // This needs to be done first and as a separate operation to avoid an exception in CodeMirror.
</span></span></pre></div>
<a id="trunkSourceWebInspectorUIUserInterfaceViewsCodeMirrorFormattersjs"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebInspectorUI/UserInterface/Views/CodeMirrorFormatters.js (183999 => 184000)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebInspectorUI/UserInterface/Views/CodeMirrorFormatters.js        2015-05-08 18:20:59 UTC (rev 183999)
+++ trunk/Source/WebInspectorUI/UserInterface/Views/CodeMirrorFormatters.js        2015-05-08 18:37:49 UTC (rev 184000)
</span><span class="lines">@@ -1,5 +1,6 @@
</span><span class="cx"> /*
</span><span class="cx">  * Copyright (C) 2013 Apple Inc. All rights reserved.
</span><ins>+ * Copyright (C) 2015 Tobias Reiss &lt;tobi+webkit@basecode.de&gt;
</ins><span class="cx">  *
</span><span class="cx">  * Redistribution and use in source and binary forms, with or without
</span><span class="cx">  * modification, are permitted provided that the following conditions
</span><span class="lines">@@ -106,6 +107,11 @@
</span><span class="cx">         return 0;
</span><span class="cx">     },
</span><span class="cx"> 
</span><ins>+    removeLastWhitespace: function(lastToken, lastContent, token, state, content, isComment)
+    {
+        return false;
+    },
+
</ins><span class="cx">     removeLastNewline: function(lastToken, lastContent, token, state, content, isComment, firstTokenOnLine)
</span><span class="cx">     {
</span><span class="cx">         if (!token) {
</span><span class="lines">@@ -374,6 +380,11 @@
</span><span class="cx">         return 0;
</span><span class="cx">     },
</span><span class="cx"> 
</span><ins>+    removeLastWhitespace: function(lastToken, lastContent, token, state, content, isComment)
+    {
+        return false;
+    },
+
</ins><span class="cx">     removeLastNewline: function(lastToken, lastContent, token, state, content, isComment, firstTokenOnLine)
</span><span class="cx">     {
</span><span class="cx">         if (isComment) { // Comment after semicolon.
</span><span class="lines">@@ -419,3 +430,83 @@
</span><span class="cx">             state._cssPrettyPrint.lineLength = 0;
</span><span class="cx">     }
</span><span class="cx"> });
</span><ins>+
+CodeMirror.extendMode(&quot;css-rule&quot;, {
+    shouldHaveSpaceBeforeToken: function(lastToken, lastContent, token, state, content, isComment)
+    {
+        return lastContent === &quot;:&quot; &amp;&amp; !lastToken;
+    },
+
+    shouldHaveSpaceAfterLastToken: function(lastToken, lastContent, token, state, content, isComment)
+    {
+        return lastContent === &quot;,&quot; &amp;&amp; !lastToken;
+    },
+
+    newlinesAfterToken: function(lastToken, lastContent, token, state, content, isComment)
+    {
+        return 0;
+    },
+
+    removeLastWhitespace: function(lastToken, lastContent, token, state, content, isComment)
+    {
+        // Remove whitespace before a comment which moves the comment to the beginning of the line.
+        if (isComment)
+            return true;
+
+        // A semicolon indicates the end of line. So remove whitespace before next line.
+        if (!lastToken)
+            return lastContent === &quot;;&quot;;
+
+        // Remove whitespace before semicolon. Like `prop: value ;`.
+        // Remove whitespace before colon. Like `prop : value;`.
+        if (!token)
+            return content === &quot;;&quot; || content === &quot;:&quot;;
+
+        // A comment is supposed to be in its own line. So remove whitespace before next line.
+        if (/\bcomment\b/.test(lastToken))
+            return true;
+
+        return false;
+    },
+
+    removeLastNewline: function(lastToken, lastContent, token, state, content, isComment, firstTokenOnLine)
+    {
+        return false;
+    },
+
+    indentAfterToken: function(lastToken, lastContent, token, state, content, isComment)
+    {
+        return false;
+    },
+
+    newlineBeforeToken: function(lastToken, lastContent, token, state, content, isComment)
+    {
+        // Add new line before comments.
+        if (isComment)
+            return true;
+
+        // Add new line before a prefixed property like `-webkit-animation`.
+        if (state.state === &quot;block&quot;)
+            return /\bmeta\b/.test(token);
+
+        // Add new line after comment
+        if (/\bcomment\b/.test(lastToken))
+            return true;
+
+        // Add new line before a regular property like `display`.
+        if (/\bproperty\b/.test(token))
+            return !(/\bmeta\b/.test(lastToken));
+
+        return false;
+    },
+
+    indentBeforeToken: function(lastToken, lastContent, token, state, content, isComment)
+    {
+        return false;
+    },
+
+    dedentsBeforeToken: function(lastToken, lastContent, token, state, content, isComment)
+    {
+        return 0;
+    }
+});
</ins></span></pre>
</div>
</div>

</body>
</html>