| /* |
| * Copyright (C) 2010, Google Inc. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY |
| * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON |
| * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "config.h" |
| #include "InspectorStyleSheet.h" |
| |
| #if ENABLE(INSPECTOR) |
| |
| #include "CSSImportRule.h" |
| #include "CSSMediaRule.h" |
| #include "CSSParser.h" |
| #include "CSSPropertySourceData.h" |
| #include "CSSRule.h" |
| #include "CSSRuleList.h" |
| #include "CSSStyleRule.h" |
| #include "CSSStyleSelector.h" |
| #include "CSSStyleSheet.h" |
| #include "Document.h" |
| #include "Element.h" |
| #include "HTMLHeadElement.h" |
| #include "HTMLParserIdioms.h" |
| #include "InspectorCSSAgent.h" |
| #include "InspectorResourceAgent.h" |
| #include "InspectorValues.h" |
| #include "Node.h" |
| #include "StyleSheetList.h" |
| #include "WebKitCSSKeyframesRule.h" |
| |
| #include <wtf/OwnPtr.h> |
| #include <wtf/PassOwnPtr.h> |
| #include <wtf/Vector.h> |
| |
| class ParsedStyleSheet { |
| public: |
| typedef Vector<RefPtr<WebCore::CSSRuleSourceData> > SourceData; |
| ParsedStyleSheet(); |
| |
| WebCore::CSSStyleSheet* cssStyleSheet() const { return m_parserOutput; } |
| const String& text() const { return m_text; } |
| void setText(const String& text); |
| bool hasText() const { return m_hasText; } |
| SourceData* sourceData() const { return m_sourceData.get(); } |
| void setSourceData(PassOwnPtr<SourceData> sourceData); |
| bool hasSourceData() const { return m_sourceData; } |
| RefPtr<WebCore::CSSRuleSourceData> ruleSourceDataAt(unsigned index) const; |
| |
| private: |
| |
| // StyleSheet constructed while parsing m_text. |
| WebCore::CSSStyleSheet* m_parserOutput; |
| String m_text; |
| bool m_hasText; |
| OwnPtr<SourceData> m_sourceData; |
| }; |
| |
| ParsedStyleSheet::ParsedStyleSheet() |
| : m_parserOutput(0) |
| , m_hasText(false) |
| { |
| } |
| |
| void ParsedStyleSheet::setText(const String& text) |
| { |
| m_hasText = true; |
| m_text = text; |
| setSourceData(0); |
| } |
| |
| void ParsedStyleSheet::setSourceData(PassOwnPtr<SourceData> sourceData) |
| { |
| m_sourceData = sourceData; |
| } |
| |
| RefPtr<WebCore::CSSRuleSourceData> ParsedStyleSheet::ruleSourceDataAt(unsigned index) const |
| { |
| if (!hasSourceData() || index >= m_sourceData->size()) |
| return 0; |
| |
| return m_sourceData->at(index); |
| } |
| |
| namespace WebCore { |
| |
| static PassRefPtr<InspectorObject> buildSourceRangeObject(const SourceRange& range) |
| { |
| RefPtr<InspectorObject> result = InspectorObject::create(); |
| result->setNumber("start", range.start); |
| result->setNumber("end", range.end); |
| return result.release(); |
| } |
| |
| static PassRefPtr<CSSRuleList> asCSSRuleList(StyleBase* styleBase) |
| { |
| if (!styleBase) |
| return 0; |
| |
| if (styleBase->isCSSStyleSheet()) |
| return CSSRuleList::create(static_cast<CSSStyleSheet*>(styleBase), true); |
| if (styleBase->isRule()) { |
| unsigned ruleType = static_cast<CSSRule*>(styleBase)->type(); |
| RefPtr<CSSRuleList> result = 0; |
| |
| switch (ruleType) { |
| case CSSRule::MEDIA_RULE: |
| result = static_cast<CSSMediaRule*>(styleBase)->cssRules(); |
| break; |
| case CSSRule::WEBKIT_KEYFRAMES_RULE: |
| result = static_cast<WebKitCSSKeyframesRule*>(styleBase)->cssRules(); |
| break; |
| case CSSRule::IMPORT_RULE: |
| case CSSRule::PAGE_RULE: |
| default: |
| return 0; |
| } |
| |
| return result.release(); |
| } |
| return 0; |
| } |
| |
| PassRefPtr<InspectorStyle> InspectorStyle::create(const InspectorCSSId& styleId, PassRefPtr<CSSStyleDeclaration> style, InspectorStyleSheet* parentStyleSheet) |
| { |
| return adoptRef(new InspectorStyle(styleId, style, parentStyleSheet)); |
| } |
| |
| InspectorStyle::InspectorStyle(const InspectorCSSId& styleId, PassRefPtr<CSSStyleDeclaration> style, InspectorStyleSheet* parentStyleSheet) |
| : m_styleId(styleId) |
| , m_style(style) |
| , m_parentStyleSheet(parentStyleSheet) |
| { |
| ASSERT(m_style); |
| } |
| |
| InspectorStyle::~InspectorStyle() |
| { |
| } |
| |
| PassRefPtr<InspectorObject> InspectorStyle::buildObjectForStyle() const |
| { |
| RefPtr<InspectorObject> result = InspectorObject::create(); |
| if (!m_styleId.isEmpty()) |
| result->setValue("styleId", m_styleId.asInspectorValue()); |
| |
| result->setString("width", m_style->getPropertyValue("width")); |
| result->setString("height", m_style->getPropertyValue("height")); |
| |
| RefPtr<CSSRuleSourceData> sourceData = m_parentStyleSheet ? m_parentStyleSheet->ruleSourceDataFor(m_style.get()) : 0; |
| if (sourceData) |
| result->setObject("range", buildSourceRangeObject(sourceData->styleSourceData->styleBodyRange)); |
| |
| populateObjectWithStyleProperties(result.get()); |
| |
| return result.release(); |
| } |
| |
| // This method does the following preprocessing of |propertyText| with |overwrite| == false and |index| past the last active property: |
| // - If the last property (if present) has no subsequent whitespace in the style declaration, a space is prepended to |propertyText|. |
| // - If the last property (if present) has no closing ";", the ";" is prepended to the current |propertyText| value. |
| // |
| // The propertyText (if not empty) is checked to be a valid style declaration (containing at least one property). If not, |
| // the method returns false (denoting an error). |
| bool InspectorStyle::setPropertyText(ErrorString* errorString, unsigned index, const String& propertyText, bool overwrite) |
| { |
| ASSERT(m_parentStyleSheet); |
| if (!m_parentStyleSheet->ensureParsedDataReady()) { |
| *errorString = "Internal error: no stylesheet parsed data available"; |
| return false; |
| } |
| |
| Vector<InspectorStyleProperty> allProperties; |
| populateAllProperties(&allProperties); |
| |
| unsigned propertyStart = 0; // Need to initialize to make the compiler happy. |
| long propertyLengthDelta; |
| |
| if (propertyText.stripWhiteSpace().length()) { |
| RefPtr<CSSMutableStyleDeclaration> tempMutableStyle = CSSMutableStyleDeclaration::create(); |
| CSSParser p; |
| RefPtr<CSSStyleSourceData> sourceData = CSSStyleSourceData::create(); |
| p.parseDeclaration(tempMutableStyle.get(), propertyText + " -webkit-boguz-propertee: none", &sourceData); |
| Vector<CSSPropertySourceData>& propertyData = sourceData->propertyData; |
| unsigned propertyCount = propertyData.size(); |
| |
| // At least one property + the bogus property added just above should be present. |
| if (propertyCount < 2) { |
| *errorString = "Invalid property value"; |
| return false; |
| } |
| |
| // Check for a proper propertyText termination (the parser could at least restore to the PROPERTY_NAME state). |
| if (propertyData.at(propertyCount - 1).name != "-webkit-boguz-propertee") { |
| *errorString = "Invalid property value"; |
| return false; |
| } |
| } |
| |
| if (overwrite) { |
| ASSERT(index < allProperties.size()); |
| InspectorStyleProperty& property = allProperties.at(index); |
| propertyStart = property.sourceData.range.start; |
| unsigned propertyEnd = property.sourceData.range.end; |
| unsigned oldLength = propertyEnd - propertyStart; |
| unsigned newLength = propertyText.length(); |
| propertyLengthDelta = newLength - oldLength; |
| |
| if (!property.disabled) { |
| bool success = replacePropertyInStyleText(property, propertyText); |
| if (!success) { |
| *errorString = "Internal error: could not replace property value"; |
| return false; |
| } |
| } else { |
| unsigned textLength = propertyText.length(); |
| unsigned disabledIndex = disabledIndexByOrdinal(index, false, allProperties); |
| if (!textLength) { |
| // Delete disabled property. |
| m_disabledProperties.remove(disabledIndex); |
| } else { |
| // Patch disabled property text. |
| m_disabledProperties.at(disabledIndex).rawText = propertyText; |
| } |
| |
| // We should not shift subsequent disabled properties when altering a disabled property. |
| return true; |
| } |
| } else { |
| // Insert at index. |
| RefPtr<CSSRuleSourceData> sourceData = m_parentStyleSheet->ruleSourceDataFor(m_style.get()); |
| if (!sourceData) { |
| *errorString = "Internal error: no CSS rule source found"; |
| return false; |
| } |
| String text; |
| bool success = styleText(&text); |
| if (!success) { |
| *errorString = "Internal error: could not fetch style text"; |
| return false; |
| } |
| propertyLengthDelta = propertyText.length(); |
| |
| bool insertLast = true; |
| if (index < allProperties.size()) { |
| InspectorStyleProperty& property = allProperties.at(index); |
| if (property.hasSource) { |
| propertyStart = property.sourceData.range.start; |
| // If inserting before a disabled property, it should be shifted, too. |
| insertLast = false; |
| } |
| } |
| |
| String textToSet = propertyText; |
| if (insertLast) { |
| propertyStart = sourceData->styleSourceData->styleBodyRange.end - sourceData->styleSourceData->styleBodyRange.start; |
| if (propertyStart && propertyText.length()) { |
| const UChar* characters = text.characters(); |
| |
| unsigned curPos = propertyStart - 1; // The last position of style declaration, since propertyStart points past one. |
| while (curPos && isHTMLSpace(characters[curPos])) |
| --curPos; |
| if (curPos && characters[curPos] != ';') { |
| // Prepend a ";" to the property text if appending to a style declaration where |
| // the last property has no trailing ";". |
| textToSet.insert("; ", 0); |
| } else if (!isHTMLSpace(characters[propertyStart - 1])) { |
| // Prepend a " " if the last declaration character is not an HTML space. |
| textToSet.insert(" ", 0); |
| } |
| } |
| } |
| |
| text.insert(textToSet, propertyStart); |
| m_parentStyleSheet->setStyleText(m_style.get(), text); |
| } |
| |
| // Recompute subsequent disabled property ranges if acting on a non-disabled property. |
| shiftDisabledProperties(disabledIndexByOrdinal(index, true, allProperties), propertyLengthDelta); |
| |
| return true; |
| } |
| |
| bool InspectorStyle::toggleProperty(ErrorString* errorString, unsigned index, bool disable) |
| { |
| ASSERT(m_parentStyleSheet); |
| if (!m_parentStyleSheet->ensureParsedDataReady()) { |
| *errorString = "Can toggle only source-based properties"; |
| return false; |
| } |
| RefPtr<CSSRuleSourceData> sourceData = m_parentStyleSheet->ruleSourceDataFor(m_style.get()); |
| if (!sourceData) { |
| *errorString = "Internal error: No source data for the style found"; |
| return false; |
| } |
| |
| Vector<InspectorStyleProperty> allProperties; |
| populateAllProperties(&allProperties); |
| if (index >= allProperties.size()) { |
| *errorString = "Property index is outside of property range"; |
| return false; |
| } |
| |
| InspectorStyleProperty& property = allProperties.at(index); |
| if (property.disabled == disable) |
| return true; // Idempotent operation. |
| |
| bool success; |
| if (!disable) |
| success = enableProperty(index, allProperties); |
| else |
| success = disableProperty(index, allProperties); |
| |
| return success; |
| } |
| |
| // static |
| unsigned InspectorStyle::disabledIndexByOrdinal(unsigned ordinal, bool canUseSubsequent, Vector<InspectorStyleProperty>& allProperties) |
| { |
| unsigned disabledIndex = 0; |
| for (unsigned i = 0, size = allProperties.size(); i < size; ++i) { |
| InspectorStyleProperty& property = allProperties.at(i); |
| if (property.disabled) { |
| if (i == ordinal || (canUseSubsequent && i > ordinal)) |
| return disabledIndex; |
| ++disabledIndex; |
| } |
| } |
| |
| return UINT_MAX; |
| } |
| |
| bool InspectorStyle::styleText(String* result) const |
| { |
| // Precondition: m_parentStyleSheet->ensureParsedDataReady() has been called successfully. |
| RefPtr<CSSRuleSourceData> sourceData = m_parentStyleSheet->ruleSourceDataFor(m_style.get()); |
| if (!sourceData) |
| return false; |
| |
| String styleSheetText; |
| bool success = m_parentStyleSheet->text(&styleSheetText); |
| if (!success) |
| return false; |
| |
| SourceRange& bodyRange = sourceData->styleSourceData->styleBodyRange; |
| *result = styleSheetText.substring(bodyRange.start, bodyRange.end - bodyRange.start); |
| return true; |
| } |
| |
| bool InspectorStyle::disableProperty(unsigned indexToDisable, Vector<InspectorStyleProperty>& allProperties) |
| { |
| // Precondition: |indexToEnable| points to an enabled property. |
| const InspectorStyleProperty& property = allProperties.at(indexToDisable); |
| unsigned propertyStart = property.sourceData.range.start; |
| InspectorStyleProperty disabledProperty(property); |
| String oldStyleText; |
| bool success = styleText(&oldStyleText); |
| if (!success) |
| return false; |
| disabledProperty.setRawTextFromStyleDeclaration(oldStyleText); |
| disabledProperty.disabled = true; |
| disabledProperty.sourceData.range.end = propertyStart; |
| // This may have to be negated below. |
| long propertyLength = property.sourceData.range.end - propertyStart; |
| success = replacePropertyInStyleText(property, ""); |
| if (!success) |
| return false; |
| |
| // Add disabled property at correct position. |
| unsigned insertionIndex = disabledIndexByOrdinal(indexToDisable, true, allProperties); |
| if (insertionIndex == UINT_MAX) |
| m_disabledProperties.append(disabledProperty); |
| else { |
| m_disabledProperties.insert(insertionIndex, disabledProperty); |
| shiftDisabledProperties(insertionIndex + 1, -propertyLength); // Property removed from text - shift these back. |
| } |
| return true; |
| } |
| |
| bool InspectorStyle::enableProperty(unsigned indexToEnable, Vector<InspectorStyleProperty>& allProperties) |
| { |
| // Precondition: |indexToEnable| points to a disabled property. |
| unsigned disabledIndex = disabledIndexByOrdinal(indexToEnable, false, allProperties); |
| if (disabledIndex == UINT_MAX) |
| return false; |
| |
| InspectorStyleProperty disabledProperty = m_disabledProperties.at(disabledIndex); |
| m_disabledProperties.remove(disabledIndex); |
| bool success = replacePropertyInStyleText(disabledProperty, disabledProperty.rawText); |
| if (success) |
| shiftDisabledProperties(disabledIndex, disabledProperty.rawText.length()); |
| return success; |
| } |
| |
| bool InspectorStyle::populateAllProperties(Vector<InspectorStyleProperty>* result) const |
| { |
| HashSet<String> foundShorthands; |
| HashSet<String> sourcePropertyNames; |
| unsigned disabledIndex = 0; |
| unsigned disabledLength = m_disabledProperties.size(); |
| InspectorStyleProperty disabledProperty; |
| if (disabledIndex < disabledLength) |
| disabledProperty = m_disabledProperties.at(disabledIndex); |
| |
| RefPtr<CSSRuleSourceData> sourceData = (m_parentStyleSheet && m_parentStyleSheet->ensureParsedDataReady()) ? m_parentStyleSheet->ruleSourceDataFor(m_style.get()) : 0; |
| Vector<CSSPropertySourceData>* sourcePropertyData = sourceData ? &(sourceData->styleSourceData->propertyData) : 0; |
| if (sourcePropertyData) { |
| String styleDeclaration; |
| bool isStyleTextKnown = styleText(&styleDeclaration); |
| ASSERT_UNUSED(isStyleTextKnown, isStyleTextKnown); |
| for (Vector<CSSPropertySourceData>::const_iterator it = sourcePropertyData->begin(); it != sourcePropertyData->end(); ++it) { |
| while (disabledIndex < disabledLength && disabledProperty.sourceData.range.start <= it->range.start) { |
| result->append(disabledProperty); |
| if (++disabledIndex < disabledLength) |
| disabledProperty = m_disabledProperties.at(disabledIndex); |
| } |
| InspectorStyleProperty p(*it, true, false); |
| p.setRawTextFromStyleDeclaration(styleDeclaration); |
| result->append(p); |
| sourcePropertyNames.add(it->name.lower()); |
| } |
| } |
| |
| while (disabledIndex < disabledLength) { |
| disabledProperty = m_disabledProperties.at(disabledIndex++); |
| result->append(disabledProperty); |
| } |
| |
| for (int i = 0, size = m_style->length(); i < size; ++i) { |
| String name = m_style->item(i); |
| if (sourcePropertyNames.contains(name.lower())) |
| continue; |
| |
| sourcePropertyNames.add(name.lower()); |
| result->append(InspectorStyleProperty(CSSPropertySourceData(name, m_style->getPropertyValue(name), !m_style->getPropertyPriority(name).isEmpty(), true, SourceRange()), false, false)); |
| } |
| |
| return true; |
| } |
| |
| void InspectorStyle::populateObjectWithStyleProperties(InspectorObject* result) const |
| { |
| Vector<InspectorStyleProperty> properties; |
| populateAllProperties(&properties); |
| |
| RefPtr<InspectorArray> propertiesObject = InspectorArray::create(); |
| RefPtr<InspectorArray> shorthandEntries = InspectorArray::create(); |
| HashMap<String, RefPtr<InspectorObject> > propertyNameToPreviousActiveProperty; |
| HashSet<String> foundShorthands; |
| |
| for (Vector<InspectorStyleProperty>::iterator it = properties.begin(), itEnd = properties.end(); it != itEnd; ++it) { |
| const CSSPropertySourceData& propertyEntry = it->sourceData; |
| const String& name = propertyEntry.name; |
| |
| RefPtr<InspectorObject> property = InspectorObject::create(); |
| propertiesObject->pushObject(property); |
| String status = it->disabled ? "disabled" : "active"; |
| |
| // Default "parsedOk" == true. |
| if (!propertyEntry.parsedOk) |
| property->setBoolean("parsedOk", false); |
| if (it->hasRawText()) |
| property->setString("text", it->rawText); |
| property->setString("name", name); |
| property->setString("value", propertyEntry.value); |
| |
| // Default "priority" == "". |
| if (propertyEntry.important) |
| property->setString("priority", "important"); |
| if (!it->disabled) { |
| if (it->hasSource) { |
| property->setBoolean("implicit", false); |
| property->setObject("range", buildSourceRangeObject(propertyEntry.range)); |
| |
| // Parsed property overrides any property with the same name. Non-parsed property overrides |
| // previous non-parsed property with the same name (if any). |
| bool shouldInactivate = false; |
| HashMap<String, RefPtr<InspectorObject> >::iterator activeIt = propertyNameToPreviousActiveProperty.find(name); |
| if (activeIt != propertyNameToPreviousActiveProperty.end()) { |
| if (propertyEntry.parsedOk) |
| shouldInactivate = true; |
| else { |
| bool previousParsedOk; |
| bool success = activeIt->second->getBoolean("parsedOk", &previousParsedOk); |
| if (success && !previousParsedOk) |
| shouldInactivate = true; |
| } |
| } else |
| propertyNameToPreviousActiveProperty.set(name, property); |
| |
| if (shouldInactivate) { |
| activeIt->second->setString("status", "inactive"); |
| activeIt->second->remove("shorthandName"); |
| propertyNameToPreviousActiveProperty.set(name, property); |
| } |
| } else { |
| bool implicit = m_style->isPropertyImplicit(name); |
| // Default "implicit" == false. |
| if (implicit) |
| property->setBoolean("implicit", true); |
| status = ""; |
| } |
| } |
| |
| // Default "status" == "style". |
| if (!status.isEmpty()) |
| property->setString("status", status); |
| |
| if (propertyEntry.parsedOk) { |
| // Both for style-originated and parsed source properties. |
| String shorthand = m_style->getPropertyShorthand(name); |
| if (!shorthand.isEmpty()) { |
| // Default "shorthandName" == "". |
| property->setString("shorthandName", shorthand); |
| if (!foundShorthands.contains(shorthand)) { |
| foundShorthands.add(shorthand); |
| RefPtr<InspectorObject> shorthandEntry = InspectorObject::create(); |
| shorthandEntry->setString("name", shorthand); |
| shorthandEntry->setString("value", shorthandValue(shorthand)); |
| shorthandEntries->pushObject(shorthandEntry.release()); |
| } |
| } |
| } |
| // else shorthandName is not set |
| } |
| |
| result->setArray("cssProperties", propertiesObject); |
| result->setArray("shorthandEntries", shorthandEntries); |
| } |
| |
| |
| void InspectorStyle::shiftDisabledProperties(unsigned fromIndex, long delta) |
| { |
| for (unsigned i = fromIndex, size = m_disabledProperties.size(); i < size; ++i) { |
| SourceRange& range = m_disabledProperties.at(i).sourceData.range; |
| range.start += delta; |
| range.end += delta; |
| } |
| } |
| |
| bool InspectorStyle::replacePropertyInStyleText(const InspectorStyleProperty& property, const String& newText) |
| { |
| // Precondition: m_parentStyleSheet->ensureParsedDataReady() has been called successfully. |
| String text; |
| bool success = styleText(&text); |
| if (!success) |
| return false; |
| const SourceRange& range = property.sourceData.range; |
| text.replace(range.start, range.end - range.start, newText); |
| success = m_parentStyleSheet->setStyleText(m_style.get(), text); |
| return success; |
| } |
| |
| String InspectorStyle::shorthandValue(const String& shorthandProperty) const |
| { |
| String value = m_style->getPropertyValue(shorthandProperty); |
| if (value.isEmpty()) { |
| for (unsigned i = 0; i < m_style->length(); ++i) { |
| String individualProperty = m_style->item(i); |
| if (m_style->getPropertyShorthand(individualProperty) != shorthandProperty) |
| continue; |
| if (m_style->isPropertyImplicit(individualProperty)) |
| continue; |
| String individualValue = m_style->getPropertyValue(individualProperty); |
| if (individualValue == "initial") |
| continue; |
| if (value.length()) |
| value.append(" "); |
| value.append(individualValue); |
| } |
| } |
| return value; |
| } |
| |
| String InspectorStyle::shorthandPriority(const String& shorthandProperty) const |
| { |
| String priority = m_style->getPropertyPriority(shorthandProperty); |
| if (priority.isEmpty()) { |
| for (unsigned i = 0; i < m_style->length(); ++i) { |
| String individualProperty = m_style->item(i); |
| if (m_style->getPropertyShorthand(individualProperty) != shorthandProperty) |
| continue; |
| priority = m_style->getPropertyPriority(individualProperty); |
| break; |
| } |
| } |
| return priority; |
| } |
| |
| Vector<String> InspectorStyle::longhandProperties(const String& shorthandProperty) const |
| { |
| Vector<String> properties; |
| HashSet<String> foundProperties; |
| for (unsigned i = 0; i < m_style->length(); ++i) { |
| String individualProperty = m_style->item(i); |
| if (foundProperties.contains(individualProperty) || m_style->getPropertyShorthand(individualProperty) != shorthandProperty) |
| continue; |
| |
| foundProperties.add(individualProperty); |
| properties.append(individualProperty); |
| } |
| return properties; |
| } |
| |
| PassRefPtr<InspectorStyleSheet> InspectorStyleSheet::create(const String& id, PassRefPtr<CSSStyleSheet> pageStyleSheet, const String& origin, const String& documentURL) |
| { |
| return adoptRef(new InspectorStyleSheet(id, pageStyleSheet, origin, documentURL)); |
| } |
| |
| InspectorStyleSheet::InspectorStyleSheet(const String& id, PassRefPtr<CSSStyleSheet> pageStyleSheet, const String& origin, const String& documentURL) |
| : m_id(id) |
| , m_pageStyleSheet(pageStyleSheet) |
| , m_origin(origin) |
| , m_documentURL(documentURL) |
| , m_isRevalidating(false) |
| { |
| m_parsedStyleSheet = new ParsedStyleSheet(); |
| } |
| |
| InspectorStyleSheet::~InspectorStyleSheet() |
| { |
| delete m_parsedStyleSheet; |
| } |
| |
| String InspectorStyleSheet::finalURL() const |
| { |
| if (m_pageStyleSheet && !m_pageStyleSheet->finalURL().isEmpty()) |
| return m_pageStyleSheet->finalURL().string(); |
| return m_documentURL; |
| } |
| |
| void InspectorStyleSheet::reparseStyleSheet(const String& text) |
| { |
| for (unsigned i = 0, size = m_pageStyleSheet->length(); i < size; ++i) |
| m_pageStyleSheet->remove(0); |
| m_pageStyleSheet->parseString(text, m_pageStyleSheet->useStrictParsing()); |
| m_pageStyleSheet->styleSheetChanged(); |
| m_inspectorStyles.clear(); |
| } |
| |
| bool InspectorStyleSheet::setText(const String& text) |
| { |
| if (!m_parsedStyleSheet) |
| return false; |
| |
| m_parsedStyleSheet->setText(text); |
| m_flatRules.clear(); |
| |
| return true; |
| } |
| |
| bool InspectorStyleSheet::setRuleSelector(const InspectorCSSId& id, const String& selector) |
| { |
| CSSStyleRule* rule = ruleForId(id); |
| if (!rule) |
| return false; |
| CSSStyleSheet* styleSheet = InspectorCSSAgent::parentStyleSheet(rule); |
| if (!styleSheet || !ensureParsedDataReady()) |
| return false; |
| |
| rule->setSelectorText(selector); |
| RefPtr<CSSRuleSourceData> sourceData = ruleSourceDataFor(rule->style()); |
| if (!sourceData) |
| return false; |
| |
| String sheetText = m_parsedStyleSheet->text(); |
| sheetText.replace(sourceData->selectorListRange.start, sourceData->selectorListRange.end - sourceData->selectorListRange.start, selector); |
| m_parsedStyleSheet->setText(sheetText); |
| return true; |
| } |
| |
| CSSStyleRule* InspectorStyleSheet::addRule(const String& selector) |
| { |
| String styleSheetText; |
| bool success = text(&styleSheetText); |
| if (!success) |
| return 0; |
| |
| ExceptionCode ec = 0; |
| m_pageStyleSheet->addRule(selector, "", ec); |
| if (ec) |
| return 0; |
| RefPtr<CSSRuleList> rules = m_pageStyleSheet->cssRules(); |
| ASSERT(rules->length()); |
| CSSStyleRule* rule = InspectorCSSAgent::asCSSStyleRule(rules->item(rules->length() - 1)); |
| ASSERT(rule); |
| |
| if (styleSheetText.length()) |
| styleSheetText += "\n"; |
| |
| styleSheetText += selector; |
| styleSheetText += " {}"; |
| // Using setText() as this operation changes the style sheet rule set. |
| setText(styleSheetText); |
| |
| return rule; |
| } |
| |
| CSSStyleRule* InspectorStyleSheet::ruleForId(const InspectorCSSId& id) const |
| { |
| if (!m_pageStyleSheet) |
| return 0; |
| |
| ASSERT(!id.isEmpty()); |
| ensureFlatRules(); |
| return id.ordinal() >= m_flatRules.size() ? 0 : m_flatRules.at(id.ordinal()); |
| |
| } |
| |
| PassRefPtr<InspectorObject> InspectorStyleSheet::buildObjectForStyleSheet() |
| { |
| CSSStyleSheet* styleSheet = pageStyleSheet(); |
| if (!styleSheet) |
| return 0; |
| |
| RefPtr<InspectorObject> result = InspectorObject::create(); |
| result->setString("styleSheetId", id()); |
| RefPtr<CSSRuleList> cssRuleList = CSSRuleList::create(styleSheet, true); |
| RefPtr<InspectorArray> cssRules = buildArrayForRuleList(cssRuleList.get()); |
| result->setArray("rules", cssRules.release()); |
| |
| String styleSheetText; |
| bool success = text(&styleSheetText); |
| if (success) |
| result->setString("text", styleSheetText); |
| |
| return result.release(); |
| } |
| |
| PassRefPtr<InspectorObject> InspectorStyleSheet::buildObjectForStyleSheetInfo() |
| { |
| CSSStyleSheet* styleSheet = pageStyleSheet(); |
| if (!styleSheet) |
| return 0; |
| |
| RefPtr<InspectorObject> result = InspectorObject::create(); |
| result->setString("styleSheetId", id()); |
| result->setBoolean("disabled", styleSheet->disabled()); |
| result->setString("sourceURL", finalURL()); |
| result->setString("title", styleSheet->title()); |
| return result.release(); |
| } |
| |
| PassRefPtr<InspectorObject> InspectorStyleSheet::buildObjectForRule(CSSStyleRule* rule) |
| { |
| CSSStyleSheet* styleSheet = pageStyleSheet(); |
| if (!styleSheet) |
| return 0; |
| |
| RefPtr<InspectorObject> result = InspectorObject::create(); |
| result->setString("selectorText", rule->selectorText()); |
| // "sourceURL" is present only for regular rules, otherwise "origin" should be used in the frontend. |
| if (!m_origin.length()) |
| result->setString("sourceURL", finalURL()); |
| result->setNumber("sourceLine", rule->sourceLine()); |
| result->setString("origin", m_origin); |
| |
| result->setObject("style", buildObjectForStyle(rule->style())); |
| if (canBind()) { |
| InspectorCSSId id(ruleId(rule)); |
| if (!id.isEmpty()) |
| result->setValue("ruleId", id.asInspectorValue()); |
| } |
| |
| RefPtr<CSSRuleSourceData> sourceData; |
| if (ensureParsedDataReady()) |
| sourceData = ruleSourceDataFor(rule->style()); |
| if (sourceData) { |
| RefPtr<InspectorObject> selectorRange = InspectorObject::create(); |
| selectorRange->setNumber("start", sourceData->selectorListRange.start); |
| selectorRange->setNumber("end", sourceData->selectorListRange.end); |
| result->setObject("selectorRange", selectorRange.release()); |
| } |
| |
| return result.release(); |
| } |
| |
| PassRefPtr<InspectorObject> InspectorStyleSheet::buildObjectForStyle(CSSStyleDeclaration* style) |
| { |
| RefPtr<CSSRuleSourceData> sourceData; |
| if (ensureParsedDataReady()) |
| sourceData = ruleSourceDataFor(style); |
| |
| InspectorCSSId id = ruleOrStyleId(style); |
| if (id.isEmpty()) { |
| RefPtr<InspectorObject> bogusStyle = InspectorObject::create(); |
| bogusStyle->setArray("cssProperties", InspectorArray::create()); |
| bogusStyle->setObject("shorthandValues", InspectorObject::create()); |
| bogusStyle->setObject("properties", InspectorObject::create()); |
| return bogusStyle.release(); |
| } |
| RefPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id); |
| RefPtr<InspectorObject> result = inspectorStyle->buildObjectForStyle(); |
| |
| // Style text cannot be retrieved without stylesheet, so set cssText here. |
| if (sourceData) { |
| String sheetText; |
| bool success = text(&sheetText); |
| if (success) { |
| const SourceRange& bodyRange = sourceData->styleSourceData->styleBodyRange; |
| result->setString("cssText", sheetText.substring(bodyRange.start, bodyRange.end - bodyRange.start)); |
| } |
| } |
| |
| return result.release(); |
| } |
| |
| bool InspectorStyleSheet::setPropertyText(ErrorString* errorString, const InspectorCSSId& id, unsigned propertyIndex, const String& text, bool overwrite) |
| { |
| RefPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id); |
| if (!inspectorStyle) { |
| *errorString = "No style found for given id"; |
| return false; |
| } |
| |
| return inspectorStyle->setPropertyText(errorString, propertyIndex, text, overwrite); |
| } |
| |
| bool InspectorStyleSheet::toggleProperty(ErrorString* errorString, const InspectorCSSId& id, unsigned propertyIndex, bool disable) |
| { |
| RefPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id); |
| if (!inspectorStyle) { |
| *errorString = "No style found for given id"; |
| return false; |
| } |
| |
| bool success = inspectorStyle->toggleProperty(errorString, propertyIndex, disable); |
| if (success) { |
| if (disable) |
| rememberInspectorStyle(inspectorStyle); |
| else if (!inspectorStyle->hasDisabledProperties()) |
| forgetInspectorStyle(inspectorStyle->cssStyle()); |
| } |
| return success; |
| } |
| |
| bool InspectorStyleSheet::text(String* result) const |
| { |
| if (!ensureText()) |
| return false; |
| *result = m_parsedStyleSheet->text(); |
| return true; |
| } |
| |
| CSSStyleDeclaration* InspectorStyleSheet::styleForId(const InspectorCSSId& id) const |
| { |
| CSSStyleRule* rule = ruleForId(id); |
| if (!rule) |
| return 0; |
| |
| return rule->style(); |
| } |
| |
| PassRefPtr<InspectorStyle> InspectorStyleSheet::inspectorStyleForId(const InspectorCSSId& id) |
| { |
| CSSStyleDeclaration* style = styleForId(id); |
| if (!style) |
| return 0; |
| |
| InspectorStyleMap::iterator it = m_inspectorStyles.find(style); |
| if (it == m_inspectorStyles.end()) { |
| RefPtr<InspectorStyle> inspectorStyle = InspectorStyle::create(id, style, this); |
| return inspectorStyle.release(); |
| } |
| return it->second; |
| } |
| |
| void InspectorStyleSheet::rememberInspectorStyle(RefPtr<InspectorStyle> inspectorStyle) |
| { |
| m_inspectorStyles.set(inspectorStyle->cssStyle(), inspectorStyle); |
| } |
| |
| void InspectorStyleSheet::forgetInspectorStyle(CSSStyleDeclaration* style) |
| { |
| m_inspectorStyles.remove(style); |
| } |
| |
| InspectorCSSId InspectorStyleSheet::ruleOrStyleId(CSSStyleDeclaration* style) const |
| { |
| unsigned index = ruleIndexByStyle(style); |
| if (index != UINT_MAX) |
| return InspectorCSSId(id(), index); |
| return InspectorCSSId(); |
| } |
| |
| Document* InspectorStyleSheet::ownerDocument() const |
| { |
| return m_pageStyleSheet->document(); |
| } |
| |
| RefPtr<CSSRuleSourceData> InspectorStyleSheet::ruleSourceDataFor(CSSStyleDeclaration* style) const |
| { |
| return m_parsedStyleSheet->ruleSourceDataAt(ruleIndexByStyle(style)); |
| } |
| |
| unsigned InspectorStyleSheet::ruleIndexByStyle(CSSStyleDeclaration* pageStyle) const |
| { |
| ensureFlatRules(); |
| unsigned index = 0; |
| for (unsigned i = 0, size = m_flatRules.size(); i < size; ++i) { |
| if (m_flatRules.at(i)->style() == pageStyle) |
| return index; |
| |
| ++index; |
| } |
| return UINT_MAX; |
| } |
| |
| bool InspectorStyleSheet::ensureParsedDataReady() |
| { |
| return ensureText() && ensureSourceData(); |
| } |
| |
| bool InspectorStyleSheet::ensureText() const |
| { |
| if (!m_parsedStyleSheet) |
| return false; |
| if (m_parsedStyleSheet->hasText()) |
| return true; |
| |
| String text; |
| bool success = originalStyleSheetText(&text); |
| if (success) |
| m_parsedStyleSheet->setText(text); |
| // No need to clear m_flatRules here - it's empty. |
| |
| return success; |
| } |
| |
| bool InspectorStyleSheet::ensureSourceData() |
| { |
| if (m_parsedStyleSheet->hasSourceData()) |
| return true; |
| |
| if (!m_parsedStyleSheet->hasText()) |
| return false; |
| |
| RefPtr<CSSStyleSheet> newStyleSheet = CSSStyleSheet::create(); |
| CSSParser p; |
| StyleRuleRangeMap ruleRangeMap; |
| p.parseSheet(newStyleSheet.get(), m_parsedStyleSheet->text(), 0, &ruleRangeMap); |
| OwnPtr<ParsedStyleSheet::SourceData> rangesVector(new ParsedStyleSheet::SourceData()); |
| |
| Vector<CSSStyleRule*> rules; |
| RefPtr<CSSRuleList> ruleList = asCSSRuleList(newStyleSheet.get()); |
| collectFlatRules(ruleList, &rules); |
| for (unsigned i = 0, size = rules.size(); i < size; ++i) { |
| StyleRuleRangeMap::iterator it = ruleRangeMap.find(rules.at(i)); |
| if (it != ruleRangeMap.end()) { |
| fixUnparsedPropertyRanges(it->second.get(), m_parsedStyleSheet->text()); |
| rangesVector->append(it->second); |
| } |
| } |
| |
| m_parsedStyleSheet->setSourceData(rangesVector.release()); |
| return m_parsedStyleSheet->hasSourceData(); |
| } |
| |
| void InspectorStyleSheet::ensureFlatRules() const |
| { |
| // We are fine with redoing this for empty stylesheets as this will run fast. |
| if (m_flatRules.isEmpty()) |
| collectFlatRules(asCSSRuleList(pageStyleSheet()), &m_flatRules); |
| } |
| |
| bool InspectorStyleSheet::setStyleText(CSSStyleDeclaration* style, const String& text) |
| { |
| if (!pageStyleSheet()) |
| return false; |
| if (!ensureParsedDataReady()) |
| return false; |
| |
| String patchedStyleSheetText; |
| bool success = styleSheetTextWithChangedStyle(style, text, &patchedStyleSheetText); |
| if (!success) |
| return false; |
| |
| InspectorCSSId id = ruleOrStyleId(style); |
| if (id.isEmpty()) |
| return false; |
| |
| ExceptionCode ec = 0; |
| style->setCssText(text, ec); |
| if (!ec) |
| m_parsedStyleSheet->setText(patchedStyleSheetText); |
| |
| return !ec; |
| } |
| |
| bool InspectorStyleSheet::styleSheetTextWithChangedStyle(CSSStyleDeclaration* style, const String& newStyleText, String* result) |
| { |
| if (!style) |
| return false; |
| |
| if (!ensureParsedDataReady()) |
| return false; |
| |
| RefPtr<CSSRuleSourceData> sourceData = ruleSourceDataFor(style); |
| unsigned bodyStart = sourceData->styleSourceData->styleBodyRange.start; |
| unsigned bodyEnd = sourceData->styleSourceData->styleBodyRange.end; |
| ASSERT(bodyStart <= bodyEnd); |
| |
| String text = m_parsedStyleSheet->text(); |
| ASSERT(bodyEnd <= text.length()); // bodyEnd is exclusive |
| |
| text.replace(bodyStart, bodyEnd - bodyStart, newStyleText); |
| *result = text; |
| return true; |
| } |
| |
| InspectorCSSId InspectorStyleSheet::ruleId(CSSStyleRule* rule) const |
| { |
| return ruleOrStyleId(rule->style()); |
| } |
| |
| void InspectorStyleSheet::revalidateStyle(CSSStyleDeclaration* pageStyle) |
| { |
| if (m_isRevalidating) |
| return; |
| |
| m_isRevalidating = true; |
| ensureFlatRules(); |
| for (unsigned i = 0, size = m_flatRules.size(); i < size; ++i) { |
| CSSStyleRule* parsedRule = m_flatRules.at(i); |
| if (parsedRule->style() == pageStyle) { |
| if (parsedRule->style()->cssText() != pageStyle->cssText()) { |
| // Clear the disabled properties for the invalid style here. |
| m_inspectorStyles.remove(pageStyle); |
| setStyleText(pageStyle, pageStyle->cssText()); |
| } |
| break; |
| } |
| } |
| m_isRevalidating = false; |
| } |
| |
| bool InspectorStyleSheet::originalStyleSheetText(String* result) const |
| { |
| String text; |
| bool success = inlineStyleSheetText(&text); |
| if (!success) |
| success = resourceStyleSheetText(&text); |
| if (success) |
| *result = text; |
| return success; |
| } |
| |
| bool InspectorStyleSheet::resourceStyleSheetText(String* result) const |
| { |
| if (m_origin == "user" || m_origin == "user-agent") |
| return false; |
| |
| if (!m_pageStyleSheet || !ownerDocument()) |
| return false; |
| |
| String error; |
| InspectorResourceAgent::resourceContent(&error, ownerDocument()->frame(), m_pageStyleSheet->finalURL(), result); |
| return error.isEmpty(); |
| } |
| |
| bool InspectorStyleSheet::inlineStyleSheetText(String* result) const |
| { |
| if (!m_pageStyleSheet) |
| return false; |
| |
| Node* ownerNode = m_pageStyleSheet->ownerNode(); |
| if (!ownerNode || ownerNode->nodeType() != Node::ELEMENT_NODE) |
| return false; |
| Element* ownerElement = static_cast<Element*>(ownerNode); |
| if (ownerElement->tagName().lower() != "style") |
| return false; |
| *result = ownerElement->innerText(); |
| return true; |
| } |
| |
| PassRefPtr<InspectorArray> InspectorStyleSheet::buildArrayForRuleList(CSSRuleList* ruleList) |
| { |
| RefPtr<InspectorArray> result = InspectorArray::create(); |
| if (!ruleList) |
| return result.release(); |
| |
| RefPtr<CSSRuleList> refRuleList = ruleList; |
| Vector<CSSStyleRule*> rules; |
| collectFlatRules(refRuleList, &rules); |
| |
| for (unsigned i = 0, size = rules.size(); i < size; ++i) |
| result->pushObject(buildObjectForRule(rules.at(i))); |
| |
| return result.release(); |
| } |
| |
| void InspectorStyleSheet::fixUnparsedPropertyRanges(CSSRuleSourceData* ruleData, const String& styleSheetText) |
| { |
| Vector<CSSPropertySourceData>& propertyData = ruleData->styleSourceData->propertyData; |
| unsigned size = propertyData.size(); |
| if (!size) |
| return; |
| |
| unsigned styleStart = ruleData->styleSourceData->styleBodyRange.start; |
| const UChar* characters = styleSheetText.characters(); |
| CSSPropertySourceData* nextData = &(propertyData.at(0)); |
| for (unsigned i = 0; i < size; ++i) { |
| CSSPropertySourceData* currentData = nextData; |
| nextData = i < size - 1 ? &(propertyData.at(i + 1)) : 0; |
| |
| if (currentData->parsedOk) |
| continue; |
| if (currentData->range.end > 0 && characters[styleStart + currentData->range.end - 1] == ';') |
| continue; |
| |
| unsigned propertyEndInStyleSheet; |
| if (!nextData) |
| propertyEndInStyleSheet = ruleData->styleSourceData->styleBodyRange.end - 1; |
| else |
| propertyEndInStyleSheet = styleStart + nextData->range.start - 1; |
| |
| while (isHTMLSpace(characters[propertyEndInStyleSheet])) |
| --propertyEndInStyleSheet; |
| |
| // propertyEndInStyleSheet points at the last property text character. |
| unsigned newPropertyEnd = propertyEndInStyleSheet - styleStart + 1; // Exclusive of the last property text character. |
| if (currentData->range.end != newPropertyEnd) { |
| currentData->range.end = newPropertyEnd; |
| unsigned valueStartInStyleSheet = styleStart + currentData->range.start + currentData->name.length(); |
| while (valueStartInStyleSheet < propertyEndInStyleSheet && characters[valueStartInStyleSheet] != ':') |
| ++valueStartInStyleSheet; |
| if (valueStartInStyleSheet < propertyEndInStyleSheet) |
| ++valueStartInStyleSheet; // Shift past the ':'. |
| while (valueStartInStyleSheet < propertyEndInStyleSheet && isHTMLSpace(characters[valueStartInStyleSheet])) |
| ++valueStartInStyleSheet; |
| // Need to exclude the trailing ';' from the property value. |
| currentData->value = styleSheetText.substring(valueStartInStyleSheet, propertyEndInStyleSheet - valueStartInStyleSheet + (characters[propertyEndInStyleSheet] == ';' ? 0 : 1)); |
| } |
| } |
| } |
| |
| void InspectorStyleSheet::collectFlatRules(PassRefPtr<CSSRuleList> ruleList, Vector<CSSStyleRule*>* result) |
| { |
| if (!ruleList) |
| return; |
| |
| for (unsigned i = 0, size = ruleList->length(); i < size; ++i) { |
| CSSRule* rule = ruleList->item(i); |
| CSSStyleRule* styleRule = InspectorCSSAgent::asCSSStyleRule(rule); |
| if (styleRule) |
| result->append(styleRule); |
| else { |
| RefPtr<CSSRuleList> childRuleList = asCSSRuleList(rule); |
| if (childRuleList) |
| collectFlatRules(childRuleList, result); |
| } |
| } |
| } |
| |
| PassRefPtr<InspectorStyleSheetForInlineStyle> InspectorStyleSheetForInlineStyle::create(const String& id, PassRefPtr<Element> element, const String& origin) |
| { |
| return adoptRef(new InspectorStyleSheetForInlineStyle(id, element, origin)); |
| } |
| |
| InspectorStyleSheetForInlineStyle::InspectorStyleSheetForInlineStyle(const String& id, PassRefPtr<Element> element, const String& origin) |
| : InspectorStyleSheet(id, 0, origin, "") |
| , m_element(element) |
| , m_ruleSourceData(0) |
| { |
| ASSERT(m_element); |
| m_inspectorStyle = InspectorStyle::create(InspectorCSSId(id, 0), inlineStyle(), this); |
| m_styleText = m_element->isStyledElement() ? m_element->getAttribute("style").string() : String(); |
| } |
| |
| void InspectorStyleSheetForInlineStyle::didModifyElementAttribute() |
| { |
| String newStyleText = elementStyleText(); |
| bool shouldDropSourceData = false; |
| if (m_element->isStyledElement() && m_element->style() != m_inspectorStyle->cssStyle()) { |
| m_inspectorStyle = InspectorStyle::create(InspectorCSSId(id(), 0), inlineStyle(), this); |
| shouldDropSourceData = true; |
| } |
| if (newStyleText != m_styleText) { |
| m_styleText = newStyleText; |
| shouldDropSourceData = true; |
| } |
| if (shouldDropSourceData) |
| m_ruleSourceData.clear(); |
| } |
| |
| bool InspectorStyleSheetForInlineStyle::text(String* result) const |
| { |
| *result = m_styleText; |
| return true; |
| } |
| |
| bool InspectorStyleSheetForInlineStyle::setStyleText(CSSStyleDeclaration* style, const String& text) |
| { |
| ASSERT_UNUSED(style, style == inlineStyle()); |
| ExceptionCode ec = 0; |
| m_element->setAttribute("style", text, ec); |
| m_styleText = text; |
| m_ruleSourceData.clear(); |
| return !ec; |
| } |
| |
| Document* InspectorStyleSheetForInlineStyle::ownerDocument() const |
| { |
| return m_element->document(); |
| } |
| |
| bool InspectorStyleSheetForInlineStyle::ensureParsedDataReady() |
| { |
| // The "style" property value can get changed indirectly, e.g. via element.style.borderWidth = "2px". |
| const String& currentStyleText = elementStyleText(); |
| if (m_styleText != currentStyleText) { |
| m_ruleSourceData.clear(); |
| m_styleText = currentStyleText; |
| } |
| |
| if (m_ruleSourceData) |
| return true; |
| |
| m_ruleSourceData = CSSRuleSourceData::create(); |
| RefPtr<CSSStyleSourceData> sourceData = CSSStyleSourceData::create(); |
| bool success = getStyleAttributeRanges(&sourceData); |
| if (!success) |
| return false; |
| |
| m_ruleSourceData->styleSourceData = sourceData.release(); |
| return true; |
| } |
| |
| PassRefPtr<InspectorStyle> InspectorStyleSheetForInlineStyle::inspectorStyleForId(const InspectorCSSId& id) |
| { |
| ASSERT_UNUSED(id, !id.ordinal()); |
| return m_inspectorStyle; |
| } |
| |
| CSSStyleDeclaration* InspectorStyleSheetForInlineStyle::inlineStyle() const |
| { |
| return m_element->style(); |
| } |
| |
| const String& InspectorStyleSheetForInlineStyle::elementStyleText() const |
| { |
| return m_element->getAttribute("style").string(); |
| } |
| |
| bool InspectorStyleSheetForInlineStyle::getStyleAttributeRanges(RefPtr<CSSStyleSourceData>* result) |
| { |
| if (!m_element->isStyledElement()) |
| return false; |
| |
| if (m_styleText.isEmpty()) { |
| (*result)->styleBodyRange.start = 0; |
| (*result)->styleBodyRange.end = 0; |
| return true; |
| } |
| |
| RefPtr<CSSMutableStyleDeclaration> tempDeclaration = CSSMutableStyleDeclaration::create(); |
| CSSParser p; |
| p.parseDeclaration(tempDeclaration.get(), m_styleText, result); |
| return true; |
| } |
| |
| } // namespace WebCore |
| |
| #endif // ENABLE(INSPECTOR) |