| /* |
| * Copyright (C) 2011, 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: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. 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. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS 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 APPLE INC. OR ITS 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. |
| */ |
| |
| #include "config.h" |
| #include "core/inspector/InspectorStyleTextEditor.h" |
| |
| #include "core/css/CSSPropertySourceData.h" |
| #include "core/html/parser/HTMLParserIdioms.h" |
| #include "core/inspector/InspectorStyleSheet.h" |
| |
| namespace WebCore { |
| |
| InspectorStyleTextEditor::InspectorStyleTextEditor(Vector<InspectorStyleProperty>* allProperties, const String& styleText, const NewLineAndWhitespace& format) |
| : m_allProperties(allProperties) |
| , m_styleText(styleText) |
| , m_format(format) |
| { |
| } |
| |
| void InspectorStyleTextEditor::insertProperty(unsigned index, const String& propertyText, unsigned styleBodyLength) |
| { |
| long propertyStart = 0; |
| |
| bool insertLast = true; |
| if (index < m_allProperties->size()) { |
| const InspectorStyleProperty& property = m_allProperties->at(index); |
| if (property.hasSource) { |
| propertyStart = property.sourceData.range.start; |
| // If inserting before a disabled property, it should be shifted, too. |
| insertLast = false; |
| } |
| } |
| |
| bool insertFirstInSource = !m_allProperties->size() || !m_allProperties->at(0).hasSource; |
| bool insertLastInSource = true; |
| for (unsigned i = index, size = m_allProperties->size(); i < size; ++i) { |
| const InspectorStyleProperty& property = m_allProperties->at(i); |
| if (property.hasSource) { |
| insertLastInSource = false; |
| break; |
| } |
| } |
| |
| String textToSet = propertyText; |
| |
| int formattingPrependOffset = 0; |
| if (insertLast && !insertFirstInSource) { |
| propertyStart = styleBodyLength; |
| if (propertyStart && textToSet.length()) { |
| long curPos = propertyStart - 1; // The last position of style declaration, since propertyStart points past one. |
| while (curPos && isHTMLSpace<UChar>(m_styleText[curPos])) |
| --curPos; |
| if (curPos) { |
| bool terminated = m_styleText[curPos] == ';' || (m_styleText[curPos] == '/' && m_styleText[curPos - 1] == '*'); |
| if (!terminated) { |
| // Prepend a ";" to the property text if appending to a style declaration where |
| // the last property has no trailing ";". |
| textToSet.insert(";", 0); |
| formattingPrependOffset = 1; |
| } |
| } |
| } |
| } |
| |
| const String& formatLineFeed = m_format.first; |
| const String& formatPropertyPrefix = m_format.second; |
| if (insertLastInSource) { |
| long formatPropertyPrefixLength = formatPropertyPrefix.length(); |
| if (!formattingPrependOffset && (propertyStart < formatPropertyPrefixLength || m_styleText.substring(propertyStart - formatPropertyPrefixLength, formatPropertyPrefixLength) != formatPropertyPrefix)) { |
| textToSet.insert(formatPropertyPrefix, formattingPrependOffset); |
| if (!propertyStart || !isHTMLLineBreak(m_styleText[propertyStart - 1])) |
| textToSet.insert(formatLineFeed, formattingPrependOffset); |
| } |
| if (!isHTMLLineBreak(m_styleText[propertyStart])) |
| textToSet.append(formatLineFeed); |
| } else { |
| String fullPrefix = formatLineFeed + formatPropertyPrefix; |
| long fullPrefixLength = fullPrefix.length(); |
| textToSet.append(fullPrefix); |
| if (insertFirstInSource && (propertyStart < fullPrefixLength || m_styleText.substring(propertyStart - fullPrefixLength, fullPrefixLength) != fullPrefix)) |
| textToSet.insert(fullPrefix, formattingPrependOffset); |
| } |
| m_styleText.insert(textToSet, propertyStart); |
| } |
| |
| void InspectorStyleTextEditor::replaceProperty(unsigned index, const String& newText) |
| { |
| ASSERT_WITH_SECURITY_IMPLICATION(index < m_allProperties->size()); |
| internalReplaceProperty(m_allProperties->at(index), newText); |
| } |
| |
| void InspectorStyleTextEditor::removeProperty(unsigned index) |
| { |
| replaceProperty(index, ""); |
| } |
| |
| void InspectorStyleTextEditor::enableProperty(unsigned index) |
| { |
| InspectorStyleProperty& disabledProperty = m_allProperties->at(index); |
| ASSERT(disabledProperty.sourceData.disabled); |
| internalReplaceProperty(disabledProperty, disabledProperty.rawText.substring(2, disabledProperty.rawText.length() - 4).stripWhiteSpace()); |
| } |
| |
| void InspectorStyleTextEditor::disableProperty(unsigned index) |
| { |
| ASSERT(!m_allProperties->at(index).sourceData.disabled); |
| |
| InspectorStyleProperty& property = m_allProperties->at(index); |
| property.setRawTextFromStyleDeclaration(m_styleText); |
| property.sourceData.disabled = true; |
| |
| internalReplaceProperty(property, "/* " + property.rawText + " */"); |
| } |
| |
| void InspectorStyleTextEditor::internalReplaceProperty(const InspectorStyleProperty& property, const String& newText) |
| { |
| const SourceRange& range = property.sourceData.range; |
| long replaceRangeStart = range.start; |
| long replaceRangeEnd = range.end; |
| long newTextLength = newText.length(); |
| String finalNewText = newText; |
| |
| // Removing a property - remove preceding prefix. |
| String fullPrefix = m_format.first + m_format.second; |
| long fullPrefixLength = fullPrefix.length(); |
| if (!newTextLength && fullPrefixLength) { |
| if (replaceRangeStart >= fullPrefixLength && m_styleText.substring(replaceRangeStart - fullPrefixLength, fullPrefixLength) == fullPrefix) |
| replaceRangeStart -= fullPrefixLength; |
| } else if (newTextLength) { |
| if (isHTMLLineBreak(newText[newTextLength - 1])) { |
| // Coalesce newlines of the original and new property values (to avoid a lot of blank lines while incrementally applying property values). |
| bool foundNewline = false; |
| bool isLastNewline = false; |
| int i; |
| int textLength = m_styleText.length(); |
| for (i = replaceRangeEnd; i < textLength && isSpaceOrNewline(m_styleText[i]); ++i) { |
| isLastNewline = isHTMLLineBreak(m_styleText[i]); |
| if (isLastNewline) |
| foundNewline = true; |
| else if (foundNewline && !isLastNewline) { |
| replaceRangeEnd = i; |
| break; |
| } |
| } |
| if (foundNewline && isLastNewline) |
| replaceRangeEnd = i; |
| } |
| |
| if (fullPrefixLength > replaceRangeStart || m_styleText.substring(replaceRangeStart - fullPrefixLength, fullPrefixLength) != fullPrefix) |
| finalNewText.insert(fullPrefix, 0); |
| } |
| |
| int replacedLength = replaceRangeEnd - replaceRangeStart; |
| m_styleText.replace(replaceRangeStart, replacedLength, finalNewText); |
| } |
| |
| } // namespace WebCore |
| |