| Index: Source/devtools/front_end/CSSFormatter.js | 
| diff --git a/Source/devtools/front_end/CSSFormatter.js b/Source/devtools/front_end/CSSFormatter.js | 
| new file mode 100644 | 
| index 0000000000000000000000000000000000000000..1c0844daacb13a35289ee716565d823e9d5831c3 | 
| --- /dev/null | 
| +++ b/Source/devtools/front_end/CSSFormatter.js | 
| @@ -0,0 +1,291 @@ | 
| +/* | 
| + * Copyright (C) 2013 Google Inc. All rights reserved. | 
| + * | 
| + * Redistribution and use in source and binary forms, with or without | 
| + * modification, are permitted provided that the following conditions are | 
| + * met: | 
| + * | 
| + *     * Redistributions of source code must retain the above copyright | 
| + * notice, this list of conditions and the following disclaimer. | 
| + *     * Redistributions in binary form must reproduce the above | 
| + * copyright notice, this list of conditions and the following disclaimer | 
| + * in the documentation and/or other materials provided with the | 
| + * distribution. | 
| + *     * Neither the name of Google Inc. nor the names of its | 
| + * contributors may be used to endorse or promote products derived from | 
| + * this software without specific prior written permission. | 
| + * | 
| + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 
| + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 
| + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 
| + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 
| + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 
| + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 
| + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 
| + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 
| + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
| + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
| + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
| + */ | 
| + | 
| +/** | 
| + * @constructor | 
| + * @param {string} content | 
| + * @param {!FormatterWorker.CSSFormattedContentBuilder} builder | 
| + */ | 
| +FormatterWorker.CSSFormatter = function(content, builder) | 
| +{ | 
| +    this._content = content; | 
| +    this._builder = builder; | 
| +    this._lastLine = -1; | 
| +    this._state = {}; | 
| +} | 
| + | 
| +FormatterWorker.CSSFormatter.prototype = { | 
| +    format: function() | 
| +    { | 
| +        this._lineEndings = this._lineEndings(this._content); | 
| +        var tokenize = WebInspector.CodeMirrorUtils.createTokenizer("text/css"); | 
| +        var lines = this._content.split("\n"); | 
| + | 
| +        for (var i = 0; i < lines.length; ++i) { | 
| +            var line = lines[i]; | 
| +            tokenize(line, this._tokenCallback.bind(this, i)); | 
| +        } | 
| +        this._builder.flushNewLines(true); | 
| +    }, | 
| + | 
| +    /** | 
| +     * @param {string} text | 
| +     */ | 
| +    _lineEndings: function(text) | 
| +    { | 
| +        var lineEndings = []; | 
| +        var i = text.indexOf("\n"); | 
| +        while (i !== -1) { | 
| +            lineEndings.push(i); | 
| +            i = text.indexOf("\n", i + 1); | 
| +        } | 
| +        lineEndings.push(text.length); | 
| +        return lineEndings; | 
| +    }, | 
| + | 
| +    /** | 
| +     * @param {number} startLine | 
| +     * @param {string} token | 
| +     * @param {string} type | 
| +     * @param {number} startColumn | 
| +     */ | 
| +    _tokenCallback: function(startLine, token, type, startColumn) | 
| +    { | 
| +        if (startLine !== this._lastLine) | 
| +            this._state.eatWhitespace = true; | 
| +        if (/^css-property/.test(type) && !this._state.inPropertyValue) | 
| +            this._state.seenProperty = true; | 
| +        this._lastLine = startLine; | 
| +        var isWhitespace = /^\s+$/.test(token); | 
| +        if (isWhitespace) { | 
| +            if (!this._state.eatWhitespace) | 
| +                this._builder.addSpace(); | 
| +            return; | 
| +        } | 
| +        this._state.eatWhitespace = false; | 
| +        if (token === "\n") | 
| +            return; | 
| + | 
| +        if (token !== "}") { | 
| +            if (this._state.afterClosingBrace) | 
| +                this._builder.addNewLine(); | 
| +            this._state.afterClosingBrace = false; | 
| +        } | 
| +        var startPosition = (startLine ? this._lineEndings[startLine - 1] : 0) + startColumn; | 
| +        if (token === "}") { | 
| +            if (this._state.inPropertyValue) | 
| +                this._builder.addNewLine(); | 
| +            this._builder.decreaseNestingLevel(); | 
| +            this._state.afterClosingBrace = true; | 
| +            this._state.inPropertyValue = false; | 
| +        } else if (token === ":" && !this._state.inPropertyValue && this._state.seenProperty) { | 
| +            this._builder.addToken(token, startPosition, startLine, startColumn); | 
| +            this._builder.addSpace(); | 
| +            this._state.eatWhitespace = true; | 
| +            this._state.inPropertyValue = true; | 
| +            this._state.seenProperty = false; | 
| +            return; | 
| +        } else if (token === "{") { | 
| +            this._builder.addSpace(); | 
| +            this._builder.addToken(token, startPosition, startLine, startColumn); | 
| +            this._builder.addNewLine(); | 
| +            this._builder.increaseNestingLevel(); | 
| +            return; | 
| +        } | 
| + | 
| +        this._builder.addToken(token, startPosition, startLine, startColumn); | 
| + | 
| +        if (type === "css-comment" && !this._state.inPropertyValue && !this._state.seenProperty) | 
| +            this._builder.addNewLine(); | 
| +        if (token === ";" && this._state.inPropertyValue) { | 
| +            this._state.inPropertyValue = false; | 
| +            this._builder.addNewLine(); | 
| +        } else if (token === "}") { | 
| +            this._builder.addNewLine(); | 
| +        } | 
| +    } | 
| +} | 
| + | 
| +/** | 
| + * @constructor | 
| + * @param {string} content | 
| + * @param {{original: Array.<number>, formatted: Array.<number>}} mapping | 
| + * @param {number} originalOffset | 
| + * @param {number} formattedOffset | 
| + * @param {string} indentString | 
| + */ | 
| +FormatterWorker.CSSFormattedContentBuilder = function(content, mapping, originalOffset, formattedOffset, indentString) | 
| +{ | 
| +    this._originalContent = content; | 
| +    this._originalOffset = originalOffset; | 
| +    this._lastOriginalPosition = 0; | 
| + | 
| +    this._formattedContent = []; | 
| +    this._formattedContentLength = 0; | 
| +    this._formattedOffset = formattedOffset; | 
| +    this._lastFormattedPosition = 0; | 
| + | 
| +    this._mapping = mapping; | 
| + | 
| +    this._lineNumber = 0; | 
| +    this._nestingLevel = 0; | 
| +    this._needNewLines = 0; | 
| +    this._atLineStart = true; | 
| +    this._indentString = indentString; | 
| +    this._cachedIndents = {}; | 
| +} | 
| + | 
| +FormatterWorker.CSSFormattedContentBuilder.prototype = { | 
| +    /** | 
| +     * @param {string} token | 
| +     * @param {number} startPosition | 
| +     * @param {number} startLine | 
| +     * @param {number} startColumn | 
| +     */ | 
| +    addToken: function(token, startPosition, startLine, startColumn) | 
| +    { | 
| +        if ((this._isWhitespaceRun || this._atLineStart) && /^\s+$/.test(token)) | 
| +            return; | 
| + | 
| +        if (this._isWhitespaceRun && this._lineNumber === startLine && !this._needNewLines) | 
| +            this._addText(" "); | 
| + | 
| +        this._isWhitespaceRun = false; | 
| +        this._atLineStart = false; | 
| + | 
| +        while (this._lineNumber < startLine) { | 
| +            this._addText("\n"); | 
| +            this._addIndent(); | 
| +            this._needNewLines = 0; | 
| +            this._lineNumber += 1; | 
| +            this._atLineStart = true; | 
| +        } | 
| + | 
| +        if (this._needNewLines) { | 
| +            this.flushNewLines(); | 
| +            this._addIndent(); | 
| +            this._atLineStart = true; | 
| +        } | 
| + | 
| +        this._addMappingIfNeeded(startPosition); | 
| +        this._addText(token); | 
| +        this._lineNumber = startLine; | 
| +    }, | 
| + | 
| +    addSpace: function() | 
| +    { | 
| +        if (this._isWhitespaceRun) | 
| +            return; | 
| +        this._isWhitespaceRun = true; | 
| +    }, | 
| + | 
| +    addNewLine: function() | 
| +    { | 
| +        ++this._needNewLines; | 
| +    }, | 
| + | 
| +    /** | 
| +     * @param {boolean=} atLeastOne | 
| +     */ | 
| +    flushNewLines: function(atLeastOne) | 
| +    { | 
| +        var newLineCount = atLeastOne && !this._needNewLines ? 1 : this._needNewLines; | 
| +        if (newLineCount) | 
| +            this._isWhitespaceRun = false; | 
| +        for (var i = 0; i < newLineCount; ++i) | 
| +            this._addText("\n"); | 
| +        this._needNewLines = 0; | 
| +    }, | 
| + | 
| +    increaseNestingLevel: function() | 
| +    { | 
| +        this._nestingLevel += 1; | 
| +    }, | 
| + | 
| +    /** | 
| +     * @param {boolean=} addNewline | 
| +     */ | 
| +    decreaseNestingLevel: function(addNewline) | 
| +    { | 
| +        if (this._nestingLevel) | 
| +            this._nestingLevel -= 1; | 
| +        if (addNewline) | 
| +            this.addNewLine(); | 
| +    }, | 
| + | 
| +    /** | 
| +     * @return {string} | 
| +     */ | 
| +    content: function() | 
| +    { | 
| +        return this._formattedContent.join(""); | 
| +    }, | 
| + | 
| +    _addIndent: function() | 
| +    { | 
| +        if (this._cachedIndents[this._nestingLevel]) { | 
| +            this._addText(this._cachedIndents[this._nestingLevel]); | 
| +            return; | 
| +        } | 
| + | 
| +        var fullIndent = ""; | 
| +        for (var i = 0; i < this._nestingLevel; ++i) | 
| +            fullIndent += this._indentString; | 
| +        this._addText(fullIndent); | 
| + | 
| +        // Cache a maximum of 20 nesting level indents. | 
| +        if (this._nestingLevel <= 20) | 
| +            this._cachedIndents[this._nestingLevel] = fullIndent; | 
| +    }, | 
| + | 
| +    /** | 
| +     * @param {string} text | 
| +     */ | 
| +    _addText: function(text) | 
| +    { | 
| +        if (!text) | 
| +            return; | 
| +        this._formattedContent.push(text); | 
| +        this._formattedContentLength += text.length; | 
| +    }, | 
| + | 
| +    /** | 
| +     * @param {number} originalPosition | 
| +     */ | 
| +    _addMappingIfNeeded: function(originalPosition) | 
| +    { | 
| +        if (originalPosition - this._lastOriginalPosition === this._formattedContentLength - this._lastFormattedPosition) | 
| +            return; | 
| +        this._mapping.original.push(this._originalOffset + originalPosition); | 
| +        this._lastOriginalPosition = originalPosition; | 
| +        this._mapping.formatted.push(this._formattedOffset + this._formattedContentLength); | 
| +        this._lastFormattedPosition = this._formattedContentLength; | 
| +    } | 
| +} | 
|  |