Home | History | Annotate | Download | only in inspector
      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 
     56 using blink::TypeBuilder::Array;
     57 using blink::RuleSourceDataList;
     58 using blink::CSSRuleSourceData;
     59 using blink::CSSStyleSheet;
     60 
     61 namespace {
     62 
     63 using namespace blink;
     64 
     65 static CSSParserContext parserContextForDocument(Document *document)
     66 {
     67     return document ? CSSParserContext(*document, 0) : strictCSSParserContext();
     68 }
     69 
     70 class StyleSheetHandler FINAL : public CSSParserObserver {
     71 public:
     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 
     85 private:
     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 
    114 void 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 
    126 template <typename CharacterType>
    127 inline 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 
    141 void 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 
    151 void StyleSheetHandler::startSelector(unsigned offset)
    152 {
    153     m_selectorRangeStart = offset;
    154 }
    155 
    156 void 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 
    163 void 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 
    172 void 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 
    185 void 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 
    193 PassRefPtrWillBeRawPtr<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 
    202 template <typename CharacterType>
    203 static 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 
    246 void 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 
    259 void StyleSheetHandler::startProperty(unsigned offset)
    260 {
    261     if (m_currentRuleDataStack.isEmpty() || !m_currentRuleDataStack.last()->styleSourceData)
    262         return;
    263     m_propertyRangeStart = offset;
    264 }
    265 
    266 void 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 
    295 void StyleSheetHandler::startComment(unsigned offset)
    296 {
    297     ASSERT(m_commentRangeStart == UINT_MAX);
    298     m_commentRangeStart = offset;
    299 }
    300 
    301 void 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 
    352 class ParsedStyleSheet {
    353     WTF_MAKE_FAST_ALLOCATED;
    354 public:
    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 
    365 private:
    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 
    375 ParsedStyleSheet::ParsedStyleSheet(CSSStyleSheet* pageStyleSheet)
    376     : m_hasText(false)
    377     , m_pageStyleSheet(pageStyleSheet)
    378 {
    379 }
    380 
    381 void ParsedStyleSheet::setText(const String& text)
    382 {
    383     m_hasText = true;
    384     m_text = text;
    385     setSourceData(nullptr);
    386 }
    387 
    388 void 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 
    415 bool 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 
    431 void 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 
    445 PassRefPtrWillBeRawPtr<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 
    453 namespace blink {
    454 
    455 enum MediaListSource {
    456     MediaListSourceLinkedSheet,
    457     MediaListSourceInlineSheet,
    458     MediaListSourceMediaRule,
    459     MediaListSourceImportRule
    460 };
    461 
    462 static 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 
    477 static 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 
    491 PassRefPtrWillBeRawPtr<InspectorStyle> InspectorStyle::create(const InspectorCSSId& styleId, PassRefPtrWillBeRawPtr<CSSStyleDeclaration> style, InspectorStyleSheetBase* parentStyleSheet)
    492 {
    493     return adoptRefWillBeNoop(new InspectorStyle(styleId, style, parentStyleSheet));
    494 }
    495 
    496 InspectorStyle::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 
    505 PassRefPtr<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 
    518 PassRefPtr<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 
    535 bool 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 
    558 bool 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 
    604 bool 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 
    613 bool 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 
    627 void 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 
    655 PassRefPtr<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 
    711 PassRefPtrWillBeRawPtr<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 
    718 String 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 
    743 NewLineAndWhitespace& 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 
    808 Document* InspectorStyle::ownerDocument() const
    809 {
    810     return m_parentStyleSheet->ownerDocument();
    811 }
    812 
    813 void InspectorStyle::trace(Visitor* visitor)
    814 {
    815     visitor->trace(m_style);
    816     visitor->trace(m_parentStyleSheet);
    817 }
    818 
    819 InspectorStyleSheetBase::InspectorStyleSheetBase(const String& id, Listener* listener)
    820     : m_id(id)
    821     , m_listener(listener)
    822 {
    823 }
    824 
    825 bool 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 
    835 bool 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 
    843 void InspectorStyleSheetBase::fireStyleSheetChanged()
    844 {
    845     if (listener())
    846         listener()->styleSheetChanged(this);
    847 }
    848 
    849 PassRefPtr<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 
    878 PassOwnPtr<Vector<unsigned> > InspectorStyleSheetBase::lineEndings()
    879 {
    880     String text;
    881     if (!getText(&text))
    882         return PassOwnPtr<Vector<unsigned> >();
    883     return WTF::lineEndings(text);
    884 }
    885 
    886 bool 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 
    899 bool 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 
    937 PassRefPtrWillBeRawPtr<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 
    942 InspectorStyleSheet::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 
    953 InspectorStyleSheet::~InspectorStyleSheet()
    954 {
    955 }
    956 
    957 void 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 
    966 static String styleSheetURL(CSSStyleSheet* pageStyleSheet)
    967 {
    968     if (pageStyleSheet && !pageStyleSheet->contents()->baseURL().isEmpty())
    969         return pageStyleSheet->contents()->baseURL().string();
    970     return emptyString();
    971 }
    972 
    973 String InspectorStyleSheet::finalURL() const
    974 {
    975     String url = styleSheetURL(m_pageStyleSheet.get());
    976     return url.isEmpty() ? m_documentURL : url;
    977 }
    978 
    979 bool 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 
   1005 String 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 
   1015 bool 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 
   1042 unsigned 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 
   1056 CSSStyleRule* 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 
   1070 CSSStyleRule* 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 
   1084 CSSStyleRule* 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 
   1111 bool 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 
   1143 CSSStyleRule* 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 
   1181 bool 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 
   1230 void 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 
   1239 CSSStyleRule* 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 
   1246 PassRefPtr<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 
   1275 PassRefPtr<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 
   1298 PassRefPtr<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 
   1323 static bool canBind(TypeBuilder::CSS::StyleSheetOrigin::Enum origin)
   1324 {
   1325     return origin != TypeBuilder::CSS::StyleSheetOrigin::User_agent && origin != TypeBuilder::CSS::StyleSheetOrigin::User;
   1326 }
   1327 
   1328 PassRefPtr<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 
   1351 bool InspectorStyleSheet::getText(String* result) const
   1352 {
   1353     if (!ensureText())
   1354         return false;
   1355     *result = m_parsedStyleSheet->text();
   1356     return true;
   1357 }
   1358 
   1359 CSSStyleDeclaration* 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 
   1368 PassRefPtr<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 
   1384 PassRefPtrWillBeRawPtr<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 
   1393 unsigned InspectorStyleSheet::ruleCount()
   1394 {
   1395     return m_parsedStyleSheet->ruleCount();
   1396 }
   1397 
   1398 String 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 
   1422 String 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 
   1441 bool InspectorStyleSheet::hasSourceURL() const
   1442 {
   1443     return !sourceURL().isEmpty();
   1444 }
   1445 
   1446 bool InspectorStyleSheet::startsAtZero() const
   1447 {
   1448     CSSStyleSheet* styleSheet = pageStyleSheet();
   1449     if (!styleSheet)
   1450         return true;
   1451 
   1452     return styleSheet->startPositionInSource() == TextPosition::minimumPosition();
   1453 }
   1454 
   1455 String 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 
   1473 InspectorCSSId 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 
   1481 bool 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 
   1497 const CSSRuleVector& InspectorStyleSheet::flatRules()
   1498 {
   1499     ensureFlatRules();
   1500     return m_flatRules;
   1501 }
   1502 
   1503 Document* InspectorStyleSheet::ownerDocument() const
   1504 {
   1505     return m_pageStyleSheet->ownerDocument();
   1506 }
   1507 
   1508 PassRefPtrWillBeRawPtr<CSSRuleSourceData> InspectorStyleSheet::ruleSourceDataAt(unsigned ruleIndex) const
   1509 {
   1510     return m_parsedStyleSheet->ruleSourceDataAt(ruleIndex);
   1511 }
   1512 
   1513 unsigned 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 
   1524 bool InspectorStyleSheet::ensureParsedDataReady()
   1525 {
   1526     return ensureText() && m_parsedStyleSheet->ensureSourceData();
   1527 }
   1528 
   1529 bool 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 
   1543 template <typename RuleList>
   1544 static 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 
   1574 void 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 
   1581 bool 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 
   1605 bool 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 
   1625 InspectorCSSId InspectorStyleSheet::ruleId(CSSStyleRule* rule) const
   1626 {
   1627     return styleId(rule->style());
   1628 }
   1629 
   1630 bool InspectorStyleSheet::originalStyleSheetText(String* result) const
   1631 {
   1632     bool success = inlineStyleSheetText(result);
   1633     if (!success)
   1634         success = resourceStyleSheetText(result);
   1635     return success;
   1636 }
   1637 
   1638 bool 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 
   1655 Element* 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 
   1667 bool 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 
   1676 PassRefPtrWillBeRawPtr<InspectorStyleSheetForInlineStyle> InspectorStyleSheetForInlineStyle::create(const String& id, PassRefPtrWillBeRawPtr<Element> element, Listener* listener)
   1677 {
   1678     return adoptRefWillBeNoop(new InspectorStyleSheetForInlineStyle(id, element, listener));
   1679 }
   1680 
   1681 InspectorStyleSheetForInlineStyle::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 
   1692 void 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 
   1700 bool 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 
   1710 bool 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 
   1720 bool 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 
   1741 Document* InspectorStyleSheetForInlineStyle::ownerDocument() const
   1742 {
   1743     return &m_element->document();
   1744 }
   1745 
   1746 bool 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 
   1770 PassRefPtrWillBeRawPtr<InspectorStyle> InspectorStyleSheetForInlineStyle::inspectorStyleForId(const InspectorCSSId& id)
   1771 {
   1772     ASSERT_UNUSED(id, !id.ordinal());
   1773     return m_inspectorStyle;
   1774 }
   1775 
   1776 CSSStyleDeclaration* InspectorStyleSheetForInlineStyle::inlineStyle() const
   1777 {
   1778     return m_element->style();
   1779 }
   1780 
   1781 const String& InspectorStyleSheetForInlineStyle::elementStyleText() const
   1782 {
   1783     return m_element->getAttribute("style").string();
   1784 }
   1785 
   1786 PassRefPtrWillBeRawPtr<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 
   1805 void 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