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