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