2b08411050d4c571bce83ad5522971d6737ce187
[WebKit-https.git] / Source / WebCore / inspector / InspectorStyleSheet.cpp
1 /*
2  * Copyright (C) 2010, 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
27 #if ENABLE(INSPECTOR)
28
29 #include "InspectorStyleSheet.h"
30
31 #include "CSSHostRule.h"
32 #include "CSSImportRule.h"
33 #include "CSSMediaRule.h"
34 #include "CSSParser.h"
35 #include "CSSPropertyNames.h"
36 #include "CSSPropertySourceData.h"
37 #include "CSSRule.h"
38 #include "CSSRuleList.h"
39 #include "CSSStyleRule.h"
40 #include "CSSStyleSheet.h"
41 #include "CSSSupportsRule.h"
42 #include "ContentSearchUtils.h"
43 #include "ContentSecurityPolicy.h"
44 #include "Document.h"
45 #include "Element.h"
46 #include "HTMLHeadElement.h"
47 #include "HTMLNames.h"
48 #include "HTMLParserIdioms.h"
49 #include "HTMLStyleElement.h"
50 #include "InspectorCSSAgent.h"
51 #include "InspectorPageAgent.h"
52 #include "InspectorValues.h"
53 #include "Node.h"
54 #include "RegularExpression.h"
55 #include "SVGNames.h"
56 #include "StylePropertySet.h"
57 #include "StyleResolver.h"
58 #include "StyleRule.h"
59 #include "StyleRuleImport.h"
60 #include "StyleSheetContents.h"
61 #include "StyleSheetList.h"
62 #include "WebKitCSSKeyframesRule.h"
63
64 #include <wtf/OwnPtr.h>
65 #include <wtf/PassOwnPtr.h>
66 #include <wtf/Vector.h>
67 #include <wtf/text/StringBuilder.h>
68
69 using WebCore::TypeBuilder::Array;
70 using WebCore::RuleSourceDataList;
71 using WebCore::CSSRuleSourceData;
72
73 class ParsedStyleSheet {
74     WTF_MAKE_FAST_ALLOCATED;
75 public:
76     ParsedStyleSheet();
77
78     WebCore::CSSStyleSheet* cssStyleSheet() const { return m_parserOutput; }
79     const String& text() const { ASSERT(m_hasText); return m_text; }
80     void setText(const String& text);
81     bool hasText() const { return m_hasText; }
82     RuleSourceDataList* sourceData() const { return m_sourceData.get(); }
83     void setSourceData(PassOwnPtr<RuleSourceDataList>);
84     bool hasSourceData() const { return m_sourceData; }
85     PassRefPtr<WebCore::CSSRuleSourceData> ruleSourceDataAt(unsigned) const;
86
87 private:
88
89     // StyleSheet constructed while parsing m_text.
90     WebCore::CSSStyleSheet* m_parserOutput;
91     String m_text;
92     bool m_hasText;
93     OwnPtr<RuleSourceDataList> m_sourceData;
94 };
95
96 ParsedStyleSheet::ParsedStyleSheet()
97     : m_parserOutput(0)
98     , m_hasText(false)
99 {
100 }
101
102 void ParsedStyleSheet::setText(const String& text)
103 {
104     m_hasText = true;
105     m_text = text;
106     setSourceData(nullptr);
107 }
108
109 static void flattenSourceData(RuleSourceDataList* dataList, RuleSourceDataList* target)
110 {
111     for (size_t i = 0; i < dataList->size(); ++i) {
112         RefPtr<CSSRuleSourceData>& data = dataList->at(i);
113         if (data->type == CSSRuleSourceData::STYLE_RULE)
114             target->append(data);
115         else if (data->type == CSSRuleSourceData::MEDIA_RULE)
116             flattenSourceData(&data->childRules, target);
117 #if ENABLE(SHADOW_DOM)
118         else if (data->type == CSSRuleSourceData::HOST_RULE)
119             flattenSourceData(&data->childRules, target);
120 #endif
121 #if ENABLE(CSS3_CONDITIONAL_RULES)
122         else if (data->type == CSSRuleSourceData::SUPPORTS_RULE)
123             flattenSourceData(&data->childRules, target);
124 #endif
125     }
126 }
127
128 void ParsedStyleSheet::setSourceData(PassOwnPtr<RuleSourceDataList> sourceData)
129 {
130     if (!sourceData) {
131         m_sourceData.clear();
132         return;
133     }
134
135     m_sourceData = adoptPtr(new RuleSourceDataList());
136
137     // FIXME: This is a temporary solution to retain the original flat sourceData structure
138     // containing only style rules, even though CSSParser now provides the full rule source data tree.
139     // Normally, we should just assign m_sourceData = sourceData;
140     flattenSourceData(sourceData.get(), m_sourceData.get());
141 }
142
143 PassRefPtr<WebCore::CSSRuleSourceData> ParsedStyleSheet::ruleSourceDataAt(unsigned index) const
144 {
145     if (!hasSourceData() || index >= m_sourceData->size())
146         return 0;
147
148     return m_sourceData->at(index);
149 }
150
151 namespace WebCore {
152
153 enum MediaListSource {
154     MediaListSourceLinkedSheet,
155     MediaListSourceInlineSheet,
156     MediaListSourceMediaRule,
157     MediaListSourceImportRule
158 };
159
160 static PassRefPtr<TypeBuilder::CSS::SourceRange> buildSourceRangeObject(const SourceRange& range, Vector<size_t>* lineEndings)
161 {
162     if (!lineEndings)
163         return 0;
164     TextPosition start = ContentSearchUtils::textPositionFromOffset(range.start, *lineEndings);
165     TextPosition end = ContentSearchUtils::textPositionFromOffset(range.end, *lineEndings);
166
167     RefPtr<TypeBuilder::CSS::SourceRange> result = TypeBuilder::CSS::SourceRange::create()
168         .setStartLine(start.m_line.zeroBasedInt())
169         .setStartColumn(start.m_column.zeroBasedInt())
170         .setEndLine(end.m_line.zeroBasedInt())
171         .setEndColumn(end.m_column.zeroBasedInt());
172     return result.release();
173 }
174
175 static PassRefPtr<TypeBuilder::CSS::CSSMedia> buildMediaObject(const MediaList* media, MediaListSource mediaListSource, const String& sourceURL)
176 {
177     // Make certain compilers happy by initializing |source| up-front.
178     TypeBuilder::CSS::CSSMedia::Source::Enum source = TypeBuilder::CSS::CSSMedia::Source::InlineSheet;
179     switch (mediaListSource) {
180     case MediaListSourceMediaRule:
181         source = TypeBuilder::CSS::CSSMedia::Source::MediaRule;
182         break;
183     case MediaListSourceImportRule:
184         source = TypeBuilder::CSS::CSSMedia::Source::ImportRule;
185         break;
186     case MediaListSourceLinkedSheet:
187         source = TypeBuilder::CSS::CSSMedia::Source::LinkedSheet;
188         break;
189     case MediaListSourceInlineSheet:
190         source = TypeBuilder::CSS::CSSMedia::Source::InlineSheet;
191         break;
192     }
193
194     RefPtr<TypeBuilder::CSS::CSSMedia> mediaObject = TypeBuilder::CSS::CSSMedia::create()
195         .setText(media->mediaText())
196         .setSource(source);
197
198     if (!sourceURL.isEmpty()) {
199         mediaObject->setSourceURL(sourceURL);
200         mediaObject->setSourceLine(media->queries()->lastLine());
201     }
202     return mediaObject.release();
203 }
204
205 static PassRefPtr<CSSRuleList> asCSSRuleList(CSSStyleSheet* styleSheet)
206 {
207     if (!styleSheet)
208         return 0;
209
210     RefPtr<StaticCSSRuleList> list = StaticCSSRuleList::create();
211     Vector<RefPtr<CSSRule> >& listRules = list->rules();
212     for (unsigned i = 0, size = styleSheet->length(); i < size; ++i) {
213         CSSRule* item = styleSheet->item(i);
214         if (item->type() == CSSRule::CHARSET_RULE)
215             continue;
216         listRules.append(item);
217     }
218     return list.release();
219 }
220
221 static PassRefPtr<CSSRuleList> asCSSRuleList(CSSRule* rule)
222 {
223     if (!rule)
224         return 0;
225
226     if (rule->type() == CSSRule::MEDIA_RULE)
227         return static_cast<CSSMediaRule*>(rule)->cssRules();
228
229     if (rule->type() == CSSRule::WEBKIT_KEYFRAMES_RULE)
230         return static_cast<WebKitCSSKeyframesRule*>(rule)->cssRules();
231
232 #if ENABLE(SHADOW_DOM)
233     if (rule->type() == CSSRule::HOST_RULE)
234         return static_cast<CSSHostRule*>(rule)->cssRules();
235 #endif
236
237 #if ENABLE(CSS3_CONDITIONAL_RULES)
238     if (rule->type() == CSSRule::SUPPORTS_RULE)
239         return static_cast<CSSSupportsRule*>(rule)->cssRules();
240 #endif
241
242     return 0;
243 }
244
245 static void fillMediaListChain(CSSRule* rule, Array<TypeBuilder::CSS::CSSMedia>* mediaArray)
246 {
247     MediaList* mediaList;
248     CSSRule* parentRule = rule;
249     String sourceURL;
250     while (parentRule) {
251         CSSStyleSheet* parentStyleSheet = 0;
252         bool isMediaRule = true;
253         if (parentRule->type() == CSSRule::MEDIA_RULE) {
254             CSSMediaRule* mediaRule = static_cast<CSSMediaRule*>(parentRule);
255             mediaList = mediaRule->media();
256             parentStyleSheet = mediaRule->parentStyleSheet();
257         } else if (parentRule->type() == CSSRule::IMPORT_RULE) {
258             CSSImportRule* importRule = static_cast<CSSImportRule*>(parentRule);
259             mediaList = importRule->media();
260             parentStyleSheet = importRule->parentStyleSheet();
261             isMediaRule = false;
262         } else
263             mediaList = 0;
264
265         if (parentStyleSheet) {
266             sourceURL = parentStyleSheet->contents()->baseURL();
267             if (sourceURL.isEmpty())
268                 sourceURL = InspectorDOMAgent::documentURLString(parentStyleSheet->ownerDocument());
269         } else
270             sourceURL = "";
271
272         if (mediaList && mediaList->length())
273             mediaArray->addItem(buildMediaObject(mediaList, isMediaRule ? MediaListSourceMediaRule : MediaListSourceImportRule, sourceURL));
274
275         if (parentRule->parentRule())
276             parentRule = parentRule->parentRule();
277         else {
278             CSSStyleSheet* styleSheet = parentRule->parentStyleSheet();
279             while (styleSheet) {
280                 mediaList = styleSheet->media();
281                 if (mediaList && mediaList->length()) {
282                     Document* doc = styleSheet->ownerDocument();
283                     if (doc)
284                         sourceURL = doc->url();
285                     else if (!styleSheet->contents()->baseURL().isEmpty())
286                         sourceURL = styleSheet->contents()->baseURL();
287                     else
288                         sourceURL = "";
289                     mediaArray->addItem(buildMediaObject(mediaList, styleSheet->ownerNode() ? MediaListSourceLinkedSheet : MediaListSourceInlineSheet, sourceURL));
290                 }
291                 parentRule = styleSheet->ownerRule();
292                 if (parentRule)
293                     break;
294                 styleSheet = styleSheet->parentStyleSheet();
295             }
296         }
297     }
298 }
299
300 static PassOwnPtr<CSSParser> createCSSParser(Document* document)
301 {
302     return adoptPtr(new CSSParser(document ? CSSParserContext(document) : strictCSSParserContext()));
303 }
304
305 PassRefPtr<InspectorStyle> InspectorStyle::create(const InspectorCSSId& styleId, PassRefPtr<CSSStyleDeclaration> style, InspectorStyleSheet* parentStyleSheet)
306 {
307     return adoptRef(new InspectorStyle(styleId, style, parentStyleSheet));
308 }
309
310 InspectorStyle::InspectorStyle(const InspectorCSSId& styleId, PassRefPtr<CSSStyleDeclaration> style, InspectorStyleSheet* parentStyleSheet)
311     : m_styleId(styleId)
312     , m_style(style)
313     , m_parentStyleSheet(parentStyleSheet)
314     , m_formatAcquired(false)
315 {
316     ASSERT(m_style);
317 }
318
319 InspectorStyle::~InspectorStyle()
320 {
321 }
322
323 PassRefPtr<TypeBuilder::CSS::CSSStyle> InspectorStyle::buildObjectForStyle() const
324 {
325     RefPtr<TypeBuilder::CSS::CSSStyle> result = styleWithProperties();
326     if (!m_styleId.isEmpty())
327         result->setStyleId(m_styleId.asProtocolValue<TypeBuilder::CSS::CSSStyleId>());
328
329     result->setWidth(m_style->getPropertyValue("width"));
330     result->setHeight(m_style->getPropertyValue("height"));
331
332     RefPtr<CSSRuleSourceData> sourceData = extractSourceData();
333     if (sourceData)
334         result->setRange(buildSourceRangeObject(sourceData->ruleBodyRange, m_parentStyleSheet->lineEndings().get()));
335
336     return result.release();
337 }
338
339 PassRefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSComputedStyleProperty> > InspectorStyle::buildArrayForComputedStyle() const
340 {
341     RefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSComputedStyleProperty> > result = TypeBuilder::Array<TypeBuilder::CSS::CSSComputedStyleProperty>::create();
342     Vector<InspectorStyleProperty> properties;
343     populateAllProperties(&properties);
344
345     for (Vector<InspectorStyleProperty>::iterator it = properties.begin(), itEnd = properties.end(); it != itEnd; ++it) {
346         const CSSPropertySourceData& propertyEntry = it->sourceData;
347         RefPtr<TypeBuilder::CSS::CSSComputedStyleProperty> entry = TypeBuilder::CSS::CSSComputedStyleProperty::create()
348             .setName(propertyEntry.name)
349             .setValue(propertyEntry.value);
350         result->addItem(entry);
351     }
352
353     return result.release();
354 }
355
356 // This method does the following preprocessing of |propertyText| with |overwrite| == false and |index| past the last active property:
357 // - If the last property (if present) has no closing ";", the ";" is prepended to the current |propertyText| value.
358 // - A heuristic formatting is attempted to retain the style structure.
359 //
360 // The propertyText (if not empty) is checked to be a valid style declaration (containing at least one property). If not,
361 // the method returns false (denoting an error).
362 bool InspectorStyle::setPropertyText(unsigned index, const String& propertyText, bool overwrite, String* oldText, ExceptionCode& ec)
363 {
364     ASSERT(m_parentStyleSheet);
365     DEFINE_STATIC_LOCAL(String, bogusPropertyName, (ASCIILiteral("-webkit-boguz-propertee")));
366
367     if (!m_parentStyleSheet->ensureParsedDataReady()) {
368         ec = NOT_FOUND_ERR;
369         return false;
370     }
371
372     Vector<InspectorStyleProperty> allProperties;
373     populateAllProperties(&allProperties);
374
375     if (propertyText.stripWhiteSpace().length()) {
376         RefPtr<MutableStylePropertySet> tempMutableStyle = MutableStylePropertySet::create();
377         RefPtr<CSSRuleSourceData> sourceData = CSSRuleSourceData::create(CSSRuleSourceData::STYLE_RULE);
378         Document* ownerDocument = m_parentStyleSheet->pageStyleSheet() ? m_parentStyleSheet->pageStyleSheet()->ownerDocument() : 0;
379         createCSSParser(ownerDocument)->parseDeclaration(tempMutableStyle.get(), propertyText + " " + bogusPropertyName + ": none", sourceData, m_style->parentStyleSheet()->contents());
380         Vector<CSSPropertySourceData>& propertyData = sourceData->styleSourceData->propertyData;
381         unsigned propertyCount = propertyData.size();
382
383         // At least one property + the bogus property added just above should be present.
384         if (propertyCount < 2) {
385             ec = SYNTAX_ERR;
386             return false;
387         }
388
389         // Check for a proper propertyText termination (the parser could at least restore to the PROPERTY_NAME state).
390         if (propertyData.at(propertyCount - 1).name != bogusPropertyName) {
391             ec = SYNTAX_ERR;
392             return false;
393         }
394     }
395
396     RefPtr<CSSRuleSourceData> sourceData = extractSourceData();
397     if (!sourceData) {
398         ec = NOT_FOUND_ERR;
399         return false;
400     }
401
402     String text;
403     bool success = styleText(&text);
404     if (!success) {
405         ec = NOT_FOUND_ERR;
406         return false;
407     }
408
409     InspectorStyleTextEditor editor(&allProperties, &m_disabledProperties, text, newLineAndWhitespaceDelimiters());
410     if (overwrite) {
411         if (index >= allProperties.size()) {
412             ec = INDEX_SIZE_ERR;
413             return false;
414         }
415         *oldText = allProperties.at(index).rawText;
416         editor.replaceProperty(index, propertyText);
417     } else
418         editor.insertProperty(index, propertyText, sourceData->ruleBodyRange.length());
419
420     return applyStyleText(editor.styleText());
421 }
422
423 bool InspectorStyle::toggleProperty(unsigned index, bool disable, ExceptionCode& ec)
424 {
425     ASSERT(m_parentStyleSheet);
426     if (!m_parentStyleSheet->ensureParsedDataReady()) {
427         ec = NO_MODIFICATION_ALLOWED_ERR;
428         return false;
429     }
430
431     RefPtr<CSSRuleSourceData> sourceData = extractSourceData();
432     if (!sourceData) {
433         ec = NOT_FOUND_ERR;
434         return false;
435     }
436
437     String text;
438     bool success = styleText(&text);
439     if (!success) {
440         ec = NOT_FOUND_ERR;
441         return false;
442     }
443
444     Vector<InspectorStyleProperty> allProperties;
445     populateAllProperties(&allProperties);
446     if (index >= allProperties.size()) {
447         ec = INDEX_SIZE_ERR;
448         return false;
449     }
450
451     InspectorStyleProperty& property = allProperties.at(index);
452     if (property.disabled == disable)
453         return true; // Idempotent operation.
454
455     InspectorStyleTextEditor editor(&allProperties, &m_disabledProperties, text, newLineAndWhitespaceDelimiters());
456     if (disable)
457         editor.disableProperty(index);
458     else
459         editor.enableProperty(index);
460
461     return applyStyleText(editor.styleText());
462 }
463
464 bool InspectorStyle::getText(String* result) const
465 {
466     // Precondition: m_parentStyleSheet->ensureParsedDataReady() has been called successfully.
467     RefPtr<CSSRuleSourceData> sourceData = extractSourceData();
468     if (!sourceData)
469         return false;
470
471     String styleSheetText;
472     bool success = m_parentStyleSheet->getText(&styleSheetText);
473     if (!success)
474         return false;
475
476     SourceRange& bodyRange = sourceData->ruleBodyRange;
477     *result = styleSheetText.substring(bodyRange.start, bodyRange.end - bodyRange.start);
478     return true;
479 }
480
481 bool InspectorStyle::populateAllProperties(Vector<InspectorStyleProperty>* result) const
482 {
483     HashSet<String> foundShorthands;
484     HashSet<String> sourcePropertyNames;
485     unsigned disabledIndex = 0;
486     unsigned disabledLength = m_disabledProperties.size();
487     InspectorStyleProperty disabledProperty;
488     if (disabledIndex < disabledLength)
489         disabledProperty = m_disabledProperties.at(disabledIndex);
490
491     RefPtr<CSSRuleSourceData> sourceData = extractSourceData();
492     Vector<CSSPropertySourceData>* sourcePropertyData = sourceData ? &(sourceData->styleSourceData->propertyData) : 0;
493     if (sourcePropertyData) {
494         String styleDeclaration;
495         bool isStyleTextKnown = styleText(&styleDeclaration);
496         ASSERT_UNUSED(isStyleTextKnown, isStyleTextKnown);
497         for (Vector<CSSPropertySourceData>::const_iterator it = sourcePropertyData->begin(); it != sourcePropertyData->end(); ++it) {
498             while (disabledIndex < disabledLength && disabledProperty.sourceData.range.start <= it->range.start) {
499                 result->append(disabledProperty);
500                 if (++disabledIndex < disabledLength)
501                     disabledProperty = m_disabledProperties.at(disabledIndex);
502             }
503             InspectorStyleProperty p(*it, true, false);
504             p.setRawTextFromStyleDeclaration(styleDeclaration);
505             result->append(p);
506             sourcePropertyNames.add(it->name.lower());
507         }
508     }
509
510     while (disabledIndex < disabledLength) {
511         disabledProperty = m_disabledProperties.at(disabledIndex++);
512         result->append(disabledProperty);
513     }
514
515     for (int i = 0, size = m_style->length(); i < size; ++i) {
516         String name = m_style->item(i);
517         if (sourcePropertyNames.contains(name.lower()))
518             continue;
519
520         sourcePropertyNames.add(name.lower());
521         result->append(InspectorStyleProperty(CSSPropertySourceData(name, m_style->getPropertyValue(name), !m_style->getPropertyPriority(name).isEmpty(), true, SourceRange()), false, false));
522     }
523
524     return true;
525 }
526
527 PassRefPtr<TypeBuilder::CSS::CSSStyle> InspectorStyle::styleWithProperties() const
528 {
529     Vector<InspectorStyleProperty> properties;
530     populateAllProperties(&properties);
531
532     RefPtr<Array<TypeBuilder::CSS::CSSProperty> > propertiesObject = Array<TypeBuilder::CSS::CSSProperty>::create();
533     RefPtr<Array<TypeBuilder::CSS::ShorthandEntry> > shorthandEntries = Array<TypeBuilder::CSS::ShorthandEntry>::create();
534     HashMap<String, RefPtr<TypeBuilder::CSS::CSSProperty> > propertyNameToPreviousActiveProperty;
535     HashSet<String> foundShorthands;
536     String previousPriority;
537     String previousStatus;
538     OwnPtr<Vector<size_t> > lineEndings(m_parentStyleSheet ? m_parentStyleSheet->lineEndings() : PassOwnPtr<Vector<size_t> >());
539     RefPtr<CSSRuleSourceData> sourceData = extractSourceData();
540     unsigned ruleBodyRangeStart = sourceData ? sourceData->ruleBodyRange.start : 0;
541
542     for (Vector<InspectorStyleProperty>::iterator it = properties.begin(), itEnd = properties.end(); it != itEnd; ++it) {
543         const CSSPropertySourceData& propertyEntry = it->sourceData;
544         const String& name = propertyEntry.name;
545
546         TypeBuilder::CSS::CSSProperty::Status::Enum status = it->disabled ? TypeBuilder::CSS::CSSProperty::Status::Disabled : TypeBuilder::CSS::CSSProperty::Status::Active;
547
548         RefPtr<TypeBuilder::CSS::CSSProperty> property = TypeBuilder::CSS::CSSProperty::create()
549             .setName(name)
550             .setValue(propertyEntry.value);
551
552         propertiesObject->addItem(property);
553
554         // Default "parsedOk" == true.
555         if (!propertyEntry.parsedOk)
556             property->setParsedOk(false);
557         if (it->hasRawText())
558             property->setText(it->rawText);
559
560         // Default "priority" == "".
561         if (propertyEntry.important)
562             property->setPriority("important");
563         if (!it->disabled) {
564             if (it->hasSource) {
565                 ASSERT(sourceData);
566                 property->setImplicit(false);
567                 // The property range is relative to the style body start.
568                 // Should be converted into an absolute range (relative to the stylesheet start)
569                 // for the proper conversion into line:column.
570                 SourceRange absolutePropertyRange = propertyEntry.range;
571                 absolutePropertyRange.start += ruleBodyRangeStart;
572                 absolutePropertyRange.end += ruleBodyRangeStart;
573                 property->setRange(buildSourceRangeObject(absolutePropertyRange, lineEndings.get()));
574
575                 // Parsed property overrides any property with the same name. Non-parsed property overrides
576                 // previous non-parsed property with the same name (if any).
577                 bool shouldInactivate = false;
578                 CSSPropertyID propertyId = cssPropertyID(name);
579                 // Canonicalize property names to treat non-prefixed and vendor-prefixed property names the same (opacity vs. -webkit-opacity).
580                 String canonicalPropertyName = propertyId ? getPropertyNameString(propertyId) : name;
581                 HashMap<String, RefPtr<TypeBuilder::CSS::CSSProperty> >::iterator activeIt = propertyNameToPreviousActiveProperty.find(canonicalPropertyName);
582                 if (activeIt != propertyNameToPreviousActiveProperty.end()) {
583                     if (propertyEntry.parsedOk) {
584                         bool successPriority = activeIt->value->getString(TypeBuilder::CSS::CSSProperty::Priority, &previousPriority);
585                         bool successStatus = activeIt->value->getString(TypeBuilder::CSS::CSSProperty::Status, &previousStatus);
586                         if (successStatus && previousStatus != "inactive") {
587                             if (propertyEntry.important || !successPriority) // Priority not set == "not important".
588                                 shouldInactivate = true;
589                             else if (status == TypeBuilder::CSS::CSSProperty::Status::Active) {
590                                 // Inactivate a non-important property following the same-named important property.
591                                 status = TypeBuilder::CSS::CSSProperty::Status::Inactive;
592                             }
593                         }
594                     } else {
595                         bool previousParsedOk;
596                         bool success = activeIt->value->getBoolean(TypeBuilder::CSS::CSSProperty::ParsedOk, &previousParsedOk);
597                         if (success && !previousParsedOk)
598                             shouldInactivate = true;
599                     }
600                 } else
601                     propertyNameToPreviousActiveProperty.set(canonicalPropertyName, property);
602
603                 if (shouldInactivate) {
604                     activeIt->value->setStatus(TypeBuilder::CSS::CSSProperty::Status::Inactive);
605                     propertyNameToPreviousActiveProperty.set(canonicalPropertyName, property);
606                 }
607             } else {
608                 bool implicit = m_style->isPropertyImplicit(name);
609                 // Default "implicit" == false.
610                 if (implicit)
611                     property->setImplicit(true);
612                 status = TypeBuilder::CSS::CSSProperty::Status::Style;
613
614                 String shorthand = m_style->getPropertyShorthand(name);
615                 if (!shorthand.isEmpty()) {
616                     if (!foundShorthands.contains(shorthand)) {
617                         foundShorthands.add(shorthand);
618                         RefPtr<TypeBuilder::CSS::ShorthandEntry> entry = TypeBuilder::CSS::ShorthandEntry::create()
619                             .setName(shorthand)
620                             .setValue(shorthandValue(shorthand));
621                         shorthandEntries->addItem(entry);
622                     }
623                 }
624             }
625         }
626
627         // Default "status" == "style".
628         if (status != TypeBuilder::CSS::CSSProperty::Status::Style)
629             property->setStatus(status);
630     }
631
632     RefPtr<TypeBuilder::CSS::CSSStyle> result = TypeBuilder::CSS::CSSStyle::create()
633         .setCssProperties(propertiesObject)
634         .setShorthandEntries(shorthandEntries);
635     return result.release();
636 }
637
638 PassRefPtr<CSSRuleSourceData> InspectorStyle::extractSourceData() const
639 {
640     if (!m_parentStyleSheet || !m_parentStyleSheet->ensureParsedDataReady())
641         return 0;
642     return m_parentStyleSheet->ruleSourceDataFor(m_style.get());
643 }
644
645 bool InspectorStyle::setText(const String& text, ExceptionCode& ec)
646 {
647     return m_parentStyleSheet->setStyleText(m_style.get(), text, ec);
648 }
649
650 String InspectorStyle::shorthandValue(const String& shorthandProperty) const
651 {
652     String value = m_style->getPropertyValue(shorthandProperty);
653     if (value.isEmpty()) {
654         for (unsigned i = 0; i < m_style->length(); ++i) {
655             String individualProperty = m_style->item(i);
656             if (m_style->getPropertyShorthand(individualProperty) != shorthandProperty)
657                 continue;
658             if (m_style->isPropertyImplicit(individualProperty))
659                 continue;
660             String individualValue = m_style->getPropertyValue(individualProperty);
661             if (individualValue == "initial")
662                 continue;
663             if (value.length())
664                 value.append(" ");
665             value.append(individualValue);
666         }
667     }
668     return value;
669 }
670
671 String InspectorStyle::shorthandPriority(const String& shorthandProperty) const
672 {
673     String priority = m_style->getPropertyPriority(shorthandProperty);
674     if (priority.isEmpty()) {
675         for (unsigned i = 0; i < m_style->length(); ++i) {
676             String individualProperty = m_style->item(i);
677             if (m_style->getPropertyShorthand(individualProperty) != shorthandProperty)
678                 continue;
679             priority = m_style->getPropertyPriority(individualProperty);
680             break;
681         }
682     }
683     return priority;
684 }
685
686 Vector<String> InspectorStyle::longhandProperties(const String& shorthandProperty) const
687 {
688     Vector<String> properties;
689     HashSet<String> foundProperties;
690     for (unsigned i = 0; i < m_style->length(); ++i) {
691         String individualProperty = m_style->item(i);
692         if (foundProperties.contains(individualProperty) || m_style->getPropertyShorthand(individualProperty) != shorthandProperty)
693             continue;
694
695         foundProperties.add(individualProperty);
696         properties.append(individualProperty);
697     }
698     return properties;
699 }
700
701 NewLineAndWhitespace& InspectorStyle::newLineAndWhitespaceDelimiters() const
702 {
703     DEFINE_STATIC_LOCAL(String, defaultPrefix, (ASCIILiteral("    ")));
704
705     if (m_formatAcquired)
706         return m_format;
707
708     RefPtr<CSSRuleSourceData> sourceData = extractSourceData();
709     Vector<CSSPropertySourceData>* sourcePropertyData = sourceData ? &(sourceData->styleSourceData->propertyData) : 0;
710     int propertyCount;
711     if (!sourcePropertyData || !(propertyCount = sourcePropertyData->size())) {
712         m_format.first = "\n";
713         m_format.second = defaultPrefix;
714         return m_format; // Do not remember the default formatting and attempt to acquire it later.
715     }
716
717     String text;
718     bool success = styleText(&text);
719     ASSERT_UNUSED(success, success);
720
721     m_formatAcquired = true;
722
723     String candidatePrefix = defaultPrefix;
724     StringBuilder formatLineFeed;
725     StringBuilder prefix;
726     int scanStart = 0;
727     int propertyIndex = 0;
728     bool isFullPrefixScanned = false;
729     bool lineFeedTerminated = false;
730     const UChar* characters = text.characters();
731     while (propertyIndex < propertyCount) {
732         const WebCore::CSSPropertySourceData& currentProperty = sourcePropertyData->at(propertyIndex++);
733
734         bool processNextProperty = false;
735         int scanEnd = currentProperty.range.start;
736         for (int i = scanStart; i < scanEnd; ++i) {
737             UChar ch = characters[i];
738             bool isLineFeed = isHTMLLineBreak(ch);
739             if (isLineFeed) {
740                 if (!lineFeedTerminated)
741                     formatLineFeed.append(ch);
742                 prefix.clear();
743             } else if (isHTMLSpace(ch))
744                 prefix.append(ch);
745             else {
746                 candidatePrefix = prefix.toString();
747                 prefix.clear();
748                 scanStart = currentProperty.range.end;
749                 ++propertyIndex;
750                 processNextProperty = true;
751                 break;
752             }
753             if (!isLineFeed && formatLineFeed.length())
754                 lineFeedTerminated = true;
755         }
756         if (!processNextProperty) {
757             isFullPrefixScanned = true;
758             break;
759         }
760     }
761
762     m_format.first = formatLineFeed.toString();
763     m_format.second = isFullPrefixScanned ? prefix.toString() : candidatePrefix;
764     return m_format;
765 }
766
767 PassRefPtr<InspectorStyleSheet> InspectorStyleSheet::create(InspectorPageAgent* pageAgent, const String& id, PassRefPtr<CSSStyleSheet> pageStyleSheet, TypeBuilder::CSS::StyleSheetOrigin::Enum origin, const String& documentURL, Listener* listener)
768 {
769     return adoptRef(new InspectorStyleSheet(pageAgent, id, pageStyleSheet, origin, documentURL, listener));
770 }
771
772 // static
773 String InspectorStyleSheet::styleSheetURL(CSSStyleSheet* pageStyleSheet)
774 {
775     if (pageStyleSheet && !pageStyleSheet->contents()->baseURL().isEmpty())
776         return pageStyleSheet->contents()->baseURL().string();
777     return emptyString();
778 }
779
780 InspectorStyleSheet::InspectorStyleSheet(InspectorPageAgent* pageAgent, const String& id, PassRefPtr<CSSStyleSheet> pageStyleSheet, TypeBuilder::CSS::StyleSheetOrigin::Enum origin, const String& documentURL, Listener* listener)
781     : m_pageAgent(pageAgent)
782     , m_id(id)
783     , m_pageStyleSheet(pageStyleSheet)
784     , m_origin(origin)
785     , m_documentURL(documentURL)
786     , m_isRevalidating(false)
787     , m_listener(listener)
788 {
789     m_parsedStyleSheet = new ParsedStyleSheet();
790 }
791
792 InspectorStyleSheet::~InspectorStyleSheet()
793 {
794     delete m_parsedStyleSheet;
795 }
796
797 String InspectorStyleSheet::finalURL() const
798 {
799     String url = styleSheetURL(m_pageStyleSheet.get());
800     return url.isEmpty() ? m_documentURL : url;
801 }
802
803 void InspectorStyleSheet::reparseStyleSheet(const String& text)
804 {
805     {
806         // Have a separate scope for clearRules() (bug 95324).
807         CSSStyleSheet::RuleMutationScope mutationScope(m_pageStyleSheet.get());
808         m_pageStyleSheet->contents()->clearRules();
809     }
810     {
811         CSSStyleSheet::RuleMutationScope mutationScope(m_pageStyleSheet.get());
812         m_pageStyleSheet->contents()->parseString(text);
813         m_pageStyleSheet->clearChildRuleCSSOMWrappers();
814         m_inspectorStyles.clear();
815         fireStyleSheetChanged();
816     }
817 }
818
819 bool InspectorStyleSheet::setText(const String& text, ExceptionCode& ec)
820 {
821     if (!checkPageStyleSheet(ec))
822         return false;
823     if (!m_parsedStyleSheet)
824         return false;
825
826     m_parsedStyleSheet->setText(text);
827     m_flatRules.clear();
828
829     return true;
830 }
831
832 String InspectorStyleSheet::ruleSelector(const InspectorCSSId& id, ExceptionCode& ec)
833 {
834     CSSStyleRule* rule = ruleForId(id);
835     if (!rule) {
836         ec = NOT_FOUND_ERR;
837         return "";
838     }
839     return rule->selectorText();
840 }
841
842 bool InspectorStyleSheet::setRuleSelector(const InspectorCSSId& id, const String& selector, ExceptionCode& ec)
843 {
844     if (!checkPageStyleSheet(ec))
845         return false;
846     CSSStyleRule* rule = ruleForId(id);
847     if (!rule) {
848         ec = NOT_FOUND_ERR;
849         return false;
850     }
851     CSSStyleSheet* styleSheet = rule->parentStyleSheet();
852     if (!styleSheet || !ensureParsedDataReady()) {
853         ec = NOT_FOUND_ERR;
854         return false;
855     }
856
857     rule->setSelectorText(selector);
858     RefPtr<CSSRuleSourceData> sourceData = ruleSourceDataFor(rule->style());
859     if (!sourceData) {
860         ec = NOT_FOUND_ERR;
861         return false;
862     }
863
864     String sheetText = m_parsedStyleSheet->text();
865     sheetText.replace(sourceData->ruleHeaderRange.start, sourceData->ruleHeaderRange.length(), selector);
866     m_parsedStyleSheet->setText(sheetText);
867     fireStyleSheetChanged();
868     return true;
869 }
870
871 static bool checkStyleRuleSelector(Document* document, const String& selector)
872 {
873     CSSSelectorList selectorList;
874     createCSSParser(document)->parseSelector(selector, selectorList);
875     return selectorList.isValid();
876 }
877
878 CSSStyleRule* InspectorStyleSheet::addRule(const String& selector, ExceptionCode& ec)
879 {
880     if (!checkPageStyleSheet(ec))
881         return 0;
882     if (!checkStyleRuleSelector(m_pageStyleSheet->ownerDocument(), selector)) {
883         ec = SYNTAX_ERR;
884         return 0;
885     }
886
887     String text;
888     bool success = getText(&text);
889     if (!success) {
890         ec = NOT_FOUND_ERR;
891         return 0;
892     }
893     StringBuilder styleSheetText;
894     styleSheetText.append(text);
895
896     m_pageStyleSheet->addRule(selector, "", ec);
897     if (ec)
898         return 0;
899     ASSERT(m_pageStyleSheet->length());
900     unsigned lastRuleIndex = m_pageStyleSheet->length() - 1;
901     CSSRule* rule = m_pageStyleSheet->item(lastRuleIndex);
902     ASSERT(rule);
903
904     CSSStyleRule* styleRule = InspectorCSSAgent::asCSSStyleRule(rule);
905     if (!styleRule) {
906         // What we just added has to be a CSSStyleRule - we cannot handle other types of rules yet.
907         // If it is not a style rule, pretend we never touched the stylesheet.
908         m_pageStyleSheet->deleteRule(lastRuleIndex, ASSERT_NO_EXCEPTION);
909         ec = SYNTAX_ERR;
910         return 0;
911     }
912
913     if (!styleSheetText.isEmpty())
914         styleSheetText.append('\n');
915
916     styleSheetText.append(selector);
917     styleSheetText.appendLiteral(" {}");
918     // Using setText() as this operation changes the style sheet rule set.
919     setText(styleSheetText.toString(), ASSERT_NO_EXCEPTION);
920
921     fireStyleSheetChanged();
922
923     return styleRule;
924 }
925
926 bool InspectorStyleSheet::deleteRule(const InspectorCSSId& id, ExceptionCode& ec)
927 {
928     if (!checkPageStyleSheet(ec))
929         return false;
930     RefPtr<CSSStyleRule> rule = ruleForId(id);
931     if (!rule) {
932         ec = NOT_FOUND_ERR;
933         return false;
934     }
935     CSSStyleSheet* styleSheet = rule->parentStyleSheet();
936     if (!styleSheet || !ensureParsedDataReady()) {
937         ec = NOT_FOUND_ERR;
938         return false;
939     }
940
941     RefPtr<CSSRuleSourceData> sourceData = ruleSourceDataFor(rule->style());
942     if (!sourceData) {
943         ec = NOT_FOUND_ERR;
944         return false;
945     }
946
947     styleSheet->deleteRule(id.ordinal(), ec);
948     // |rule| MAY NOT be addressed after this line!
949
950     if (ec)
951         return false;
952
953     String sheetText = m_parsedStyleSheet->text();
954     sheetText.remove(sourceData->ruleHeaderRange.start, sourceData->ruleBodyRange.end - sourceData->ruleHeaderRange.start + 1);
955     setText(sheetText, ASSERT_NO_EXCEPTION);
956     fireStyleSheetChanged();
957     return true;
958 }
959
960 CSSStyleRule* InspectorStyleSheet::ruleForId(const InspectorCSSId& id) const
961 {
962     if (!m_pageStyleSheet)
963         return 0;
964
965     ASSERT(!id.isEmpty());
966     ensureFlatRules();
967     return id.ordinal() >= m_flatRules.size() ? 0 : m_flatRules.at(id.ordinal()).get();
968
969 }
970
971 PassRefPtr<TypeBuilder::CSS::CSSStyleSheetBody> InspectorStyleSheet::buildObjectForStyleSheet()
972 {
973     CSSStyleSheet* styleSheet = pageStyleSheet();
974     if (!styleSheet)
975         return 0;
976
977     RefPtr<CSSRuleList> cssRuleList = asCSSRuleList(styleSheet);
978
979     RefPtr<TypeBuilder::CSS::CSSStyleSheetBody> result = TypeBuilder::CSS::CSSStyleSheetBody::create()
980         .setStyleSheetId(id())
981         .setRules(buildArrayForRuleList(cssRuleList.get()));
982
983     String styleSheetText;
984     bool success = getText(&styleSheetText);
985     if (success)
986         result->setText(styleSheetText);
987
988     return result.release();
989 }
990
991 PassRefPtr<TypeBuilder::CSS::CSSStyleSheetHeader> InspectorStyleSheet::buildObjectForStyleSheetInfo()
992 {
993     CSSStyleSheet* styleSheet = pageStyleSheet();
994     if (!styleSheet)
995         return 0;
996
997     Document* document = styleSheet->ownerDocument();
998     Frame* frame = document ? document->frame() : 0;
999     RefPtr<TypeBuilder::CSS::CSSStyleSheetHeader> result = TypeBuilder::CSS::CSSStyleSheetHeader::create()
1000         .setStyleSheetId(id())
1001         .setOrigin(m_origin)
1002         .setDisabled(styleSheet->disabled())
1003         .setSourceURL(finalURL())
1004         .setTitle(styleSheet->title())
1005         .setFrameId(m_pageAgent->frameId(frame));
1006
1007     return result.release();
1008 }
1009
1010 static PassRefPtr<TypeBuilder::Array<String> > selectorsFromSource(const CSSRuleSourceData* sourceData, const String& sheetText)
1011 {
1012     DEFINE_STATIC_LOCAL(RegularExpression, comment, ("/\\*[^]*?\\*/", TextCaseSensitive, MultilineEnabled));
1013     RefPtr<TypeBuilder::Array<String> > result = TypeBuilder::Array<String>::create();
1014     const SelectorRangeList& ranges = sourceData->selectorRanges;
1015     for (size_t i = 0, size = ranges.size(); i < size; ++i) {
1016         const SourceRange& range = ranges.at(i);
1017         String selector = sheetText.substring(range.start, range.length());
1018
1019         // We don't want to see any comments in the selector components, only the meaningful parts.
1020         replace(selector, comment, "");
1021         result->addItem(selector.stripWhiteSpace());
1022     }
1023     return result.release();
1024 }
1025
1026 PassRefPtr<TypeBuilder::CSS::SelectorList> InspectorStyleSheet::buildObjectForSelectorList(CSSStyleRule* rule)
1027 {
1028     RefPtr<CSSRuleSourceData> sourceData;
1029     if (ensureParsedDataReady())
1030         sourceData = ruleSourceDataFor(rule->style());
1031     RefPtr<TypeBuilder::Array<String> > selectors;
1032
1033     // This intentionally does not rely on the source data to avoid catching the trailing comments (before the declaration starting '{').
1034     String selectorText = rule->selectorText();
1035
1036     if (sourceData)
1037         selectors = selectorsFromSource(sourceData.get(), m_parsedStyleSheet->text());
1038     else {
1039         selectors = TypeBuilder::Array<String>::create();
1040         const CSSSelectorList& selectorList = rule->styleRule()->selectorList();
1041         for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(selector))
1042             selectors->addItem(selector->selectorText());
1043     }
1044     RefPtr<TypeBuilder::CSS::SelectorList> result = TypeBuilder::CSS::SelectorList::create()
1045         .setSelectors(selectors)
1046         .setText(selectorText)
1047         .release();
1048     if (sourceData)
1049         result->setRange(buildSourceRangeObject(sourceData->ruleHeaderRange, lineEndings().get()));
1050     return result.release();
1051 }
1052
1053 PassRefPtr<TypeBuilder::CSS::CSSRule> InspectorStyleSheet::buildObjectForRule(CSSStyleRule* rule)
1054 {
1055     CSSStyleSheet* styleSheet = pageStyleSheet();
1056     if (!styleSheet)
1057         return 0;
1058
1059     RefPtr<TypeBuilder::CSS::CSSRule> result = TypeBuilder::CSS::CSSRule::create()
1060         .setSelectorList(buildObjectForSelectorList(rule))
1061         .setSourceLine(rule->styleRule()->sourceLine())
1062         .setOrigin(m_origin)
1063         .setStyle(buildObjectForStyle(rule->style()));
1064
1065     // "sourceURL" is present only for regular rules, otherwise "origin" should be used in the frontend.
1066     if (m_origin == TypeBuilder::CSS::StyleSheetOrigin::Regular)
1067         result->setSourceURL(finalURL());
1068
1069     if (canBind()) {
1070         InspectorCSSId id(ruleId(rule));
1071         if (!id.isEmpty())
1072             result->setRuleId(id.asProtocolValue<TypeBuilder::CSS::CSSRuleId>());
1073     }
1074
1075     RefPtr<Array<TypeBuilder::CSS::CSSMedia> > mediaArray = Array<TypeBuilder::CSS::CSSMedia>::create();
1076
1077     fillMediaListChain(rule, mediaArray.get());
1078     if (mediaArray->length())
1079         result->setMedia(mediaArray.release());
1080
1081     return result.release();
1082 }
1083
1084 PassRefPtr<TypeBuilder::CSS::CSSStyle> InspectorStyleSheet::buildObjectForStyle(CSSStyleDeclaration* style)
1085 {
1086     RefPtr<CSSRuleSourceData> sourceData;
1087     if (ensureParsedDataReady())
1088         sourceData = ruleSourceDataFor(style);
1089
1090     InspectorCSSId id = ruleOrStyleId(style);
1091     if (id.isEmpty()) {
1092         RefPtr<TypeBuilder::CSS::CSSStyle> bogusStyle = TypeBuilder::CSS::CSSStyle::create()
1093             .setCssProperties(Array<TypeBuilder::CSS::CSSProperty>::create())
1094             .setShorthandEntries(Array<TypeBuilder::CSS::ShorthandEntry>::create());
1095         return bogusStyle.release();
1096     }
1097     RefPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id);
1098     RefPtr<TypeBuilder::CSS::CSSStyle> result = inspectorStyle->buildObjectForStyle();
1099
1100     // Style text cannot be retrieved without stylesheet, so set cssText here.
1101     if (sourceData) {
1102         String sheetText;
1103         bool success = getText(&sheetText);
1104         if (success) {
1105             const SourceRange& bodyRange = sourceData->ruleBodyRange;
1106             result->setCssText(sheetText.substring(bodyRange.start, bodyRange.end - bodyRange.start));
1107         }
1108     }
1109
1110     return result.release();
1111 }
1112
1113 bool InspectorStyleSheet::setStyleText(const InspectorCSSId& id, const String& text, String* oldText, ExceptionCode& ec)
1114 {
1115     RefPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id);
1116     if (!inspectorStyle) {
1117         ec = NOT_FOUND_ERR;
1118         return false;
1119     }
1120
1121     if (oldText && !inspectorStyle->getText(oldText))
1122         return false;
1123
1124     bool success = inspectorStyle->setText(text, ec);
1125     if (success)
1126         fireStyleSheetChanged();
1127     return success;
1128 }
1129
1130 bool InspectorStyleSheet::setPropertyText(const InspectorCSSId& id, unsigned propertyIndex, const String& text, bool overwrite, String* oldText, ExceptionCode& ec)
1131 {
1132     RefPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id);
1133     if (!inspectorStyle) {
1134         ec = NOT_FOUND_ERR;
1135         return false;
1136     }
1137
1138     bool success = inspectorStyle->setPropertyText(propertyIndex, text, overwrite, oldText, ec);
1139     if (success)
1140         fireStyleSheetChanged();
1141     return success;
1142 }
1143
1144 bool InspectorStyleSheet::toggleProperty(const InspectorCSSId& id, unsigned propertyIndex, bool disable, ExceptionCode& ec)
1145 {
1146     RefPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id);
1147     if (!inspectorStyle) {
1148         ec = NOT_FOUND_ERR;
1149         return false;
1150     }
1151
1152     bool success = inspectorStyle->toggleProperty(propertyIndex, disable, ec);
1153     if (success) {
1154         if (disable)
1155             rememberInspectorStyle(inspectorStyle);
1156         else if (!inspectorStyle->hasDisabledProperties())
1157             forgetInspectorStyle(inspectorStyle->cssStyle());
1158         fireStyleSheetChanged();
1159     }
1160     return success;
1161 }
1162
1163 bool InspectorStyleSheet::getText(String* result) const
1164 {
1165     if (!ensureText())
1166         return false;
1167     *result = m_parsedStyleSheet->text();
1168     return true;
1169 }
1170
1171 CSSStyleDeclaration* InspectorStyleSheet::styleForId(const InspectorCSSId& id) const
1172 {
1173     CSSStyleRule* rule = ruleForId(id);
1174     if (!rule)
1175         return 0;
1176
1177     return rule->style();
1178 }
1179
1180 void InspectorStyleSheet::fireStyleSheetChanged()
1181 {
1182     if (m_listener)
1183         m_listener->styleSheetChanged(this);
1184 }
1185
1186 PassRefPtr<InspectorStyle> InspectorStyleSheet::inspectorStyleForId(const InspectorCSSId& id)
1187 {
1188     CSSStyleDeclaration* style = styleForId(id);
1189     if (!style)
1190         return 0;
1191
1192     InspectorStyleMap::iterator it = m_inspectorStyles.find(style);
1193     if (it == m_inspectorStyles.end()) {
1194         RefPtr<InspectorStyle> inspectorStyle = InspectorStyle::create(id, style, this);
1195         return inspectorStyle.release();
1196     }
1197     return it->value;
1198 }
1199
1200 void InspectorStyleSheet::rememberInspectorStyle(RefPtr<InspectorStyle> inspectorStyle)
1201 {
1202     m_inspectorStyles.set(inspectorStyle->cssStyle(), inspectorStyle);
1203 }
1204
1205 void InspectorStyleSheet::forgetInspectorStyle(CSSStyleDeclaration* style)
1206 {
1207     m_inspectorStyles.remove(style);
1208 }
1209
1210 InspectorCSSId InspectorStyleSheet::ruleOrStyleId(CSSStyleDeclaration* style) const
1211 {
1212     unsigned index = ruleIndexByStyle(style);
1213     if (index != UINT_MAX)
1214         return InspectorCSSId(id(), index);
1215     return InspectorCSSId();
1216 }
1217
1218 Document* InspectorStyleSheet::ownerDocument() const
1219 {
1220     return m_pageStyleSheet->ownerDocument();
1221 }
1222
1223 RefPtr<CSSRuleSourceData> InspectorStyleSheet::ruleSourceDataFor(CSSStyleDeclaration* style) const
1224 {
1225     return m_parsedStyleSheet->ruleSourceDataAt(ruleIndexByStyle(style));
1226 }
1227
1228 PassOwnPtr<Vector<size_t> > InspectorStyleSheet::lineEndings() const
1229 {
1230     if (!m_parsedStyleSheet->hasText())
1231         return PassOwnPtr<Vector<size_t> >();
1232     return ContentSearchUtils::lineEndings(m_parsedStyleSheet->text());
1233 }
1234
1235 unsigned InspectorStyleSheet::ruleIndexByStyle(CSSStyleDeclaration* pageStyle) const
1236 {
1237     ensureFlatRules();
1238     unsigned index = 0;
1239     for (unsigned i = 0, size = m_flatRules.size(); i < size; ++i) {
1240         if (m_flatRules.at(i)->style() == pageStyle)
1241             return index;
1242
1243         ++index;
1244     }
1245     return UINT_MAX;
1246 }
1247
1248 bool InspectorStyleSheet::checkPageStyleSheet(ExceptionCode& ec) const
1249 {
1250     if (!m_pageStyleSheet) {
1251         ec = NOT_SUPPORTED_ERR;
1252         return false;
1253     }
1254     return true;
1255 }
1256
1257 bool InspectorStyleSheet::ensureParsedDataReady()
1258 {
1259     return ensureText() && ensureSourceData();
1260 }
1261
1262 bool InspectorStyleSheet::ensureText() const
1263 {
1264     if (!m_parsedStyleSheet)
1265         return false;
1266     if (m_parsedStyleSheet->hasText())
1267         return true;
1268
1269     String text;
1270     bool success = originalStyleSheetText(&text);
1271     if (success)
1272         m_parsedStyleSheet->setText(text);
1273     // No need to clear m_flatRules here - it's empty.
1274
1275     return success;
1276 }
1277
1278 bool InspectorStyleSheet::ensureSourceData()
1279 {
1280     if (m_parsedStyleSheet->hasSourceData())
1281         return true;
1282
1283     if (!m_parsedStyleSheet->hasText())
1284         return false;
1285
1286     RefPtr<StyleSheetContents> newStyleSheet = StyleSheetContents::create();
1287     OwnPtr<RuleSourceDataList> ruleSourceDataResult = adoptPtr(new RuleSourceDataList());
1288     createCSSParser(m_pageStyleSheet->ownerDocument())->parseSheet(newStyleSheet.get(), m_parsedStyleSheet->text(), 0, ruleSourceDataResult.get());
1289     m_parsedStyleSheet->setSourceData(ruleSourceDataResult.release());
1290     return m_parsedStyleSheet->hasSourceData();
1291 }
1292
1293 void InspectorStyleSheet::ensureFlatRules() const
1294 {
1295     // We are fine with redoing this for empty stylesheets as this will run fast.
1296     if (m_flatRules.isEmpty())
1297         collectFlatRules(asCSSRuleList(pageStyleSheet()), &m_flatRules);
1298 }
1299
1300 bool InspectorStyleSheet::setStyleText(CSSStyleDeclaration* style, const String& text, ExceptionCode& ec)
1301 {
1302     if (!m_pageStyleSheet)
1303         return false;
1304     if (!ensureParsedDataReady())
1305         return false;
1306
1307     String patchedStyleSheetText;
1308     bool success = styleSheetTextWithChangedStyle(style, text, &patchedStyleSheetText);
1309     if (!success)
1310         return false;
1311
1312     InspectorCSSId id = ruleOrStyleId(style);
1313     if (id.isEmpty())
1314         return false;
1315
1316     style->setCssText(text, ec);
1317     if (!ec)
1318         m_parsedStyleSheet->setText(patchedStyleSheetText);
1319
1320     return !ec;
1321 }
1322
1323 bool InspectorStyleSheet::styleSheetTextWithChangedStyle(CSSStyleDeclaration* style, const String& newStyleText, String* result)
1324 {
1325     if (!style)
1326         return false;
1327
1328     if (!ensureParsedDataReady())
1329         return false;
1330
1331     RefPtr<CSSRuleSourceData> sourceData = ruleSourceDataFor(style);
1332     unsigned bodyStart = sourceData->ruleBodyRange.start;
1333     unsigned bodyEnd = sourceData->ruleBodyRange.end;
1334     ASSERT(bodyStart <= bodyEnd);
1335
1336     String text = m_parsedStyleSheet->text();
1337     ASSERT_WITH_SECURITY_IMPLICATION(bodyEnd <= text.length()); // bodyEnd is exclusive
1338
1339     text.replace(bodyStart, bodyEnd - bodyStart, newStyleText);
1340     *result = text;
1341     return true;
1342 }
1343
1344 InspectorCSSId InspectorStyleSheet::ruleId(CSSStyleRule* rule) const
1345 {
1346     return ruleOrStyleId(rule->style());
1347 }
1348
1349 void InspectorStyleSheet::revalidateStyle(CSSStyleDeclaration* pageStyle)
1350 {
1351     if (m_isRevalidating)
1352         return;
1353
1354     m_isRevalidating = true;
1355     ensureFlatRules();
1356     for (unsigned i = 0, size = m_flatRules.size(); i < size; ++i) {
1357         CSSStyleRule* parsedRule = m_flatRules.at(i).get();
1358         if (parsedRule->style() == pageStyle) {
1359             if (parsedRule->styleRule()->properties().asText() != pageStyle->cssText()) {
1360                 // Clear the disabled properties for the invalid style here.
1361                 m_inspectorStyles.remove(pageStyle);
1362
1363                 ExceptionCode ec = 0;
1364                 setStyleText(pageStyle, pageStyle->cssText(), ec);
1365             }
1366             break;
1367         }
1368     }
1369     m_isRevalidating = false;
1370 }
1371
1372 bool InspectorStyleSheet::originalStyleSheetText(String* result) const
1373 {
1374     bool success = inlineStyleSheetText(result);
1375     if (!success)
1376         success = resourceStyleSheetText(result);
1377     return success;
1378 }
1379
1380 bool InspectorStyleSheet::resourceStyleSheetText(String* result) const
1381 {
1382     if (m_origin == TypeBuilder::CSS::StyleSheetOrigin::User || m_origin == TypeBuilder::CSS::StyleSheetOrigin::User_agent)
1383         return false;
1384
1385     if (!m_pageStyleSheet || !ownerDocument() || !ownerDocument()->frame())
1386         return false;
1387
1388     String error;
1389     bool base64Encoded;
1390     InspectorPageAgent::resourceContent(&error, ownerDocument()->frame(), KURL(ParsedURLString, m_pageStyleSheet->href()), result, &base64Encoded);
1391     return error.isEmpty() && !base64Encoded;
1392 }
1393
1394 bool InspectorStyleSheet::inlineStyleSheetText(String* result) const
1395 {
1396     if (!m_pageStyleSheet)
1397         return false;
1398
1399     Node* ownerNode = m_pageStyleSheet->ownerNode();
1400     if (!ownerNode || ownerNode->nodeType() != Node::ELEMENT_NODE)
1401         return false;
1402     Element* ownerElement = toElement(ownerNode);
1403
1404     if (!isHTMLStyleElement(ownerElement)
1405 #if ENABLE(SVG)
1406         && !ownerElement->hasTagName(SVGNames::styleTag)
1407 #endif
1408     )
1409         return false;
1410     *result = ownerElement->textContent();
1411     return true;
1412 }
1413
1414 PassRefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSRule> > InspectorStyleSheet::buildArrayForRuleList(CSSRuleList* ruleList)
1415 {
1416     RefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSRule> > result = TypeBuilder::Array<TypeBuilder::CSS::CSSRule>::create();
1417     if (!ruleList)
1418         return result.release();
1419
1420     RefPtr<CSSRuleList> refRuleList = ruleList;
1421     CSSStyleRuleVector rules;
1422     collectFlatRules(refRuleList, &rules);
1423
1424     for (unsigned i = 0, size = rules.size(); i < size; ++i)
1425         result->addItem(buildObjectForRule(rules.at(i).get()));
1426
1427     return result.release();
1428 }
1429
1430 void InspectorStyleSheet::collectFlatRules(PassRefPtr<CSSRuleList> ruleList, CSSStyleRuleVector* result)
1431 {
1432     if (!ruleList)
1433         return;
1434
1435     for (unsigned i = 0, size = ruleList->length(); i < size; ++i) {
1436         CSSRule* rule = ruleList->item(i);
1437         CSSStyleRule* styleRule = InspectorCSSAgent::asCSSStyleRule(rule);
1438         if (styleRule)
1439             result->append(styleRule);
1440         else {
1441             RefPtr<CSSRuleList> childRuleList = asCSSRuleList(rule);
1442             if (childRuleList)
1443                 collectFlatRules(childRuleList, result);
1444         }
1445     }
1446 }
1447
1448 PassRefPtr<InspectorStyleSheetForInlineStyle> InspectorStyleSheetForInlineStyle::create(InspectorPageAgent* pageAgent, const String& id, PassRefPtr<Element> element, TypeBuilder::CSS::StyleSheetOrigin::Enum origin, Listener* listener)
1449 {
1450     return adoptRef(new InspectorStyleSheetForInlineStyle(pageAgent, id, element, origin, listener));
1451 }
1452
1453 InspectorStyleSheetForInlineStyle::InspectorStyleSheetForInlineStyle(InspectorPageAgent* pageAgent, const String& id, PassRefPtr<Element> element, TypeBuilder::CSS::StyleSheetOrigin::Enum origin, Listener* listener)
1454     : InspectorStyleSheet(pageAgent, id, 0, origin, "", listener)
1455     , m_element(element)
1456     , m_ruleSourceData(0)
1457     , m_isStyleTextValid(false)
1458 {
1459     ASSERT(m_element);
1460     m_inspectorStyle = InspectorStyle::create(InspectorCSSId(id, 0), inlineStyle(), this);
1461     m_styleText = m_element->isStyledElement() ? m_element->getAttribute("style").string() : String();
1462 }
1463
1464 void InspectorStyleSheetForInlineStyle::didModifyElementAttribute()
1465 {
1466     m_isStyleTextValid = false;
1467     if (m_element->isStyledElement() && m_element->style() != m_inspectorStyle->cssStyle())
1468         m_inspectorStyle = InspectorStyle::create(InspectorCSSId(id(), 0), inlineStyle(), this);
1469     m_ruleSourceData.clear();
1470 }
1471
1472 bool InspectorStyleSheetForInlineStyle::getText(String* result) const
1473 {
1474     if (!m_isStyleTextValid) {
1475         m_styleText = elementStyleText();
1476         m_isStyleTextValid = true;
1477     }
1478     *result = m_styleText;
1479     return true;
1480 }
1481
1482 bool InspectorStyleSheetForInlineStyle::setStyleText(CSSStyleDeclaration* style, const String& text, ExceptionCode& ec)
1483 {
1484     ASSERT_UNUSED(style, style == inlineStyle());
1485
1486     {
1487         InspectorCSSAgent::InlineStyleOverrideScope overrideScope(m_element->document());
1488         m_element->setAttribute("style", text, ec);
1489     }
1490
1491     m_styleText = text;
1492     m_isStyleTextValid = true;
1493     m_ruleSourceData.clear();
1494     return !ec;
1495 }
1496
1497 PassOwnPtr<Vector<size_t> > InspectorStyleSheetForInlineStyle::lineEndings() const
1498 {
1499     return ContentSearchUtils::lineEndings(elementStyleText());
1500 }
1501
1502 Document* InspectorStyleSheetForInlineStyle::ownerDocument() const
1503 {
1504     return m_element->document();
1505 }
1506
1507 bool InspectorStyleSheetForInlineStyle::ensureParsedDataReady()
1508 {
1509     // The "style" property value can get changed indirectly, e.g. via element.style.borderWidth = "2px".
1510     const String& currentStyleText = elementStyleText();
1511     if (m_styleText != currentStyleText) {
1512         m_ruleSourceData.clear();
1513         m_styleText = currentStyleText;
1514         m_isStyleTextValid = true;
1515     }
1516
1517     if (m_ruleSourceData)
1518         return true;
1519
1520     m_ruleSourceData = CSSRuleSourceData::create(CSSRuleSourceData::STYLE_RULE);
1521     bool success = getStyleAttributeRanges(m_ruleSourceData.get());
1522     if (!success)
1523         return false;
1524
1525     return true;
1526 }
1527
1528 PassRefPtr<InspectorStyle> InspectorStyleSheetForInlineStyle::inspectorStyleForId(const InspectorCSSId& id)
1529 {
1530     ASSERT_UNUSED(id, !id.ordinal());
1531     return m_inspectorStyle;
1532 }
1533
1534 CSSStyleDeclaration* InspectorStyleSheetForInlineStyle::inlineStyle() const
1535 {
1536     return m_element->style();
1537 }
1538
1539 const String& InspectorStyleSheetForInlineStyle::elementStyleText() const
1540 {
1541     return m_element->getAttribute("style").string();
1542 }
1543
1544 bool InspectorStyleSheetForInlineStyle::getStyleAttributeRanges(CSSRuleSourceData* result) const
1545 {
1546     if (!m_element->isStyledElement())
1547         return false;
1548
1549     if (m_styleText.isEmpty()) {
1550         result->ruleBodyRange.start = 0;
1551         result->ruleBodyRange.end = 0;
1552         return true;
1553     }
1554
1555     RefPtr<MutableStylePropertySet> tempDeclaration = MutableStylePropertySet::create();
1556     createCSSParser(m_element->document())->parseDeclaration(tempDeclaration.get(), m_styleText, result, m_element->document()->elementSheet()->contents());
1557     return true;
1558 }
1559
1560 } // namespace WebCore
1561
1562 #endif // ENABLE(INSPECTOR)