1/* 2 * Copyright (C) 2011, Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY 14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 15 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 16 * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY 17 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 18 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 19 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 20 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 21 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 22 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 23 */ 24 25#include "config.h" 26#include "core/inspector/InspectorStyleTextEditor.h" 27 28#include "core/css/CSSPropertySourceData.h" 29#include "core/html/parser/HTMLParserIdioms.h" 30#include "core/inspector/InspectorStyleSheet.h" 31 32namespace blink { 33 34InspectorStyleTextEditor::InspectorStyleTextEditor(WillBeHeapVector<InspectorStyleProperty>* allProperties, const String& styleText, const SourceRange& styleRange, const NewLineAndWhitespace& format) 35 : m_allProperties(allProperties) 36 , m_styleText(styleText) 37 , m_styleRange(styleRange) 38 , m_format(format) 39{ 40} 41 42void InspectorStyleTextEditor::insertProperty(unsigned index, const String& propertyText) 43{ 44 unsigned styleBodyLength = m_styleRange.length(); 45 long propertyStart = 0; 46 47 bool insertLast = true; 48 if (index < m_allProperties->size()) { 49 const InspectorStyleProperty& property = m_allProperties->at(index); 50 if (property.hasSource) { 51 propertyStart = property.sourceData.range.start - m_styleRange.start; 52 // If inserting before a disabled property, it should be shifted, too. 53 insertLast = false; 54 } 55 } 56 57 bool insertFirstInSource = !m_allProperties->size() || !m_allProperties->at(0).hasSource; 58 bool insertLastInSource = true; 59 for (unsigned i = index, size = m_allProperties->size(); i < size; ++i) { 60 const InspectorStyleProperty& property = m_allProperties->at(i); 61 if (property.hasSource) { 62 insertLastInSource = false; 63 break; 64 } 65 } 66 67 String textToSet = propertyText; 68 69 int formattingPrependOffset = 0; 70 if (insertLast && !insertFirstInSource) { 71 propertyStart = styleBodyLength; 72 if (propertyStart && textToSet.length()) { 73 long curPos = propertyStart - 1; // The last position of style declaration, since propertyStart points past one. 74 while (curPos && isHTMLSpace<UChar>(m_styleText[curPos])) 75 --curPos; 76 if (curPos) { 77 bool terminated = m_styleText[curPos] == ';' || (m_styleText[curPos] == '/' && m_styleText[curPos - 1] == '*'); 78 if (!terminated) { 79 // Prepend a ";" to the property text if appending to a style declaration where 80 // the last property has no trailing ";". 81 textToSet.insert(";", 0); 82 formattingPrependOffset = 1; 83 } 84 } 85 } 86 } 87 88 const String& formatLineFeed = m_format.first; 89 const String& formatPropertyPrefix = m_format.second; 90 if (insertLastInSource) { 91 long formatPropertyPrefixLength = formatPropertyPrefix.length(); 92 if (!formattingPrependOffset && (propertyStart < formatPropertyPrefixLength || m_styleText.substring(propertyStart - formatPropertyPrefixLength, formatPropertyPrefixLength) != formatPropertyPrefix)) { 93 textToSet.insert(formatPropertyPrefix, formattingPrependOffset); 94 if (!propertyStart || !isHTMLLineBreak(m_styleText[propertyStart - 1])) 95 textToSet.insert(formatLineFeed, formattingPrependOffset); 96 } 97 if (!isHTMLLineBreak(m_styleText[propertyStart])) 98 textToSet = textToSet + formatLineFeed; 99 } else { 100 String fullPrefix = formatLineFeed + formatPropertyPrefix; 101 long fullPrefixLength = fullPrefix.length(); 102 textToSet = textToSet + fullPrefix; 103 if (insertFirstInSource && (propertyStart < fullPrefixLength || m_styleText.substring(propertyStart - fullPrefixLength, fullPrefixLength) != fullPrefix)) 104 textToSet.insert(fullPrefix, formattingPrependOffset); 105 } 106 m_styleText.insert(textToSet, propertyStart); 107} 108 109void InspectorStyleTextEditor::replaceProperty(unsigned index, const String& newText) 110{ 111 ASSERT_WITH_SECURITY_IMPLICATION(index < m_allProperties->size()); 112 internalReplaceProperty(m_allProperties->at(index), newText); 113} 114 115void InspectorStyleTextEditor::internalReplaceProperty(const InspectorStyleProperty& property, const String& newText) 116{ 117 const SourceRange& range = property.sourceData.range; 118 long replaceRangeStart = range.start - m_styleRange.start; 119 long replaceRangeEnd = range.end - m_styleRange.start; 120 long newTextLength = newText.length(); 121 String finalNewText = newText; 122 123 // Removing a property - remove preceding prefix. 124 String fullPrefix = m_format.first + m_format.second; 125 long fullPrefixLength = fullPrefix.length(); 126 if (!newTextLength && fullPrefixLength) { 127 if (replaceRangeStart >= fullPrefixLength && m_styleText.substring(replaceRangeStart - fullPrefixLength, fullPrefixLength) == fullPrefix) 128 replaceRangeStart -= fullPrefixLength; 129 } else if (newTextLength) { 130 if (isHTMLLineBreak(newText[newTextLength - 1])) { 131 // Coalesce newlines of the original and new property values (to avoid a lot of blank lines while incrementally applying property values). 132 bool foundNewline = false; 133 bool isLastNewline = false; 134 int i; 135 int textLength = m_styleText.length(); 136 for (i = replaceRangeEnd; i < textLength && isSpaceOrNewline(m_styleText[i]); ++i) { 137 isLastNewline = isHTMLLineBreak(m_styleText[i]); 138 if (isLastNewline) 139 foundNewline = true; 140 else if (foundNewline && !isLastNewline) { 141 replaceRangeEnd = i; 142 break; 143 } 144 } 145 if (foundNewline && isLastNewline) 146 replaceRangeEnd = i; 147 } 148 149 if (fullPrefixLength > replaceRangeStart || m_styleText.substring(replaceRangeStart - fullPrefixLength, fullPrefixLength) != fullPrefix) 150 finalNewText.insert(fullPrefix, 0); 151 } 152 153 int replacedLength = replaceRangeEnd - replaceRangeStart; 154 m_styleText.replace(replaceRangeStart, replacedLength, finalNewText); 155} 156 157} // namespace blink 158 159