#if ENABLE(INSPECTOR)
+#include "CSSImportRule.h"
+#include "CSSMediaRule.h"
#include "CSSParser.h"
#include "CSSPropertySourceData.h"
#include "CSSRule.h"
#include "InspectorValues.h"
#include "Node.h"
#include "StyleSheetList.h"
+#include "WebKitCSSKeyframesRule.h"
#include <wtf/OwnPtr.h>
#include <wtf/PassOwnPtr.h>
namespace WebCore {
+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<InspectorObject> InspectorStyle::buildObjectForStyle() const
{
RefPtr<InspectorObject> result = InspectorObject::create();
if (!m_styleId.isEmpty())
- result->setString("styleId", m_styleId.asString());
+ result->setValue("styleId", m_styleId.asInspectorValue());
RefPtr<InspectorObject> propertiesObject = InspectorObject::create();
propertiesObject->setString("width", m_style->getPropertyValue("width"));
p.parseDeclaration(tempMutableStyle.get(), propertyText + " -webkit-boguz-propertee: none", &sourceData);
Vector<CSSPropertySourceData>& propertyData = sourceData->propertyData;
unsigned propertyCount = propertyData.size();
- if (!propertyCount)
+
+ // At least one property + the bogus property added just above should be present.
+ if (propertyCount < 2)
return false;
// Check for a proper propertyText termination (the parser could at least restore to the PROPERTY_NAME state).
property->setNumber("startOffset", propertyEntry.range.start);
property->setNumber("endOffset", propertyEntry.range.end);
+ // 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->setString("shorthandName", "");
+ propertyNameToPreviousActiveProperty.set(name, property);
}
- propertyNameToPreviousActiveProperty.set(name, property);
} else {
property->setBoolean("implicit", m_style->isPropertyImplicit(name));
property->setString("status", "style");
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(i);
+ 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);
- for (unsigned i = 0, size = m_pageStyleSheet->length(); i < size; ++i)
- m_pageStyleSheet->remove(i);
- m_inspectorStyles.clear();
+ m_flatRules.clear();
- m_pageStyleSheet->parseString(text, m_pageStyleSheet->useStrictParsing());
return true;
}
styleSheetText += selector;
styleSheetText += " {}";
- m_parsedStyleSheet->setText(styleSheetText);
+ // Using setText() as this operation changes the style sheet rule set.
+ setText(styleSheetText);
return rule;
}
return 0;
ASSERT(!id.isEmpty());
- bool ok;
- unsigned index = id.ordinal().toUInt(&ok);
- if (!ok)
- return 0;
+ ensureFlatRules();
+ return id.ordinal() >= m_flatRules.size() ? 0 : m_flatRules.at(id.ordinal());
- unsigned currentIndex = 0;
- for (unsigned i = 0, size = m_pageStyleSheet->length(); i < size; ++i) {
- CSSStyleRule* rule = InspectorCSSAgent::asCSSStyleRule(m_pageStyleSheet->item(i));
- if (!rule)
- continue;
- if (index == currentIndex)
- return rule;
-
- ++currentIndex;
- }
- return 0;
}
PassRefPtr<InspectorObject> InspectorStyleSheet::buildObjectForStyleSheet()
RefPtr<InspectorObject> result = InspectorObject::create();
result->setBoolean("disabled", styleSheet->disabled());
- result->setString("sourceURL", styleSheet->href());
+ result->setString("sourceURL", finalURL());
result->setString("title", styleSheet->title());
RefPtr<CSSRuleList> cssRuleList = CSSRuleList::create(styleSheet, true);
RefPtr<InspectorArray> cssRules = buildArrayForRuleList(cssRuleList.get());
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", !styleSheet->href().isEmpty() ? styleSheet->href() : m_documentURL);
+ result->setString("sourceURL", finalURL());
result->setNumber("sourceLine", rule->sourceLine());
result->setString("origin", m_origin);
result->setObject("style", buildObjectForStyle(rule->style()));
- if (canBind())
- result->setString("ruleId", ruleId(rule).asString());
+ if (canBind()) {
+ InspectorCSSId id(ruleId(rule));
+ if (!id.isEmpty())
+ result->setValue("ruleId", id.asInspectorValue());
+ }
RefPtr<CSSRuleSourceData> sourceData;
if (ensureParsedDataReady())
if (ensureParsedDataReady())
sourceData = ruleSourceDataFor(style);
- RefPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(ruleOrStyleId(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.
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);
{
unsigned index = ruleIndexByStyle(style);
if (index != UINT_MAX)
- return InspectorCSSId::createFromParts(id(), String::number(index));
+ return InspectorCSSId(id(), index);
return InspectorCSSId();
}
-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));
- }
- }
-}
-
Document* InspectorStyleSheet::ownerDocument() const
{
return m_pageStyleSheet->document();
unsigned InspectorStyleSheet::ruleIndexByStyle(CSSStyleDeclaration* pageStyle) const
{
+ ensureFlatRules();
unsigned index = 0;
- for (unsigned i = 0, size = m_pageStyleSheet->length(); i < size; ++i) {
- CSSStyleRule* rule = InspectorCSSAgent::asCSSStyleRule(m_pageStyleSheet->item(i));
- if (!rule)
- continue;
- if (rule->style() == pageStyle)
+ for (unsigned i = 0, size = m_flatRules.size(); i < size; ++i) {
+ if (m_flatRules.at(i)->style() == pageStyle)
return index;
++index;
bool InspectorStyleSheet::ensureParsedDataReady()
{
- return ensureText() && ensureSourceData(pageStyleSheet()->ownerNode());
-}
-
-bool InspectorStyleSheet::text(String* result) const
-{
- if (!ensureText())
- return false;
- *result = m_parsedStyleSheet->text();
- return true;
+ return ensureText() && ensureSourceData();
}
bool InspectorStyleSheet::ensureText() const
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(Node* ownerNode)
+bool InspectorStyleSheet::ensureSourceData()
{
if (m_parsedStyleSheet->hasSourceData())
return true;
if (!m_parsedStyleSheet->hasText())
return false;
- RefPtr<CSSStyleSheet> newStyleSheet = CSSStyleSheet::create(ownerNode);
+ 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());
- for (unsigned i = 0, length = newStyleSheet->length(); i < length; ++i) {
- CSSStyleRule* rule = InspectorCSSAgent::asCSSStyleRule(newStyleSheet->item(i));
- if (!rule)
- continue;
- StyleRuleRangeMap::iterator it = ruleRangeMap.find(rule);
+ 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);
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 true;
}
-CSSStyleRule* InspectorStyleSheet::findPageRuleWithStyle(CSSStyleDeclaration* style)
-{
- for (unsigned i = 0, size = m_pageStyleSheet->length(); i < size; ++i) {
- CSSStyleRule* rule = InspectorCSSAgent::asCSSStyleRule(m_pageStyleSheet->item(i));
- if (!rule)
- continue;
- if (rule->style() == style)
- return rule;
- }
- return 0;
-}
-
InspectorCSSId InspectorStyleSheet::ruleId(CSSStyleRule* rule) const
{
return ruleOrStyleId(rule->style());
return;
m_isRevalidating = true;
- CSSStyleSheet* parsedSheet = m_parsedStyleSheet->cssStyleSheet();
- for (unsigned i = 0, size = parsedSheet->length(); i < size; ++i) {
- StyleBase* styleBase = parsedSheet->item(i);
- CSSStyleRule* parsedRule = InspectorCSSAgent::asCSSStyleRule(styleBase);
- if (!parsedRule)
- continue;
+ 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.
bool InspectorStyleSheet::resourceStyleSheetText(String* result) const
{
+ if (m_origin == "user" || m_origin == "user-agent")
+ return false;
+
if (!m_pageStyleSheet || !ownerDocument())
return false;
if (!ruleList)
return result.release();
- for (unsigned i = 0, size = ruleList->length(); i < size; ++i) {
- CSSStyleRule* rule = InspectorCSSAgent::asCSSStyleRule(ruleList->item(i));
- if (!rule)
+ 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;
- result->pushObject(buildObjectForRule(rule));
+ 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));
+ }
}
- return result.release();
}
+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);
+ }
+ }
+}
InspectorStyleSheetForInlineStyle::InspectorStyleSheetForInlineStyle(const String& id, Element* element, const String& origin)
: InspectorStyleSheet(id, 0, origin, "")
, m_ruleSourceData(0)
{
ASSERT(element);
- m_inspectorStyle = InspectorStyle::create(InspectorCSSId::createFromParts(id, "0"), inlineStyle(), this);
+ m_inspectorStyle = InspectorStyle::create(InspectorCSSId(id, 0), inlineStyle(), this);
+}
+
+bool InspectorStyleSheetForInlineStyle::text(String* result) const
+{
+ *result = m_element->getAttribute("style");
+ return true;
}
bool InspectorStyleSheetForInlineStyle::setStyleText(CSSStyleDeclaration* style, const String& text)
return !ec;
}
-bool InspectorStyleSheetForInlineStyle::text(String* result) const
-{
- *result = m_element->getAttribute("style");
- return true;
-}
-
Document* InspectorStyleSheetForInlineStyle::ownerDocument() const
{
return m_element->document();
PassRefPtr<InspectorStyle> InspectorStyleSheetForInlineStyle::inspectorStyleForId(const InspectorCSSId& id)
{
- ASSERT_UNUSED(id, id.ordinal() == "0");
+ ASSERT_UNUSED(id, !id.ordinal());
return m_inspectorStyle;
}