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