initial import
[vuplus_webkit] / Source / WebCore / inspector / InspectorStyleTextEditor.cpp
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 "InspectorStyleTextEditor.h"
27
28 #if ENABLE(INSPECTOR)
29
30 #include "CSSPropertySourceData.h"
31 #include "HTMLParserIdioms.h"
32 #include "InspectorStyleSheet.h"
33
34 namespace WebCore {
35
36 InspectorStyleTextEditor::InspectorStyleTextEditor(Vector<InspectorStyleProperty>* allProperties, Vector<InspectorStyleProperty>* disabledProperties, const String& styleText, const NewLineAndWhitespace& format)
37     : m_allProperties(allProperties)
38     , m_disabledProperties(disabledProperties)
39     , m_styleText(styleText)
40     , m_format(format)
41 {
42 }
43
44 void InspectorStyleTextEditor::insertProperty(unsigned index, const String& propertyText, unsigned styleBodyLength)
45 {
46     long propertyStart = 0;
47
48     bool insertLast = true;
49     if (index < m_allProperties->size()) {
50         const InspectorStyleProperty& property = m_allProperties->at(index);
51         if (property.hasSource) {
52             propertyStart = property.sourceData.range.start;
53             // If inserting before a disabled property, it should be shifted, too.
54             insertLast = false;
55         }
56     }
57
58     bool insertFirstInSource = true;
59     for (unsigned i = 0, size = m_allProperties->size(); i < index && i < size; ++i) {
60         const InspectorStyleProperty& property = m_allProperties->at(i);
61         if (property.hasSource && !property.disabled) {
62             insertFirstInSource = false;
63             break;
64         }
65     }
66
67     bool insertLastInSource = true;
68     for (unsigned i = index, size = m_allProperties->size(); i < size; ++i) {
69       const InspectorStyleProperty& property = m_allProperties->at(i);
70         if (property.hasSource && !property.disabled) {
71             insertLastInSource = false;
72             break;
73         }
74     }
75
76     String textToSet = propertyText;
77
78     int formattingPrependOffset = 0;
79     if (insertLast && !insertFirstInSource) {
80         propertyStart = styleBodyLength;
81         if (propertyStart && textToSet.length()) {
82             const UChar* characters = m_styleText.characters();
83
84             long curPos = propertyStart - 1; // The last position of style declaration, since propertyStart points past one.
85             while (curPos && isHTMLSpace(characters[curPos]))
86                 --curPos;
87             if (curPos && characters[curPos] != ';') {
88                 // Prepend a ";" to the property text if appending to a style declaration where
89                 // the last property has no trailing ";".
90                 textToSet.insert(";", 0);
91                 formattingPrependOffset = 1;
92             }
93         }
94     }
95
96     const String& formatLineFeed = m_format.first;
97     const String& formatPropertyPrefix = m_format.second;
98     if (insertLastInSource) {
99         long formatPropertyPrefixLength = formatPropertyPrefix.length();
100         if (!formattingPrependOffset && (propertyStart < formatPropertyPrefixLength || m_styleText.substring(propertyStart - formatPropertyPrefixLength, formatPropertyPrefixLength) != formatPropertyPrefix)) {
101             textToSet.insert(formatPropertyPrefix, formattingPrependOffset);
102             if (!propertyStart || !isHTMLLineBreak(m_styleText[propertyStart - 1]))
103                 textToSet.insert(formatLineFeed, formattingPrependOffset);
104         }
105         if (!isHTMLLineBreak(m_styleText[propertyStart]))
106             textToSet += formatLineFeed;
107     } else {
108         String fullPrefix = formatLineFeed + formatPropertyPrefix;
109         long fullPrefixLength = fullPrefix.length();
110         textToSet += fullPrefix;
111         if (insertFirstInSource && (propertyStart < fullPrefixLength || m_styleText.substring(propertyStart - fullPrefixLength, fullPrefixLength) != fullPrefix))
112             textToSet.insert(fullPrefix, formattingPrependOffset);
113     }
114     m_styleText.insert(textToSet, propertyStart);
115
116     // Recompute disabled property ranges after an inserted property.
117     long propertyLengthDelta = textToSet.length();
118     shiftDisabledProperties(disabledIndexByOrdinal(index, true), propertyLengthDelta);
119 }
120
121 void InspectorStyleTextEditor::replaceProperty(unsigned index, const String& newText)
122 {
123     ASSERT(index < m_allProperties->size());
124
125     const InspectorStyleProperty& property = m_allProperties->at(index);
126     long propertyStart = property.sourceData.range.start;
127     long propertyEnd = property.sourceData.range.end;
128     long oldLength = propertyEnd - propertyStart;
129     long newLength = newText.length();
130     long propertyLengthDelta = newLength - oldLength;
131
132     if (!property.disabled) {
133         SourceRange overwrittenRange;
134         unsigned insertedLength;
135         internalReplaceProperty(property, newText, &overwrittenRange, &insertedLength);
136         propertyLengthDelta = static_cast<long>(insertedLength) - static_cast<long>(overwrittenRange.length());
137
138         // Recompute subsequent disabled property ranges if acting on a non-disabled property.
139         shiftDisabledProperties(disabledIndexByOrdinal(index, true), propertyLengthDelta);
140     } else {
141         long textLength = newText.length();
142         unsigned disabledIndex = disabledIndexByOrdinal(index, false);
143         if (!textLength) {
144             // Delete disabled property.
145             m_disabledProperties->remove(disabledIndex);
146         } else {
147             // Patch disabled property text.
148             m_disabledProperties->at(disabledIndex).rawText = newText;
149         }
150     }
151 }
152
153 void InspectorStyleTextEditor::removeProperty(unsigned index)
154 {
155     replaceProperty(index, "");
156 }
157
158 void InspectorStyleTextEditor::enableProperty(unsigned index)
159 {
160     ASSERT(m_allProperties->at(index).disabled);
161
162     unsigned disabledIndex = disabledIndexByOrdinal(index, false);
163     ASSERT(disabledIndex != UINT_MAX);
164
165     InspectorStyleProperty disabledProperty = m_disabledProperties->at(disabledIndex);
166     m_disabledProperties->remove(disabledIndex);
167     SourceRange removedRange;
168     unsigned insertedLength;
169     internalReplaceProperty(disabledProperty, disabledProperty.rawText, &removedRange, &insertedLength);
170     shiftDisabledProperties(disabledIndex, static_cast<long>(insertedLength) - static_cast<long>(removedRange.length()));
171 }
172
173 void InspectorStyleTextEditor::disableProperty(unsigned index)
174 {
175     ASSERT(!m_allProperties->at(index).disabled);
176
177     const InspectorStyleProperty& property = m_allProperties->at(index);
178     InspectorStyleProperty disabledProperty(property);
179     disabledProperty.setRawTextFromStyleDeclaration(m_styleText);
180     disabledProperty.disabled = true;
181
182     SourceRange removedRange;
183     unsigned insertedLength;
184     internalReplaceProperty(property, "", &removedRange, &insertedLength);
185
186     // If some preceding formatting has been removed, move the property to the start of the removed range.
187     if (property.sourceData.range.start > removedRange.start)
188         disabledProperty.sourceData.range.start = removedRange.start;
189     disabledProperty.sourceData.range.end = disabledProperty.sourceData.range.start;
190
191     // Add disabled property at correct position.
192     unsigned insertionIndex = disabledIndexByOrdinal(index, true);
193     if (insertionIndex == UINT_MAX)
194         m_disabledProperties->append(disabledProperty);
195     else {
196         m_disabledProperties->insert(insertionIndex, disabledProperty);
197         long styleLengthDelta = -(static_cast<long>(removedRange.length()));
198         shiftDisabledProperties(insertionIndex + 1, styleLengthDelta); // Property removed from text - shift these back.
199     }
200 }
201
202 unsigned InspectorStyleTextEditor::disabledIndexByOrdinal(unsigned ordinal, bool canUseSubsequent)
203 {
204     unsigned disabledIndex = 0;
205     for (unsigned i = 0, size = m_allProperties->size(); i < size; ++i) {
206         if (m_allProperties->at(i).disabled) {
207             if (i == ordinal || (canUseSubsequent && i > ordinal))
208                 return disabledIndex;
209             ++disabledIndex;
210         }
211     }
212
213     return UINT_MAX;
214 }
215
216 void InspectorStyleTextEditor::shiftDisabledProperties(unsigned fromIndex, long delta)
217 {
218     for (unsigned i = fromIndex, size = m_disabledProperties->size(); i < size; ++i) {
219         SourceRange& range = m_disabledProperties->at(i).sourceData.range;
220         range.start += delta;
221         range.end += delta;
222     }
223 }
224
225 void InspectorStyleTextEditor::internalReplaceProperty(const InspectorStyleProperty& property, const String& newText, SourceRange* removedRange, unsigned* insertedLength)
226 {
227     const SourceRange& range = property.sourceData.range;
228     long replaceRangeStart = range.start;
229     long replaceRangeEnd = range.end;
230     const UChar* characters = m_styleText.characters();
231     long newTextLength = newText.length();
232     String finalNewText = newText;
233
234     // Removing a property - remove preceding prefix.
235     String fullPrefix = m_format.first + m_format.second;
236     long fullPrefixLength = fullPrefix.length();
237     if (!newTextLength && fullPrefixLength) {
238         if (replaceRangeStart >= fullPrefixLength && m_styleText.substring(replaceRangeStart - fullPrefixLength, fullPrefixLength) == fullPrefix)
239             replaceRangeStart -= fullPrefixLength;
240     } else if (newTextLength) {
241         if (isHTMLLineBreak(newText.characters()[newTextLength - 1])) {
242             // Coalesce newlines of the original and new property values (to avoid a lot of blank lines while incrementally applying property values).
243             bool foundNewline = false;
244             bool isLastNewline = false;
245             int i;
246             int textLength = m_styleText.length();
247             for (i = replaceRangeEnd; i < textLength && isSpaceOrNewline(characters[i]); ++i) {
248                 isLastNewline = isHTMLLineBreak(characters[i]);
249                 if (isLastNewline)
250                     foundNewline = true;
251                 else if (foundNewline && !isLastNewline) {
252                     replaceRangeEnd = i;
253                     break;
254                 }
255             }
256             if (foundNewline && isLastNewline)
257                 replaceRangeEnd = i;
258         }
259
260         if (fullPrefixLength > replaceRangeStart || m_styleText.substring(replaceRangeStart - fullPrefixLength, fullPrefixLength) != fullPrefix)
261             finalNewText.insert(fullPrefix, 0);
262     }
263
264     int replacedLength = replaceRangeEnd - replaceRangeStart;
265     m_styleText.replace(replaceRangeStart, replacedLength, finalNewText);
266     *removedRange = SourceRange(replaceRangeStart, replaceRangeEnd);
267     *insertedLength = finalNewText.length();
268 }
269
270 } // namespace WebCore
271
272 #endif // ENABLE(INSPECTOR)