2 * Copyright (C) 2010, Google Inc. All rights reserved.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
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.
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.
26 #include "InspectorStyleSheet.h"
30 #include "CSSImportRule.h"
31 #include "CSSMediaRule.h"
32 #include "CSSParser.h"
33 #include "CSSPropertySourceData.h"
35 #include "CSSRuleList.h"
36 #include "CSSStyleRule.h"
37 #include "CSSStyleSelector.h"
38 #include "CSSStyleSheet.h"
41 #include "HTMLHeadElement.h"
42 #include "HTMLNames.h"
43 #include "HTMLParserIdioms.h"
44 #include "InspectorCSSAgent.h"
45 #include "InspectorPageAgent.h"
46 #include "InspectorValues.h"
49 #include "StyleSheetList.h"
50 #include "WebKitCSSKeyframesRule.h"
52 #include <wtf/OwnPtr.h>
53 #include <wtf/PassOwnPtr.h>
54 #include <wtf/Vector.h>
56 class ParsedStyleSheet {
58 typedef Vector<RefPtr<WebCore::CSSRuleSourceData> > SourceData;
61 WebCore::CSSStyleSheet* cssStyleSheet() const { return m_parserOutput; }
62 const String& text() const { return m_text; }
63 void setText(const String& text);
64 bool hasText() const { return m_hasText; }
65 SourceData* sourceData() const { return m_sourceData.get(); }
66 void setSourceData(PassOwnPtr<SourceData> sourceData);
67 bool hasSourceData() const { return m_sourceData; }
68 RefPtr<WebCore::CSSRuleSourceData> ruleSourceDataAt(unsigned index) const;
72 // StyleSheet constructed while parsing m_text.
73 WebCore::CSSStyleSheet* m_parserOutput;
76 OwnPtr<SourceData> m_sourceData;
79 ParsedStyleSheet::ParsedStyleSheet()
85 void ParsedStyleSheet::setText(const String& text)
89 setSourceData(nullptr);
92 void ParsedStyleSheet::setSourceData(PassOwnPtr<SourceData> sourceData)
94 m_sourceData = sourceData;
97 RefPtr<WebCore::CSSRuleSourceData> ParsedStyleSheet::ruleSourceDataAt(unsigned index) const
99 if (!hasSourceData() || index >= m_sourceData->size())
102 return m_sourceData->at(index);
107 enum MediaListSource {
108 MediaListSourceLinkedSheet,
109 MediaListSourceInlineSheet,
110 MediaListSourceMediaRule,
111 MediaListSourceImportRule
114 static PassRefPtr<InspectorObject> buildSourceRangeObject(const SourceRange& range)
116 RefPtr<InspectorObject> result = InspectorObject::create();
117 result->setNumber("start", range.start);
118 result->setNumber("end", range.end);
119 return result.release();
122 static PassRefPtr<InspectorObject> buildMediaObject(const MediaList* media, MediaListSource mediaListSource, const String& sourceURL)
124 RefPtr<InspectorObject> mediaObject = InspectorObject::create();
125 switch (mediaListSource) {
126 case MediaListSourceMediaRule:
127 mediaObject->setString("source", "mediaRule");
129 case MediaListSourceImportRule:
130 mediaObject->setString("source", "importRule");
132 case MediaListSourceLinkedSheet:
133 mediaObject->setString("source", "linkedSheet");
135 case MediaListSourceInlineSheet:
136 mediaObject->setString("source", "inlineSheet");
139 if (!sourceURL.isEmpty()) {
140 mediaObject->setString("sourceURL", sourceURL);
141 mediaObject->setNumber("sourceLine", media->lastLine());
143 mediaObject->setString("text", media->mediaText());
144 return mediaObject.release();
147 static PassRefPtr<CSSRuleList> asCSSRuleList(CSSStyleSheet* styleSheet)
152 return CSSRuleList::create(styleSheet, true);
155 static PassRefPtr<CSSRuleList> asCSSRuleList(CSSRule* rule)
160 if (rule->isMediaRule())
161 return static_cast<CSSMediaRule*>(rule)->cssRules();
163 if (rule->isKeyframesRule())
164 return static_cast<WebKitCSSKeyframesRule*>(rule)->cssRules();
169 static void fillMediaListChain(CSSRule* rule, InspectorArray* mediaArray)
171 MediaList* mediaList;
172 CSSRule* parentRule = rule;
175 CSSStyleSheet* parentStyleSheet = 0;
176 bool isMediaRule = true;
177 if (parentRule->isMediaRule()) {
178 CSSMediaRule* mediaRule = static_cast<CSSMediaRule*>(parentRule);
179 mediaList = mediaRule->media();
180 parentStyleSheet = mediaRule->parentStyleSheet();
181 } else if (parentRule->isImportRule()) {
182 CSSImportRule* importRule = static_cast<CSSImportRule*>(parentRule);
183 mediaList = importRule->media();
184 parentStyleSheet = importRule->parentStyleSheet();
189 if (parentStyleSheet) {
190 sourceURL = parentStyleSheet->finalURL();
191 if (sourceURL.isEmpty())
192 sourceURL = InspectorDOMAgent::documentURLString(parentStyleSheet->findDocument());
196 if (mediaList && mediaList->length())
197 mediaArray->pushObject(buildMediaObject(mediaList, isMediaRule ? MediaListSourceMediaRule : MediaListSourceImportRule, sourceURL));
199 if (parentRule->parentRule())
200 parentRule = parentRule->parentRule();
202 CSSStyleSheet* styleSheet = parentRule->parentStyleSheet();
204 mediaList = styleSheet->media();
205 if (mediaList && mediaList->length()) {
206 Document* doc = styleSheet->findDocument();
208 sourceURL = doc->url();
209 else if (!styleSheet->finalURL().isEmpty())
210 sourceURL = styleSheet->finalURL();
213 mediaArray->pushObject(buildMediaObject(mediaList, styleSheet->ownerNode() ? MediaListSourceLinkedSheet : MediaListSourceInlineSheet, sourceURL));
215 parentRule = styleSheet->parentRule();
218 styleSheet = styleSheet->parentStyleSheet();
224 PassRefPtr<InspectorStyle> InspectorStyle::create(const InspectorCSSId& styleId, PassRefPtr<CSSStyleDeclaration> style, InspectorStyleSheet* parentStyleSheet)
226 return adoptRef(new InspectorStyle(styleId, style, parentStyleSheet));
229 InspectorStyle::InspectorStyle(const InspectorCSSId& styleId, PassRefPtr<CSSStyleDeclaration> style, InspectorStyleSheet* parentStyleSheet)
232 , m_parentStyleSheet(parentStyleSheet)
233 , m_formatAcquired(false)
238 InspectorStyle::~InspectorStyle()
242 PassRefPtr<InspectorObject> InspectorStyle::buildObjectForStyle() const
244 RefPtr<InspectorObject> result = InspectorObject::create();
245 if (!m_styleId.isEmpty())
246 result->setValue("styleId", m_styleId.asInspectorValue());
248 result->setString("width", m_style->getPropertyValue("width"));
249 result->setString("height", m_style->getPropertyValue("height"));
251 RefPtr<CSSRuleSourceData> sourceData = m_parentStyleSheet ? m_parentStyleSheet->ruleSourceDataFor(m_style.get()) : 0;
253 result->setObject("range", buildSourceRangeObject(sourceData->styleSourceData->styleBodyRange));
255 populateObjectWithStyleProperties(result.get());
257 return result.release();
260 PassRefPtr<InspectorArray> InspectorStyle::buildArrayForComputedStyle() const
262 RefPtr<InspectorArray> result = InspectorArray::create();
263 Vector<InspectorStyleProperty> properties;
264 populateAllProperties(&properties);
266 for (Vector<InspectorStyleProperty>::iterator it = properties.begin(), itEnd = properties.end(); it != itEnd; ++it) {
267 const CSSPropertySourceData& propertyEntry = it->sourceData;
268 RefPtr<InspectorObject> entry = InspectorObject::create();
269 entry->setString("name", propertyEntry.name);
270 entry->setString("value", propertyEntry.value);
271 result->pushObject(entry);
274 return result.release();
277 // This method does the following preprocessing of |propertyText| with |overwrite| == false and |index| past the last active property:
278 // - If the last property (if present) has no closing ";", the ";" is prepended to the current |propertyText| value.
279 // - A heuristic formatting is attempted to retain the style structure.
281 // The propertyText (if not empty) is checked to be a valid style declaration (containing at least one property). If not,
282 // the method returns false (denoting an error).
283 bool InspectorStyle::setPropertyText(ErrorString* errorString, unsigned index, const String& propertyText, bool overwrite)
285 ASSERT(m_parentStyleSheet);
286 DEFINE_STATIC_LOCAL(String, bogusPropertyName, ("-webkit-boguz-propertee"));
288 if (!m_parentStyleSheet->ensureParsedDataReady()) {
289 *errorString = "Internal error: no stylesheet parsed data available";
293 Vector<InspectorStyleProperty> allProperties;
294 populateAllProperties(&allProperties);
296 if (propertyText.stripWhiteSpace().length()) {
297 RefPtr<CSSMutableStyleDeclaration> tempMutableStyle = CSSMutableStyleDeclaration::create();
298 RefPtr<CSSStyleSourceData> sourceData = CSSStyleSourceData::create();
300 p.parseDeclaration(tempMutableStyle.get(), propertyText + " " + bogusPropertyName + ": none", &sourceData, m_style->parentStyleSheet());
301 Vector<CSSPropertySourceData>& propertyData = sourceData->propertyData;
302 unsigned propertyCount = propertyData.size();
304 // At least one property + the bogus property added just above should be present.
305 if (propertyCount < 2) {
306 *errorString = "Invalid property value";
310 // Check for a proper propertyText termination (the parser could at least restore to the PROPERTY_NAME state).
311 if (propertyData.at(propertyCount - 1).name != bogusPropertyName) {
312 *errorString = "Invalid property value";
317 RefPtr<CSSRuleSourceData> sourceData = m_parentStyleSheet->ruleSourceDataFor(m_style.get());
319 *errorString = "Internal error: no CSS rule source found";
324 bool success = styleText(&text);
326 *errorString = "Internal error: could not fetch style text";
330 InspectorStyleTextEditor editor(&allProperties, &m_disabledProperties, text, newLineAndWhitespaceDelimiters());
332 editor.replaceProperty(index, propertyText);
334 editor.insertProperty(index, propertyText, sourceData->styleSourceData->styleBodyRange.length());
336 return applyStyleText(editor.styleText());
339 bool InspectorStyle::toggleProperty(ErrorString* errorString, unsigned index, bool disable)
341 ASSERT(m_parentStyleSheet);
342 if (!m_parentStyleSheet->ensureParsedDataReady()) {
343 *errorString = "Can toggle only source-based properties";
347 RefPtr<CSSRuleSourceData> sourceData = m_parentStyleSheet->ruleSourceDataFor(m_style.get());
349 *errorString = "Internal error: No source data for the style found";
354 bool success = styleText(&text);
356 *errorString = "Internal error: could not fetch style text";
360 Vector<InspectorStyleProperty> allProperties;
361 populateAllProperties(&allProperties);
362 if (index >= allProperties.size()) {
363 *errorString = "Property index is outside of property range";
367 InspectorStyleProperty& property = allProperties.at(index);
368 if (property.disabled == disable)
369 return true; // Idempotent operation.
371 InspectorStyleTextEditor editor(&allProperties, &m_disabledProperties, text, newLineAndWhitespaceDelimiters());
373 editor.disableProperty(index);
375 editor.enableProperty(index);
377 return applyStyleText(editor.styleText());
380 bool InspectorStyle::styleText(String* result) const
382 // Precondition: m_parentStyleSheet->ensureParsedDataReady() has been called successfully.
383 RefPtr<CSSRuleSourceData> sourceData = m_parentStyleSheet->ruleSourceDataFor(m_style.get());
387 String styleSheetText;
388 bool success = m_parentStyleSheet->text(&styleSheetText);
392 SourceRange& bodyRange = sourceData->styleSourceData->styleBodyRange;
393 *result = styleSheetText.substring(bodyRange.start, bodyRange.end - bodyRange.start);
397 bool InspectorStyle::populateAllProperties(Vector<InspectorStyleProperty>* result) const
399 HashSet<String> foundShorthands;
400 HashSet<String> sourcePropertyNames;
401 unsigned disabledIndex = 0;
402 unsigned disabledLength = m_disabledProperties.size();
403 InspectorStyleProperty disabledProperty;
404 if (disabledIndex < disabledLength)
405 disabledProperty = m_disabledProperties.at(disabledIndex);
407 RefPtr<CSSRuleSourceData> sourceData = (m_parentStyleSheet && m_parentStyleSheet->ensureParsedDataReady()) ? m_parentStyleSheet->ruleSourceDataFor(m_style.get()) : 0;
408 Vector<CSSPropertySourceData>* sourcePropertyData = sourceData ? &(sourceData->styleSourceData->propertyData) : 0;
409 if (sourcePropertyData) {
410 String styleDeclaration;
411 bool isStyleTextKnown = styleText(&styleDeclaration);
412 ASSERT_UNUSED(isStyleTextKnown, isStyleTextKnown);
413 for (Vector<CSSPropertySourceData>::const_iterator it = sourcePropertyData->begin(); it != sourcePropertyData->end(); ++it) {
414 while (disabledIndex < disabledLength && disabledProperty.sourceData.range.start <= it->range.start) {
415 result->append(disabledProperty);
416 if (++disabledIndex < disabledLength)
417 disabledProperty = m_disabledProperties.at(disabledIndex);
419 InspectorStyleProperty p(*it, true, false);
420 p.setRawTextFromStyleDeclaration(styleDeclaration);
422 sourcePropertyNames.add(it->name.lower());
426 while (disabledIndex < disabledLength) {
427 disabledProperty = m_disabledProperties.at(disabledIndex++);
428 result->append(disabledProperty);
431 for (int i = 0, size = m_style->length(); i < size; ++i) {
432 String name = m_style->item(i);
433 if (sourcePropertyNames.contains(name.lower()))
436 sourcePropertyNames.add(name.lower());
437 result->append(InspectorStyleProperty(CSSPropertySourceData(name, m_style->getPropertyValue(name), !m_style->getPropertyPriority(name).isEmpty(), true, SourceRange()), false, false));
443 void InspectorStyle::populateObjectWithStyleProperties(InspectorObject* result) const
445 Vector<InspectorStyleProperty> properties;
446 populateAllProperties(&properties);
448 RefPtr<InspectorArray> propertiesObject = InspectorArray::create();
449 RefPtr<InspectorArray> shorthandEntries = InspectorArray::create();
450 HashMap<String, RefPtr<InspectorObject> > propertyNameToPreviousActiveProperty;
451 HashSet<String> foundShorthands;
453 for (Vector<InspectorStyleProperty>::iterator it = properties.begin(), itEnd = properties.end(); it != itEnd; ++it) {
454 const CSSPropertySourceData& propertyEntry = it->sourceData;
455 const String& name = propertyEntry.name;
457 RefPtr<InspectorObject> property = InspectorObject::create();
458 propertiesObject->pushObject(property);
459 String status = it->disabled ? "disabled" : "active";
461 // Default "parsedOk" == true.
462 if (!propertyEntry.parsedOk)
463 property->setBoolean("parsedOk", false);
464 if (it->hasRawText())
465 property->setString("text", it->rawText);
466 property->setString("name", name);
467 property->setString("value", propertyEntry.value);
469 // Default "priority" == "".
470 if (propertyEntry.important)
471 property->setString("priority", "important");
474 property->setBoolean("implicit", false);
475 property->setObject("range", buildSourceRangeObject(propertyEntry.range));
477 // Parsed property overrides any property with the same name. Non-parsed property overrides
478 // previous non-parsed property with the same name (if any).
479 bool shouldInactivate = false;
480 HashMap<String, RefPtr<InspectorObject> >::iterator activeIt = propertyNameToPreviousActiveProperty.find(name);
481 if (activeIt != propertyNameToPreviousActiveProperty.end()) {
482 if (propertyEntry.parsedOk)
483 shouldInactivate = true;
485 bool previousParsedOk;
486 bool success = activeIt->second->getBoolean("parsedOk", &previousParsedOk);
487 if (success && !previousParsedOk)
488 shouldInactivate = true;
491 propertyNameToPreviousActiveProperty.set(name, property);
493 if (shouldInactivate) {
494 activeIt->second->setString("status", "inactive");
495 activeIt->second->remove("shorthandName");
496 propertyNameToPreviousActiveProperty.set(name, property);
499 bool implicit = m_style->isPropertyImplicit(name);
500 // Default "implicit" == false.
502 property->setBoolean("implicit", true);
507 // Default "status" == "style".
508 if (!status.isEmpty())
509 property->setString("status", status);
511 if (propertyEntry.parsedOk) {
512 // Both for style-originated and parsed source properties.
513 String shorthand = m_style->getPropertyShorthand(name);
514 if (!shorthand.isEmpty()) {
515 // Default "shorthandName" == "".
516 property->setString("shorthandName", shorthand);
517 if (!foundShorthands.contains(shorthand)) {
518 foundShorthands.add(shorthand);
519 RefPtr<InspectorObject> shorthandEntry = InspectorObject::create();
520 shorthandEntry->setString("name", shorthand);
521 shorthandEntry->setString("value", shorthandValue(shorthand));
522 shorthandEntries->pushObject(shorthandEntry.release());
526 // else shorthandName is not set
529 result->setArray("cssProperties", propertiesObject);
530 result->setArray("shorthandEntries", shorthandEntries);
533 bool InspectorStyle::applyStyleText(const String& text)
535 return m_parentStyleSheet->setStyleText(m_style.get(), text);
538 String InspectorStyle::shorthandValue(const String& shorthandProperty) const
540 String value = m_style->getPropertyValue(shorthandProperty);
541 if (value.isEmpty()) {
542 for (unsigned i = 0; i < m_style->length(); ++i) {
543 String individualProperty = m_style->item(i);
544 if (m_style->getPropertyShorthand(individualProperty) != shorthandProperty)
546 if (m_style->isPropertyImplicit(individualProperty))
548 String individualValue = m_style->getPropertyValue(individualProperty);
549 if (individualValue == "initial")
553 value.append(individualValue);
559 String InspectorStyle::shorthandPriority(const String& shorthandProperty) const
561 String priority = m_style->getPropertyPriority(shorthandProperty);
562 if (priority.isEmpty()) {
563 for (unsigned i = 0; i < m_style->length(); ++i) {
564 String individualProperty = m_style->item(i);
565 if (m_style->getPropertyShorthand(individualProperty) != shorthandProperty)
567 priority = m_style->getPropertyPriority(individualProperty);
574 Vector<String> InspectorStyle::longhandProperties(const String& shorthandProperty) const
576 Vector<String> properties;
577 HashSet<String> foundProperties;
578 for (unsigned i = 0; i < m_style->length(); ++i) {
579 String individualProperty = m_style->item(i);
580 if (foundProperties.contains(individualProperty) || m_style->getPropertyShorthand(individualProperty) != shorthandProperty)
583 foundProperties.add(individualProperty);
584 properties.append(individualProperty);
589 NewLineAndWhitespace& InspectorStyle::newLineAndWhitespaceDelimiters() const
591 DEFINE_STATIC_LOCAL(String, defaultPrefix, (" "));
593 if (m_formatAcquired)
596 RefPtr<CSSRuleSourceData> sourceData = (m_parentStyleSheet && m_parentStyleSheet->ensureParsedDataReady()) ? m_parentStyleSheet->ruleSourceDataFor(m_style.get()) : 0;
597 Vector<CSSPropertySourceData>* sourcePropertyData = sourceData ? &(sourceData->styleSourceData->propertyData) : 0;
599 if (!sourcePropertyData || !(propertyCount = sourcePropertyData->size())) {
600 m_format.first = "\n";
601 m_format.second = defaultPrefix;
602 return m_format; // Do not remember the default formatting and attempt to acquire it later.
606 bool success = styleText(&text);
607 ASSERT_UNUSED(success, success);
609 m_formatAcquired = true;
611 String formatLineFeed = "";
612 String formatPropertyPrefix = "";
614 String candidatePrefix = defaultPrefix;
616 int propertyIndex = 0;
617 bool isFullPrefixScanned = false;
618 bool lineFeedTerminated = false;
619 const UChar* characters = text.characters();
620 while (propertyIndex < propertyCount) {
621 const WebCore::CSSPropertySourceData& currentProperty = sourcePropertyData->at(propertyIndex++);
623 bool processNextProperty = false;
624 int scanEnd = currentProperty.range.start;
625 for (int i = scanStart; i < scanEnd; ++i) {
626 UChar ch = characters[i];
627 bool isLineFeed = isHTMLLineBreak(ch);
629 if (!lineFeedTerminated)
630 formatLineFeed.append(ch);
631 } else if (isHTMLSpace(ch))
634 candidatePrefix = prefix;
636 scanStart = currentProperty.range.end;
638 processNextProperty = true;
641 if (!isLineFeed && formatLineFeed.length())
642 lineFeedTerminated = true;
644 if (!processNextProperty) {
645 isFullPrefixScanned = true;
650 m_format.first = formatLineFeed;
651 m_format.second = isFullPrefixScanned ? prefix : candidatePrefix;
655 PassRefPtr<InspectorStyleSheet> InspectorStyleSheet::create(const String& id, PassRefPtr<CSSStyleSheet> pageStyleSheet, const String& origin, const String& documentURL)
657 return adoptRef(new InspectorStyleSheet(id, pageStyleSheet, origin, documentURL));
660 InspectorStyleSheet::InspectorStyleSheet(const String& id, PassRefPtr<CSSStyleSheet> pageStyleSheet, const String& origin, const String& documentURL)
662 , m_pageStyleSheet(pageStyleSheet)
664 , m_documentURL(documentURL)
665 , m_isRevalidating(false)
667 m_parsedStyleSheet = new ParsedStyleSheet();
670 InspectorStyleSheet::~InspectorStyleSheet()
672 delete m_parsedStyleSheet;
675 String InspectorStyleSheet::finalURL() const
677 if (m_pageStyleSheet && !m_pageStyleSheet->finalURL().isEmpty())
678 return m_pageStyleSheet->finalURL().string();
679 return m_documentURL;
682 void InspectorStyleSheet::reparseStyleSheet(const String& text)
684 for (unsigned i = 0, size = m_pageStyleSheet->length(); i < size; ++i)
685 m_pageStyleSheet->remove(0);
686 m_pageStyleSheet->parseString(text, m_pageStyleSheet->useStrictParsing());
687 m_pageStyleSheet->styleSheetChanged();
688 m_inspectorStyles.clear();
691 bool InspectorStyleSheet::setText(const String& text)
693 if (!m_parsedStyleSheet)
696 m_parsedStyleSheet->setText(text);
702 bool InspectorStyleSheet::setRuleSelector(const InspectorCSSId& id, const String& selector)
704 CSSStyleRule* rule = ruleForId(id);
707 CSSStyleSheet* styleSheet = InspectorCSSAgent::parentStyleSheet(rule);
708 if (!styleSheet || !ensureParsedDataReady())
711 rule->setSelectorText(selector);
712 RefPtr<CSSRuleSourceData> sourceData = ruleSourceDataFor(rule->style());
716 String sheetText = m_parsedStyleSheet->text();
717 sheetText.replace(sourceData->selectorListRange.start, sourceData->selectorListRange.end - sourceData->selectorListRange.start, selector);
718 m_parsedStyleSheet->setText(sheetText);
722 CSSStyleRule* InspectorStyleSheet::addRule(const String& selector)
724 String styleSheetText;
725 bool success = text(&styleSheetText);
729 ExceptionCode ec = 0;
730 m_pageStyleSheet->addRule(selector, "", ec);
733 RefPtr<CSSRuleList> rules = m_pageStyleSheet->cssRules();
734 ASSERT(rules->length());
735 CSSStyleRule* rule = InspectorCSSAgent::asCSSStyleRule(rules->item(rules->length() - 1));
738 if (styleSheetText.length())
739 styleSheetText += "\n";
741 styleSheetText += selector;
742 styleSheetText += " {}";
743 // Using setText() as this operation changes the style sheet rule set.
744 setText(styleSheetText);
749 CSSStyleRule* InspectorStyleSheet::ruleForId(const InspectorCSSId& id) const
751 if (!m_pageStyleSheet)
754 ASSERT(!id.isEmpty());
756 return id.ordinal() >= m_flatRules.size() ? 0 : m_flatRules.at(id.ordinal());
760 PassRefPtr<InspectorObject> InspectorStyleSheet::buildObjectForStyleSheet()
762 CSSStyleSheet* styleSheet = pageStyleSheet();
766 RefPtr<InspectorObject> result = InspectorObject::create();
767 result->setString("styleSheetId", id());
768 RefPtr<CSSRuleList> cssRuleList = CSSRuleList::create(styleSheet, true);
769 RefPtr<InspectorArray> cssRules = buildArrayForRuleList(cssRuleList.get());
770 result->setArray("rules", cssRules.release());
772 String styleSheetText;
773 bool success = text(&styleSheetText);
775 result->setString("text", styleSheetText);
777 return result.release();
780 PassRefPtr<InspectorObject> InspectorStyleSheet::buildObjectForStyleSheetInfo()
782 CSSStyleSheet* styleSheet = pageStyleSheet();
786 RefPtr<InspectorObject> result = InspectorObject::create();
787 result->setString("styleSheetId", id());
788 result->setBoolean("disabled", styleSheet->disabled());
789 result->setString("sourceURL", finalURL());
790 result->setString("title", styleSheet->title());
791 return result.release();
794 PassRefPtr<InspectorObject> InspectorStyleSheet::buildObjectForRule(CSSStyleRule* rule)
796 CSSStyleSheet* styleSheet = pageStyleSheet();
800 RefPtr<InspectorObject> result = InspectorObject::create();
801 result->setString("selectorText", rule->selectorText());
802 // "sourceURL" is present only for regular rules, otherwise "origin" should be used in the frontend.
803 if (!m_origin.length())
804 result->setString("sourceURL", finalURL());
805 result->setNumber("sourceLine", rule->sourceLine());
806 result->setString("origin", m_origin);
808 result->setObject("style", buildObjectForStyle(rule->style()));
810 InspectorCSSId id(ruleId(rule));
812 result->setValue("ruleId", id.asInspectorValue());
815 RefPtr<CSSRuleSourceData> sourceData;
816 if (ensureParsedDataReady())
817 sourceData = ruleSourceDataFor(rule->style());
819 RefPtr<InspectorObject> selectorRange = InspectorObject::create();
820 selectorRange->setNumber("start", sourceData->selectorListRange.start);
821 selectorRange->setNumber("end", sourceData->selectorListRange.end);
822 result->setObject("selectorRange", selectorRange.release());
825 RefPtr<InspectorArray> mediaArray = InspectorArray::create();
827 fillMediaListChain(rule, mediaArray.get());
828 if (mediaArray->length())
829 result->setArray("media", mediaArray.release());
831 return result.release();
834 PassRefPtr<InspectorObject> InspectorStyleSheet::buildObjectForStyle(CSSStyleDeclaration* style)
836 RefPtr<CSSRuleSourceData> sourceData;
837 if (ensureParsedDataReady())
838 sourceData = ruleSourceDataFor(style);
840 InspectorCSSId id = ruleOrStyleId(style);
842 RefPtr<InspectorObject> bogusStyle = InspectorObject::create();
843 bogusStyle->setArray("cssProperties", InspectorArray::create());
844 bogusStyle->setObject("shorthandValues", InspectorObject::create());
845 bogusStyle->setObject("properties", InspectorObject::create());
846 return bogusStyle.release();
848 RefPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id);
849 RefPtr<InspectorObject> result = inspectorStyle->buildObjectForStyle();
851 // Style text cannot be retrieved without stylesheet, so set cssText here.
854 bool success = text(&sheetText);
856 const SourceRange& bodyRange = sourceData->styleSourceData->styleBodyRange;
857 result->setString("cssText", sheetText.substring(bodyRange.start, bodyRange.end - bodyRange.start));
861 return result.release();
864 bool InspectorStyleSheet::setPropertyText(ErrorString* errorString, const InspectorCSSId& id, unsigned propertyIndex, const String& text, bool overwrite)
866 RefPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id);
867 if (!inspectorStyle) {
868 *errorString = "No style found for given id";
872 return inspectorStyle->setPropertyText(errorString, propertyIndex, text, overwrite);
875 bool InspectorStyleSheet::toggleProperty(ErrorString* errorString, const InspectorCSSId& id, unsigned propertyIndex, bool disable)
877 RefPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id);
878 if (!inspectorStyle) {
879 *errorString = "No style found for given id";
883 bool success = inspectorStyle->toggleProperty(errorString, propertyIndex, disable);
886 rememberInspectorStyle(inspectorStyle);
887 else if (!inspectorStyle->hasDisabledProperties())
888 forgetInspectorStyle(inspectorStyle->cssStyle());
893 bool InspectorStyleSheet::text(String* result) const
897 *result = m_parsedStyleSheet->text();
901 CSSStyleDeclaration* InspectorStyleSheet::styleForId(const InspectorCSSId& id) const
903 CSSStyleRule* rule = ruleForId(id);
907 return rule->style();
910 PassRefPtr<InspectorStyle> InspectorStyleSheet::inspectorStyleForId(const InspectorCSSId& id)
912 CSSStyleDeclaration* style = styleForId(id);
916 InspectorStyleMap::iterator it = m_inspectorStyles.find(style);
917 if (it == m_inspectorStyles.end()) {
918 RefPtr<InspectorStyle> inspectorStyle = InspectorStyle::create(id, style, this);
919 return inspectorStyle.release();
924 void InspectorStyleSheet::rememberInspectorStyle(RefPtr<InspectorStyle> inspectorStyle)
926 m_inspectorStyles.set(inspectorStyle->cssStyle(), inspectorStyle);
929 void InspectorStyleSheet::forgetInspectorStyle(CSSStyleDeclaration* style)
931 m_inspectorStyles.remove(style);
934 InspectorCSSId InspectorStyleSheet::ruleOrStyleId(CSSStyleDeclaration* style) const
936 unsigned index = ruleIndexByStyle(style);
937 if (index != UINT_MAX)
938 return InspectorCSSId(id(), index);
939 return InspectorCSSId();
942 Document* InspectorStyleSheet::ownerDocument() const
944 return m_pageStyleSheet->findDocument();
947 RefPtr<CSSRuleSourceData> InspectorStyleSheet::ruleSourceDataFor(CSSStyleDeclaration* style) const
949 return m_parsedStyleSheet->ruleSourceDataAt(ruleIndexByStyle(style));
952 unsigned InspectorStyleSheet::ruleIndexByStyle(CSSStyleDeclaration* pageStyle) const
956 for (unsigned i = 0, size = m_flatRules.size(); i < size; ++i) {
957 if (m_flatRules.at(i)->style() == pageStyle)
965 bool InspectorStyleSheet::ensureParsedDataReady()
967 return ensureText() && ensureSourceData();
970 bool InspectorStyleSheet::ensureText() const
972 if (!m_parsedStyleSheet)
974 if (m_parsedStyleSheet->hasText())
978 bool success = originalStyleSheetText(&text);
980 m_parsedStyleSheet->setText(text);
981 // No need to clear m_flatRules here - it's empty.
986 bool InspectorStyleSheet::ensureSourceData()
988 if (m_parsedStyleSheet->hasSourceData())
991 if (!m_parsedStyleSheet->hasText())
994 RefPtr<CSSStyleSheet> newStyleSheet = CSSStyleSheet::create();
996 StyleRuleRangeMap ruleRangeMap;
997 p.parseSheet(newStyleSheet.get(), m_parsedStyleSheet->text(), 0, &ruleRangeMap);
998 OwnPtr<ParsedStyleSheet::SourceData> rangesVector(adoptPtr(new ParsedStyleSheet::SourceData));
1000 Vector<CSSStyleRule*> rules;
1001 RefPtr<CSSRuleList> ruleList = asCSSRuleList(newStyleSheet.get());
1002 collectFlatRules(ruleList, &rules);
1003 for (unsigned i = 0, size = rules.size(); i < size; ++i) {
1004 StyleRuleRangeMap::iterator it = ruleRangeMap.find(rules.at(i));
1005 if (it != ruleRangeMap.end()) {
1006 fixUnparsedPropertyRanges(it->second.get(), m_parsedStyleSheet->text());
1007 rangesVector->append(it->second);
1011 m_parsedStyleSheet->setSourceData(rangesVector.release());
1012 return m_parsedStyleSheet->hasSourceData();
1015 void InspectorStyleSheet::ensureFlatRules() const
1017 // We are fine with redoing this for empty stylesheets as this will run fast.
1018 if (m_flatRules.isEmpty())
1019 collectFlatRules(asCSSRuleList(pageStyleSheet()), &m_flatRules);
1022 bool InspectorStyleSheet::setStyleText(CSSStyleDeclaration* style, const String& text)
1024 if (!pageStyleSheet())
1026 if (!ensureParsedDataReady())
1029 String patchedStyleSheetText;
1030 bool success = styleSheetTextWithChangedStyle(style, text, &patchedStyleSheetText);
1034 InspectorCSSId id = ruleOrStyleId(style);
1038 ExceptionCode ec = 0;
1039 style->setCssText(text, ec);
1041 m_parsedStyleSheet->setText(patchedStyleSheetText);
1046 bool InspectorStyleSheet::styleSheetTextWithChangedStyle(CSSStyleDeclaration* style, const String& newStyleText, String* result)
1051 if (!ensureParsedDataReady())
1054 RefPtr<CSSRuleSourceData> sourceData = ruleSourceDataFor(style);
1055 unsigned bodyStart = sourceData->styleSourceData->styleBodyRange.start;
1056 unsigned bodyEnd = sourceData->styleSourceData->styleBodyRange.end;
1057 ASSERT(bodyStart <= bodyEnd);
1059 String text = m_parsedStyleSheet->text();
1060 ASSERT(bodyEnd <= text.length()); // bodyEnd is exclusive
1062 text.replace(bodyStart, bodyEnd - bodyStart, newStyleText);
1067 InspectorCSSId InspectorStyleSheet::ruleId(CSSStyleRule* rule) const
1069 return ruleOrStyleId(rule->style());
1072 void InspectorStyleSheet::revalidateStyle(CSSStyleDeclaration* pageStyle)
1074 if (m_isRevalidating)
1077 m_isRevalidating = true;
1079 for (unsigned i = 0, size = m_flatRules.size(); i < size; ++i) {
1080 CSSStyleRule* parsedRule = m_flatRules.at(i);
1081 if (parsedRule->style() == pageStyle) {
1082 if (parsedRule->style()->cssText() != pageStyle->cssText()) {
1083 // Clear the disabled properties for the invalid style here.
1084 m_inspectorStyles.remove(pageStyle);
1085 setStyleText(pageStyle, pageStyle->cssText());
1090 m_isRevalidating = false;
1093 bool InspectorStyleSheet::originalStyleSheetText(String* result) const
1095 bool success = inlineStyleSheetText(result);
1097 success = resourceStyleSheetText(result);
1101 bool InspectorStyleSheet::resourceStyleSheetText(String* result) const
1103 if (m_origin == "user" || m_origin == "user-agent")
1106 if (!m_pageStyleSheet || !ownerDocument() || !ownerDocument()->frame())
1111 InspectorPageAgent::resourceContent(&error, ownerDocument()->frame(), KURL(ParsedURLString, m_pageStyleSheet->href()), result, &base64Encoded);
1112 return error.isEmpty() && !base64Encoded;
1115 bool InspectorStyleSheet::inlineStyleSheetText(String* result) const
1117 if (!m_pageStyleSheet)
1120 Node* ownerNode = m_pageStyleSheet->ownerNode();
1121 if (!ownerNode || ownerNode->nodeType() != Node::ELEMENT_NODE)
1123 Element* ownerElement = static_cast<Element*>(ownerNode);
1125 if (!ownerElement->hasTagName(HTMLNames::styleTag)
1127 && !ownerElement->hasTagName(SVGNames::styleTag)
1131 *result = ownerElement->innerText();
1135 PassRefPtr<InspectorArray> InspectorStyleSheet::buildArrayForRuleList(CSSRuleList* ruleList)
1137 RefPtr<InspectorArray> result = InspectorArray::create();
1139 return result.release();
1141 RefPtr<CSSRuleList> refRuleList = ruleList;
1142 Vector<CSSStyleRule*> rules;
1143 collectFlatRules(refRuleList, &rules);
1145 for (unsigned i = 0, size = rules.size(); i < size; ++i)
1146 result->pushObject(buildObjectForRule(rules.at(i)));
1148 return result.release();
1151 void InspectorStyleSheet::fixUnparsedPropertyRanges(CSSRuleSourceData* ruleData, const String& styleSheetText)
1153 Vector<CSSPropertySourceData>& propertyData = ruleData->styleSourceData->propertyData;
1154 unsigned size = propertyData.size();
1158 unsigned styleStart = ruleData->styleSourceData->styleBodyRange.start;
1159 const UChar* characters = styleSheetText.characters();
1160 CSSPropertySourceData* nextData = &(propertyData.at(0));
1161 for (unsigned i = 0; i < size; ++i) {
1162 CSSPropertySourceData* currentData = nextData;
1163 nextData = i < size - 1 ? &(propertyData.at(i + 1)) : 0;
1165 if (currentData->parsedOk)
1167 if (currentData->range.end > 0 && characters[styleStart + currentData->range.end - 1] == ';')
1170 unsigned propertyEndInStyleSheet;
1172 propertyEndInStyleSheet = ruleData->styleSourceData->styleBodyRange.end - 1;
1174 propertyEndInStyleSheet = styleStart + nextData->range.start - 1;
1176 while (isHTMLSpace(characters[propertyEndInStyleSheet]))
1177 --propertyEndInStyleSheet;
1179 // propertyEndInStyleSheet points at the last property text character.
1180 unsigned newPropertyEnd = propertyEndInStyleSheet - styleStart + 1; // Exclusive of the last property text character.
1181 if (currentData->range.end != newPropertyEnd) {
1182 currentData->range.end = newPropertyEnd;
1183 unsigned valueStartInStyleSheet = styleStart + currentData->range.start + currentData->name.length();
1184 while (valueStartInStyleSheet < propertyEndInStyleSheet && characters[valueStartInStyleSheet] != ':')
1185 ++valueStartInStyleSheet;
1186 if (valueStartInStyleSheet < propertyEndInStyleSheet)
1187 ++valueStartInStyleSheet; // Shift past the ':'.
1188 while (valueStartInStyleSheet < propertyEndInStyleSheet && isHTMLSpace(characters[valueStartInStyleSheet]))
1189 ++valueStartInStyleSheet;
1190 // Need to exclude the trailing ';' from the property value.
1191 currentData->value = styleSheetText.substring(valueStartInStyleSheet, propertyEndInStyleSheet - valueStartInStyleSheet + (characters[propertyEndInStyleSheet] == ';' ? 0 : 1));
1196 void InspectorStyleSheet::collectFlatRules(PassRefPtr<CSSRuleList> ruleList, Vector<CSSStyleRule*>* result)
1201 for (unsigned i = 0, size = ruleList->length(); i < size; ++i) {
1202 CSSRule* rule = ruleList->item(i);
1203 CSSStyleRule* styleRule = InspectorCSSAgent::asCSSStyleRule(rule);
1205 result->append(styleRule);
1207 RefPtr<CSSRuleList> childRuleList = asCSSRuleList(rule);
1209 collectFlatRules(childRuleList, result);
1214 PassRefPtr<InspectorStyleSheetForInlineStyle> InspectorStyleSheetForInlineStyle::create(const String& id, PassRefPtr<Element> element, const String& origin)
1216 return adoptRef(new InspectorStyleSheetForInlineStyle(id, element, origin));
1219 InspectorStyleSheetForInlineStyle::InspectorStyleSheetForInlineStyle(const String& id, PassRefPtr<Element> element, const String& origin)
1220 : InspectorStyleSheet(id, 0, origin, "")
1221 , m_element(element)
1222 , m_ruleSourceData(0)
1223 , m_isStyleTextValid(false)
1226 m_inspectorStyle = InspectorStyle::create(InspectorCSSId(id, 0), inlineStyle(), this);
1227 m_styleText = m_element->isStyledElement() ? m_element->getAttribute("style").string() : String();
1230 void InspectorStyleSheetForInlineStyle::didModifyElementAttribute()
1232 m_isStyleTextValid = false;
1233 if (m_element->isStyledElement() && m_element->style() != m_inspectorStyle->cssStyle())
1234 m_inspectorStyle = InspectorStyle::create(InspectorCSSId(id(), 0), inlineStyle(), this);
1235 m_ruleSourceData.clear();
1238 bool InspectorStyleSheetForInlineStyle::text(String* result) const
1240 if (!m_isStyleTextValid) {
1241 m_styleText = elementStyleText();
1242 m_isStyleTextValid = true;
1244 *result = m_styleText;
1248 bool InspectorStyleSheetForInlineStyle::setStyleText(CSSStyleDeclaration* style, const String& text)
1250 ASSERT_UNUSED(style, style == inlineStyle());
1251 ExceptionCode ec = 0;
1252 m_element->setAttribute("style", text, ec);
1254 m_isStyleTextValid = true;
1255 m_ruleSourceData.clear();
1259 Document* InspectorStyleSheetForInlineStyle::ownerDocument() const
1261 return m_element->document();
1264 bool InspectorStyleSheetForInlineStyle::ensureParsedDataReady()
1266 // The "style" property value can get changed indirectly, e.g. via element.style.borderWidth = "2px".
1267 const String& currentStyleText = elementStyleText();
1268 if (m_styleText != currentStyleText) {
1269 m_ruleSourceData.clear();
1270 m_styleText = currentStyleText;
1271 m_isStyleTextValid = true;
1274 if (m_ruleSourceData)
1277 m_ruleSourceData = CSSRuleSourceData::create();
1278 RefPtr<CSSStyleSourceData> sourceData = CSSStyleSourceData::create();
1279 bool success = getStyleAttributeRanges(&sourceData);
1283 m_ruleSourceData->styleSourceData = sourceData.release();
1287 PassRefPtr<InspectorStyle> InspectorStyleSheetForInlineStyle::inspectorStyleForId(const InspectorCSSId& id)
1289 ASSERT_UNUSED(id, !id.ordinal());
1290 return m_inspectorStyle;
1293 CSSStyleDeclaration* InspectorStyleSheetForInlineStyle::inlineStyle() const
1295 return m_element->style();
1298 const String& InspectorStyleSheetForInlineStyle::elementStyleText() const
1300 return m_element->getAttribute("style").string();
1303 bool InspectorStyleSheetForInlineStyle::getStyleAttributeRanges(RefPtr<CSSStyleSourceData>* result) const
1305 if (!m_element->isStyledElement())
1308 if (m_styleText.isEmpty()) {
1309 (*result)->styleBodyRange.start = 0;
1310 (*result)->styleBodyRange.end = 0;
1314 RefPtr<CSSMutableStyleDeclaration> tempDeclaration = CSSMutableStyleDeclaration::create();
1316 p.parseDeclaration(tempDeclaration.get(), m_styleText, result, m_element->document()->elementSheet());
1320 } // namespace WebCore
1322 #endif // ENABLE(INSPECTOR)