c7c4edb77a3894b2b4be62e481d54e739ee4a2c7
[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 #include "InspectorStyleSheet.h"
27
28 #if ENABLE(INSPECTOR)
29
30 #include "CSSImportRule.h"
31 #include "CSSMediaRule.h"
32 #include "CSSParser.h"
33 #include "CSSPropertySourceData.h"
34 #include "CSSRule.h"
35 #include "CSSRuleList.h"
36 #include "CSSStyleRule.h"
37 #include "CSSStyleSelector.h"
38 #include "CSSStyleSheet.h"
39 #include "Document.h"
40 #include "Element.h"
41 #include "HTMLHeadElement.h"
42 #include "HTMLNames.h"
43 #include "HTMLParserIdioms.h"
44 #include "InspectorCSSAgent.h"
45 #include "InspectorPageAgent.h"
46 #include "InspectorValues.h"
47 #include "Node.h"
48 #include "SVGNames.h"
49 #include "StyleSheetList.h"
50 #include "WebKitCSSKeyframesRule.h"
51
52 #include <wtf/OwnPtr.h>
53 #include <wtf/PassOwnPtr.h>
54 #include <wtf/Vector.h>
55
56 class ParsedStyleSheet {
57 public:
58     typedef Vector<RefPtr<WebCore::CSSRuleSourceData> > SourceData;
59     ParsedStyleSheet();
60
61     WebCore::CSSStyleSheet* cssStyleSheet() const { return m_parserOutput; }
62     const String& text() const { return m_text; }
63     void setText(const String& text);
64     bool hasText() const { return m_hasText; }
65     SourceData* sourceData() const { return m_sourceData.get(); }
66     void setSourceData(PassOwnPtr<SourceData> sourceData);
67     bool hasSourceData() const { return m_sourceData; }
68     RefPtr<WebCore::CSSRuleSourceData> ruleSourceDataAt(unsigned index) const;
69
70 private:
71
72     // StyleSheet constructed while parsing m_text.
73     WebCore::CSSStyleSheet* m_parserOutput;
74     String m_text;
75     bool m_hasText;
76     OwnPtr<SourceData> m_sourceData;
77 };
78
79 ParsedStyleSheet::ParsedStyleSheet()
80     : m_parserOutput(0)
81     , m_hasText(false)
82 {
83 }
84
85 void ParsedStyleSheet::setText(const String& text)
86 {
87     m_hasText = true;
88     m_text = text;
89     setSourceData(nullptr);
90 }
91
92 void ParsedStyleSheet::setSourceData(PassOwnPtr<SourceData> sourceData)
93 {
94     m_sourceData = sourceData;
95 }
96
97 RefPtr<WebCore::CSSRuleSourceData> ParsedStyleSheet::ruleSourceDataAt(unsigned index) const
98 {
99     if (!hasSourceData() || index >= m_sourceData->size())
100         return 0;
101
102     return m_sourceData->at(index);
103 }
104
105 namespace WebCore {
106
107 enum MediaListSource {
108     MediaListSourceLinkedSheet,
109     MediaListSourceInlineSheet,
110     MediaListSourceMediaRule,
111     MediaListSourceImportRule
112 };
113
114 static PassRefPtr<InspectorObject> buildSourceRangeObject(const SourceRange& range)
115 {
116     RefPtr<InspectorObject> result = InspectorObject::create();
117     result->setNumber("start", range.start);
118     result->setNumber("end", range.end);
119     return result.release();
120 }
121
122 static PassRefPtr<InspectorObject> buildMediaObject(const MediaList* media, MediaListSource mediaListSource, const String& sourceURL)
123 {
124     RefPtr<InspectorObject> mediaObject = InspectorObject::create();
125     switch (mediaListSource) {
126     case MediaListSourceMediaRule:
127         mediaObject->setString("source", "mediaRule");
128         break;
129     case MediaListSourceImportRule:
130         mediaObject->setString("source", "importRule");
131         break;
132     case MediaListSourceLinkedSheet:
133         mediaObject->setString("source", "linkedSheet");
134         break;
135     case MediaListSourceInlineSheet:
136         mediaObject->setString("source", "inlineSheet");
137         break;
138     }
139     if (!sourceURL.isEmpty()) {
140         mediaObject->setString("sourceURL", sourceURL);
141         mediaObject->setNumber("sourceLine", media->lastLine());
142     }
143     mediaObject->setString("text", media->mediaText());
144     return mediaObject.release();
145 }
146
147 static PassRefPtr<CSSRuleList> asCSSRuleList(CSSStyleSheet* styleSheet)
148 {
149     if (!styleSheet)
150         return 0;
151
152     return CSSRuleList::create(styleSheet);
153 }
154
155 static PassRefPtr<CSSRuleList> asCSSRuleList(CSSRule* rule)
156 {
157     if (!rule)
158         return 0;
159
160     if (rule->isMediaRule())
161         return static_cast<CSSMediaRule*>(rule)->cssRules();
162
163     if (rule->isKeyframesRule())
164         return static_cast<WebKitCSSKeyframesRule*>(rule)->cssRules();
165
166     return 0;
167 }
168
169 static void fillMediaListChain(CSSRule* rule, InspectorArray* mediaArray)
170 {
171     MediaList* mediaList;
172     CSSRule* parentRule = rule;
173     String sourceURL;
174     while (parentRule) {
175         CSSStyleSheet* parentStyleSheet = 0;
176         bool isMediaRule = true;
177         if (parentRule->isMediaRule()) {
178             CSSMediaRule* mediaRule = static_cast<CSSMediaRule*>(parentRule);
179             mediaList = mediaRule->media();
180             parentStyleSheet = mediaRule->parentStyleSheet();
181         } else if (parentRule->isImportRule()) {
182             CSSImportRule* importRule = static_cast<CSSImportRule*>(parentRule);
183             mediaList = importRule->media();
184             parentStyleSheet = importRule->parentStyleSheet();
185             isMediaRule = false;
186         } else
187             mediaList = 0;
188
189         if (parentStyleSheet) {
190             sourceURL = parentStyleSheet->finalURL();
191             if (sourceURL.isEmpty())
192                 sourceURL = InspectorDOMAgent::documentURLString(parentStyleSheet->findDocument());
193         } else
194             sourceURL = "";
195
196         if (mediaList && mediaList->length())
197             mediaArray->pushObject(buildMediaObject(mediaList, isMediaRule ? MediaListSourceMediaRule : MediaListSourceImportRule, sourceURL));
198
199         if (parentRule->parentRule())
200             parentRule = parentRule->parentRule();
201         else {
202             CSSStyleSheet* styleSheet = parentRule->parentStyleSheet();
203             while (styleSheet) {
204                 mediaList = styleSheet->media();
205                 if (mediaList && mediaList->length()) {
206                     Document* doc = styleSheet->findDocument();
207                     if (doc)
208                         sourceURL = doc->url();
209                     else if (!styleSheet->finalURL().isEmpty())
210                         sourceURL = styleSheet->finalURL();
211                     else
212                         sourceURL = "";
213                     mediaArray->pushObject(buildMediaObject(mediaList, styleSheet->ownerNode() ? MediaListSourceLinkedSheet : MediaListSourceInlineSheet, sourceURL));
214                 }
215                 parentRule = styleSheet->ownerRule();
216                 if (parentRule)
217                     break;
218                 styleSheet = styleSheet->parentStyleSheet();
219             }
220         }
221     }
222 }
223
224 PassRefPtr<InspectorStyle> InspectorStyle::create(const InspectorCSSId& styleId, PassRefPtr<CSSStyleDeclaration> style, InspectorStyleSheet* parentStyleSheet)
225 {
226     return adoptRef(new InspectorStyle(styleId, style, parentStyleSheet));
227 }
228
229 InspectorStyle::InspectorStyle(const InspectorCSSId& styleId, PassRefPtr<CSSStyleDeclaration> style, InspectorStyleSheet* parentStyleSheet)
230     : m_styleId(styleId)
231     , m_style(style)
232     , m_parentStyleSheet(parentStyleSheet)
233     , m_formatAcquired(false)
234 {
235     ASSERT(m_style);
236 }
237
238 InspectorStyle::~InspectorStyle()
239 {
240 }
241
242 PassRefPtr<InspectorObject> InspectorStyle::buildObjectForStyle() const
243 {
244     RefPtr<InspectorObject> result = InspectorObject::create();
245     if (!m_styleId.isEmpty())
246         result->setValue("styleId", m_styleId.asInspectorValue());
247
248     result->setString("width", m_style->getPropertyValue("width"));
249     result->setString("height", m_style->getPropertyValue("height"));
250
251     RefPtr<CSSRuleSourceData> sourceData = m_parentStyleSheet ? m_parentStyleSheet->ruleSourceDataFor(m_style.get()) : 0;
252     if (sourceData)
253         result->setObject("range", buildSourceRangeObject(sourceData->styleSourceData->styleBodyRange));
254
255     populateObjectWithStyleProperties(result.get());
256
257     return result.release();
258 }
259
260 PassRefPtr<InspectorArray> InspectorStyle::buildArrayForComputedStyle() const
261 {
262     RefPtr<InspectorArray> result = InspectorArray::create();
263     Vector<InspectorStyleProperty> properties;
264     populateAllProperties(&properties);
265
266     for (Vector<InspectorStyleProperty>::iterator it = properties.begin(), itEnd = properties.end(); it != itEnd; ++it) {
267         const CSSPropertySourceData& propertyEntry = it->sourceData;
268         RefPtr<InspectorObject> entry = InspectorObject::create();
269         entry->setString("name", propertyEntry.name);
270         entry->setString("value", propertyEntry.value);
271         result->pushObject(entry);
272     }
273
274     return result.release();
275 }
276
277 // This method does the following preprocessing of |propertyText| with |overwrite| == false and |index| past the last active property:
278 // - If the last property (if present) has no closing ";", the ";" is prepended to the current |propertyText| value.
279 // - A heuristic formatting is attempted to retain the style structure.
280 //
281 // The propertyText (if not empty) is checked to be a valid style declaration (containing at least one property). If not,
282 // the method returns false (denoting an error).
283 bool InspectorStyle::setPropertyText(ErrorString* errorString, unsigned index, const String& propertyText, bool overwrite, String* oldText)
284 {
285     ASSERT(m_parentStyleSheet);
286     DEFINE_STATIC_LOCAL(String, bogusPropertyName, ("-webkit-boguz-propertee"));
287
288     if (!m_parentStyleSheet->ensureParsedDataReady()) {
289         *errorString = "Internal error: no stylesheet parsed data available";
290         return false;
291     }
292
293     Vector<InspectorStyleProperty> allProperties;
294     populateAllProperties(&allProperties);
295
296     if (propertyText.stripWhiteSpace().length()) {
297         RefPtr<StylePropertySet> tempMutableStyle = StylePropertySet::create();
298         RefPtr<CSSStyleSourceData> sourceData = CSSStyleSourceData::create();
299         CSSParser p;
300         p.parseDeclaration(tempMutableStyle.get(), propertyText + " " + bogusPropertyName + ": none", &sourceData, m_style->parentStyleSheet());
301         Vector<CSSPropertySourceData>& propertyData = sourceData->propertyData;
302         unsigned propertyCount = propertyData.size();
303
304         // At least one property + the bogus property added just above should be present.
305         if (propertyCount < 2) {
306             *errorString = "Invalid property value";
307             return false;
308         }
309
310         // Check for a proper propertyText termination (the parser could at least restore to the PROPERTY_NAME state).
311         if (propertyData.at(propertyCount - 1).name != bogusPropertyName) {
312             *errorString = "Invalid property value";
313             return false;
314         }
315     }
316
317     RefPtr<CSSRuleSourceData> sourceData = m_parentStyleSheet->ruleSourceDataFor(m_style.get());
318     if (!sourceData) {
319         *errorString = "Internal error: no CSS rule source found";
320         return false;
321     }
322
323     String text;
324     bool success = styleText(&text);
325     if (!success) {
326         *errorString = "Internal error: could not fetch style text";
327         return false;
328     }
329
330     InspectorStyleTextEditor editor(&allProperties, &m_disabledProperties, text, newLineAndWhitespaceDelimiters());
331     if (overwrite) {
332         *oldText = allProperties.at(index).rawText;
333         editor.replaceProperty(index, propertyText);
334     } else
335         editor.insertProperty(index, propertyText, sourceData->styleSourceData->styleBodyRange.length());
336
337     return applyStyleText(editor.styleText());
338 }
339
340 bool InspectorStyle::toggleProperty(ErrorString* errorString, unsigned index, bool disable)
341 {
342     ASSERT(m_parentStyleSheet);
343     if (!m_parentStyleSheet->ensureParsedDataReady()) {
344         *errorString = "Can toggle only source-based properties";
345         return false;
346     }
347
348     RefPtr<CSSRuleSourceData> sourceData = m_parentStyleSheet->ruleSourceDataFor(m_style.get());
349     if (!sourceData) {
350         *errorString = "Internal error: No source data for the style found";
351         return false;
352     }
353
354     String text;
355     bool success = styleText(&text);
356     if (!success) {
357         *errorString = "Internal error: could not fetch style text";
358         return false;
359     }
360
361     Vector<InspectorStyleProperty> allProperties;
362     populateAllProperties(&allProperties);
363     if (index >= allProperties.size()) {
364         *errorString = "Property index is outside of property range";
365         return false;
366     }
367
368     InspectorStyleProperty& property = allProperties.at(index);
369     if (property.disabled == disable)
370         return true; // Idempotent operation.
371
372     InspectorStyleTextEditor editor(&allProperties, &m_disabledProperties, text, newLineAndWhitespaceDelimiters());
373     if (disable)
374         editor.disableProperty(index);
375     else
376         editor.enableProperty(index);
377
378     return applyStyleText(editor.styleText());
379 }
380
381 bool InspectorStyle::styleText(String* result) const
382 {
383     // Precondition: m_parentStyleSheet->ensureParsedDataReady() has been called successfully.
384     RefPtr<CSSRuleSourceData> sourceData = m_parentStyleSheet->ruleSourceDataFor(m_style.get());
385     if (!sourceData)
386         return false;
387
388     String styleSheetText;
389     bool success = m_parentStyleSheet->getText(&styleSheetText);
390     if (!success)
391         return false;
392
393     SourceRange& bodyRange = sourceData->styleSourceData->styleBodyRange;
394     *result = styleSheetText.substring(bodyRange.start, bodyRange.end - bodyRange.start);
395     return true;
396 }
397
398 bool InspectorStyle::populateAllProperties(Vector<InspectorStyleProperty>* result) const
399 {
400     HashSet<String> foundShorthands;
401     HashSet<String> sourcePropertyNames;
402     unsigned disabledIndex = 0;
403     unsigned disabledLength = m_disabledProperties.size();
404     InspectorStyleProperty disabledProperty;
405     if (disabledIndex < disabledLength)
406         disabledProperty = m_disabledProperties.at(disabledIndex);
407
408     RefPtr<CSSRuleSourceData> sourceData = (m_parentStyleSheet && m_parentStyleSheet->ensureParsedDataReady()) ? m_parentStyleSheet->ruleSourceDataFor(m_style.get()) : 0;
409     Vector<CSSPropertySourceData>* sourcePropertyData = sourceData ? &(sourceData->styleSourceData->propertyData) : 0;
410     if (sourcePropertyData) {
411         String styleDeclaration;
412         bool isStyleTextKnown = styleText(&styleDeclaration);
413         ASSERT_UNUSED(isStyleTextKnown, isStyleTextKnown);
414         for (Vector<CSSPropertySourceData>::const_iterator it = sourcePropertyData->begin(); it != sourcePropertyData->end(); ++it) {
415             while (disabledIndex < disabledLength && disabledProperty.sourceData.range.start <= it->range.start) {
416                 result->append(disabledProperty);
417                 if (++disabledIndex < disabledLength)
418                     disabledProperty = m_disabledProperties.at(disabledIndex);
419             }
420             InspectorStyleProperty p(*it, true, false);
421             p.setRawTextFromStyleDeclaration(styleDeclaration);
422             result->append(p);
423             sourcePropertyNames.add(it->name.lower());
424         }
425     }
426
427     while (disabledIndex < disabledLength) {
428         disabledProperty = m_disabledProperties.at(disabledIndex++);
429         result->append(disabledProperty);
430     }
431
432     for (int i = 0, size = m_style->length(); i < size; ++i) {
433         String name = m_style->item(i);
434         if (sourcePropertyNames.contains(name.lower()))
435             continue;
436
437         sourcePropertyNames.add(name.lower());
438         result->append(InspectorStyleProperty(CSSPropertySourceData(name, m_style->getPropertyValue(name), !m_style->getPropertyPriority(name).isEmpty(), true, SourceRange()), false, false));
439     }
440
441     return true;
442 }
443
444 void InspectorStyle::populateObjectWithStyleProperties(InspectorObject* result) const
445 {
446     Vector<InspectorStyleProperty> properties;
447     populateAllProperties(&properties);
448
449     RefPtr<InspectorArray> propertiesObject = InspectorArray::create();
450     RefPtr<InspectorArray> shorthandEntries = InspectorArray::create();
451     HashMap<String, RefPtr<InspectorObject> > propertyNameToPreviousActiveProperty;
452     HashSet<String> foundShorthands;
453
454     for (Vector<InspectorStyleProperty>::iterator it = properties.begin(), itEnd = properties.end(); it != itEnd; ++it) {
455         const CSSPropertySourceData& propertyEntry = it->sourceData;
456         const String& name = propertyEntry.name;
457
458         RefPtr<InspectorObject> property = InspectorObject::create();
459         propertiesObject->pushObject(property);
460         String status = it->disabled ? "disabled" : "active";
461
462         // Default "parsedOk" == true.
463         if (!propertyEntry.parsedOk)
464             property->setBoolean("parsedOk", false);
465         if (it->hasRawText())
466             property->setString("text", it->rawText);
467         property->setString("name", name);
468         property->setString("value", propertyEntry.value);
469
470         // Default "priority" == "".
471         if (propertyEntry.important)
472             property->setString("priority", "important");
473         if (!it->disabled) {
474             if (it->hasSource) {
475                 property->setBoolean("implicit", false);
476                 property->setObject("range", buildSourceRangeObject(propertyEntry.range));
477
478                 // Parsed property overrides any property with the same name. Non-parsed property overrides
479                 // previous non-parsed property with the same name (if any).
480                 bool shouldInactivate = false;
481                 HashMap<String, RefPtr<InspectorObject> >::iterator activeIt = propertyNameToPreviousActiveProperty.find(name);
482                 if (activeIt != propertyNameToPreviousActiveProperty.end()) {
483                     if (propertyEntry.parsedOk)
484                         shouldInactivate = true;
485                     else {
486                         bool previousParsedOk;
487                         bool success = activeIt->second->getBoolean("parsedOk", &previousParsedOk);
488                         if (success && !previousParsedOk)
489                             shouldInactivate = true;
490                     }
491                 } else
492                     propertyNameToPreviousActiveProperty.set(name, property);
493
494                 if (shouldInactivate) {
495                     activeIt->second->setString("status", "inactive");
496                     activeIt->second->remove("shorthandName");
497                     propertyNameToPreviousActiveProperty.set(name, property);
498                 }
499             } else {
500                 bool implicit = m_style->isPropertyImplicit(name);
501                 // Default "implicit" == false.
502                 if (implicit)
503                     property->setBoolean("implicit", true);
504                 status = "";
505             }
506         }
507
508         // Default "status" == "style".
509         if (!status.isEmpty())
510             property->setString("status", status);
511
512         if (propertyEntry.parsedOk) {
513             // Both for style-originated and parsed source properties.
514             String shorthand = m_style->getPropertyShorthand(name);
515             if (!shorthand.isEmpty()) {
516                 // Default "shorthandName" == "".
517                 property->setString("shorthandName", shorthand);
518                 if (!foundShorthands.contains(shorthand)) {
519                     foundShorthands.add(shorthand);
520                     RefPtr<InspectorObject> shorthandEntry = InspectorObject::create();
521                     shorthandEntry->setString("name", shorthand);
522                     shorthandEntry->setString("value", shorthandValue(shorthand));
523                     shorthandEntries->pushObject(shorthandEntry.release());
524                 }
525             }
526         }
527         // else shorthandName is not set
528     }
529
530     result->setArray("cssProperties", propertiesObject);
531     result->setArray("shorthandEntries", shorthandEntries);
532 }
533
534 bool InspectorStyle::applyStyleText(const String& text)
535 {
536     return m_parentStyleSheet->setStyleText(m_style.get(), text);
537 }
538
539 String InspectorStyle::shorthandValue(const String& shorthandProperty) const
540 {
541     String value = m_style->getPropertyValue(shorthandProperty);
542     if (value.isEmpty()) {
543         for (unsigned i = 0; i < m_style->length(); ++i) {
544             String individualProperty = m_style->item(i);
545             if (m_style->getPropertyShorthand(individualProperty) != shorthandProperty)
546                 continue;
547             if (m_style->isPropertyImplicit(individualProperty))
548                 continue;
549             String individualValue = m_style->getPropertyValue(individualProperty);
550             if (individualValue == "initial")
551                 continue;
552             if (value.length())
553                 value.append(" ");
554             value.append(individualValue);
555         }
556     }
557     return value;
558 }
559
560 String InspectorStyle::shorthandPriority(const String& shorthandProperty) const
561 {
562     String priority = m_style->getPropertyPriority(shorthandProperty);
563     if (priority.isEmpty()) {
564         for (unsigned i = 0; i < m_style->length(); ++i) {
565             String individualProperty = m_style->item(i);
566             if (m_style->getPropertyShorthand(individualProperty) != shorthandProperty)
567                 continue;
568             priority = m_style->getPropertyPriority(individualProperty);
569             break;
570         }
571     }
572     return priority;
573 }
574
575 Vector<String> InspectorStyle::longhandProperties(const String& shorthandProperty) const
576 {
577     Vector<String> properties;
578     HashSet<String> foundProperties;
579     for (unsigned i = 0; i < m_style->length(); ++i) {
580         String individualProperty = m_style->item(i);
581         if (foundProperties.contains(individualProperty) || m_style->getPropertyShorthand(individualProperty) != shorthandProperty)
582             continue;
583
584         foundProperties.add(individualProperty);
585         properties.append(individualProperty);
586     }
587     return properties;
588 }
589
590 NewLineAndWhitespace& InspectorStyle::newLineAndWhitespaceDelimiters() const
591 {
592     DEFINE_STATIC_LOCAL(String, defaultPrefix, ("    "));
593
594     if (m_formatAcquired)
595         return m_format;
596
597     RefPtr<CSSRuleSourceData> sourceData = (m_parentStyleSheet && m_parentStyleSheet->ensureParsedDataReady()) ? m_parentStyleSheet->ruleSourceDataFor(m_style.get()) : 0;
598     Vector<CSSPropertySourceData>* sourcePropertyData = sourceData ? &(sourceData->styleSourceData->propertyData) : 0;
599     int propertyCount;
600     if (!sourcePropertyData || !(propertyCount = sourcePropertyData->size())) {
601         m_format.first = "\n";
602         m_format.second = defaultPrefix;
603         return m_format; // Do not remember the default formatting and attempt to acquire it later.
604     }
605
606     String text;
607     bool success = styleText(&text);
608     ASSERT_UNUSED(success, success);
609
610     m_formatAcquired = true;
611
612     String formatLineFeed = "";
613     String formatPropertyPrefix = "";
614     String prefix;
615     String candidatePrefix = defaultPrefix;
616     int scanStart = 0;
617     int propertyIndex = 0;
618     bool isFullPrefixScanned = false;
619     bool lineFeedTerminated = false;
620     const UChar* characters = text.characters();
621     while (propertyIndex < propertyCount) {
622         const WebCore::CSSPropertySourceData& currentProperty = sourcePropertyData->at(propertyIndex++);
623
624         bool processNextProperty = false;
625         int scanEnd = currentProperty.range.start;
626         for (int i = scanStart; i < scanEnd; ++i) {
627             UChar ch = characters[i];
628             bool isLineFeed = isHTMLLineBreak(ch);
629             if (isLineFeed) {
630                 if (!lineFeedTerminated)
631                     formatLineFeed.append(ch);
632             } else if (isHTMLSpace(ch))
633                 prefix.append(ch);
634             else {
635                 candidatePrefix = prefix;
636                 prefix = "";
637                 scanStart = currentProperty.range.end;
638                 ++propertyIndex;
639                 processNextProperty = true;
640                 break;
641             }
642             if (!isLineFeed && formatLineFeed.length())
643                 lineFeedTerminated = true;
644         }
645         if (!processNextProperty) {
646             isFullPrefixScanned = true;
647             break;
648         }
649     }
650
651     m_format.first = formatLineFeed;
652     m_format.second = isFullPrefixScanned ? prefix : candidatePrefix;
653     return m_format;
654 }
655
656 PassRefPtr<InspectorStyleSheet> InspectorStyleSheet::create(const String& id, PassRefPtr<CSSStyleSheet> pageStyleSheet, const String& origin, const String& documentURL)
657 {
658     return adoptRef(new InspectorStyleSheet(id, pageStyleSheet, origin, documentURL));
659 }
660
661 // static
662 String InspectorStyleSheet::styleSheetURL(CSSStyleSheet* pageStyleSheet)
663 {
664     if (pageStyleSheet && !pageStyleSheet->finalURL().isEmpty())
665         return pageStyleSheet->finalURL().string();
666     return emptyString();
667 }
668
669 InspectorStyleSheet::InspectorStyleSheet(const String& id, PassRefPtr<CSSStyleSheet> pageStyleSheet, const String& origin, const String& documentURL)
670     : m_id(id)
671     , m_pageStyleSheet(pageStyleSheet)
672     , m_origin(origin)
673     , m_documentURL(documentURL)
674     , m_isRevalidating(false)
675 {
676     m_parsedStyleSheet = new ParsedStyleSheet();
677 }
678
679 InspectorStyleSheet::~InspectorStyleSheet()
680 {
681     delete m_parsedStyleSheet;
682 }
683
684 String InspectorStyleSheet::finalURL() const
685 {
686     String url = styleSheetURL(m_pageStyleSheet.get());
687     return url.isEmpty() ? m_documentURL : url;
688 }
689
690 void InspectorStyleSheet::reparseStyleSheet(const String& text)
691 {
692     for (unsigned i = 0, size = m_pageStyleSheet->length(); i < size; ++i)
693         m_pageStyleSheet->remove(0);
694     m_pageStyleSheet->parseString(text, m_pageStyleSheet->useStrictParsing());
695     m_pageStyleSheet->styleSheetChanged();
696     m_inspectorStyles.clear();
697 }
698
699 bool InspectorStyleSheet::setText(const String& text)
700 {
701     if (!m_parsedStyleSheet)
702         return false;
703
704     m_parsedStyleSheet->setText(text);
705     m_flatRules.clear();
706
707     return true;
708 }
709
710 bool InspectorStyleSheet::setRuleSelector(const InspectorCSSId& id, const String& selector)
711 {
712     CSSStyleRule* rule = ruleForId(id);
713     if (!rule)
714         return false;
715     CSSStyleSheet* styleSheet = rule->parentStyleSheet();
716     if (!styleSheet || !ensureParsedDataReady())
717         return false;
718
719     rule->setSelectorText(selector);
720     RefPtr<CSSRuleSourceData> sourceData = ruleSourceDataFor(rule->style());
721     if (!sourceData)
722         return false;
723
724     String sheetText = m_parsedStyleSheet->text();
725     sheetText.replace(sourceData->selectorListRange.start, sourceData->selectorListRange.end - sourceData->selectorListRange.start, selector);
726     m_parsedStyleSheet->setText(sheetText);
727     return true;
728 }
729
730 CSSStyleRule* InspectorStyleSheet::addRule(const String& selector)
731 {
732     String styleSheetText;
733     bool success = getText(&styleSheetText);
734     if (!success)
735         return 0;
736
737     ExceptionCode ec = 0;
738     m_pageStyleSheet->addRule(selector, "", ec);
739     if (ec)
740         return 0;
741     RefPtr<CSSRuleList> rules = CSSRuleList::create(m_pageStyleSheet.get());
742     ASSERT(rules->length());
743     CSSStyleRule* rule = InspectorCSSAgent::asCSSStyleRule(rules->item(rules->length() - 1));
744     ASSERT(rule);
745
746     if (styleSheetText.length())
747         styleSheetText += "\n";
748
749     styleSheetText += selector;
750     styleSheetText += " {}";
751     // Using setText() as this operation changes the style sheet rule set.
752     setText(styleSheetText);
753
754     return rule;
755 }
756
757 CSSStyleRule* InspectorStyleSheet::ruleForId(const InspectorCSSId& id) const
758 {
759     if (!m_pageStyleSheet)
760         return 0;
761
762     ASSERT(!id.isEmpty());
763     ensureFlatRules();
764     return id.ordinal() >= m_flatRules.size() ? 0 : m_flatRules.at(id.ordinal());
765
766 }
767
768 PassRefPtr<InspectorObject> InspectorStyleSheet::buildObjectForStyleSheet()
769 {
770     CSSStyleSheet* styleSheet = pageStyleSheet();
771     if (!styleSheet)
772         return 0;
773
774     RefPtr<InspectorObject> result = InspectorObject::create();
775     result->setString("styleSheetId", id());
776     RefPtr<CSSRuleList> cssRuleList = CSSRuleList::create(styleSheet);
777     RefPtr<InspectorArray> cssRules = buildArrayForRuleList(cssRuleList.get());
778     result->setArray("rules", cssRules.release());
779
780     String styleSheetText;
781     bool success = getText(&styleSheetText);
782     if (success)
783         result->setString("text", styleSheetText);
784
785     return result.release();
786 }
787
788 PassRefPtr<InspectorObject> InspectorStyleSheet::buildObjectForStyleSheetInfo()
789 {
790     CSSStyleSheet* styleSheet = pageStyleSheet();
791     if (!styleSheet)
792         return 0;
793
794     RefPtr<InspectorObject> result = InspectorObject::create();
795     result->setString("styleSheetId", id());
796     result->setBoolean("disabled", styleSheet->disabled());
797     result->setString("sourceURL", finalURL());
798     result->setString("title", styleSheet->title());
799     return result.release();
800 }
801
802 PassRefPtr<InspectorObject> InspectorStyleSheet::buildObjectForRule(CSSStyleRule* rule)
803 {
804     CSSStyleSheet* styleSheet = pageStyleSheet();
805     if (!styleSheet)
806         return 0;
807
808     RefPtr<InspectorObject> result = InspectorObject::create();
809     result->setString("selectorText", rule->selectorText());
810     // "sourceURL" is present only for regular rules, otherwise "origin" should be used in the frontend.
811     if (m_origin == "regular")
812         result->setString("sourceURL", finalURL());
813     result->setNumber("sourceLine", rule->sourceLine());
814     result->setString("origin", m_origin);
815
816     result->setObject("style", buildObjectForStyle(rule->style()));
817     if (canBind()) {
818         InspectorCSSId id(ruleId(rule));
819         if (!id.isEmpty())
820             result->setValue("ruleId", id.asInspectorValue());
821     }
822
823     RefPtr<CSSRuleSourceData> sourceData;
824     if (ensureParsedDataReady())
825         sourceData = ruleSourceDataFor(rule->style());
826     if (sourceData) {
827         RefPtr<InspectorObject> selectorRange = InspectorObject::create();
828         selectorRange->setNumber("start", sourceData->selectorListRange.start);
829         selectorRange->setNumber("end", sourceData->selectorListRange.end);
830         result->setObject("selectorRange", selectorRange.release());
831     }
832
833     RefPtr<InspectorArray> mediaArray = InspectorArray::create();
834
835     fillMediaListChain(rule, mediaArray.get());
836     if (mediaArray->length())
837         result->setArray("media", mediaArray.release());
838
839     return result.release();
840 }
841
842 PassRefPtr<InspectorObject> InspectorStyleSheet::buildObjectForStyle(CSSStyleDeclaration* style)
843 {
844     RefPtr<CSSRuleSourceData> sourceData;
845     if (ensureParsedDataReady())
846         sourceData = ruleSourceDataFor(style);
847
848     InspectorCSSId id = ruleOrStyleId(style);
849     if (id.isEmpty()) {
850         RefPtr<InspectorObject> bogusStyle = InspectorObject::create();
851         bogusStyle->setArray("cssProperties", InspectorArray::create());
852         bogusStyle->setObject("shorthandEntries", InspectorObject::create());
853         return bogusStyle.release();
854     }
855     RefPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id);
856     RefPtr<InspectorObject> result = inspectorStyle->buildObjectForStyle();
857
858     // Style text cannot be retrieved without stylesheet, so set cssText here.
859     if (sourceData) {
860         String sheetText;
861         bool success = getText(&sheetText);
862         if (success) {
863             const SourceRange& bodyRange = sourceData->styleSourceData->styleBodyRange;
864             result->setString("cssText", sheetText.substring(bodyRange.start, bodyRange.end - bodyRange.start));
865         }
866     }
867
868     return result.release();
869 }
870
871 bool InspectorStyleSheet::setPropertyText(ErrorString* errorString, const InspectorCSSId& id, unsigned propertyIndex, const String& text, bool overwrite, String* oldText)
872 {
873     RefPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id);
874     if (!inspectorStyle) {
875         *errorString = "No style found for given id";
876         return false;
877     }
878
879     return inspectorStyle->setPropertyText(errorString, propertyIndex, text, overwrite, oldText);
880 }
881
882 bool InspectorStyleSheet::toggleProperty(ErrorString* errorString, const InspectorCSSId& id, unsigned propertyIndex, bool disable)
883 {
884     RefPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id);
885     if (!inspectorStyle) {
886         *errorString = "No style found for given id";
887         return false;
888     }
889
890     bool success = inspectorStyle->toggleProperty(errorString, propertyIndex, disable);
891     if (success) {
892         if (disable)
893             rememberInspectorStyle(inspectorStyle);
894         else if (!inspectorStyle->hasDisabledProperties())
895             forgetInspectorStyle(inspectorStyle->cssStyle());
896     }
897     return success;
898 }
899
900 bool InspectorStyleSheet::getText(String* result) const
901 {
902     if (!ensureText())
903         return false;
904     *result = m_parsedStyleSheet->text();
905     return true;
906 }
907
908 CSSStyleDeclaration* InspectorStyleSheet::styleForId(const InspectorCSSId& id) const
909 {
910     CSSStyleRule* rule = ruleForId(id);
911     if (!rule)
912         return 0;
913
914     return rule->style();
915 }
916
917 PassRefPtr<InspectorStyle> InspectorStyleSheet::inspectorStyleForId(const InspectorCSSId& id)
918 {
919     CSSStyleDeclaration* style = styleForId(id);
920     if (!style)
921         return 0;
922
923     InspectorStyleMap::iterator it = m_inspectorStyles.find(style);
924     if (it == m_inspectorStyles.end()) {
925         RefPtr<InspectorStyle> inspectorStyle = InspectorStyle::create(id, style, this);
926         return inspectorStyle.release();
927     }
928     return it->second;
929 }
930
931 void InspectorStyleSheet::rememberInspectorStyle(RefPtr<InspectorStyle> inspectorStyle)
932 {
933     m_inspectorStyles.set(inspectorStyle->cssStyle(), inspectorStyle);
934 }
935
936 void InspectorStyleSheet::forgetInspectorStyle(CSSStyleDeclaration* style)
937 {
938     m_inspectorStyles.remove(style);
939 }
940
941 InspectorCSSId InspectorStyleSheet::ruleOrStyleId(CSSStyleDeclaration* style) const
942 {
943     unsigned index = ruleIndexByStyle(style);
944     if (index != UINT_MAX)
945         return InspectorCSSId(id(), index);
946     return InspectorCSSId();
947 }
948
949 Document* InspectorStyleSheet::ownerDocument() const
950 {
951     return m_pageStyleSheet->findDocument();
952 }
953
954 RefPtr<CSSRuleSourceData> InspectorStyleSheet::ruleSourceDataFor(CSSStyleDeclaration* style) const
955 {
956     return m_parsedStyleSheet->ruleSourceDataAt(ruleIndexByStyle(style));
957 }
958
959 unsigned InspectorStyleSheet::ruleIndexByStyle(CSSStyleDeclaration* pageStyle) const
960 {
961     ensureFlatRules();
962     unsigned index = 0;
963     for (unsigned i = 0, size = m_flatRules.size(); i < size; ++i) {
964         if (m_flatRules.at(i)->style() == pageStyle)
965             return index;
966
967         ++index;
968     }
969     return UINT_MAX;
970 }
971
972 bool InspectorStyleSheet::ensureParsedDataReady()
973 {
974     return ensureText() && ensureSourceData();
975 }
976
977 bool InspectorStyleSheet::ensureText() const
978 {
979     if (!m_parsedStyleSheet)
980         return false;
981     if (m_parsedStyleSheet->hasText())
982         return true;
983
984     String text;
985     bool success = originalStyleSheetText(&text);
986     if (success)
987         m_parsedStyleSheet->setText(text);
988     // No need to clear m_flatRules here - it's empty.
989
990     return success;
991 }
992
993 bool InspectorStyleSheet::ensureSourceData()
994 {
995     if (m_parsedStyleSheet->hasSourceData())
996         return true;
997
998     if (!m_parsedStyleSheet->hasText())
999         return false;
1000
1001     RefPtr<CSSStyleSheet> newStyleSheet = CSSStyleSheet::create();
1002     CSSParser p;
1003     StyleRuleRangeMap ruleRangeMap;
1004     p.parseSheet(newStyleSheet.get(), m_parsedStyleSheet->text(), 0, &ruleRangeMap);
1005     OwnPtr<ParsedStyleSheet::SourceData> rangesVector(adoptPtr(new ParsedStyleSheet::SourceData));
1006
1007     Vector<CSSStyleRule*> rules;
1008     RefPtr<CSSRuleList> ruleList = asCSSRuleList(newStyleSheet.get());
1009     collectFlatRules(ruleList, &rules);
1010     for (unsigned i = 0, size = rules.size(); i < size; ++i) {
1011         StyleRuleRangeMap::iterator it = ruleRangeMap.find(rules.at(i));
1012         if (it != ruleRangeMap.end()) {
1013             fixUnparsedPropertyRanges(it->second.get(), m_parsedStyleSheet->text());
1014             rangesVector->append(it->second);
1015         }
1016     }
1017
1018     m_parsedStyleSheet->setSourceData(rangesVector.release());
1019     return m_parsedStyleSheet->hasSourceData();
1020 }
1021
1022 void InspectorStyleSheet::ensureFlatRules() const
1023 {
1024     // We are fine with redoing this for empty stylesheets as this will run fast.
1025     if (m_flatRules.isEmpty())
1026         collectFlatRules(asCSSRuleList(pageStyleSheet()), &m_flatRules);
1027 }
1028
1029 bool InspectorStyleSheet::setStyleText(CSSStyleDeclaration* style, const String& text)
1030 {
1031     if (!pageStyleSheet())
1032         return false;
1033     if (!ensureParsedDataReady())
1034         return false;
1035
1036     String patchedStyleSheetText;
1037     bool success = styleSheetTextWithChangedStyle(style, text, &patchedStyleSheetText);
1038     if (!success)
1039         return false;
1040
1041     InspectorCSSId id = ruleOrStyleId(style);
1042     if (id.isEmpty())
1043         return false;
1044
1045     ExceptionCode ec = 0;
1046     style->setCssText(text, ec);
1047     if (!ec)
1048         m_parsedStyleSheet->setText(patchedStyleSheetText);
1049
1050     return !ec;
1051 }
1052
1053 bool InspectorStyleSheet::styleSheetTextWithChangedStyle(CSSStyleDeclaration* style, const String& newStyleText, String* result)
1054 {
1055     if (!style)
1056         return false;
1057
1058     if (!ensureParsedDataReady())
1059         return false;
1060
1061     RefPtr<CSSRuleSourceData> sourceData = ruleSourceDataFor(style);
1062     unsigned bodyStart = sourceData->styleSourceData->styleBodyRange.start;
1063     unsigned bodyEnd = sourceData->styleSourceData->styleBodyRange.end;
1064     ASSERT(bodyStart <= bodyEnd);
1065
1066     String text = m_parsedStyleSheet->text();
1067     ASSERT(bodyEnd <= text.length()); // bodyEnd is exclusive
1068
1069     text.replace(bodyStart, bodyEnd - bodyStart, newStyleText);
1070     *result = text;
1071     return true;
1072 }
1073
1074 InspectorCSSId InspectorStyleSheet::ruleId(CSSStyleRule* rule) const
1075 {
1076     return ruleOrStyleId(rule->style());
1077 }
1078
1079 void InspectorStyleSheet::revalidateStyle(CSSStyleDeclaration* pageStyle)
1080 {
1081     if (m_isRevalidating)
1082         return;
1083
1084     m_isRevalidating = true;
1085     ensureFlatRules();
1086     for (unsigned i = 0, size = m_flatRules.size(); i < size; ++i) {
1087         CSSStyleRule* parsedRule = m_flatRules.at(i);
1088         if (parsedRule->style() == pageStyle) {
1089             if (parsedRule->declaration()->asText() != pageStyle->cssText()) {
1090                 // Clear the disabled properties for the invalid style here.
1091                 m_inspectorStyles.remove(pageStyle);
1092                 setStyleText(pageStyle, pageStyle->cssText());
1093             }
1094             break;
1095         }
1096     }
1097     m_isRevalidating = false;
1098 }
1099
1100 bool InspectorStyleSheet::originalStyleSheetText(String* result) const
1101 {
1102     bool success = inlineStyleSheetText(result);
1103     if (!success)
1104         success = resourceStyleSheetText(result);
1105     return success;
1106 }
1107
1108 bool InspectorStyleSheet::resourceStyleSheetText(String* result) const
1109 {
1110     if (m_origin == "user" || m_origin == "user-agent")
1111         return false;
1112
1113     if (!m_pageStyleSheet || !ownerDocument() || !ownerDocument()->frame())
1114         return false;
1115
1116     String error;
1117     bool base64Encoded;
1118     InspectorPageAgent::resourceContent(&error, ownerDocument()->frame(), KURL(ParsedURLString, m_pageStyleSheet->href()), result, &base64Encoded);
1119     return error.isEmpty() && !base64Encoded;
1120 }
1121
1122 bool InspectorStyleSheet::inlineStyleSheetText(String* result) const
1123 {
1124     if (!m_pageStyleSheet)
1125         return false;
1126
1127     Node* ownerNode = m_pageStyleSheet->ownerNode();
1128     if (!ownerNode || ownerNode->nodeType() != Node::ELEMENT_NODE)
1129         return false;
1130     Element* ownerElement = static_cast<Element*>(ownerNode);
1131
1132     if (!ownerElement->hasTagName(HTMLNames::styleTag)
1133 #if ENABLE(SVG)
1134         && !ownerElement->hasTagName(SVGNames::styleTag)
1135 #endif
1136     )
1137         return false;
1138     *result = ownerElement->innerText();
1139     return true;
1140 }
1141
1142 PassRefPtr<InspectorArray> InspectorStyleSheet::buildArrayForRuleList(CSSRuleList* ruleList)
1143 {
1144     RefPtr<InspectorArray> result = InspectorArray::create();
1145     if (!ruleList)
1146         return result.release();
1147
1148     RefPtr<CSSRuleList> refRuleList = ruleList;
1149     Vector<CSSStyleRule*> rules;
1150     collectFlatRules(refRuleList, &rules);
1151
1152     for (unsigned i = 0, size = rules.size(); i < size; ++i)
1153         result->pushObject(buildObjectForRule(rules.at(i)));
1154
1155     return result.release();
1156 }
1157
1158 void InspectorStyleSheet::fixUnparsedPropertyRanges(CSSRuleSourceData* ruleData, const String& styleSheetText)
1159 {
1160     Vector<CSSPropertySourceData>& propertyData = ruleData->styleSourceData->propertyData;
1161     unsigned size = propertyData.size();
1162     if (!size)
1163         return;
1164
1165     unsigned styleStart = ruleData->styleSourceData->styleBodyRange.start;
1166     const UChar* characters = styleSheetText.characters();
1167     CSSPropertySourceData* nextData = &(propertyData.at(0));
1168     for (unsigned i = 0; i < size; ++i) {
1169         CSSPropertySourceData* currentData = nextData;
1170         nextData = i < size - 1 ? &(propertyData.at(i + 1)) : 0;
1171
1172         if (currentData->parsedOk)
1173             continue;
1174         if (currentData->range.end > 0 && characters[styleStart + currentData->range.end - 1] == ';')
1175             continue;
1176
1177         unsigned propertyEndInStyleSheet;
1178         if (!nextData)
1179             propertyEndInStyleSheet = ruleData->styleSourceData->styleBodyRange.end - 1;
1180         else
1181             propertyEndInStyleSheet = styleStart + nextData->range.start - 1;
1182
1183         while (isHTMLSpace(characters[propertyEndInStyleSheet]))
1184             --propertyEndInStyleSheet;
1185
1186         // propertyEndInStyleSheet points at the last property text character.
1187         unsigned newPropertyEnd = propertyEndInStyleSheet - styleStart + 1; // Exclusive of the last property text character.
1188         if (currentData->range.end != newPropertyEnd) {
1189             currentData->range.end = newPropertyEnd;
1190             unsigned valueStartInStyleSheet = styleStart + currentData->range.start + currentData->name.length();
1191             while (valueStartInStyleSheet < propertyEndInStyleSheet && characters[valueStartInStyleSheet] != ':')
1192                 ++valueStartInStyleSheet;
1193             if (valueStartInStyleSheet < propertyEndInStyleSheet)
1194                 ++valueStartInStyleSheet; // Shift past the ':'.
1195             while (valueStartInStyleSheet < propertyEndInStyleSheet && isHTMLSpace(characters[valueStartInStyleSheet]))
1196                 ++valueStartInStyleSheet;
1197             // Need to exclude the trailing ';' from the property value.
1198             currentData->value = styleSheetText.substring(valueStartInStyleSheet, propertyEndInStyleSheet - valueStartInStyleSheet + (characters[propertyEndInStyleSheet] == ';' ? 0 : 1));
1199         }
1200     }
1201 }
1202
1203 void InspectorStyleSheet::collectFlatRules(PassRefPtr<CSSRuleList> ruleList, Vector<CSSStyleRule*>* result)
1204 {
1205     if (!ruleList)
1206         return;
1207
1208     for (unsigned i = 0, size = ruleList->length(); i < size; ++i) {
1209         CSSRule* rule = ruleList->item(i);
1210         CSSStyleRule* styleRule = InspectorCSSAgent::asCSSStyleRule(rule);
1211         if (styleRule)
1212             result->append(styleRule);
1213         else {
1214             RefPtr<CSSRuleList> childRuleList = asCSSRuleList(rule);
1215             if (childRuleList)
1216                 collectFlatRules(childRuleList, result);
1217         }
1218     }
1219 }
1220
1221 PassRefPtr<InspectorStyleSheetForInlineStyle> InspectorStyleSheetForInlineStyle::create(const String& id, PassRefPtr<Element> element, const String& origin)
1222 {
1223     return adoptRef(new InspectorStyleSheetForInlineStyle(id, element, origin));
1224 }
1225
1226 InspectorStyleSheetForInlineStyle::InspectorStyleSheetForInlineStyle(const String& id, PassRefPtr<Element> element, const String& origin)
1227     : InspectorStyleSheet(id, 0, origin, "")
1228     , m_element(element)
1229     , m_ruleSourceData(0)
1230     , m_isStyleTextValid(false)
1231 {
1232     ASSERT(m_element);
1233     m_inspectorStyle = InspectorStyle::create(InspectorCSSId(id, 0), inlineStyle(), this);
1234     m_styleText = m_element->isStyledElement() ? m_element->getAttribute("style").string() : String();
1235 }
1236
1237 void InspectorStyleSheetForInlineStyle::didModifyElementAttribute()
1238 {
1239     m_isStyleTextValid = false;
1240     if (m_element->isStyledElement() && m_element->style() != m_inspectorStyle->cssStyle())
1241         m_inspectorStyle = InspectorStyle::create(InspectorCSSId(id(), 0), inlineStyle(), this);
1242     m_ruleSourceData.clear();
1243 }
1244
1245 bool InspectorStyleSheetForInlineStyle::getText(String* result) const
1246 {
1247     if (!m_isStyleTextValid) {
1248         m_styleText = elementStyleText();
1249         m_isStyleTextValid = true;
1250     }
1251     *result = m_styleText;
1252     return true;
1253 }
1254
1255 bool InspectorStyleSheetForInlineStyle::setStyleText(CSSStyleDeclaration* style, const String& text)
1256 {
1257     ASSERT_UNUSED(style, style == inlineStyle());
1258     ExceptionCode ec = 0;
1259     m_element->setAttribute("style", text, ec);
1260     m_styleText = text;
1261     m_isStyleTextValid = true;
1262     m_ruleSourceData.clear();
1263     return !ec;
1264 }
1265
1266 Document* InspectorStyleSheetForInlineStyle::ownerDocument() const
1267 {
1268     return m_element->document();
1269 }
1270
1271 bool InspectorStyleSheetForInlineStyle::ensureParsedDataReady()
1272 {
1273     // The "style" property value can get changed indirectly, e.g. via element.style.borderWidth = "2px".
1274     const String& currentStyleText = elementStyleText();
1275     if (m_styleText != currentStyleText) {
1276         m_ruleSourceData.clear();
1277         m_styleText = currentStyleText;
1278         m_isStyleTextValid = true;
1279     }
1280
1281     if (m_ruleSourceData)
1282         return true;
1283
1284     m_ruleSourceData = CSSRuleSourceData::create();
1285     RefPtr<CSSStyleSourceData> sourceData = CSSStyleSourceData::create();
1286     bool success = getStyleAttributeRanges(&sourceData);
1287     if (!success)
1288         return false;
1289
1290     m_ruleSourceData->styleSourceData = sourceData.release();
1291     return true;
1292 }
1293
1294 PassRefPtr<InspectorStyle> InspectorStyleSheetForInlineStyle::inspectorStyleForId(const InspectorCSSId& id)
1295 {
1296     ASSERT_UNUSED(id, !id.ordinal());
1297     return m_inspectorStyle;
1298 }
1299
1300 CSSStyleDeclaration* InspectorStyleSheetForInlineStyle::inlineStyle() const
1301 {
1302     return m_element->style();
1303 }
1304
1305 const String& InspectorStyleSheetForInlineStyle::elementStyleText() const
1306 {
1307     return m_element->getAttribute("style").string();
1308 }
1309
1310 bool InspectorStyleSheetForInlineStyle::getStyleAttributeRanges(RefPtr<CSSStyleSourceData>* result) const
1311 {
1312     if (!m_element->isStyledElement())
1313         return false;
1314
1315     if (m_styleText.isEmpty()) {
1316         (*result)->styleBodyRange.start = 0;
1317         (*result)->styleBodyRange.end = 0;
1318         return true;
1319     }
1320
1321     RefPtr<StylePropertySet> tempDeclaration = StylePropertySet::create();
1322     CSSParser p;
1323     p.parseDeclaration(tempDeclaration.get(), m_styleText, result, m_element->document()->elementSheet());
1324     return true;
1325 }
1326
1327 } // namespace WebCore
1328
1329 #endif // ENABLE(INSPECTOR)