15c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)/*
25c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * Copyright (C) 2011, Google Inc. All rights reserved.
35c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *
45c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * Redistribution and use in source and binary forms, with or without
55c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * modification, are permitted provided that the following conditions
65c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * are met:
75c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * 1.  Redistributions of source code must retain the above copyright
85c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *     notice, this list of conditions and the following disclaimer.
95c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * 2.  Redistributions in binary form must reproduce the above copyright
105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *     notice, this list of conditions and the following disclaimer in the
115c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *     documentation and/or other materials provided with the distribution.
125c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) *
135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
155c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
185c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
195c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles) */
245c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)#include "config.h"
2653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)#include "core/inspector/InspectorStyleTextEditor.h"
275c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
2853e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)#include "core/css/CSSPropertySourceData.h"
2953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)#include "core/html/parser/HTMLParserIdioms.h"
3053e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)#include "core/inspector/InspectorStyleSheet.h"
315c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
32c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)namespace blink {
335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
347242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano TucciInspectorStyleTextEditor::InspectorStyleTextEditor(WillBeHeapVector<InspectorStyleProperty>* allProperties, const String& styleText, const SourceRange& styleRange, const NewLineAndWhitespace& format)
355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    : m_allProperties(allProperties)
365c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    , m_styleText(styleText)
377242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci    , m_styleRange(styleRange)
385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    , m_format(format)
395c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
415c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
427242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tuccivoid InspectorStyleTextEditor::insertProperty(unsigned index, const String& propertyText)
435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
447242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci    unsigned styleBodyLength = m_styleRange.length();
455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    long propertyStart = 0;
465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
475c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    bool insertLast = true;
485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (index < m_allProperties->size()) {
495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        const InspectorStyleProperty& property = m_allProperties->at(index);
505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if (property.hasSource) {
517242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci            propertyStart = property.sourceData.range.start - m_styleRange.start;
525c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            // If inserting before a disabled property, it should be shifted, too.
535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            insertLast = false;
545c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        }
555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
5753e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)    bool insertFirstInSource = !m_allProperties->size() || !m_allProperties->at(0).hasSource;
585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    bool insertLastInSource = true;
595c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    for (unsigned i = index, size = m_allProperties->size(); i < size; ++i) {
6053e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        const InspectorStyleProperty& property = m_allProperties->at(i);
6153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)        if (property.hasSource) {
625c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            insertLastInSource = false;
635c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            break;
645c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        }
655c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
665c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
675c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    String textToSet = propertyText;
685c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
695c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    int formattingPrependOffset = 0;
705c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (insertLast && !insertFirstInSource) {
715c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        propertyStart = styleBodyLength;
725c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if (propertyStart && textToSet.length()) {
735c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            long curPos = propertyStart - 1; // The last position of style declaration, since propertyStart points past one.
7406f816c7c76bc45a15e452ade8a34e8af077693eTorne (Richard Coles)            while (curPos && isHTMLSpace<UChar>(m_styleText[curPos]))
755c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                --curPos;
7653e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)            if (curPos) {
77591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch                bool terminated = m_styleText[curPos] == ';' || (m_styleText[curPos] == '/' && m_styleText[curPos - 1] == '*');
7853e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                if (!terminated) {
7953e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                    // Prepend a ";" to the property text if appending to a style declaration where
8053e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                    // the last property has no trailing ";".
8153e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                    textToSet.insert(";", 0);
8253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                    formattingPrependOffset = 1;
8353e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)                }
845c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            }
855c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        }
865c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
875c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
885c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    const String& formatLineFeed = m_format.first;
895c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    const String& formatPropertyPrefix = m_format.second;
905c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (insertLastInSource) {
915c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        long formatPropertyPrefixLength = formatPropertyPrefix.length();
925c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if (!formattingPrependOffset && (propertyStart < formatPropertyPrefixLength || m_styleText.substring(propertyStart - formatPropertyPrefixLength, formatPropertyPrefixLength) != formatPropertyPrefix)) {
935c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            textToSet.insert(formatPropertyPrefix, formattingPrependOffset);
945c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            if (!propertyStart || !isHTMLLineBreak(m_styleText[propertyStart - 1]))
955c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                textToSet.insert(formatLineFeed, formattingPrependOffset);
965c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        }
975c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if (!isHTMLLineBreak(m_styleText[propertyStart]))
9809380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)            textToSet = textToSet + formatLineFeed;
995c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    } else {
1005c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        String fullPrefix = formatLineFeed + formatPropertyPrefix;
1015c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        long fullPrefixLength = fullPrefix.length();
10209380295ba73501a205346becac22c6978e4671dTorne (Richard Coles)        textToSet = textToSet + fullPrefix;
1035c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if (insertFirstInSource && (propertyStart < fullPrefixLength || m_styleText.substring(propertyStart - fullPrefixLength, fullPrefixLength) != fullPrefix))
1045c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            textToSet.insert(fullPrefix, formattingPrependOffset);
1055c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
1065c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    m_styleText.insert(textToSet, propertyStart);
1075c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
1085c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1095c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)void InspectorStyleTextEditor::replaceProperty(unsigned index, const String& newText)
1105c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
111926b001d589ce2f10facb93dd4b87578ea35a855Torne (Richard Coles)    ASSERT_WITH_SECURITY_IMPLICATION(index < m_allProperties->size());
11253e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)    internalReplaceProperty(m_allProperties->at(index), newText);
1135c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
1145c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
11553e740f4a82e17f3ae59772501622dc354e42336Torne (Richard Coles)void InspectorStyleTextEditor::internalReplaceProperty(const InspectorStyleProperty& property, const String& newText)
1165c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles){
1175c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    const SourceRange& range = property.sourceData.range;
1187242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci    long replaceRangeStart = range.start - m_styleRange.start;
1197242dc3dbeb210b5e876a3c42d1ec1a667fc621aPrimiano Tucci    long replaceRangeEnd = range.end - m_styleRange.start;
1205c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    long newTextLength = newText.length();
1215c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    String finalNewText = newText;
1225c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1235c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    // Removing a property - remove preceding prefix.
1245c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    String fullPrefix = m_format.first + m_format.second;
1255c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    long fullPrefixLength = fullPrefix.length();
1265c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    if (!newTextLength && fullPrefixLength) {
1275c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if (replaceRangeStart >= fullPrefixLength && m_styleText.substring(replaceRangeStart - fullPrefixLength, fullPrefixLength) == fullPrefix)
1285c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            replaceRangeStart -= fullPrefixLength;
1295c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    } else if (newTextLength) {
130591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch        if (isHTMLLineBreak(newText[newTextLength - 1])) {
1315c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            // Coalesce newlines of the original and new property values (to avoid a lot of blank lines while incrementally applying property values).
1325c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            bool foundNewline = false;
1335c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            bool isLastNewline = false;
1345c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            int i;
1355c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            int textLength = m_styleText.length();
136591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch            for (i = replaceRangeEnd; i < textLength && isSpaceOrNewline(m_styleText[i]); ++i) {
137591b958dee2cf159d33a0b931e6231072eaf38d5Ben Murdoch                isLastNewline = isHTMLLineBreak(m_styleText[i]);
1385c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                if (isLastNewline)
1395c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                    foundNewline = true;
1405c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                else if (foundNewline && !isLastNewline) {
1415c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                    replaceRangeEnd = i;
1425c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                    break;
1435c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                }
1445c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            }
1455c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            if (foundNewline && isLastNewline)
1465c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)                replaceRangeEnd = i;
1475c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        }
1485c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1495c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)        if (fullPrefixLength > replaceRangeStart || m_styleText.substring(replaceRangeStart - fullPrefixLength, fullPrefixLength) != fullPrefix)
1505c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)            finalNewText.insert(fullPrefix, 0);
1515c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    }
1525c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
1535c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    int replacedLength = replaceRangeEnd - replaceRangeStart;
1545c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)    m_styleText.replace(replaceRangeStart, replacedLength, finalNewText);
1555c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)}
1565c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
157c1847b1379d12d0e05df27436bf19a9b1bf12deaTorne (Richard Coles)} // namespace blink
1585c87bf8b86a7c82ef50fb7a89697d8e02e2553beTorne (Richard Coles)
159