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