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 "InspectorStyleSheet.h"
     27 
     28 #if ENABLE(INSPECTOR)
     29 
     30 #include "CSSImportRule.h"
     31 #include "CSSMediaRule.h"
     32 #include "CSSParser.h"
     33 #include "CSSPropertySourceData.h"
     34 #include "CSSRule.h"
     35 #include "CSSRuleList.h"
     36 #include "CSSStyleRule.h"
     37 #include "CSSStyleSelector.h"
     38 #include "CSSStyleSheet.h"
     39 #include "Document.h"
     40 #include "Element.h"
     41 #include "HTMLHeadElement.h"
     42 #include "HTMLParserIdioms.h"
     43 #include "InspectorCSSAgent.h"
     44 #include "InspectorResourceAgent.h"
     45 #include "InspectorValues.h"
     46 #include "Node.h"
     47 #include "StyleSheetList.h"
     48 #include "WebKitCSSKeyframesRule.h"
     49 
     50 #include <wtf/OwnPtr.h>
     51 #include <wtf/PassOwnPtr.h>
     52 #include <wtf/Vector.h>
     53 
     54 class ParsedStyleSheet {
     55 public:
     56     typedef Vector<RefPtr<WebCore::CSSRuleSourceData> > SourceData;
     57     ParsedStyleSheet();
     58 
     59     WebCore::CSSStyleSheet* cssStyleSheet() const { return m_parserOutput; }
     60     const String& text() const { return m_text; }
     61     void setText(const String& text);
     62     bool hasText() const { return m_hasText; }
     63     SourceData* sourceData() const { return m_sourceData.get(); }
     64     void setSourceData(PassOwnPtr<SourceData> sourceData);
     65     bool hasSourceData() const { return m_sourceData; }
     66     RefPtr<WebCore::CSSRuleSourceData> ruleSourceDataAt(unsigned index) const;
     67 
     68 private:
     69 
     70     // StyleSheet constructed while parsing m_text.
     71     WebCore::CSSStyleSheet* m_parserOutput;
     72     String m_text;
     73     bool m_hasText;
     74     OwnPtr<SourceData> m_sourceData;
     75 };
     76 
     77 ParsedStyleSheet::ParsedStyleSheet()
     78     : m_parserOutput(0)
     79     , m_hasText(false)
     80 {
     81 }
     82 
     83 void ParsedStyleSheet::setText(const String& text)
     84 {
     85     m_hasText = true;
     86     m_text = text;
     87     setSourceData(0);
     88 }
     89 
     90 void ParsedStyleSheet::setSourceData(PassOwnPtr<SourceData> sourceData)
     91 {
     92     m_sourceData = sourceData;
     93 }
     94 
     95 RefPtr<WebCore::CSSRuleSourceData> ParsedStyleSheet::ruleSourceDataAt(unsigned index) const
     96 {
     97     if (!hasSourceData() || index >= m_sourceData->size())
     98         return 0;
     99 
    100     return m_sourceData->at(index);
    101 }
    102 
    103 namespace WebCore {
    104 
    105 static PassRefPtr<InspectorObject> buildSourceRangeObject(const SourceRange& range)
    106 {
    107     RefPtr<InspectorObject> result = InspectorObject::create();
    108     result->setNumber("start", range.start);
    109     result->setNumber("end", range.end);
    110     return result.release();
    111 }
    112 
    113 static PassRefPtr<CSSRuleList> asCSSRuleList(StyleBase* styleBase)
    114 {
    115     if (!styleBase)
    116         return 0;
    117 
    118     if (styleBase->isCSSStyleSheet())
    119         return CSSRuleList::create(static_cast<CSSStyleSheet*>(styleBase), true);
    120     if (styleBase->isRule()) {
    121         unsigned ruleType = static_cast<CSSRule*>(styleBase)->type();
    122         RefPtr<CSSRuleList> result = 0;
    123 
    124         switch (ruleType) {
    125         case CSSRule::MEDIA_RULE:
    126             result = static_cast<CSSMediaRule*>(styleBase)->cssRules();
    127             break;
    128         case CSSRule::WEBKIT_KEYFRAMES_RULE:
    129             result = static_cast<WebKitCSSKeyframesRule*>(styleBase)->cssRules();
    130             break;
    131         case CSSRule::IMPORT_RULE:
    132         case CSSRule::PAGE_RULE:
    133         default:
    134             return 0;
    135         }
    136 
    137         return result.release();
    138     }
    139     return 0;
    140 }
    141 
    142 PassRefPtr<InspectorStyle> InspectorStyle::create(const InspectorCSSId& styleId, PassRefPtr<CSSStyleDeclaration> style, InspectorStyleSheet* parentStyleSheet)
    143 {
    144     return adoptRef(new InspectorStyle(styleId, style, parentStyleSheet));
    145 }
    146 
    147 InspectorStyle::InspectorStyle(const InspectorCSSId& styleId, PassRefPtr<CSSStyleDeclaration> style, InspectorStyleSheet* parentStyleSheet)
    148     : m_styleId(styleId)
    149     , m_style(style)
    150     , m_parentStyleSheet(parentStyleSheet)
    151 {
    152     ASSERT(m_style);
    153 }
    154 
    155 InspectorStyle::~InspectorStyle()
    156 {
    157 }
    158 
    159 PassRefPtr<InspectorObject> InspectorStyle::buildObjectForStyle() const
    160 {
    161     RefPtr<InspectorObject> result = InspectorObject::create();
    162     if (!m_styleId.isEmpty())
    163         result->setValue("styleId", m_styleId.asInspectorValue());
    164 
    165     result->setString("width", m_style->getPropertyValue("width"));
    166     result->setString("height", m_style->getPropertyValue("height"));
    167 
    168     RefPtr<CSSRuleSourceData> sourceData = m_parentStyleSheet ? m_parentStyleSheet->ruleSourceDataFor(m_style.get()) : 0;
    169     if (sourceData)
    170         result->setObject("range", buildSourceRangeObject(sourceData->styleSourceData->styleBodyRange));
    171 
    172     populateObjectWithStyleProperties(result.get());
    173 
    174     return result.release();
    175 }
    176 
    177 // This method does the following preprocessing of |propertyText| with |overwrite| == false and |index| past the last active property:
    178 // - If the last property (if present) has no subsequent whitespace in the style declaration, a space is prepended to |propertyText|.
    179 // - If the last property (if present) has no closing ";", the ";" is prepended to the current |propertyText| value.
    180 //
    181 // The propertyText (if not empty) is checked to be a valid style declaration (containing at least one property). If not,
    182 // the method returns false (denoting an error).
    183 bool InspectorStyle::setPropertyText(ErrorString* errorString, unsigned index, const String& propertyText, bool overwrite)
    184 {
    185     ASSERT(m_parentStyleSheet);
    186     if (!m_parentStyleSheet->ensureParsedDataReady()) {
    187         *errorString = "Internal error: no stylesheet parsed data available";
    188         return false;
    189     }
    190 
    191     Vector<InspectorStyleProperty> allProperties;
    192     populateAllProperties(&allProperties);
    193 
    194     unsigned propertyStart = 0; // Need to initialize to make the compiler happy.
    195     long propertyLengthDelta;
    196 
    197     if (propertyText.stripWhiteSpace().length()) {
    198         RefPtr<CSSMutableStyleDeclaration> tempMutableStyle = CSSMutableStyleDeclaration::create();
    199         CSSParser p;
    200         RefPtr<CSSStyleSourceData> sourceData = CSSStyleSourceData::create();
    201         p.parseDeclaration(tempMutableStyle.get(), propertyText + " -webkit-boguz-propertee: none", &sourceData);
    202         Vector<CSSPropertySourceData>& propertyData = sourceData->propertyData;
    203         unsigned propertyCount = propertyData.size();
    204 
    205         // At least one property + the bogus property added just above should be present.
    206         if (propertyCount < 2) {
    207             *errorString = "Invalid property value";
    208             return false;
    209         }
    210 
    211         // Check for a proper propertyText termination (the parser could at least restore to the PROPERTY_NAME state).
    212         if (propertyData.at(propertyCount - 1).name != "-webkit-boguz-propertee") {
    213             *errorString = "Invalid property value";
    214             return false;
    215         }
    216     }
    217 
    218     if (overwrite) {
    219         ASSERT(index < allProperties.size());
    220         InspectorStyleProperty& property = allProperties.at(index);
    221         propertyStart = property.sourceData.range.start;
    222         unsigned propertyEnd = property.sourceData.range.end;
    223         unsigned oldLength = propertyEnd - propertyStart;
    224         unsigned newLength = propertyText.length();
    225         propertyLengthDelta = newLength - oldLength;
    226 
    227         if (!property.disabled) {
    228             bool success = replacePropertyInStyleText(property, propertyText);
    229             if (!success) {
    230                 *errorString = "Internal error: could not replace property value";
    231                 return false;
    232             }
    233         } else {
    234             unsigned textLength = propertyText.length();
    235             unsigned disabledIndex = disabledIndexByOrdinal(index, false, allProperties);
    236             if (!textLength) {
    237                 // Delete disabled property.
    238                 m_disabledProperties.remove(disabledIndex);
    239             } else {
    240                 // Patch disabled property text.
    241                 m_disabledProperties.at(disabledIndex).rawText = propertyText;
    242             }
    243 
    244             // We should not shift subsequent disabled properties when altering a disabled property.
    245             return true;
    246         }
    247     } else {
    248         // Insert at index.
    249         RefPtr<CSSRuleSourceData> sourceData = m_parentStyleSheet->ruleSourceDataFor(m_style.get());
    250         if (!sourceData) {
    251             *errorString = "Internal error: no CSS rule source found";
    252             return false;
    253         }
    254         String text;
    255         bool success = styleText(&text);
    256         if (!success) {
    257             *errorString = "Internal error: could not fetch style text";
    258             return false;
    259         }
    260         propertyLengthDelta = propertyText.length();
    261 
    262         bool insertLast = true;
    263         if (index < allProperties.size()) {
    264             InspectorStyleProperty& property = allProperties.at(index);
    265             if (property.hasSource) {
    266                 propertyStart = property.sourceData.range.start;
    267                 // If inserting before a disabled property, it should be shifted, too.
    268                 insertLast = false;
    269             }
    270         }
    271 
    272         String textToSet = propertyText;
    273         if (insertLast) {
    274             propertyStart = sourceData->styleSourceData->styleBodyRange.end - sourceData->styleSourceData->styleBodyRange.start;
    275             if (propertyStart && propertyText.length()) {
    276                 const UChar* characters = text.characters();
    277 
    278                 unsigned curPos = propertyStart - 1; // The last position of style declaration, since propertyStart points past one.
    279                 while (curPos && isHTMLSpace(characters[curPos]))
    280                     --curPos;
    281                 if (curPos && characters[curPos] != ';') {
    282                     // Prepend a ";" to the property text if appending to a style declaration where
    283                     // the last property has no trailing ";".
    284                     textToSet.insert("; ", 0);
    285                 } else if (!isHTMLSpace(characters[propertyStart - 1])) {
    286                     // Prepend a " " if the last declaration character is not an HTML space.
    287                     textToSet.insert(" ", 0);
    288                 }
    289             }
    290         }
    291 
    292         text.insert(textToSet, propertyStart);
    293         m_parentStyleSheet->setStyleText(m_style.get(), text);
    294     }
    295 
    296     // Recompute subsequent disabled property ranges if acting on a non-disabled property.
    297     shiftDisabledProperties(disabledIndexByOrdinal(index, true, allProperties), propertyLengthDelta);
    298 
    299     return true;
    300 }
    301 
    302 bool InspectorStyle::toggleProperty(ErrorString* errorString, unsigned index, bool disable)
    303 {
    304     ASSERT(m_parentStyleSheet);
    305     if (!m_parentStyleSheet->ensureParsedDataReady()) {
    306         *errorString = "Can toggle only source-based properties";
    307         return false;
    308     }
    309     RefPtr<CSSRuleSourceData> sourceData = m_parentStyleSheet->ruleSourceDataFor(m_style.get());
    310     if (!sourceData) {
    311         *errorString = "Internal error: No source data for the style found";
    312         return false;
    313     }
    314 
    315     Vector<InspectorStyleProperty> allProperties;
    316     populateAllProperties(&allProperties);
    317     if (index >= allProperties.size()) {
    318         *errorString = "Property index is outside of property range";
    319         return false;
    320     }
    321 
    322     InspectorStyleProperty& property = allProperties.at(index);
    323     if (property.disabled == disable)
    324         return true; // Idempotent operation.
    325 
    326     bool success;
    327     if (!disable)
    328         success = enableProperty(index, allProperties);
    329     else
    330         success = disableProperty(index, allProperties);
    331 
    332     return success;
    333 }
    334 
    335 // static
    336 unsigned InspectorStyle::disabledIndexByOrdinal(unsigned ordinal, bool canUseSubsequent, Vector<InspectorStyleProperty>& allProperties)
    337 {
    338     unsigned disabledIndex = 0;
    339     for (unsigned i = 0, size = allProperties.size(); i < size; ++i) {
    340         InspectorStyleProperty& property = allProperties.at(i);
    341         if (property.disabled) {
    342             if (i == ordinal || (canUseSubsequent && i > ordinal))
    343                 return disabledIndex;
    344             ++disabledIndex;
    345         }
    346     }
    347 
    348     return UINT_MAX;
    349 }
    350 
    351 bool InspectorStyle::styleText(String* result) const
    352 {
    353     // Precondition: m_parentStyleSheet->ensureParsedDataReady() has been called successfully.
    354     RefPtr<CSSRuleSourceData> sourceData = m_parentStyleSheet->ruleSourceDataFor(m_style.get());
    355     if (!sourceData)
    356         return false;
    357 
    358     String styleSheetText;
    359     bool success = m_parentStyleSheet->text(&styleSheetText);
    360     if (!success)
    361         return false;
    362 
    363     SourceRange& bodyRange = sourceData->styleSourceData->styleBodyRange;
    364     *result = styleSheetText.substring(bodyRange.start, bodyRange.end - bodyRange.start);
    365     return true;
    366 }
    367 
    368 bool InspectorStyle::disableProperty(unsigned indexToDisable, Vector<InspectorStyleProperty>& allProperties)
    369 {
    370     // Precondition: |indexToEnable| points to an enabled property.
    371     const InspectorStyleProperty& property = allProperties.at(indexToDisable);
    372     unsigned propertyStart = property.sourceData.range.start;
    373     InspectorStyleProperty disabledProperty(property);
    374     String oldStyleText;
    375     bool success = styleText(&oldStyleText);
    376     if (!success)
    377         return false;
    378     disabledProperty.setRawTextFromStyleDeclaration(oldStyleText);
    379     disabledProperty.disabled = true;
    380     disabledProperty.sourceData.range.end = propertyStart;
    381     // This may have to be negated below.
    382     long propertyLength = property.sourceData.range.end - propertyStart;
    383     success = replacePropertyInStyleText(property, "");
    384     if (!success)
    385         return false;
    386 
    387     // Add disabled property at correct position.
    388     unsigned insertionIndex = disabledIndexByOrdinal(indexToDisable, true, allProperties);
    389     if (insertionIndex == UINT_MAX)
    390         m_disabledProperties.append(disabledProperty);
    391     else {
    392         m_disabledProperties.insert(insertionIndex, disabledProperty);
    393         shiftDisabledProperties(insertionIndex + 1, -propertyLength); // Property removed from text - shift these back.
    394     }
    395     return true;
    396 }
    397 
    398 bool InspectorStyle::enableProperty(unsigned indexToEnable, Vector<InspectorStyleProperty>& allProperties)
    399 {
    400     // Precondition: |indexToEnable| points to a disabled property.
    401     unsigned disabledIndex = disabledIndexByOrdinal(indexToEnable, false, allProperties);
    402     if (disabledIndex == UINT_MAX)
    403         return false;
    404 
    405     InspectorStyleProperty disabledProperty = m_disabledProperties.at(disabledIndex);
    406     m_disabledProperties.remove(disabledIndex);
    407     bool success = replacePropertyInStyleText(disabledProperty, disabledProperty.rawText);
    408     if (success)
    409         shiftDisabledProperties(disabledIndex, disabledProperty.rawText.length());
    410     return success;
    411 }
    412 
    413 bool InspectorStyle::populateAllProperties(Vector<InspectorStyleProperty>* result) const
    414 {
    415     HashSet<String> foundShorthands;
    416     HashSet<String> sourcePropertyNames;
    417     unsigned disabledIndex = 0;
    418     unsigned disabledLength = m_disabledProperties.size();
    419     InspectorStyleProperty disabledProperty;
    420     if (disabledIndex < disabledLength)
    421         disabledProperty = m_disabledProperties.at(disabledIndex);
    422 
    423     RefPtr<CSSRuleSourceData> sourceData = (m_parentStyleSheet && m_parentStyleSheet->ensureParsedDataReady()) ? m_parentStyleSheet->ruleSourceDataFor(m_style.get()) : 0;
    424     Vector<CSSPropertySourceData>* sourcePropertyData = sourceData ? &(sourceData->styleSourceData->propertyData) : 0;
    425     if (sourcePropertyData) {
    426         String styleDeclaration;
    427         bool isStyleTextKnown = styleText(&styleDeclaration);
    428         ASSERT_UNUSED(isStyleTextKnown, isStyleTextKnown);
    429         for (Vector<CSSPropertySourceData>::const_iterator it = sourcePropertyData->begin(); it != sourcePropertyData->end(); ++it) {
    430             while (disabledIndex < disabledLength && disabledProperty.sourceData.range.start <= it->range.start) {
    431                 result->append(disabledProperty);
    432                 if (++disabledIndex < disabledLength)
    433                     disabledProperty = m_disabledProperties.at(disabledIndex);
    434             }
    435             InspectorStyleProperty p(*it, true, false);
    436             p.setRawTextFromStyleDeclaration(styleDeclaration);
    437             result->append(p);
    438             sourcePropertyNames.add(it->name.lower());
    439         }
    440     }
    441 
    442     while (disabledIndex < disabledLength) {
    443         disabledProperty = m_disabledProperties.at(disabledIndex++);
    444         result->append(disabledProperty);
    445     }
    446 
    447     for (int i = 0, size = m_style->length(); i < size; ++i) {
    448         String name = m_style->item(i);
    449         if (sourcePropertyNames.contains(name.lower()))
    450             continue;
    451 
    452         sourcePropertyNames.add(name.lower());
    453         result->append(InspectorStyleProperty(CSSPropertySourceData(name, m_style->getPropertyValue(name), !m_style->getPropertyPriority(name).isEmpty(), true, SourceRange()), false, false));
    454     }
    455 
    456     return true;
    457 }
    458 
    459 void InspectorStyle::populateObjectWithStyleProperties(InspectorObject* result) const
    460 {
    461     Vector<InspectorStyleProperty> properties;
    462     populateAllProperties(&properties);
    463 
    464     RefPtr<InspectorArray> propertiesObject = InspectorArray::create();
    465     RefPtr<InspectorArray> shorthandEntries = InspectorArray::create();
    466     HashMap<String, RefPtr<InspectorObject> > propertyNameToPreviousActiveProperty;
    467     HashSet<String> foundShorthands;
    468 
    469     for (Vector<InspectorStyleProperty>::iterator it = properties.begin(), itEnd = properties.end(); it != itEnd; ++it) {
    470         const CSSPropertySourceData& propertyEntry = it->sourceData;
    471         const String& name = propertyEntry.name;
    472 
    473         RefPtr<InspectorObject> property = InspectorObject::create();
    474         propertiesObject->pushObject(property);
    475         String status = it->disabled ? "disabled" : "active";
    476 
    477         // Default "parsedOk" == true.
    478         if (!propertyEntry.parsedOk)
    479             property->setBoolean("parsedOk", false);
    480         if (it->hasRawText())
    481             property->setString("text", it->rawText);
    482         property->setString("name", name);
    483         property->setString("value", propertyEntry.value);
    484 
    485         // Default "priority" == "".
    486         if (propertyEntry.important)
    487             property->setString("priority", "important");
    488         if (!it->disabled) {
    489             if (it->hasSource) {
    490                 property->setBoolean("implicit", false);
    491                 property->setObject("range", buildSourceRangeObject(propertyEntry.range));
    492 
    493                 // Parsed property overrides any property with the same name. Non-parsed property overrides
    494                 // previous non-parsed property with the same name (if any).
    495                 bool shouldInactivate = false;
    496                 HashMap<String, RefPtr<InspectorObject> >::iterator activeIt = propertyNameToPreviousActiveProperty.find(name);
    497                 if (activeIt != propertyNameToPreviousActiveProperty.end()) {
    498                     if (propertyEntry.parsedOk)
    499                         shouldInactivate = true;
    500                     else {
    501                         bool previousParsedOk;
    502                         bool success = activeIt->second->getBoolean("parsedOk", &previousParsedOk);
    503                         if (success && !previousParsedOk)
    504                             shouldInactivate = true;
    505                     }
    506                 } else
    507                     propertyNameToPreviousActiveProperty.set(name, property);
    508 
    509                 if (shouldInactivate) {
    510                     activeIt->second->setString("status", "inactive");
    511                     activeIt->second->remove("shorthandName");
    512                     propertyNameToPreviousActiveProperty.set(name, property);
    513                 }
    514             } else {
    515                 bool implicit = m_style->isPropertyImplicit(name);
    516                 // Default "implicit" == false.
    517                 if (implicit)
    518                     property->setBoolean("implicit", true);
    519                 status = "";
    520             }
    521         }
    522 
    523         // Default "status" == "style".
    524         if (!status.isEmpty())
    525             property->setString("status", status);
    526 
    527         if (propertyEntry.parsedOk) {
    528             // Both for style-originated and parsed source properties.
    529             String shorthand = m_style->getPropertyShorthand(name);
    530             if (!shorthand.isEmpty()) {
    531                 // Default "shorthandName" == "".
    532                 property->setString("shorthandName", shorthand);
    533                 if (!foundShorthands.contains(shorthand)) {
    534                     foundShorthands.add(shorthand);
    535                     RefPtr<InspectorObject> shorthandEntry = InspectorObject::create();
    536                     shorthandEntry->setString("name", shorthand);
    537                     shorthandEntry->setString("value", shorthandValue(shorthand));
    538                     shorthandEntries->pushObject(shorthandEntry.release());
    539                 }
    540             }
    541         }
    542         // else shorthandName is not set
    543     }
    544 
    545     result->setArray("cssProperties", propertiesObject);
    546     result->setArray("shorthandEntries", shorthandEntries);
    547 }
    548 
    549 
    550 void InspectorStyle::shiftDisabledProperties(unsigned fromIndex, long delta)
    551 {
    552     for (unsigned i = fromIndex, size = m_disabledProperties.size(); i < size; ++i) {
    553         SourceRange& range = m_disabledProperties.at(i).sourceData.range;
    554         range.start += delta;
    555         range.end += delta;
    556     }
    557 }
    558 
    559 bool InspectorStyle::replacePropertyInStyleText(const InspectorStyleProperty& property, const String& newText)
    560 {
    561     // Precondition: m_parentStyleSheet->ensureParsedDataReady() has been called successfully.
    562     String text;
    563     bool success = styleText(&text);
    564     if (!success)
    565         return false;
    566     const SourceRange& range = property.sourceData.range;
    567     text.replace(range.start, range.end - range.start, newText);
    568     success = m_parentStyleSheet->setStyleText(m_style.get(), text);
    569     return success;
    570 }
    571 
    572 String InspectorStyle::shorthandValue(const String& shorthandProperty) const
    573 {
    574     String value = m_style->getPropertyValue(shorthandProperty);
    575     if (value.isEmpty()) {
    576         for (unsigned i = 0; i < m_style->length(); ++i) {
    577             String individualProperty = m_style->item(i);
    578             if (m_style->getPropertyShorthand(individualProperty) != shorthandProperty)
    579                 continue;
    580             if (m_style->isPropertyImplicit(individualProperty))
    581                 continue;
    582             String individualValue = m_style->getPropertyValue(individualProperty);
    583             if (individualValue == "initial")
    584                 continue;
    585             if (value.length())
    586                 value.append(" ");
    587             value.append(individualValue);
    588         }
    589     }
    590     return value;
    591 }
    592 
    593 String InspectorStyle::shorthandPriority(const String& shorthandProperty) const
    594 {
    595     String priority = m_style->getPropertyPriority(shorthandProperty);
    596     if (priority.isEmpty()) {
    597         for (unsigned i = 0; i < m_style->length(); ++i) {
    598             String individualProperty = m_style->item(i);
    599             if (m_style->getPropertyShorthand(individualProperty) != shorthandProperty)
    600                 continue;
    601             priority = m_style->getPropertyPriority(individualProperty);
    602             break;
    603         }
    604     }
    605     return priority;
    606 }
    607 
    608 Vector<String> InspectorStyle::longhandProperties(const String& shorthandProperty) const
    609 {
    610     Vector<String> properties;
    611     HashSet<String> foundProperties;
    612     for (unsigned i = 0; i < m_style->length(); ++i) {
    613         String individualProperty = m_style->item(i);
    614         if (foundProperties.contains(individualProperty) || m_style->getPropertyShorthand(individualProperty) != shorthandProperty)
    615             continue;
    616 
    617         foundProperties.add(individualProperty);
    618         properties.append(individualProperty);
    619     }
    620     return properties;
    621 }
    622 
    623 PassRefPtr<InspectorStyleSheet> InspectorStyleSheet::create(const String& id, PassRefPtr<CSSStyleSheet> pageStyleSheet, const String& origin, const String& documentURL)
    624 {
    625     return adoptRef(new InspectorStyleSheet(id, pageStyleSheet, origin, documentURL));
    626 }
    627 
    628 InspectorStyleSheet::InspectorStyleSheet(const String& id, PassRefPtr<CSSStyleSheet> pageStyleSheet, const String& origin, const String& documentURL)
    629     : m_id(id)
    630     , m_pageStyleSheet(pageStyleSheet)
    631     , m_origin(origin)
    632     , m_documentURL(documentURL)
    633     , m_isRevalidating(false)
    634 {
    635     m_parsedStyleSheet = new ParsedStyleSheet();
    636 }
    637 
    638 InspectorStyleSheet::~InspectorStyleSheet()
    639 {
    640     delete m_parsedStyleSheet;
    641 }
    642 
    643 String InspectorStyleSheet::finalURL() const
    644 {
    645     if (m_pageStyleSheet && !m_pageStyleSheet->finalURL().isEmpty())
    646         return m_pageStyleSheet->finalURL().string();
    647     return m_documentURL;
    648 }
    649 
    650 void InspectorStyleSheet::reparseStyleSheet(const String& text)
    651 {
    652     for (unsigned i = 0, size = m_pageStyleSheet->length(); i < size; ++i)
    653         m_pageStyleSheet->remove(0);
    654     m_pageStyleSheet->parseString(text, m_pageStyleSheet->useStrictParsing());
    655     m_pageStyleSheet->styleSheetChanged();
    656     m_inspectorStyles.clear();
    657 }
    658 
    659 bool InspectorStyleSheet::setText(const String& text)
    660 {
    661     if (!m_parsedStyleSheet)
    662         return false;
    663 
    664     m_parsedStyleSheet->setText(text);
    665     m_flatRules.clear();
    666 
    667     return true;
    668 }
    669 
    670 bool InspectorStyleSheet::setRuleSelector(const InspectorCSSId& id, const String& selector)
    671 {
    672     CSSStyleRule* rule = ruleForId(id);
    673     if (!rule)
    674         return false;
    675     CSSStyleSheet* styleSheet = InspectorCSSAgent::parentStyleSheet(rule);
    676     if (!styleSheet || !ensureParsedDataReady())
    677         return false;
    678 
    679     rule->setSelectorText(selector);
    680     RefPtr<CSSRuleSourceData> sourceData = ruleSourceDataFor(rule->style());
    681     if (!sourceData)
    682         return false;
    683 
    684     String sheetText = m_parsedStyleSheet->text();
    685     sheetText.replace(sourceData->selectorListRange.start, sourceData->selectorListRange.end - sourceData->selectorListRange.start, selector);
    686     m_parsedStyleSheet->setText(sheetText);
    687     return true;
    688 }
    689 
    690 CSSStyleRule* InspectorStyleSheet::addRule(const String& selector)
    691 {
    692     String styleSheetText;
    693     bool success = text(&styleSheetText);
    694     if (!success)
    695         return 0;
    696 
    697     ExceptionCode ec = 0;
    698     m_pageStyleSheet->addRule(selector, "", ec);
    699     if (ec)
    700         return 0;
    701     RefPtr<CSSRuleList> rules = m_pageStyleSheet->cssRules();
    702     ASSERT(rules->length());
    703     CSSStyleRule* rule = InspectorCSSAgent::asCSSStyleRule(rules->item(rules->length() - 1));
    704     ASSERT(rule);
    705 
    706     if (styleSheetText.length())
    707         styleSheetText += "\n";
    708 
    709     styleSheetText += selector;
    710     styleSheetText += " {}";
    711     // Using setText() as this operation changes the style sheet rule set.
    712     setText(styleSheetText);
    713 
    714     return rule;
    715 }
    716 
    717 CSSStyleRule* InspectorStyleSheet::ruleForId(const InspectorCSSId& id) const
    718 {
    719     if (!m_pageStyleSheet)
    720         return 0;
    721 
    722     ASSERT(!id.isEmpty());
    723     ensureFlatRules();
    724     return id.ordinal() >= m_flatRules.size() ? 0 : m_flatRules.at(id.ordinal());
    725 
    726 }
    727 
    728 PassRefPtr<InspectorObject> InspectorStyleSheet::buildObjectForStyleSheet()
    729 {
    730     CSSStyleSheet* styleSheet = pageStyleSheet();
    731     if (!styleSheet)
    732         return 0;
    733 
    734     RefPtr<InspectorObject> result = InspectorObject::create();
    735     result->setString("styleSheetId", id());
    736     RefPtr<CSSRuleList> cssRuleList = CSSRuleList::create(styleSheet, true);
    737     RefPtr<InspectorArray> cssRules = buildArrayForRuleList(cssRuleList.get());
    738     result->setArray("rules", cssRules.release());
    739 
    740     String styleSheetText;
    741     bool success = text(&styleSheetText);
    742     if (success)
    743         result->setString("text", styleSheetText);
    744 
    745     return result.release();
    746 }
    747 
    748 PassRefPtr<InspectorObject> InspectorStyleSheet::buildObjectForStyleSheetInfo()
    749 {
    750     CSSStyleSheet* styleSheet = pageStyleSheet();
    751     if (!styleSheet)
    752         return 0;
    753 
    754     RefPtr<InspectorObject> result = InspectorObject::create();
    755     result->setString("styleSheetId", id());
    756     result->setBoolean("disabled", styleSheet->disabled());
    757     result->setString("sourceURL", finalURL());
    758     result->setString("title", styleSheet->title());
    759     return result.release();
    760 }
    761 
    762 PassRefPtr<InspectorObject> InspectorStyleSheet::buildObjectForRule(CSSStyleRule* rule)
    763 {
    764     CSSStyleSheet* styleSheet = pageStyleSheet();
    765     if (!styleSheet)
    766         return 0;
    767 
    768     RefPtr<InspectorObject> result = InspectorObject::create();
    769     result->setString("selectorText", rule->selectorText());
    770     // "sourceURL" is present only for regular rules, otherwise "origin" should be used in the frontend.
    771     if (!m_origin.length())
    772         result->setString("sourceURL", finalURL());
    773     result->setNumber("sourceLine", rule->sourceLine());
    774     result->setString("origin", m_origin);
    775 
    776     result->setObject("style", buildObjectForStyle(rule->style()));
    777     if (canBind()) {
    778         InspectorCSSId id(ruleId(rule));
    779         if (!id.isEmpty())
    780             result->setValue("ruleId", id.asInspectorValue());
    781     }
    782 
    783     RefPtr<CSSRuleSourceData> sourceData;
    784     if (ensureParsedDataReady())
    785         sourceData = ruleSourceDataFor(rule->style());
    786     if (sourceData) {
    787         RefPtr<InspectorObject> selectorRange = InspectorObject::create();
    788         selectorRange->setNumber("start", sourceData->selectorListRange.start);
    789         selectorRange->setNumber("end", sourceData->selectorListRange.end);
    790         result->setObject("selectorRange", selectorRange.release());
    791     }
    792 
    793     return result.release();
    794 }
    795 
    796 PassRefPtr<InspectorObject> InspectorStyleSheet::buildObjectForStyle(CSSStyleDeclaration* style)
    797 {
    798     RefPtr<CSSRuleSourceData> sourceData;
    799     if (ensureParsedDataReady())
    800         sourceData = ruleSourceDataFor(style);
    801 
    802     InspectorCSSId id = ruleOrStyleId(style);
    803     if (id.isEmpty()) {
    804         RefPtr<InspectorObject> bogusStyle = InspectorObject::create();
    805         bogusStyle->setArray("cssProperties", InspectorArray::create());
    806         bogusStyle->setObject("shorthandValues", InspectorObject::create());
    807         bogusStyle->setObject("properties", InspectorObject::create());
    808         return bogusStyle.release();
    809     }
    810     RefPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id);
    811     RefPtr<InspectorObject> result = inspectorStyle->buildObjectForStyle();
    812 
    813     // Style text cannot be retrieved without stylesheet, so set cssText here.
    814     if (sourceData) {
    815         String sheetText;
    816         bool success = text(&sheetText);
    817         if (success) {
    818             const SourceRange& bodyRange = sourceData->styleSourceData->styleBodyRange;
    819             result->setString("cssText", sheetText.substring(bodyRange.start, bodyRange.end - bodyRange.start));
    820         }
    821     }
    822 
    823     return result.release();
    824 }
    825 
    826 bool InspectorStyleSheet::setPropertyText(ErrorString* errorString, const InspectorCSSId& id, unsigned propertyIndex, const String& text, bool overwrite)
    827 {
    828     RefPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id);
    829     if (!inspectorStyle) {
    830         *errorString = "No style found for given id";
    831         return false;
    832     }
    833 
    834     return inspectorStyle->setPropertyText(errorString, propertyIndex, text, overwrite);
    835 }
    836 
    837 bool InspectorStyleSheet::toggleProperty(ErrorString* errorString, const InspectorCSSId& id, unsigned propertyIndex, bool disable)
    838 {
    839     RefPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id);
    840     if (!inspectorStyle) {
    841         *errorString = "No style found for given id";
    842         return false;
    843     }
    844 
    845     bool success = inspectorStyle->toggleProperty(errorString, propertyIndex, disable);
    846     if (success) {
    847         if (disable)
    848             rememberInspectorStyle(inspectorStyle);
    849         else if (!inspectorStyle->hasDisabledProperties())
    850             forgetInspectorStyle(inspectorStyle->cssStyle());
    851     }
    852     return success;
    853 }
    854 
    855 bool InspectorStyleSheet::text(String* result) const
    856 {
    857     if (!ensureText())
    858         return false;
    859     *result = m_parsedStyleSheet->text();
    860     return true;
    861 }
    862 
    863 CSSStyleDeclaration* InspectorStyleSheet::styleForId(const InspectorCSSId& id) const
    864 {
    865     CSSStyleRule* rule = ruleForId(id);
    866     if (!rule)
    867         return 0;
    868 
    869     return rule->style();
    870 }
    871 
    872 PassRefPtr<InspectorStyle> InspectorStyleSheet::inspectorStyleForId(const InspectorCSSId& id)
    873 {
    874     CSSStyleDeclaration* style = styleForId(id);
    875     if (!style)
    876         return 0;
    877 
    878     InspectorStyleMap::iterator it = m_inspectorStyles.find(style);
    879     if (it == m_inspectorStyles.end()) {
    880         RefPtr<InspectorStyle> inspectorStyle = InspectorStyle::create(id, style, this);
    881         return inspectorStyle.release();
    882     }
    883     return it->second;
    884 }
    885 
    886 void InspectorStyleSheet::rememberInspectorStyle(RefPtr<InspectorStyle> inspectorStyle)
    887 {
    888     m_inspectorStyles.set(inspectorStyle->cssStyle(), inspectorStyle);
    889 }
    890 
    891 void InspectorStyleSheet::forgetInspectorStyle(CSSStyleDeclaration* style)
    892 {
    893     m_inspectorStyles.remove(style);
    894 }
    895 
    896 InspectorCSSId InspectorStyleSheet::ruleOrStyleId(CSSStyleDeclaration* style) const
    897 {
    898     unsigned index = ruleIndexByStyle(style);
    899     if (index != UINT_MAX)
    900         return InspectorCSSId(id(), index);
    901     return InspectorCSSId();
    902 }
    903 
    904 Document* InspectorStyleSheet::ownerDocument() const
    905 {
    906     return m_pageStyleSheet->document();
    907 }
    908 
    909 RefPtr<CSSRuleSourceData> InspectorStyleSheet::ruleSourceDataFor(CSSStyleDeclaration* style) const
    910 {
    911     return m_parsedStyleSheet->ruleSourceDataAt(ruleIndexByStyle(style));
    912 }
    913 
    914 unsigned InspectorStyleSheet::ruleIndexByStyle(CSSStyleDeclaration* pageStyle) const
    915 {
    916     ensureFlatRules();
    917     unsigned index = 0;
    918     for (unsigned i = 0, size = m_flatRules.size(); i < size; ++i) {
    919         if (m_flatRules.at(i)->style() == pageStyle)
    920             return index;
    921 
    922         ++index;
    923     }
    924     return UINT_MAX;
    925 }
    926 
    927 bool InspectorStyleSheet::ensureParsedDataReady()
    928 {
    929     return ensureText() && ensureSourceData();
    930 }
    931 
    932 bool InspectorStyleSheet::ensureText() const
    933 {
    934     if (!m_parsedStyleSheet)
    935         return false;
    936     if (m_parsedStyleSheet->hasText())
    937         return true;
    938 
    939     String text;
    940     bool success = originalStyleSheetText(&text);
    941     if (success)
    942         m_parsedStyleSheet->setText(text);
    943     // No need to clear m_flatRules here - it's empty.
    944 
    945     return success;
    946 }
    947 
    948 bool InspectorStyleSheet::ensureSourceData()
    949 {
    950     if (m_parsedStyleSheet->hasSourceData())
    951         return true;
    952 
    953     if (!m_parsedStyleSheet->hasText())
    954         return false;
    955 
    956     RefPtr<CSSStyleSheet> newStyleSheet = CSSStyleSheet::create();
    957     CSSParser p;
    958     StyleRuleRangeMap ruleRangeMap;
    959     p.parseSheet(newStyleSheet.get(), m_parsedStyleSheet->text(), 0, &ruleRangeMap);
    960     OwnPtr<ParsedStyleSheet::SourceData> rangesVector(new ParsedStyleSheet::SourceData());
    961 
    962     Vector<CSSStyleRule*> rules;
    963     RefPtr<CSSRuleList> ruleList = asCSSRuleList(newStyleSheet.get());
    964     collectFlatRules(ruleList, &rules);
    965     for (unsigned i = 0, size = rules.size(); i < size; ++i) {
    966         StyleRuleRangeMap::iterator it = ruleRangeMap.find(rules.at(i));
    967         if (it != ruleRangeMap.end()) {
    968             fixUnparsedPropertyRanges(it->second.get(), m_parsedStyleSheet->text());
    969             rangesVector->append(it->second);
    970         }
    971     }
    972 
    973     m_parsedStyleSheet->setSourceData(rangesVector.release());
    974     return m_parsedStyleSheet->hasSourceData();
    975 }
    976 
    977 void InspectorStyleSheet::ensureFlatRules() const
    978 {
    979     // We are fine with redoing this for empty stylesheets as this will run fast.
    980     if (m_flatRules.isEmpty())
    981         collectFlatRules(asCSSRuleList(pageStyleSheet()), &m_flatRules);
    982 }
    983 
    984 bool InspectorStyleSheet::setStyleText(CSSStyleDeclaration* style, const String& text)
    985 {
    986     if (!pageStyleSheet())
    987         return false;
    988     if (!ensureParsedDataReady())
    989         return false;
    990 
    991     String patchedStyleSheetText;
    992     bool success = styleSheetTextWithChangedStyle(style, text, &patchedStyleSheetText);
    993     if (!success)
    994         return false;
    995 
    996     InspectorCSSId id = ruleOrStyleId(style);
    997     if (id.isEmpty())
    998         return false;
    999 
   1000     ExceptionCode ec = 0;
   1001     style->setCssText(text, ec);
   1002     if (!ec)
   1003         m_parsedStyleSheet->setText(patchedStyleSheetText);
   1004 
   1005     return !ec;
   1006 }
   1007 
   1008 bool InspectorStyleSheet::styleSheetTextWithChangedStyle(CSSStyleDeclaration* style, const String& newStyleText, String* result)
   1009 {
   1010     if (!style)
   1011         return false;
   1012 
   1013     if (!ensureParsedDataReady())
   1014         return false;
   1015 
   1016     RefPtr<CSSRuleSourceData> sourceData = ruleSourceDataFor(style);
   1017     unsigned bodyStart = sourceData->styleSourceData->styleBodyRange.start;
   1018     unsigned bodyEnd = sourceData->styleSourceData->styleBodyRange.end;
   1019     ASSERT(bodyStart <= bodyEnd);
   1020 
   1021     String text = m_parsedStyleSheet->text();
   1022     ASSERT(bodyEnd <= text.length()); // bodyEnd is exclusive
   1023 
   1024     text.replace(bodyStart, bodyEnd - bodyStart, newStyleText);
   1025     *result = text;
   1026     return true;
   1027 }
   1028 
   1029 InspectorCSSId InspectorStyleSheet::ruleId(CSSStyleRule* rule) const
   1030 {
   1031     return ruleOrStyleId(rule->style());
   1032 }
   1033 
   1034 void InspectorStyleSheet::revalidateStyle(CSSStyleDeclaration* pageStyle)
   1035 {
   1036     if (m_isRevalidating)
   1037         return;
   1038 
   1039     m_isRevalidating = true;
   1040     ensureFlatRules();
   1041     for (unsigned i = 0, size = m_flatRules.size(); i < size; ++i) {
   1042         CSSStyleRule* parsedRule = m_flatRules.at(i);
   1043         if (parsedRule->style() == pageStyle) {
   1044             if (parsedRule->style()->cssText() != pageStyle->cssText()) {
   1045                 // Clear the disabled properties for the invalid style here.
   1046                 m_inspectorStyles.remove(pageStyle);
   1047                 setStyleText(pageStyle, pageStyle->cssText());
   1048             }
   1049             break;
   1050         }
   1051     }
   1052     m_isRevalidating = false;
   1053 }
   1054 
   1055 bool InspectorStyleSheet::originalStyleSheetText(String* result) const
   1056 {
   1057     String text;
   1058     bool success = inlineStyleSheetText(&text);
   1059     if (!success)
   1060         success = resourceStyleSheetText(&text);
   1061     if (success)
   1062         *result = text;
   1063     return success;
   1064 }
   1065 
   1066 bool InspectorStyleSheet::resourceStyleSheetText(String* result) const
   1067 {
   1068     if (m_origin == "user" || m_origin == "user-agent")
   1069         return false;
   1070 
   1071     if (!m_pageStyleSheet || !ownerDocument())
   1072         return false;
   1073 
   1074     String error;
   1075     InspectorResourceAgent::resourceContent(&error, ownerDocument()->frame(), m_pageStyleSheet->finalURL(), result);
   1076     return error.isEmpty();
   1077 }
   1078 
   1079 bool InspectorStyleSheet::inlineStyleSheetText(String* result) const
   1080 {
   1081     if (!m_pageStyleSheet)
   1082         return false;
   1083 
   1084     Node* ownerNode = m_pageStyleSheet->ownerNode();
   1085     if (!ownerNode || ownerNode->nodeType() != Node::ELEMENT_NODE)
   1086         return false;
   1087     Element* ownerElement = static_cast<Element*>(ownerNode);
   1088     if (ownerElement->tagName().lower() != "style")
   1089         return false;
   1090     *result = ownerElement->innerText();
   1091     return true;
   1092 }
   1093 
   1094 PassRefPtr<InspectorArray> InspectorStyleSheet::buildArrayForRuleList(CSSRuleList* ruleList)
   1095 {
   1096     RefPtr<InspectorArray> result = InspectorArray::create();
   1097     if (!ruleList)
   1098         return result.release();
   1099 
   1100     RefPtr<CSSRuleList> refRuleList = ruleList;
   1101     Vector<CSSStyleRule*> rules;
   1102     collectFlatRules(refRuleList, &rules);
   1103 
   1104     for (unsigned i = 0, size = rules.size(); i < size; ++i)
   1105         result->pushObject(buildObjectForRule(rules.at(i)));
   1106 
   1107     return result.release();
   1108 }
   1109 
   1110 void InspectorStyleSheet::fixUnparsedPropertyRanges(CSSRuleSourceData* ruleData, const String& styleSheetText)
   1111 {
   1112     Vector<CSSPropertySourceData>& propertyData = ruleData->styleSourceData->propertyData;
   1113     unsigned size = propertyData.size();
   1114     if (!size)
   1115         return;
   1116 
   1117     unsigned styleStart = ruleData->styleSourceData->styleBodyRange.start;
   1118     const UChar* characters = styleSheetText.characters();
   1119     CSSPropertySourceData* nextData = &(propertyData.at(0));
   1120     for (unsigned i = 0; i < size; ++i) {
   1121         CSSPropertySourceData* currentData = nextData;
   1122         nextData = i < size - 1 ? &(propertyData.at(i + 1)) : 0;
   1123 
   1124         if (currentData->parsedOk)
   1125             continue;
   1126         if (currentData->range.end > 0 && characters[styleStart + currentData->range.end - 1] == ';')
   1127             continue;
   1128 
   1129         unsigned propertyEndInStyleSheet;
   1130         if (!nextData)
   1131             propertyEndInStyleSheet = ruleData->styleSourceData->styleBodyRange.end - 1;
   1132         else
   1133             propertyEndInStyleSheet = styleStart + nextData->range.start - 1;
   1134 
   1135         while (isHTMLSpace(characters[propertyEndInStyleSheet]))
   1136             --propertyEndInStyleSheet;
   1137 
   1138         // propertyEndInStyleSheet points at the last property text character.
   1139         unsigned newPropertyEnd = propertyEndInStyleSheet - styleStart + 1; // Exclusive of the last property text character.
   1140         if (currentData->range.end != newPropertyEnd) {
   1141             currentData->range.end = newPropertyEnd;
   1142             unsigned valueStartInStyleSheet = styleStart + currentData->range.start + currentData->name.length();
   1143             while (valueStartInStyleSheet < propertyEndInStyleSheet && characters[valueStartInStyleSheet] != ':')
   1144                 ++valueStartInStyleSheet;
   1145             if (valueStartInStyleSheet < propertyEndInStyleSheet)
   1146                 ++valueStartInStyleSheet; // Shift past the ':'.
   1147             while (valueStartInStyleSheet < propertyEndInStyleSheet && isHTMLSpace(characters[valueStartInStyleSheet]))
   1148                 ++valueStartInStyleSheet;
   1149             // Need to exclude the trailing ';' from the property value.
   1150             currentData->value = styleSheetText.substring(valueStartInStyleSheet, propertyEndInStyleSheet - valueStartInStyleSheet + (characters[propertyEndInStyleSheet] == ';' ? 0 : 1));
   1151         }
   1152     }
   1153 }
   1154 
   1155 void InspectorStyleSheet::collectFlatRules(PassRefPtr<CSSRuleList> ruleList, Vector<CSSStyleRule*>* result)
   1156 {
   1157     if (!ruleList)
   1158         return;
   1159 
   1160     for (unsigned i = 0, size = ruleList->length(); i < size; ++i) {
   1161         CSSRule* rule = ruleList->item(i);
   1162         CSSStyleRule* styleRule = InspectorCSSAgent::asCSSStyleRule(rule);
   1163         if (styleRule)
   1164             result->append(styleRule);
   1165         else {
   1166             RefPtr<CSSRuleList> childRuleList = asCSSRuleList(rule);
   1167             if (childRuleList)
   1168                 collectFlatRules(childRuleList, result);
   1169         }
   1170     }
   1171 }
   1172 
   1173 PassRefPtr<InspectorStyleSheetForInlineStyle> InspectorStyleSheetForInlineStyle::create(const String& id, PassRefPtr<Element> element, const String& origin)
   1174 {
   1175     return adoptRef(new InspectorStyleSheetForInlineStyle(id, element, origin));
   1176 }
   1177 
   1178 InspectorStyleSheetForInlineStyle::InspectorStyleSheetForInlineStyle(const String& id, PassRefPtr<Element> element, const String& origin)
   1179     : InspectorStyleSheet(id, 0, origin, "")
   1180     , m_element(element)
   1181     , m_ruleSourceData(0)
   1182 {
   1183     ASSERT(m_element);
   1184     m_inspectorStyle = InspectorStyle::create(InspectorCSSId(id, 0), inlineStyle(), this);
   1185     m_styleText = m_element->isStyledElement() ? m_element->getAttribute("style").string() : String();
   1186 }
   1187 
   1188 void InspectorStyleSheetForInlineStyle::didModifyElementAttribute()
   1189 {
   1190     String newStyleText = elementStyleText();
   1191     bool shouldDropSourceData = false;
   1192     if (m_element->isStyledElement() && m_element->style() != m_inspectorStyle->cssStyle()) {
   1193         m_inspectorStyle = InspectorStyle::create(InspectorCSSId(id(), 0), inlineStyle(), this);
   1194         shouldDropSourceData = true;
   1195     }
   1196     if (newStyleText != m_styleText) {
   1197         m_styleText = newStyleText;
   1198         shouldDropSourceData = true;
   1199     }
   1200     if (shouldDropSourceData)
   1201         m_ruleSourceData.clear();
   1202 }
   1203 
   1204 bool InspectorStyleSheetForInlineStyle::text(String* result) const
   1205 {
   1206     *result = m_styleText;
   1207     return true;
   1208 }
   1209 
   1210 bool InspectorStyleSheetForInlineStyle::setStyleText(CSSStyleDeclaration* style, const String& text)
   1211 {
   1212     ASSERT_UNUSED(style, style == inlineStyle());
   1213     ExceptionCode ec = 0;
   1214     m_element->setAttribute("style", text, ec);
   1215     m_styleText = text;
   1216     m_ruleSourceData.clear();
   1217     return !ec;
   1218 }
   1219 
   1220 Document* InspectorStyleSheetForInlineStyle::ownerDocument() const
   1221 {
   1222     return m_element->document();
   1223 }
   1224 
   1225 bool InspectorStyleSheetForInlineStyle::ensureParsedDataReady()
   1226 {
   1227     // The "style" property value can get changed indirectly, e.g. via element.style.borderWidth = "2px".
   1228     const String& currentStyleText = elementStyleText();
   1229     if (m_styleText != currentStyleText) {
   1230         m_ruleSourceData.clear();
   1231         m_styleText = currentStyleText;
   1232     }
   1233 
   1234     if (m_ruleSourceData)
   1235         return true;
   1236 
   1237     m_ruleSourceData = CSSRuleSourceData::create();
   1238     RefPtr<CSSStyleSourceData> sourceData = CSSStyleSourceData::create();
   1239     bool success = getStyleAttributeRanges(&sourceData);
   1240     if (!success)
   1241         return false;
   1242 
   1243     m_ruleSourceData->styleSourceData = sourceData.release();
   1244     return true;
   1245 }
   1246 
   1247 PassRefPtr<InspectorStyle> InspectorStyleSheetForInlineStyle::inspectorStyleForId(const InspectorCSSId& id)
   1248 {
   1249     ASSERT_UNUSED(id, !id.ordinal());
   1250     return m_inspectorStyle;
   1251 }
   1252 
   1253 CSSStyleDeclaration* InspectorStyleSheetForInlineStyle::inlineStyle() const
   1254 {
   1255     return m_element->style();
   1256 }
   1257 
   1258 const String& InspectorStyleSheetForInlineStyle::elementStyleText() const
   1259 {
   1260     return m_element->getAttribute("style").string();
   1261 }
   1262 
   1263 bool InspectorStyleSheetForInlineStyle::getStyleAttributeRanges(RefPtr<CSSStyleSourceData>* result)
   1264 {
   1265     if (!m_element->isStyledElement())
   1266         return false;
   1267 
   1268     if (m_styleText.isEmpty()) {
   1269         (*result)->styleBodyRange.start = 0;
   1270         (*result)->styleBodyRange.end = 0;
   1271         return true;
   1272     }
   1273 
   1274     RefPtr<CSSMutableStyleDeclaration> tempDeclaration = CSSMutableStyleDeclaration::create();
   1275     CSSParser p;
   1276     p.parseDeclaration(tempDeclaration.get(), m_styleText, result);
   1277     return true;
   1278 }
   1279 
   1280 } // namespace WebCore
   1281 
   1282 #endif // ENABLE(INSPECTOR)
   1283