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 "core/inspector/InspectorStyleSheet.h"
27
28#include "bindings/core/v8/ExceptionState.h"
29#include "bindings/core/v8/ExceptionStatePlaceholder.h"
30#include "bindings/core/v8/ScriptRegexp.h"
31#include "core/CSSPropertyNames.h"
32#include "core/css/CSSKeyframesRule.h"
33#include "core/css/CSSMediaRule.h"
34#include "core/css/CSSRuleList.h"
35#include "core/css/CSSStyleRule.h"
36#include "core/css/CSSStyleSheet.h"
37#include "core/css/CSSSupportsRule.h"
38#include "core/css/StylePropertySet.h"
39#include "core/css/StyleRule.h"
40#include "core/css/StyleSheetContents.h"
41#include "core/css/parser/CSSParser.h"
42#include "core/dom/Document.h"
43#include "core/dom/Element.h"
44#include "core/html/HTMLStyleElement.h"
45#include "core/html/parser/HTMLParserIdioms.h"
46#include "core/inspector/ContentSearchUtils.h"
47#include "core/inspector/InspectorCSSAgent.h"
48#include "core/inspector/InspectorPageAgent.h"
49#include "core/inspector/InspectorResourceAgent.h"
50#include "core/svg/SVGStyleElement.h"
51#include "wtf/OwnPtr.h"
52#include "wtf/PassOwnPtr.h"
53#include "wtf/text/StringBuilder.h"
54#include "wtf/text/TextPosition.h"
55
56using blink::TypeBuilder::Array;
57using blink::RuleSourceDataList;
58using blink::CSSRuleSourceData;
59using blink::CSSStyleSheet;
60
61namespace {
62
63using namespace blink;
64
65static CSSParserContext parserContextForDocument(Document *document)
66{
67    return document ? CSSParserContext(*document, 0) : strictCSSParserContext();
68}
69
70class StyleSheetHandler FINAL : public CSSParserObserver {
71public:
72    StyleSheetHandler(const String& parsedText, Document* document, StyleSheetContents* styleSheetContents, RuleSourceDataList* result)
73        : m_parsedText(parsedText)
74        , m_document(document)
75        , m_styleSheetContents(styleSheetContents)
76        , m_result(result)
77        , m_commentParser(parserContextForDocument(document))
78        , m_propertyRangeStart(UINT_MAX)
79        , m_selectorRangeStart(UINT_MAX)
80        , m_commentRangeStart(UINT_MAX)
81    {
82        ASSERT(m_result);
83    }
84
85private:
86    virtual void startRuleHeader(CSSRuleSourceData::Type, unsigned) OVERRIDE;
87    virtual void endRuleHeader(unsigned) OVERRIDE;
88    virtual void startSelector(unsigned) OVERRIDE;
89    virtual void endSelector(unsigned) OVERRIDE;
90    virtual void startRuleBody(unsigned) OVERRIDE;
91    virtual void endRuleBody(unsigned, bool) OVERRIDE;
92    virtual void startProperty(unsigned) OVERRIDE;
93    virtual void endProperty(bool, bool, unsigned, CSSParserError) OVERRIDE;
94    virtual void startComment(unsigned) OVERRIDE;
95    virtual void endComment(unsigned) OVERRIDE;
96
97    void addNewRuleToSourceTree(PassRefPtrWillBeRawPtr<CSSRuleSourceData>);
98    PassRefPtrWillBeRawPtr<CSSRuleSourceData> popRuleData();
99    template <typename CharacterType> inline void setRuleHeaderEnd(const CharacterType*, unsigned);
100    void fixUnparsedPropertyRanges(CSSRuleSourceData*);
101
102    const String& m_parsedText;
103    Document* m_document;
104    StyleSheetContents* m_styleSheetContents;
105    RawPtrWillBeMember<RuleSourceDataList> m_result;
106    RuleSourceDataList m_currentRuleDataStack;
107    RefPtrWillBeMember<CSSRuleSourceData> m_currentRuleData;
108    CSSParser m_commentParser;
109    unsigned m_propertyRangeStart;
110    unsigned m_selectorRangeStart;
111    unsigned m_commentRangeStart;
112};
113
114void StyleSheetHandler::startRuleHeader(CSSRuleSourceData::Type type, unsigned offset)
115{
116    // Pop off data for a previous invalid rule.
117    if (m_currentRuleData)
118        m_currentRuleDataStack.removeLast();
119
120    RefPtrWillBeRawPtr<CSSRuleSourceData> data = CSSRuleSourceData::create(type);
121    data->ruleHeaderRange.start = offset;
122    m_currentRuleData = data;
123    m_currentRuleDataStack.append(data.release());
124}
125
126template <typename CharacterType>
127inline void StyleSheetHandler::setRuleHeaderEnd(const CharacterType* dataStart, unsigned listEndOffset)
128{
129    while (listEndOffset > 1) {
130        if (isHTMLSpace<CharacterType>(*(dataStart + listEndOffset - 1)))
131            --listEndOffset;
132        else
133            break;
134    }
135
136    m_currentRuleDataStack.last()->ruleHeaderRange.end = listEndOffset;
137    if (!m_currentRuleDataStack.last()->selectorRanges.isEmpty())
138        m_currentRuleDataStack.last()->selectorRanges.last().end = listEndOffset;
139}
140
141void StyleSheetHandler::endRuleHeader(unsigned offset)
142{
143    ASSERT(!m_currentRuleDataStack.isEmpty());
144
145    if (m_parsedText.is8Bit())
146        setRuleHeaderEnd<LChar>(m_parsedText.characters8(), offset);
147    else
148        setRuleHeaderEnd<UChar>(m_parsedText.characters16(), offset);
149}
150
151void StyleSheetHandler::startSelector(unsigned offset)
152{
153    m_selectorRangeStart = offset;
154}
155
156void StyleSheetHandler::endSelector(unsigned offset)
157{
158    ASSERT(m_currentRuleDataStack.size());
159    m_currentRuleDataStack.last()->selectorRanges.append(SourceRange(m_selectorRangeStart, offset));
160    m_selectorRangeStart = UINT_MAX;
161}
162
163void StyleSheetHandler::startRuleBody(unsigned offset)
164{
165    m_currentRuleData.clear();
166    ASSERT(!m_currentRuleDataStack.isEmpty());
167    if (m_parsedText[offset] == '{')
168        ++offset; // Skip the rule body opening brace.
169    m_currentRuleDataStack.last()->ruleBodyRange.start = offset;
170}
171
172void StyleSheetHandler::endRuleBody(unsigned offset, bool error)
173{
174    ASSERT(!m_currentRuleDataStack.isEmpty());
175    m_currentRuleDataStack.last()->ruleBodyRange.end = offset;
176    m_propertyRangeStart = UINT_MAX;
177    RefPtrWillBeRawPtr<CSSRuleSourceData> rule = popRuleData();
178    if (error)
179        return;
180
181    fixUnparsedPropertyRanges(rule.get());
182    addNewRuleToSourceTree(rule.release());
183}
184
185void StyleSheetHandler::addNewRuleToSourceTree(PassRefPtrWillBeRawPtr<CSSRuleSourceData> rule)
186{
187    if (m_currentRuleDataStack.isEmpty())
188        m_result->append(rule);
189    else
190        m_currentRuleDataStack.last()->childRules.append(rule);
191}
192
193PassRefPtrWillBeRawPtr<CSSRuleSourceData> StyleSheetHandler::popRuleData()
194{
195    ASSERT(!m_currentRuleDataStack.isEmpty());
196    m_currentRuleData.clear();
197    RefPtrWillBeRawPtr<CSSRuleSourceData> data = m_currentRuleDataStack.last();
198    m_currentRuleDataStack.removeLast();
199    return data.release();
200}
201
202template <typename CharacterType>
203static inline void fixUnparsedProperties(const CharacterType* characters, CSSRuleSourceData* ruleData)
204{
205    WillBeHeapVector<CSSPropertySourceData>& propertyData = ruleData->styleSourceData->propertyData;
206    unsigned size = propertyData.size();
207    if (!size)
208        return;
209
210    CSSPropertySourceData* nextData = &(propertyData.at(0));
211    for (unsigned i = 0; i < size; ++i) {
212        CSSPropertySourceData* currentData = nextData;
213        nextData = i < size - 1 ? &(propertyData.at(i + 1)) : 0;
214
215        if (currentData->parsedOk)
216            continue;
217        if (currentData->range.end > 0 && characters[currentData->range.end - 1] == ';')
218            continue;
219
220        unsigned propertyEnd;
221        if (!nextData)
222            propertyEnd = ruleData->ruleBodyRange.end - 1;
223        else
224            propertyEnd = nextData->range.start - 1;
225
226        while (isHTMLSpace<CharacterType>(characters[propertyEnd]))
227            --propertyEnd;
228
229        // propertyEnd points at the last property text character.
230        unsigned newPropertyEnd = propertyEnd + 1; // Exclusive of the last property text character.
231        if (currentData->range.end != newPropertyEnd) {
232            currentData->range.end = newPropertyEnd;
233            unsigned valueStart = currentData->range.start + currentData->name.length();
234            while (valueStart < propertyEnd && characters[valueStart] != ':')
235                ++valueStart;
236            if (valueStart < propertyEnd)
237                ++valueStart; // Shift past the ':'.
238            while (valueStart < propertyEnd && isHTMLSpace<CharacterType>(characters[valueStart]))
239                ++valueStart;
240            // Need to exclude the trailing ';' from the property value.
241            currentData->value = String(characters + valueStart, propertyEnd - valueStart + (characters[propertyEnd] == ';' ? 0 : 1));
242        }
243    }
244}
245
246void StyleSheetHandler::fixUnparsedPropertyRanges(CSSRuleSourceData* ruleData)
247{
248    if (!ruleData->styleSourceData)
249        return;
250
251    if (m_parsedText.is8Bit()) {
252        fixUnparsedProperties<LChar>(m_parsedText.characters8(), ruleData);
253        return;
254    }
255
256    fixUnparsedProperties<UChar>(m_parsedText.characters16(), ruleData);
257}
258
259void StyleSheetHandler::startProperty(unsigned offset)
260{
261    if (m_currentRuleDataStack.isEmpty() || !m_currentRuleDataStack.last()->styleSourceData)
262        return;
263    m_propertyRangeStart = offset;
264}
265
266void StyleSheetHandler::endProperty(bool isImportant, bool isParsed, unsigned offset, CSSParserError errorType)
267{
268    // FIXME: This is the only place CSSParserError is every read!?
269    if (errorType != NoCSSError)
270        m_propertyRangeStart = UINT_MAX;
271
272    if (m_propertyRangeStart == UINT_MAX || m_currentRuleDataStack.isEmpty() || !m_currentRuleDataStack.last()->styleSourceData)
273        return;
274
275    ASSERT(offset <= m_parsedText.length());
276    if (offset < m_parsedText.length() && m_parsedText[offset] == ';') // Include semicolon into the property text.
277        ++offset;
278
279    const unsigned start = m_propertyRangeStart;
280    const unsigned end = offset;
281    ASSERT(start < end);
282    String propertyString = m_parsedText.substring(start, end - start).stripWhiteSpace();
283    if (propertyString.endsWith(';'))
284        propertyString = propertyString.left(propertyString.length() - 1);
285    size_t colonIndex = propertyString.find(':');
286    ASSERT(colonIndex != kNotFound);
287
288    String name = propertyString.left(colonIndex).stripWhiteSpace();
289    String value = propertyString.substring(colonIndex + 1, propertyString.length()).stripWhiteSpace();
290    m_currentRuleDataStack.last()->styleSourceData->propertyData.append(
291        CSSPropertySourceData(name, value, isImportant, false, isParsed, SourceRange(start, end)));
292    m_propertyRangeStart = UINT_MAX;
293}
294
295void StyleSheetHandler::startComment(unsigned offset)
296{
297    ASSERT(m_commentRangeStart == UINT_MAX);
298    m_commentRangeStart = offset;
299}
300
301void StyleSheetHandler::endComment(unsigned offset)
302{
303    ASSERT(offset <= m_parsedText.length());
304
305    unsigned startOffset = m_commentRangeStart;
306    m_commentRangeStart = UINT_MAX;
307    if (m_propertyRangeStart != UINT_MAX) {
308        ASSERT(startOffset >= m_propertyRangeStart);
309        // startProperty() is called automatically at the start of a style declaration.
310        // Check if no text has been scanned yet, otherwise the comment is inside a property.
311        if (!m_parsedText.substring(m_propertyRangeStart, startOffset).stripWhiteSpace().isEmpty())
312            return;
313        m_propertyRangeStart = UINT_MAX;
314    }
315    if (m_currentRuleDataStack.isEmpty() || !m_currentRuleDataStack.last()->ruleHeaderRange.end || !m_currentRuleDataStack.last()->styleSourceData)
316        return;
317
318    // The lexer is not inside a property AND it is scanning a declaration-aware rule body.
319    String commentText = m_parsedText.substring(startOffset, offset - startOffset);
320
321    ASSERT(commentText.startsWith("/*"));
322    commentText = commentText.substring(2);
323
324    // Require well-formed comments.
325    if (!commentText.endsWith("*/"))
326        return;
327    commentText = commentText.substring(0, commentText.length() - 2).stripWhiteSpace();
328    if (commentText.isEmpty())
329        return;
330
331    // FIXME: Use the actual rule type rather than STYLE_RULE?
332    RuleSourceDataList sourceData;
333
334    // FIXME: Use another subclass of CSSParserObserver and assert that
335    // no comments are encountered (will not need m_document and m_styleSheetContents).
336    StyleSheetHandler handler(commentText, m_document, m_styleSheetContents, &sourceData);
337    RefPtrWillBeRawPtr<MutableStylePropertySet> tempMutableStyle = MutableStylePropertySet::create();
338    m_commentParser.parseDeclaration(tempMutableStyle.get(), commentText, &handler, m_styleSheetContents);
339    WillBeHeapVector<CSSPropertySourceData>& commentPropertyData = sourceData.first()->styleSourceData->propertyData;
340    if (commentPropertyData.size() != 1)
341        return;
342    CSSPropertySourceData& propertyData = commentPropertyData.at(0);
343    if (propertyData.range.length() != commentText.length())
344        return;
345
346    m_currentRuleDataStack.last()->styleSourceData->propertyData.append(
347        CSSPropertySourceData(propertyData.name, propertyData.value, false, true, true, SourceRange(startOffset, offset)));
348}
349
350} // namespace
351
352class ParsedStyleSheet {
353    WTF_MAKE_FAST_ALLOCATED;
354public:
355    ParsedStyleSheet(CSSStyleSheet* pageStyleSheet);
356
357    const String& text() const { ASSERT(m_hasText); return m_text; }
358    void setText(const String&);
359    bool hasText() const { return m_hasText; }
360    bool ensureSourceData();
361    bool hasSourceData() const { return m_sourceData; }
362    PassRefPtrWillBeRawPtr<blink::CSSRuleSourceData> ruleSourceDataAt(unsigned) const;
363    unsigned ruleCount() { return m_sourceData->size(); }
364
365private:
366    void flattenSourceData(RuleSourceDataList*);
367    void setSourceData(PassOwnPtrWillBeRawPtr<RuleSourceDataList>);
368
369    String m_text;
370    bool m_hasText;
371    OwnPtrWillBePersistent<RuleSourceDataList> m_sourceData;
372    RefPtrWillBePersistent<CSSStyleSheet> m_pageStyleSheet;
373};
374
375ParsedStyleSheet::ParsedStyleSheet(CSSStyleSheet* pageStyleSheet)
376    : m_hasText(false)
377    , m_pageStyleSheet(pageStyleSheet)
378{
379}
380
381void ParsedStyleSheet::setText(const String& text)
382{
383    m_hasText = true;
384    m_text = text;
385    setSourceData(nullptr);
386}
387
388void ParsedStyleSheet::flattenSourceData(RuleSourceDataList* dataList)
389{
390    for (size_t i = 0; i < dataList->size(); ++i) {
391        RefPtrWillBeMember<CSSRuleSourceData>& data = dataList->at(i);
392
393        // The m_sourceData->append()'ed types should be exactly the same as in collectFlatRules().
394        switch (data->type) {
395        case CSSRuleSourceData::STYLE_RULE:
396        case CSSRuleSourceData::IMPORT_RULE:
397        case CSSRuleSourceData::CHARSET_RULE:
398        case CSSRuleSourceData::PAGE_RULE:
399        case CSSRuleSourceData::FONT_FACE_RULE:
400        case CSSRuleSourceData::VIEWPORT_RULE:
401        case CSSRuleSourceData::KEYFRAMES_RULE:
402            m_sourceData->append(data);
403            break;
404        case CSSRuleSourceData::MEDIA_RULE:
405        case CSSRuleSourceData::SUPPORTS_RULE:
406            m_sourceData->append(data);
407            flattenSourceData(&data->childRules);
408            break;
409        default:
410            break;
411        }
412    }
413}
414
415bool ParsedStyleSheet::ensureSourceData()
416{
417    if (hasSourceData())
418        return true;
419
420    if (!hasText())
421        return false;
422
423    RefPtrWillBeRawPtr<StyleSheetContents> newStyleSheet = StyleSheetContents::create(strictCSSParserContext());
424    OwnPtrWillBeRawPtr<RuleSourceDataList> result = adoptPtrWillBeNoop(new RuleSourceDataList());
425    StyleSheetHandler handler(text(), m_pageStyleSheet->ownerDocument(), newStyleSheet.get(), result.get());
426    CSSParser::parseSheet(parserContextForDocument(m_pageStyleSheet->ownerDocument()), newStyleSheet.get(), text(), TextPosition::minimumPosition(), &handler);
427    setSourceData(result.release());
428    return hasSourceData();
429}
430
431void ParsedStyleSheet::setSourceData(PassOwnPtrWillBeRawPtr<RuleSourceDataList> sourceData)
432{
433    if (!sourceData) {
434        m_sourceData.clear();
435        return;
436    }
437    m_sourceData = adoptPtrWillBeNoop(new RuleSourceDataList());
438
439    // FIXME: This is a temporary solution to retain the original flat sourceData structure
440    // containing only style rules, even though BisonCSSParser now provides the full rule source data tree.
441    // Normally, we should just assign m_sourceData = sourceData;
442    flattenSourceData(sourceData.get());
443}
444
445PassRefPtrWillBeRawPtr<blink::CSSRuleSourceData> ParsedStyleSheet::ruleSourceDataAt(unsigned index) const
446{
447    if (!hasSourceData() || index >= m_sourceData->size())
448        return nullptr;
449
450    return m_sourceData->at(index);
451}
452
453namespace blink {
454
455enum MediaListSource {
456    MediaListSourceLinkedSheet,
457    MediaListSourceInlineSheet,
458    MediaListSourceMediaRule,
459    MediaListSourceImportRule
460};
461
462static PassRefPtr<TypeBuilder::CSS::SourceRange> buildSourceRangeObject(const SourceRange& range, Vector<unsigned>* lineEndings)
463{
464    if (!lineEndings)
465        return nullptr;
466    TextPosition start = TextPosition::fromOffsetAndLineEndings(range.start, *lineEndings);
467    TextPosition end = TextPosition::fromOffsetAndLineEndings(range.end, *lineEndings);
468
469    RefPtr<TypeBuilder::CSS::SourceRange> result = TypeBuilder::CSS::SourceRange::create()
470        .setStartLine(start.m_line.zeroBasedInt())
471        .setStartColumn(start.m_column.zeroBasedInt())
472        .setEndLine(end.m_line.zeroBasedInt())
473        .setEndColumn(end.m_column.zeroBasedInt());
474    return result.release();
475}
476
477static PassRefPtrWillBeRawPtr<CSSRuleList> asCSSRuleList(CSSRule* rule)
478{
479    if (!rule)
480        return nullptr;
481
482    if (rule->type() == CSSRule::MEDIA_RULE)
483        return toCSSMediaRule(rule)->cssRules();
484
485    if (rule->type() == CSSRule::SUPPORTS_RULE)
486        return toCSSSupportsRule(rule)->cssRules();
487
488    return nullptr;
489}
490
491PassRefPtrWillBeRawPtr<InspectorStyle> InspectorStyle::create(const InspectorCSSId& styleId, PassRefPtrWillBeRawPtr<CSSStyleDeclaration> style, InspectorStyleSheetBase* parentStyleSheet)
492{
493    return adoptRefWillBeNoop(new InspectorStyle(styleId, style, parentStyleSheet));
494}
495
496InspectorStyle::InspectorStyle(const InspectorCSSId& styleId, PassRefPtrWillBeRawPtr<CSSStyleDeclaration> style, InspectorStyleSheetBase* parentStyleSheet)
497    : m_styleId(styleId)
498    , m_style(style)
499    , m_parentStyleSheet(parentStyleSheet)
500    , m_formatAcquired(false)
501{
502    ASSERT(m_style);
503}
504
505PassRefPtr<TypeBuilder::CSS::CSSStyle> InspectorStyle::buildObjectForStyle() const
506{
507    RefPtr<TypeBuilder::CSS::CSSStyle> result = styleWithProperties();
508    if (!m_styleId.isEmpty())
509        result->setStyleSheetId(m_styleId.styleSheetId());
510
511    RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = extractSourceData();
512    if (sourceData)
513        result->setRange(buildSourceRangeObject(sourceData->ruleBodyRange, m_parentStyleSheet->lineEndings().get()));
514
515    return result.release();
516}
517
518PassRefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSComputedStyleProperty> > InspectorStyle::buildArrayForComputedStyle() const
519{
520    RefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSComputedStyleProperty> > result = TypeBuilder::Array<TypeBuilder::CSS::CSSComputedStyleProperty>::create();
521    WillBeHeapVector<InspectorStyleProperty> properties;
522    populateAllProperties(properties);
523
524    for (WillBeHeapVector<InspectorStyleProperty>::iterator it = properties.begin(), itEnd = properties.end(); it != itEnd; ++it) {
525        const CSSPropertySourceData& propertyEntry = it->sourceData;
526        RefPtr<TypeBuilder::CSS::CSSComputedStyleProperty> entry = TypeBuilder::CSS::CSSComputedStyleProperty::create()
527            .setName(propertyEntry.name)
528            .setValue(propertyEntry.value);
529        result->addItem(entry);
530    }
531
532    return result.release();
533}
534
535bool InspectorStyle::verifyPropertyText(const String& propertyText, bool canOmitSemicolon)
536{
537    DEFINE_STATIC_LOCAL(String, bogusPropertyName, ("-webkit-boguz-propertee"));
538    RefPtrWillBeRawPtr<MutableStylePropertySet> tempMutableStyle = MutableStylePropertySet::create();
539    RuleSourceDataList sourceData;
540    RefPtrWillBeRawPtr<StyleSheetContents> styleSheetContents = StyleSheetContents::create(strictCSSParserContext());
541    String declarationText = propertyText + (canOmitSemicolon ? ";" : " ") + bogusPropertyName + ": none";
542    StyleSheetHandler handler(declarationText, ownerDocument(), styleSheetContents.get(), &sourceData);
543    CSSParser(parserContextForDocument(ownerDocument())).parseDeclaration(tempMutableStyle.get(), declarationText, &handler, styleSheetContents.get());
544    WillBeHeapVector<CSSPropertySourceData>& propertyData = sourceData.first()->styleSourceData->propertyData;
545    unsigned propertyCount = propertyData.size();
546
547    // At least one property + the bogus property added just above should be present.
548    if (propertyCount < 2)
549        return false;
550
551    // Check for the proper propertyText termination (the parser could at least restore to the PROPERTY_NAME state).
552    if (propertyData.at(propertyCount - 1).name != bogusPropertyName)
553        return false;
554
555    return true;
556}
557
558bool InspectorStyle::setPropertyText(unsigned index, const String& propertyText, bool overwrite, ExceptionState& exceptionState)
559{
560    ASSERT(m_parentStyleSheet);
561
562    if (!m_parentStyleSheet->ensureParsedDataReady()) {
563        exceptionState.throwDOMException(NotFoundError, "The parent style sheet's data hasn't been processed.");
564        return false;
565    }
566
567    if (!propertyText.stripWhiteSpace().isEmpty()) {
568        if (!verifyPropertyText(propertyText, false) && !verifyPropertyText(propertyText, true)) {
569            exceptionState.throwDOMException(SyntaxError, "The property '" + propertyText + "' could not be set.");
570            return false;
571        }
572    }
573
574    RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = extractSourceData();
575    if (!sourceData) {
576        exceptionState.throwDOMException(NotFoundError, "The property '" + propertyText + "' could not be set.");
577        return false;
578    }
579
580    String text;
581    bool success = styleText(&text);
582    if (!success) {
583        exceptionState.throwDOMException(NotFoundError, "The property '" + propertyText + "' could not be set.");
584        return false;
585    }
586
587    WillBeHeapVector<InspectorStyleProperty> allProperties;
588    populateAllProperties(allProperties);
589
590    InspectorStyleTextEditor editor(&allProperties, text, sourceData->ruleBodyRange, newLineAndWhitespaceDelimiters());
591    if (overwrite) {
592        if (index >= allProperties.size()) {
593            exceptionState.throwDOMException(IndexSizeError, "The index provided (" + String::number(index) + ") is greater than or equal to the maximum bound (" + String::number(allProperties.size()) + ").");
594            return false;
595        }
596        editor.replaceProperty(index, propertyText);
597    } else {
598        editor.insertProperty(index, propertyText);
599    }
600
601    return m_parentStyleSheet->setStyleText(m_styleId, editor.styleText());
602}
603
604bool InspectorStyle::styleText(String* result) const
605{
606    RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = extractSourceData();
607    if (!sourceData)
608        return false;
609
610    return textForRange(sourceData->ruleBodyRange, result);
611}
612
613bool InspectorStyle::textForRange(const SourceRange& range, String* result) const
614{
615    String styleSheetText;
616    bool success = m_parentStyleSheet->getText(&styleSheetText);
617    if (!success)
618        return false;
619
620    ASSERT(0 <= range.start);
621    ASSERT(range.start <= range.end);
622    ASSERT(range.end <= styleSheetText.length());
623    *result = styleSheetText.substring(range.start, range.end - range.start);
624    return true;
625}
626
627void InspectorStyle::populateAllProperties(WillBeHeapVector<InspectorStyleProperty>& result) const
628{
629    HashSet<String> sourcePropertyNames;
630
631    RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = extractSourceData();
632    if (sourceData && sourceData->styleSourceData) {
633        WillBeHeapVector<CSSPropertySourceData>& sourcePropertyData = sourceData->styleSourceData->propertyData;
634        for (WillBeHeapVector<CSSPropertySourceData>::const_iterator it = sourcePropertyData.begin(); it != sourcePropertyData.end(); ++it) {
635            InspectorStyleProperty p(*it, true);
636            bool isPropertyTextKnown = textForRange(p.sourceData.range, &p.rawText);
637            ASSERT_UNUSED(isPropertyTextKnown, isPropertyTextKnown);
638            result.append(p);
639            sourcePropertyNames.add(it->name.lower());
640        }
641    }
642
643    for (int i = 0, size = m_style->length(); i < size; ++i) {
644        String name = m_style->item(i);
645        if (!sourcePropertyNames.add(name.lower()).isNewEntry)
646            continue;
647
648        String value = m_style->getPropertyValue(name);
649        if (value.isEmpty())
650            continue;
651        result.append(InspectorStyleProperty(CSSPropertySourceData(name, value, !m_style->getPropertyPriority(name).isEmpty(), false, true, SourceRange()), false));
652    }
653}
654
655PassRefPtr<TypeBuilder::CSS::CSSStyle> InspectorStyle::styleWithProperties() const
656{
657    RefPtr<Array<TypeBuilder::CSS::CSSProperty> > propertiesObject = Array<TypeBuilder::CSS::CSSProperty>::create();
658    RefPtr<Array<TypeBuilder::CSS::ShorthandEntry> > shorthandEntries = Array<TypeBuilder::CSS::ShorthandEntry>::create();
659    HashSet<String> foundShorthands;
660    OwnPtr<Vector<unsigned> > lineEndings(m_parentStyleSheet ? m_parentStyleSheet->lineEndings() : PassOwnPtr<Vector<unsigned> >());
661
662    WillBeHeapVector<InspectorStyleProperty> properties;
663    populateAllProperties(properties);
664
665    for (WillBeHeapVector<InspectorStyleProperty>::iterator it = properties.begin(), itEnd = properties.end(); it != itEnd; ++it) {
666        const CSSPropertySourceData& propertyEntry = it->sourceData;
667        const String& name = propertyEntry.name;
668
669        RefPtr<TypeBuilder::CSS::CSSProperty> property = TypeBuilder::CSS::CSSProperty::create()
670            .setName(name)
671            .setValue(propertyEntry.value);
672        propertiesObject->addItem(property);
673
674        // Default "parsedOk" == true.
675        if (!propertyEntry.parsedOk)
676            property->setParsedOk(false);
677        if (it->hasRawText())
678            property->setText(it->rawText);
679
680        if (propertyEntry.important)
681            property->setImportant(true);
682        if (it->hasSource) {
683            property->setRange(buildSourceRangeObject(propertyEntry.range, lineEndings.get()));
684            if (!propertyEntry.disabled)
685                property->setImplicit(false);
686            property->setDisabled(propertyEntry.disabled);
687        } else if (!propertyEntry.disabled) {
688            bool implicit = m_style->isPropertyImplicit(name);
689            // Default "implicit" == false.
690            if (implicit)
691                property->setImplicit(true);
692
693            String shorthand = m_style->getPropertyShorthand(name);
694            if (!shorthand.isEmpty()) {
695                if (foundShorthands.add(shorthand).isNewEntry) {
696                    RefPtr<TypeBuilder::CSS::ShorthandEntry> entry = TypeBuilder::CSS::ShorthandEntry::create()
697                        .setName(shorthand)
698                        .setValue(shorthandValue(shorthand));
699                    shorthandEntries->addItem(entry);
700                }
701            }
702        }
703    }
704
705    RefPtr<TypeBuilder::CSS::CSSStyle> result = TypeBuilder::CSS::CSSStyle::create()
706        .setCssProperties(propertiesObject)
707        .setShorthandEntries(shorthandEntries);
708    return result.release();
709}
710
711PassRefPtrWillBeRawPtr<CSSRuleSourceData> InspectorStyle::extractSourceData() const
712{
713    if (!m_parentStyleSheet || !m_parentStyleSheet->ensureParsedDataReady())
714        return nullptr;
715    return m_parentStyleSheet->ruleSourceDataAt(m_styleId.ordinal());
716}
717
718String InspectorStyle::shorthandValue(const String& shorthandProperty) const
719{
720    String value = m_style->getPropertyValue(shorthandProperty);
721    if (value.isEmpty()) {
722        StringBuilder builder;
723
724        for (unsigned i = 0; i < m_style->length(); ++i) {
725            String individualProperty = m_style->item(i);
726            if (m_style->getPropertyShorthand(individualProperty) != shorthandProperty)
727                continue;
728            if (m_style->isPropertyImplicit(individualProperty))
729                continue;
730            String individualValue = m_style->getPropertyValue(individualProperty);
731            if (individualValue == "initial")
732                continue;
733            if (!builder.isEmpty())
734                builder.append(' ');
735            builder.append(individualValue);
736        }
737
738        return builder.toString();
739    }
740    return value;
741}
742
743NewLineAndWhitespace& InspectorStyle::newLineAndWhitespaceDelimiters() const
744{
745    DEFINE_STATIC_LOCAL(String, defaultPrefix, ("    "));
746
747    if (m_formatAcquired)
748        return m_format;
749
750    RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = extractSourceData();
751    WillBeHeapVector<CSSPropertySourceData>* sourcePropertyData = sourceData ? &(sourceData->styleSourceData->propertyData) : 0;
752    int propertyCount = sourcePropertyData ? sourcePropertyData->size() : 0;
753    if (!propertyCount) {
754        m_format.first = "\n";
755        m_format.second = defaultPrefix;
756        return m_format; // Do not remember the default formatting and attempt to acquire it later.
757    }
758
759    String styleSheetText;
760    bool success = m_parentStyleSheet->getText(&styleSheetText);
761    ASSERT_UNUSED(success, success);
762
763    m_formatAcquired = true;
764
765    String candidatePrefix = defaultPrefix;
766    StringBuilder formatLineFeed;
767    StringBuilder prefix;
768    int scanStart = sourceData->ruleBodyRange.start;
769    int propertyIndex = 0;
770    bool isFullPrefixScanned = false;
771    bool lineFeedTerminated = false;
772    while (propertyIndex < propertyCount) {
773        const blink::CSSPropertySourceData& currentProperty = sourcePropertyData->at(propertyIndex++);
774
775        bool processNextProperty = false;
776        int scanEnd = currentProperty.range.start;
777        for (int i = scanStart; i < scanEnd; ++i) {
778            UChar ch = styleSheetText[i];
779            bool isLineFeed = isHTMLLineBreak(ch);
780            if (isLineFeed) {
781                if (!lineFeedTerminated)
782                    formatLineFeed.append(ch);
783                prefix.clear();
784            } else if (isHTMLSpace<UChar>(ch))
785                prefix.append(ch);
786            else {
787                candidatePrefix = prefix.toString();
788                prefix.clear();
789                scanStart = currentProperty.range.end;
790                ++propertyIndex;
791                processNextProperty = true;
792                break;
793            }
794            if (!isLineFeed && formatLineFeed.length())
795                lineFeedTerminated = true;
796        }
797        if (!processNextProperty) {
798            isFullPrefixScanned = true;
799            break;
800        }
801    }
802
803    m_format.first = formatLineFeed.toString();
804    m_format.second = isFullPrefixScanned ? prefix.toString() : candidatePrefix;
805    return m_format;
806}
807
808Document* InspectorStyle::ownerDocument() const
809{
810    return m_parentStyleSheet->ownerDocument();
811}
812
813void InspectorStyle::trace(Visitor* visitor)
814{
815    visitor->trace(m_style);
816    visitor->trace(m_parentStyleSheet);
817}
818
819InspectorStyleSheetBase::InspectorStyleSheetBase(const String& id, Listener* listener)
820    : m_id(id)
821    , m_listener(listener)
822{
823}
824
825bool InspectorStyleSheetBase::setPropertyText(const InspectorCSSId& id, unsigned propertyIndex, const String& text, bool overwrite, ExceptionState& exceptionState)
826{
827    RefPtrWillBeRawPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id);
828    if (!inspectorStyle) {
829        exceptionState.throwDOMException(NotFoundError, "No property could be found for the given ID.");
830        return false;
831    }
832    return inspectorStyle->setPropertyText(propertyIndex, text, overwrite, exceptionState);
833}
834
835bool InspectorStyleSheetBase::getStyleText(const InspectorCSSId& id, String* text)
836{
837    RefPtrWillBeRawPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id);
838    if (!inspectorStyle)
839        return false;
840    return inspectorStyle->styleText(text);
841}
842
843void InspectorStyleSheetBase::fireStyleSheetChanged()
844{
845    if (listener())
846        listener()->styleSheetChanged(this);
847}
848
849PassRefPtr<TypeBuilder::CSS::CSSStyle> InspectorStyleSheetBase::buildObjectForStyle(CSSStyleDeclaration* style)
850{
851    RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = nullptr;
852    if (ensureParsedDataReady())
853        sourceData = ruleSourceDataAt(styleId(style).ordinal());
854
855    InspectorCSSId id = styleId(style);
856    if (id.isEmpty()) {
857        // Any rule coming from User Agent and not from DefaultStyleSheet will not have id.
858        // See InspectorCSSAgent::buildObjectForRule for details.
859        RefPtrWillBeRawPtr<InspectorStyle> inspectorStyle = InspectorStyle::create(id, style, this);
860        return inspectorStyle->buildObjectForStyle();
861    }
862    RefPtrWillBeRawPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id);
863    RefPtr<TypeBuilder::CSS::CSSStyle> result = inspectorStyle->buildObjectForStyle();
864
865    // Style text cannot be retrieved without stylesheet, so set cssText here.
866    if (sourceData) {
867        String sheetText;
868        bool success = getText(&sheetText);
869        if (success) {
870            const SourceRange& bodyRange = sourceData->ruleBodyRange;
871            result->setCssText(sheetText.substring(bodyRange.start, bodyRange.end - bodyRange.start));
872        }
873    }
874
875    return result.release();
876}
877
878PassOwnPtr<Vector<unsigned> > InspectorStyleSheetBase::lineEndings()
879{
880    String text;
881    if (!getText(&text))
882        return PassOwnPtr<Vector<unsigned> >();
883    return WTF::lineEndings(text);
884}
885
886bool InspectorStyleSheetBase::lineNumberAndColumnToOffset(unsigned lineNumber, unsigned columnNumber, unsigned* offset)
887{
888    OwnPtr<Vector<unsigned> > endings = lineEndings();
889    if (lineNumber >= endings->size())
890        return false;
891    unsigned charactersInLine = lineNumber > 0 ? endings->at(lineNumber) - endings->at(lineNumber - 1) - 1 : endings->at(0);
892    if (columnNumber > charactersInLine)
893        return false;
894    TextPosition position(OrdinalNumber::fromZeroBasedInt(lineNumber), OrdinalNumber::fromZeroBasedInt(columnNumber));
895    *offset = position.toOffset(*endings).zeroBasedInt();
896    return true;
897}
898
899bool InspectorStyleSheetBase::findPropertyByRange(const SourceRange& sourceRange, InspectorCSSId* ruleId, unsigned* propertyIndex, bool* overwrite)
900{
901    if (!ensureParsedDataReady())
902        return false;
903    for (size_t i = 0; i < ruleCount(); ++i) {
904        RefPtrWillBeRawPtr<CSSRuleSourceData> ruleSourceData = ruleSourceDataAt(i);
905        RefPtrWillBeRawPtr<CSSStyleSourceData> styleSourceData = ruleSourceData->styleSourceData;
906        if (!styleSourceData)
907            continue;
908        if (ruleSourceData->ruleBodyRange.end < sourceRange.start || sourceRange.end < ruleSourceData->ruleBodyRange.start)
909            continue;
910        WillBeHeapVector<CSSPropertySourceData>& propertyData = styleSourceData->propertyData;
911        for (size_t j = 0; j < propertyData.size(); ++j) {
912            CSSPropertySourceData& property = propertyData.at(j);
913            unsigned styleStart = ruleSourceData->ruleBodyRange.start;
914            if (sourceRange.length() && property.range.start == sourceRange.start && property.range.end == sourceRange.end) {
915                *ruleId = InspectorCSSId(id(), i);
916                *propertyIndex = j;
917                *overwrite = true;
918                return true;
919            }
920            if (!sourceRange.length() && styleStart <= sourceRange.start && sourceRange.start <= property.range.start) {
921                *ruleId = InspectorCSSId(id(), i);
922                *propertyIndex = j;
923                *overwrite = false;
924                return true;
925            }
926        }
927        if (!sourceRange.length() && ruleSourceData->ruleBodyRange.start <= sourceRange.start && sourceRange.start <= ruleSourceData->ruleBodyRange.end) {
928            *ruleId = InspectorCSSId(id(), i);
929            *propertyIndex = propertyData.size();
930            *overwrite = false;
931            return true;
932        }
933    }
934    return false;
935}
936
937PassRefPtrWillBeRawPtr<InspectorStyleSheet> InspectorStyleSheet::create(InspectorPageAgent* pageAgent, InspectorResourceAgent* resourceAgent, const String& id, PassRefPtrWillBeRawPtr<CSSStyleSheet> pageStyleSheet, TypeBuilder::CSS::StyleSheetOrigin::Enum origin, const String& documentURL, Listener* listener)
938{
939    return adoptRefWillBeNoop(new InspectorStyleSheet(pageAgent, resourceAgent, id, pageStyleSheet, origin, documentURL, listener));
940}
941
942InspectorStyleSheet::InspectorStyleSheet(InspectorPageAgent* pageAgent, InspectorResourceAgent* resourceAgent, const String& id, PassRefPtrWillBeRawPtr<CSSStyleSheet> pageStyleSheet, TypeBuilder::CSS::StyleSheetOrigin::Enum origin, const String& documentURL, Listener* listener)
943    : InspectorStyleSheetBase(id, listener)
944    , m_pageAgent(pageAgent)
945    , m_resourceAgent(resourceAgent)
946    , m_pageStyleSheet(pageStyleSheet)
947    , m_origin(origin)
948    , m_documentURL(documentURL)
949{
950    m_parsedStyleSheet = adoptPtr(new ParsedStyleSheet(m_pageStyleSheet.get()));
951}
952
953InspectorStyleSheet::~InspectorStyleSheet()
954{
955}
956
957void InspectorStyleSheet::trace(Visitor* visitor)
958{
959    visitor->trace(m_pageAgent);
960    visitor->trace(m_resourceAgent);
961    visitor->trace(m_pageStyleSheet);
962    visitor->trace(m_flatRules);
963    InspectorStyleSheetBase::trace(visitor);
964}
965
966static String styleSheetURL(CSSStyleSheet* pageStyleSheet)
967{
968    if (pageStyleSheet && !pageStyleSheet->contents()->baseURL().isEmpty())
969        return pageStyleSheet->contents()->baseURL().string();
970    return emptyString();
971}
972
973String InspectorStyleSheet::finalURL() const
974{
975    String url = styleSheetURL(m_pageStyleSheet.get());
976    return url.isEmpty() ? m_documentURL : url;
977}
978
979bool InspectorStyleSheet::setText(const String& text, ExceptionState& exceptionState)
980{
981    updateText(text);
982    m_flatRules.clear();
983
984    if (listener())
985        listener()->willReparseStyleSheet();
986
987    {
988        // Have a separate scope for clearRules() (bug 95324).
989        CSSStyleSheet::RuleMutationScope mutationScope(m_pageStyleSheet.get());
990        m_pageStyleSheet->contents()->clearRules();
991        m_pageStyleSheet->clearChildRuleCSSOMWrappers();
992    }
993    {
994        CSSStyleSheet::RuleMutationScope mutationScope(m_pageStyleSheet.get());
995        m_pageStyleSheet->contents()->parseString(text);
996    }
997
998    if (listener())
999        listener()->didReparseStyleSheet();
1000    fireStyleSheetChanged();
1001    m_pageStyleSheet->ownerDocument()->styleResolverChanged(FullStyleUpdate);
1002    return true;
1003}
1004
1005String InspectorStyleSheet::ruleSelector(const InspectorCSSId& id, ExceptionState& exceptionState)
1006{
1007    CSSStyleRule* rule = ruleForId(id);
1008    if (!rule) {
1009        exceptionState.throwDOMException(NotFoundError, "No rule was found for the given ID.");
1010        return "";
1011    }
1012    return rule->selectorText();
1013}
1014
1015bool InspectorStyleSheet::setRuleSelector(const InspectorCSSId& id, const String& selector, ExceptionState& exceptionState)
1016{
1017    CSSStyleRule* rule = ruleForId(id);
1018    if (!rule) {
1019        exceptionState.throwDOMException(NotFoundError, "No rule was found for the given ID.");
1020        return false;
1021    }
1022    CSSStyleSheet* styleSheet = rule->parentStyleSheet();
1023    if (!styleSheet || !ensureParsedDataReady()) {
1024        exceptionState.throwDOMException(NotFoundError, "No stylesheet could be found in which to set the selector.");
1025        return false;
1026    }
1027
1028    rule->setSelectorText(selector);
1029    RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = ruleSourceDataAt(id.ordinal());
1030    if (!sourceData) {
1031        exceptionState.throwDOMException(NotFoundError, "The selector '" + selector + "' could not be set.");
1032        return false;
1033    }
1034
1035    String sheetText = m_parsedStyleSheet->text();
1036    sheetText.replace(sourceData->ruleHeaderRange.start, sourceData->ruleHeaderRange.length(), selector);
1037    updateText(sheetText);
1038    fireStyleSheetChanged();
1039    return true;
1040}
1041
1042unsigned InspectorStyleSheet::ruleIndexBySourceRange(const CSSMediaRule* parentMediaRule, const SourceRange& sourceRange)
1043{
1044    unsigned index = 0;
1045    for (size_t i = 0; i < m_flatRules.size(); ++i) {
1046        RefPtrWillBeRawPtr<CSSRule> rule = m_flatRules.at(i);
1047        if (rule->parentRule() != parentMediaRule)
1048            continue;
1049        RefPtrWillBeRawPtr<CSSRuleSourceData> ruleSourceData = m_parsedStyleSheet->ruleSourceDataAt(i);
1050        if (ruleSourceData->ruleBodyRange.end < sourceRange.start)
1051            ++index;
1052    }
1053    return index;
1054}
1055
1056CSSStyleRule* InspectorStyleSheet::insertCSSOMRuleInStyleSheet(const SourceRange& sourceRange, const String& ruleText, ExceptionState& exceptionState)
1057{
1058    unsigned index = ruleIndexBySourceRange(nullptr, sourceRange);
1059    m_pageStyleSheet->insertRule(ruleText, index, exceptionState);
1060    CSSRule* rule = m_pageStyleSheet->item(index);
1061    CSSStyleRule* styleRule = InspectorCSSAgent::asCSSStyleRule(rule);
1062    if (!styleRule) {
1063        m_pageStyleSheet->deleteRule(index, ASSERT_NO_EXCEPTION);
1064        exceptionState.throwDOMException(SyntaxError, "The rule '" + ruleText + "' could not be added in style sheet.");
1065        return 0;
1066    }
1067    return styleRule;
1068}
1069
1070CSSStyleRule* InspectorStyleSheet::insertCSSOMRuleInMediaRule(CSSMediaRule* mediaRule, const SourceRange& sourceRange, const String& ruleText, ExceptionState& exceptionState)
1071{
1072    unsigned index = ruleIndexBySourceRange(mediaRule, sourceRange);
1073    mediaRule->insertRule(ruleText, index, exceptionState);
1074    CSSRule* rule = mediaRule->item(index);
1075    CSSStyleRule* styleRule = InspectorCSSAgent::asCSSStyleRule(rule);
1076    if (!styleRule) {
1077        mediaRule->deleteRule(index, ASSERT_NO_EXCEPTION);
1078        exceptionState.throwDOMException(SyntaxError, "The rule '" + ruleText + "' could not be added in media rule.");
1079        return 0;
1080    }
1081    return styleRule;
1082}
1083
1084CSSStyleRule* InspectorStyleSheet::insertCSSOMRuleBySourceRange(const SourceRange& sourceRange, const String& ruleText, ExceptionState& exceptionState)
1085{
1086    int containingRuleIndex = -1;
1087    unsigned containingRuleLength = 0;
1088    for (size_t i = 0; i < m_parsedStyleSheet->ruleCount(); ++i) {
1089        RefPtrWillBeRawPtr<CSSRuleSourceData> ruleSourceData = m_parsedStyleSheet->ruleSourceDataAt(i);
1090        if (ruleSourceData->ruleHeaderRange.start < sourceRange.start && sourceRange.start < ruleSourceData->ruleBodyRange.start) {
1091            exceptionState.throwDOMException(NotFoundError, "Cannot insert rule inside rule selector.");
1092            return 0;
1093        }
1094        if (sourceRange.start < ruleSourceData->ruleBodyRange.start || ruleSourceData->ruleBodyRange.end < sourceRange.start)
1095            continue;
1096        if (containingRuleIndex == -1 || containingRuleLength > ruleSourceData->ruleBodyRange.length()) {
1097            containingRuleIndex = i;
1098            containingRuleLength = ruleSourceData->ruleBodyRange.length();
1099        }
1100    }
1101    if (containingRuleIndex == -1)
1102        return insertCSSOMRuleInStyleSheet(sourceRange, ruleText, exceptionState);
1103    RefPtrWillBeRawPtr<CSSRule> rule = m_flatRules.at(containingRuleIndex);
1104    if (rule->type() != CSSRule::MEDIA_RULE) {
1105        exceptionState.throwDOMException(NotFoundError, "Cannot insert rule in non-media rule.");
1106        return 0;
1107    }
1108    return insertCSSOMRuleInMediaRule(toCSSMediaRule(rule.get()), sourceRange, ruleText, exceptionState);
1109}
1110
1111bool InspectorStyleSheet::verifyRuleText(const String& ruleText)
1112{
1113    DEFINE_STATIC_LOCAL(String, bogusPropertyName, ("-webkit-boguz-propertee"));
1114    RuleSourceDataList sourceData;
1115    RefPtrWillBeRawPtr<StyleSheetContents> styleSheetContents = StyleSheetContents::create(strictCSSParserContext());
1116    String text = ruleText + " div { " + bogusPropertyName + ": none; }";
1117    StyleSheetHandler handler(text, ownerDocument(), styleSheetContents.get(), &sourceData);
1118    CSSParser::parseSheet(parserContextForDocument(ownerDocument()), styleSheetContents.get(), text, TextPosition::minimumPosition(), &handler);
1119    unsigned ruleCount = sourceData.size();
1120
1121    // Exactly two rules should be parsed.
1122    if (ruleCount != 2)
1123        return false;
1124
1125    // Added rule must be style rule.
1126    if (!sourceData.at(0)->styleSourceData)
1127        return false;
1128
1129    WillBeHeapVector<CSSPropertySourceData>& propertyData = sourceData.at(1)->styleSourceData->propertyData;
1130    unsigned propertyCount = propertyData.size();
1131
1132    // Exactly one property should be in rule.
1133    if (propertyCount != 1)
1134        return false;
1135
1136    // Check for the property name.
1137    if (propertyData.at(0).name != bogusPropertyName)
1138        return false;
1139
1140    return true;
1141}
1142
1143CSSStyleRule* InspectorStyleSheet::addRule(const String& ruleText, const SourceRange& location, ExceptionState& exceptionState)
1144{
1145    if (!ensureParsedDataReady()) {
1146        exceptionState.throwDOMException(NotFoundError, "Cannot parse style sheet.");
1147        return 0;
1148    }
1149
1150    if (location.start != location.end) {
1151        exceptionState.throwDOMException(NotFoundError, "Source range must be collapsed.");
1152        return 0;
1153    }
1154
1155    if (!verifyRuleText(ruleText)) {
1156        exceptionState.throwDOMException(SyntaxError, "Rule text is not valid.");
1157        return 0;
1158    }
1159
1160    String text;
1161    bool success = getText(&text);
1162    if (!success) {
1163        exceptionState.throwDOMException(NotFoundError, "The rule '" + ruleText + "' could not be added.");
1164        return 0;
1165    }
1166
1167    ensureFlatRules();
1168    CSSStyleRule* styleRule = insertCSSOMRuleBySourceRange(location, ruleText, exceptionState);
1169    if (exceptionState.hadException())
1170        return 0;
1171
1172    text.insert(ruleText, location.start);
1173
1174    m_parsedStyleSheet->setText(text);
1175    m_flatRules.clear();
1176
1177    fireStyleSheetChanged();
1178    return styleRule;
1179}
1180
1181bool InspectorStyleSheet::deleteRule(const InspectorCSSId& id, const String& oldText, ExceptionState& exceptionState)
1182{
1183    RefPtrWillBeRawPtr<CSSStyleRule> rule = ruleForId(id);
1184    if (!rule) {
1185        exceptionState.throwDOMException(NotFoundError, "No style rule could be found for the provided ID.");
1186        return false;
1187    }
1188    CSSStyleSheet* styleSheet = rule->parentStyleSheet();
1189    if (!styleSheet || !ensureParsedDataReady()) {
1190        exceptionState.throwDOMException(NotFoundError, "No parent stylesheet could be found.");
1191        return false;
1192    }
1193
1194    RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = ruleSourceDataAt(id.ordinal());
1195    if (!sourceData) {
1196        exceptionState.throwDOMException(NotFoundError, "No style rule could be found for the provided ID.");
1197        return false;
1198    }
1199
1200    CSSRule* parentRule = rule->parentRule();
1201    if (parentRule) {
1202        if (parentRule->type() != CSSRule::MEDIA_RULE) {
1203            exceptionState.throwDOMException(NotFoundError, "Cannot remove rule from non-media rule.");
1204            return false;
1205        }
1206        CSSMediaRule* parentMediaRule = toCSSMediaRule(parentRule);
1207        size_t index = 0;
1208        while (index < parentMediaRule->length() && parentMediaRule->item(index) != rule)
1209            ++index;
1210        ASSERT(index < parentMediaRule->length());
1211        parentMediaRule->deleteRule(index, exceptionState);
1212    } else {
1213        size_t index = 0;
1214        while (index < styleSheet->length() && styleSheet->item(index) != rule)
1215            ++index;
1216        ASSERT(index < styleSheet->length());
1217        styleSheet->deleteRule(index, exceptionState);
1218    }
1219    // |rule| MAY NOT be addressed after this line!
1220
1221    if (exceptionState.hadException())
1222        return false;
1223
1224    m_parsedStyleSheet->setText(oldText);
1225    m_flatRules.clear();
1226    fireStyleSheetChanged();
1227    return true;
1228}
1229
1230void InspectorStyleSheet::updateText(const String& newText)
1231{
1232    Element* element = ownerStyleElement();
1233    if (!element)
1234        m_pageAgent->addEditedResourceContent(finalURL(), newText);
1235    m_parsedStyleSheet->setText(newText);
1236}
1237
1238
1239CSSStyleRule* InspectorStyleSheet::ruleForId(const InspectorCSSId& id) const
1240{
1241    ASSERT(!id.isEmpty());
1242    ensureFlatRules();
1243    return InspectorCSSAgent::asCSSStyleRule(id.ordinal() >= m_flatRules.size() ? 0 : m_flatRules.at(id.ordinal()).get());
1244}
1245
1246PassRefPtr<TypeBuilder::CSS::CSSStyleSheetHeader> InspectorStyleSheet::buildObjectForStyleSheetInfo() const
1247{
1248    CSSStyleSheet* styleSheet = pageStyleSheet();
1249    if (!styleSheet)
1250        return nullptr;
1251
1252    Document* document = styleSheet->ownerDocument();
1253    LocalFrame* frame = document ? document->frame() : 0;
1254
1255    RefPtr<TypeBuilder::CSS::CSSStyleSheetHeader> result = TypeBuilder::CSS::CSSStyleSheetHeader::create()
1256        .setStyleSheetId(id())
1257        .setOrigin(m_origin)
1258        .setDisabled(styleSheet->disabled())
1259        .setSourceURL(url())
1260        .setTitle(styleSheet->title())
1261        .setFrameId(m_pageAgent->frameId(frame))
1262        .setIsInline(styleSheet->isInline() && !startsAtZero())
1263        .setStartLine(styleSheet->startPositionInSource().m_line.zeroBasedInt())
1264        .setStartColumn(styleSheet->startPositionInSource().m_column.zeroBasedInt());
1265
1266    if (hasSourceURL())
1267        result->setHasSourceURL(true);
1268
1269    String sourceMapURLValue = sourceMapURL();
1270    if (!sourceMapURLValue.isEmpty())
1271        result->setSourceMapURL(sourceMapURLValue);
1272    return result.release();
1273}
1274
1275PassRefPtr<TypeBuilder::Array<TypeBuilder::CSS::Selector> > InspectorStyleSheet::selectorsFromSource(const CSSRuleSourceData* sourceData, const String& sheetText)
1276{
1277    ScriptRegexp comment("/\\*[^]*?\\*/", TextCaseSensitive, MultilineEnabled);
1278    RefPtr<TypeBuilder::Array<TypeBuilder::CSS::Selector> > result = TypeBuilder::Array<TypeBuilder::CSS::Selector>::create();
1279    const SelectorRangeList& ranges = sourceData->selectorRanges;
1280    for (size_t i = 0, size = ranges.size(); i < size; ++i) {
1281        const SourceRange& range = ranges.at(i);
1282        String selector = sheetText.substring(range.start, range.length());
1283
1284        // We don't want to see any comments in the selector components, only the meaningful parts.
1285        int matchLength;
1286        int offset = 0;
1287        while ((offset = comment.match(selector, offset, &matchLength)) >= 0)
1288            selector.replace(offset, matchLength, "");
1289
1290        RefPtr<TypeBuilder::CSS::Selector> simpleSelector = TypeBuilder::CSS::Selector::create()
1291            .setValue(selector.stripWhiteSpace());
1292        simpleSelector->setRange(buildSourceRangeObject(range, lineEndings().get()));
1293        result->addItem(simpleSelector.release());
1294    }
1295    return result.release();
1296}
1297
1298PassRefPtr<TypeBuilder::CSS::SelectorList> InspectorStyleSheet::buildObjectForSelectorList(CSSStyleRule* rule)
1299{
1300    RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = nullptr;
1301    if (ensureParsedDataReady())
1302        sourceData = ruleSourceDataAt(styleId(rule->style()).ordinal());
1303    RefPtr<TypeBuilder::Array<TypeBuilder::CSS::Selector> > selectors;
1304
1305    // This intentionally does not rely on the source data to avoid catching the trailing comments (before the declaration starting '{').
1306    String selectorText = rule->selectorText();
1307
1308    if (sourceData)
1309        selectors = selectorsFromSource(sourceData.get(), m_parsedStyleSheet->text());
1310    else {
1311        selectors = TypeBuilder::Array<TypeBuilder::CSS::Selector>::create();
1312        const CSSSelectorList& selectorList = rule->styleRule()->selectorList();
1313        for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(*selector))
1314            selectors->addItem(TypeBuilder::CSS::Selector::create().setValue(selector->selectorText()).release());
1315    }
1316    RefPtr<TypeBuilder::CSS::SelectorList> result = TypeBuilder::CSS::SelectorList::create()
1317        .setSelectors(selectors)
1318        .setText(selectorText)
1319        .release();
1320    return result.release();
1321}
1322
1323static bool canBind(TypeBuilder::CSS::StyleSheetOrigin::Enum origin)
1324{
1325    return origin != TypeBuilder::CSS::StyleSheetOrigin::User_agent && origin != TypeBuilder::CSS::StyleSheetOrigin::User;
1326}
1327
1328PassRefPtr<TypeBuilder::CSS::CSSRule> InspectorStyleSheet::buildObjectForRule(CSSStyleRule* rule, PassRefPtr<Array<TypeBuilder::CSS::CSSMedia> > mediaStack)
1329{
1330    CSSStyleSheet* styleSheet = pageStyleSheet();
1331    if (!styleSheet)
1332        return nullptr;
1333
1334    RefPtr<TypeBuilder::CSS::CSSRule> result = TypeBuilder::CSS::CSSRule::create()
1335        .setSelectorList(buildObjectForSelectorList(rule))
1336        .setOrigin(m_origin)
1337        .setStyle(buildObjectForStyle(rule->style()));
1338
1339    if (canBind(m_origin)) {
1340        InspectorCSSId id(ruleId(rule));
1341        if (!id.isEmpty())
1342            result->setStyleSheetId(id.styleSheetId());
1343    }
1344
1345    if (mediaStack)
1346        result->setMedia(mediaStack);
1347
1348    return result.release();
1349}
1350
1351bool InspectorStyleSheet::getText(String* result) const
1352{
1353    if (!ensureText())
1354        return false;
1355    *result = m_parsedStyleSheet->text();
1356    return true;
1357}
1358
1359CSSStyleDeclaration* InspectorStyleSheet::styleForId(const InspectorCSSId& id) const
1360{
1361    CSSStyleRule* rule = ruleForId(id);
1362    if (!rule)
1363        return 0;
1364
1365    return rule->style();
1366}
1367
1368PassRefPtr<TypeBuilder::CSS::SourceRange> InspectorStyleSheet::ruleHeaderSourceRange(const CSSRule* rule)
1369{
1370    if (!ensureParsedDataReady())
1371        return nullptr;
1372
1373    ensureFlatRules();
1374    size_t index = m_flatRules.find(rule);
1375    // FIXME(lusnikov): m_flatRules are not always aligned with the m_parsedStyleSheet rule source
1376    // datas due to the CSSOM operations that add/remove rules without changing source.
1377    // This is a design issue. See crbug.com/178410
1378    if (index == kNotFound || index >= m_parsedStyleSheet->ruleCount())
1379        return nullptr;
1380    RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = m_parsedStyleSheet->ruleSourceDataAt(static_cast<unsigned>(index));
1381    return buildSourceRangeObject(sourceData->ruleHeaderRange, lineEndings().get());
1382}
1383
1384PassRefPtrWillBeRawPtr<InspectorStyle> InspectorStyleSheet::inspectorStyleForId(const InspectorCSSId& id)
1385{
1386    CSSStyleDeclaration* style = styleForId(id);
1387    if (!style)
1388        return nullptr;
1389
1390    return InspectorStyle::create(id, style, this);
1391}
1392
1393unsigned InspectorStyleSheet::ruleCount()
1394{
1395    return m_parsedStyleSheet->ruleCount();
1396}
1397
1398String InspectorStyleSheet::sourceURL() const
1399{
1400    if (!m_sourceURL.isNull())
1401        return m_sourceURL;
1402    if (m_origin != TypeBuilder::CSS::StyleSheetOrigin::Regular) {
1403        m_sourceURL = "";
1404        return m_sourceURL;
1405    }
1406
1407    String styleSheetText;
1408    bool success = getText(&styleSheetText);
1409    if (success) {
1410        bool deprecated;
1411        String commentValue = ContentSearchUtils::findSourceURL(styleSheetText, ContentSearchUtils::CSSMagicComment, &deprecated);
1412        if (!commentValue.isEmpty()) {
1413            // FIXME: add deprecated console message here.
1414            m_sourceURL = commentValue;
1415            return commentValue;
1416        }
1417    }
1418    m_sourceURL = "";
1419    return m_sourceURL;
1420}
1421
1422String InspectorStyleSheet::url() const
1423{
1424    // "sourceURL" is present only for regular rules, otherwise "origin" should be used in the frontend.
1425    if (m_origin != TypeBuilder::CSS::StyleSheetOrigin::Regular)
1426        return String();
1427
1428    CSSStyleSheet* styleSheet = pageStyleSheet();
1429    if (!styleSheet)
1430        return String();
1431
1432    if (hasSourceURL())
1433        return sourceURL();
1434
1435    if (styleSheet->isInline() && startsAtZero())
1436        return String();
1437
1438    return finalURL();
1439}
1440
1441bool InspectorStyleSheet::hasSourceURL() const
1442{
1443    return !sourceURL().isEmpty();
1444}
1445
1446bool InspectorStyleSheet::startsAtZero() const
1447{
1448    CSSStyleSheet* styleSheet = pageStyleSheet();
1449    if (!styleSheet)
1450        return true;
1451
1452    return styleSheet->startPositionInSource() == TextPosition::minimumPosition();
1453}
1454
1455String InspectorStyleSheet::sourceMapURL() const
1456{
1457    if (m_origin != TypeBuilder::CSS::StyleSheetOrigin::Regular)
1458        return String();
1459
1460    String styleSheetText;
1461    bool success = getText(&styleSheetText);
1462    if (success) {
1463        bool deprecated;
1464        String commentValue = ContentSearchUtils::findSourceMapURL(styleSheetText, ContentSearchUtils::CSSMagicComment, &deprecated);
1465        if (!commentValue.isEmpty()) {
1466            // FIXME: add deprecated console message here.
1467            return commentValue;
1468        }
1469    }
1470    return m_pageAgent->resourceSourceMapURL(finalURL());
1471}
1472
1473InspectorCSSId InspectorStyleSheet::styleId(CSSStyleDeclaration* style) const
1474{
1475    unsigned index = ruleIndexByStyle(style);
1476    if (index != UINT_MAX)
1477        return InspectorCSSId(id(), index);
1478    return InspectorCSSId();
1479}
1480
1481bool InspectorStyleSheet::findRuleBySelectorRange(const SourceRange& sourceRange, InspectorCSSId* ruleId)
1482{
1483    if (!ensureParsedDataReady())
1484        return false;
1485    for (size_t i = 0; i < ruleCount(); ++i) {
1486        RefPtrWillBeRawPtr<CSSRuleSourceData> ruleSourceData = ruleSourceDataAt(i);
1487        if (!ruleSourceData->styleSourceData)
1488            continue;
1489        if (ruleSourceData->ruleHeaderRange.start == sourceRange.start && ruleSourceData->ruleHeaderRange.end == sourceRange.end) {
1490            *ruleId = InspectorCSSId(id(), i);
1491            return true;
1492        }
1493    }
1494    return false;
1495}
1496
1497const CSSRuleVector& InspectorStyleSheet::flatRules()
1498{
1499    ensureFlatRules();
1500    return m_flatRules;
1501}
1502
1503Document* InspectorStyleSheet::ownerDocument() const
1504{
1505    return m_pageStyleSheet->ownerDocument();
1506}
1507
1508PassRefPtrWillBeRawPtr<CSSRuleSourceData> InspectorStyleSheet::ruleSourceDataAt(unsigned ruleIndex) const
1509{
1510    return m_parsedStyleSheet->ruleSourceDataAt(ruleIndex);
1511}
1512
1513unsigned InspectorStyleSheet::ruleIndexByStyle(CSSStyleDeclaration* pageStyle) const
1514{
1515    ensureFlatRules();
1516    for (unsigned i = 0, size = m_flatRules.size(); i < size; ++i) {
1517        CSSStyleRule* styleRule = InspectorCSSAgent::asCSSStyleRule(m_flatRules.at(i).get());
1518        if (styleRule && styleRule->style() == pageStyle)
1519            return i;
1520    }
1521    return UINT_MAX;
1522}
1523
1524bool InspectorStyleSheet::ensureParsedDataReady()
1525{
1526    return ensureText() && m_parsedStyleSheet->ensureSourceData();
1527}
1528
1529bool InspectorStyleSheet::ensureText() const
1530{
1531    if (m_parsedStyleSheet->hasText())
1532        return true;
1533
1534    String text;
1535    bool success = originalStyleSheetText(&text);
1536    if (success)
1537        m_parsedStyleSheet->setText(text);
1538    // No need to clear m_flatRules here - it's empty.
1539
1540    return success;
1541}
1542
1543template <typename RuleList>
1544static void collectFlatRules(RuleList ruleList, CSSRuleVector* result)
1545{
1546    if (!ruleList)
1547        return;
1548
1549    for (unsigned i = 0, size = ruleList->length(); i < size; ++i) {
1550        CSSRule* rule = ruleList->item(i);
1551
1552        // The result->append()'ed types should be exactly the same as in ParsedStyleSheet::flattenSourceData().
1553        switch (rule->type()) {
1554        case CSSRule::STYLE_RULE:
1555        case CSSRule::IMPORT_RULE:
1556        case CSSRule::CHARSET_RULE:
1557        case CSSRule::PAGE_RULE:
1558        case CSSRule::FONT_FACE_RULE:
1559        case CSSRule::VIEWPORT_RULE:
1560        case CSSRule::KEYFRAMES_RULE:
1561            result->append(rule);
1562            break;
1563        case CSSRule::MEDIA_RULE:
1564        case CSSRule::SUPPORTS_RULE:
1565            result->append(rule);
1566            collectFlatRules(asCSSRuleList(rule), result);
1567            break;
1568        default:
1569            break;
1570        }
1571    }
1572}
1573
1574void InspectorStyleSheet::ensureFlatRules() const
1575{
1576    // We are fine with redoing this for empty stylesheets as this will run fast.
1577    if (m_flatRules.isEmpty())
1578        collectFlatRules(pageStyleSheet(), &m_flatRules);
1579}
1580
1581bool InspectorStyleSheet::setStyleText(const InspectorCSSId& id, const String& text)
1582{
1583    CSSStyleDeclaration* style = styleForId(id);
1584    if (!style)
1585        return false;
1586
1587    if (!ensureParsedDataReady())
1588        return false;
1589
1590    String patchedStyleSheetText;
1591    bool success = styleSheetTextWithChangedStyle(style, text, &patchedStyleSheetText);
1592    if (!success)
1593        return false;
1594
1595    TrackExceptionState exceptionState;
1596    style->setCSSText(text, exceptionState);
1597    if (!exceptionState.hadException()) {
1598        updateText(patchedStyleSheetText);
1599        fireStyleSheetChanged();
1600    }
1601
1602    return !exceptionState.hadException();
1603}
1604
1605bool InspectorStyleSheet::styleSheetTextWithChangedStyle(CSSStyleDeclaration* style, const String& newStyleText, String* result)
1606{
1607    if (!style)
1608        return false;
1609    if (!ensureParsedDataReady())
1610        return false;
1611
1612    RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = ruleSourceDataAt(styleId(style).ordinal());
1613    unsigned bodyStart = sourceData->ruleBodyRange.start;
1614    unsigned bodyEnd = sourceData->ruleBodyRange.end;
1615    ASSERT(bodyStart <= bodyEnd);
1616
1617    String text = m_parsedStyleSheet->text();
1618    ASSERT_WITH_SECURITY_IMPLICATION(bodyEnd <= text.length()); // bodyEnd is exclusive
1619
1620    text.replace(bodyStart, bodyEnd - bodyStart, newStyleText);
1621    *result = text;
1622    return true;
1623}
1624
1625InspectorCSSId InspectorStyleSheet::ruleId(CSSStyleRule* rule) const
1626{
1627    return styleId(rule->style());
1628}
1629
1630bool InspectorStyleSheet::originalStyleSheetText(String* result) const
1631{
1632    bool success = inlineStyleSheetText(result);
1633    if (!success)
1634        success = resourceStyleSheetText(result);
1635    return success;
1636}
1637
1638bool InspectorStyleSheet::resourceStyleSheetText(String* result) const
1639{
1640    if (m_origin == TypeBuilder::CSS::StyleSheetOrigin::User || m_origin == TypeBuilder::CSS::StyleSheetOrigin::User_agent)
1641        return false;
1642
1643    if (!ownerDocument())
1644        return false;
1645
1646    KURL url(ParsedURLString, m_pageStyleSheet->href());
1647    if (m_pageAgent->getEditedResourceContent(url, result))
1648        return true;
1649
1650    bool base64Encoded;
1651    bool success = m_resourceAgent->fetchResourceContent(ownerDocument(), url, result, &base64Encoded);
1652    return success && !base64Encoded;
1653}
1654
1655Element* InspectorStyleSheet::ownerStyleElement() const
1656{
1657    Node* ownerNode = m_pageStyleSheet->ownerNode();
1658    if (!ownerNode || !ownerNode->isElementNode())
1659        return 0;
1660    Element* ownerElement = toElement(ownerNode);
1661
1662    if (!isHTMLStyleElement(ownerElement) && !isSVGStyleElement(ownerElement))
1663        return 0;
1664    return ownerElement;
1665}
1666
1667bool InspectorStyleSheet::inlineStyleSheetText(String* result) const
1668{
1669    Element* ownerElement = ownerStyleElement();
1670    if (!ownerElement)
1671        return false;
1672    *result = ownerElement->textContent();
1673    return true;
1674}
1675
1676PassRefPtrWillBeRawPtr<InspectorStyleSheetForInlineStyle> InspectorStyleSheetForInlineStyle::create(const String& id, PassRefPtrWillBeRawPtr<Element> element, Listener* listener)
1677{
1678    return adoptRefWillBeNoop(new InspectorStyleSheetForInlineStyle(id, element, listener));
1679}
1680
1681InspectorStyleSheetForInlineStyle::InspectorStyleSheetForInlineStyle(const String& id, PassRefPtrWillBeRawPtr<Element> element, Listener* listener)
1682    : InspectorStyleSheetBase(id, listener)
1683    , m_element(element)
1684    , m_ruleSourceData(nullptr)
1685    , m_isStyleTextValid(false)
1686{
1687    ASSERT(m_element);
1688    m_inspectorStyle = InspectorStyle::create(InspectorCSSId(id, 0), inlineStyle(), this);
1689    m_styleText = m_element->isStyledElement() ? m_element->getAttribute("style").string() : String();
1690}
1691
1692void InspectorStyleSheetForInlineStyle::didModifyElementAttribute()
1693{
1694    m_isStyleTextValid = false;
1695    if (m_element->isStyledElement() && m_element->style() != m_inspectorStyle->cssStyle())
1696        m_inspectorStyle = InspectorStyle::create(InspectorCSSId(id(), 0), inlineStyle(), this);
1697    m_ruleSourceData.clear();
1698}
1699
1700bool InspectorStyleSheetForInlineStyle::setText(const String& text, ExceptionState& exceptionState)
1701{
1702    bool success = setStyleText(InspectorCSSId(id(), 0), text);
1703    if (!success)
1704        exceptionState.throwDOMException(SyntaxError, "Style sheet text is invalid.");
1705    else
1706        fireStyleSheetChanged();
1707    return success;
1708}
1709
1710bool InspectorStyleSheetForInlineStyle::getText(String* result) const
1711{
1712    if (!m_isStyleTextValid) {
1713        m_styleText = elementStyleText();
1714        m_isStyleTextValid = true;
1715    }
1716    *result = m_styleText;
1717    return true;
1718}
1719
1720bool InspectorStyleSheetForInlineStyle::setStyleText(const InspectorCSSId& id, const String& text)
1721{
1722    CSSStyleDeclaration* style = styleForId(id);
1723    if (!style)
1724        return false;
1725    ASSERT_UNUSED(style, style == inlineStyle());
1726    TrackExceptionState exceptionState;
1727
1728    {
1729        InspectorCSSAgent::InlineStyleOverrideScope overrideScope(m_element->ownerDocument());
1730        m_element->setAttribute("style", AtomicString(text), exceptionState);
1731    }
1732    if (!exceptionState.hadException()) {
1733        m_styleText = text;
1734        m_isStyleTextValid = true;
1735        m_ruleSourceData.clear();
1736        fireStyleSheetChanged();
1737    }
1738    return !exceptionState.hadException();
1739}
1740
1741Document* InspectorStyleSheetForInlineStyle::ownerDocument() const
1742{
1743    return &m_element->document();
1744}
1745
1746bool InspectorStyleSheetForInlineStyle::ensureParsedDataReady()
1747{
1748    // The "style" property value can get changed indirectly, e.g. via element.style.borderWidth = "2px".
1749    const String& currentStyleText = elementStyleText();
1750    if (m_styleText != currentStyleText) {
1751        m_ruleSourceData.clear();
1752        m_styleText = currentStyleText;
1753        m_isStyleTextValid = true;
1754    }
1755
1756    if (m_ruleSourceData)
1757        return true;
1758
1759    m_ruleSourceData = getStyleAttributeData();
1760
1761    bool success = !!m_ruleSourceData;
1762    if (!success) {
1763        m_ruleSourceData = CSSRuleSourceData::create(CSSRuleSourceData::STYLE_RULE);
1764        return false;
1765    }
1766
1767    return true;
1768}
1769
1770PassRefPtrWillBeRawPtr<InspectorStyle> InspectorStyleSheetForInlineStyle::inspectorStyleForId(const InspectorCSSId& id)
1771{
1772    ASSERT_UNUSED(id, !id.ordinal());
1773    return m_inspectorStyle;
1774}
1775
1776CSSStyleDeclaration* InspectorStyleSheetForInlineStyle::inlineStyle() const
1777{
1778    return m_element->style();
1779}
1780
1781const String& InspectorStyleSheetForInlineStyle::elementStyleText() const
1782{
1783    return m_element->getAttribute("style").string();
1784}
1785
1786PassRefPtrWillBeRawPtr<CSSRuleSourceData> InspectorStyleSheetForInlineStyle::getStyleAttributeData() const
1787{
1788    if (!m_element->isStyledElement())
1789        return nullptr;
1790
1791    if (m_styleText.isEmpty()) {
1792        RefPtrWillBeRawPtr<CSSRuleSourceData> result = CSSRuleSourceData::create(CSSRuleSourceData::STYLE_RULE);
1793        result->ruleBodyRange.start = 0;
1794        result->ruleBodyRange.end = 0;
1795        return result.release();
1796    }
1797
1798    RefPtrWillBeRawPtr<MutableStylePropertySet> tempDeclaration = MutableStylePropertySet::create();
1799    RuleSourceDataList ruleSourceDataResult;
1800    StyleSheetHandler handler(m_styleText, &m_element->document(), m_element->document().elementSheet().contents(), &ruleSourceDataResult);
1801    CSSParser(parserContextForDocument(&m_element->document())).parseDeclaration(tempDeclaration.get(), m_styleText, &handler, m_element->document().elementSheet().contents());
1802    return ruleSourceDataResult.first().release();
1803}
1804
1805void InspectorStyleSheetForInlineStyle::trace(Visitor* visitor)
1806{
1807    visitor->trace(m_element);
1808    visitor->trace(m_ruleSourceData);
1809    visitor->trace(m_inspectorStyle);
1810    InspectorStyleSheetBase::trace(visitor);
1811}
1812
1813} // namespace blink
1814
1815