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 "core/inspector/InspectorStyleSheet.h"
28 #include "CSSPropertyNames.h"
29 #include "HTMLNames.h"
31 #include "bindings/v8/ExceptionState.h"
32 #include "bindings/v8/ExceptionStatePlaceholder.h"
33 #include "bindings/v8/ScriptRegexp.h"
34 #include "core/css/CSSKeyframesRule.h"
35 #include "core/css/CSSMediaRule.h"
36 #include "core/css/parser/BisonCSSParser.h"
37 #include "core/css/CSSRuleList.h"
38 #include "core/css/CSSStyleRule.h"
39 #include "core/css/CSSStyleSheet.h"
40 #include "core/css/CSSSupportsRule.h"
41 #include "core/css/StylePropertySet.h"
42 #include "core/css/StyleRule.h"
43 #include "core/css/StyleSheetContents.h"
44 #include "core/dom/Document.h"
45 #include "core/dom/Element.h"
46 #include "core/frame/PageConsole.h"
47 #include "core/html/parser/HTMLParserIdioms.h"
48 #include "core/inspector/ContentSearchUtils.h"
49 #include "core/inspector/InspectorCSSAgent.h"
50 #include "core/inspector/InspectorPageAgent.h"
51 #include "core/inspector/InspectorResourceAgent.h"
52 #include "wtf/OwnPtr.h"
53 #include "wtf/PassOwnPtr.h"
54 #include "wtf/text/StringBuilder.h"
55 #include "wtf/text/TextPosition.h"
57 using WebCore::TypeBuilder::Array;
58 using WebCore::RuleSourceDataList;
59 using WebCore::CSSRuleSourceData;
61 class ParsedStyleSheet {
62 WTF_MAKE_FAST_ALLOCATED;
66 const String& text() const { ASSERT(m_hasText); return m_text; }
67 void setText(const String& text);
68 bool hasText() const { return m_hasText; }
69 void setSourceData(PassOwnPtr<RuleSourceDataList>);
70 bool hasSourceData() const { return m_sourceData; }
71 PassRefPtr<WebCore::CSSRuleSourceData> ruleSourceDataAt(unsigned) const;
74 void flattenSourceData(RuleSourceDataList*);
78 OwnPtr<RuleSourceDataList> m_sourceData;
81 ParsedStyleSheet::ParsedStyleSheet()
86 void ParsedStyleSheet::setText(const String& text)
90 setSourceData(nullptr);
93 void ParsedStyleSheet::flattenSourceData(RuleSourceDataList* dataList)
95 for (size_t i = 0; i < dataList->size(); ++i) {
96 RefPtr<CSSRuleSourceData>& data = dataList->at(i);
97 if (data->type == CSSRuleSourceData::STYLE_RULE) {
98 m_sourceData->append(data);
99 } else if (data->type == CSSRuleSourceData::IMPORT_RULE) {
100 m_sourceData->append(data);
101 } else if (data->type == CSSRuleSourceData::MEDIA_RULE) {
102 m_sourceData->append(data);
103 flattenSourceData(&data->childRules);
104 } else if (data->type == CSSRuleSourceData::SUPPORTS_RULE) {
105 flattenSourceData(&data->childRules);
110 void ParsedStyleSheet::setSourceData(PassOwnPtr<RuleSourceDataList> sourceData)
113 m_sourceData.clear();
117 m_sourceData = adoptPtr(new RuleSourceDataList());
119 // FIXME: This is a temporary solution to retain the original flat sourceData structure
120 // containing only style rules, even though BisonCSSParser now provides the full rule source data tree.
121 // Normally, we should just assign m_sourceData = sourceData;
122 flattenSourceData(sourceData.get());
125 PassRefPtr<WebCore::CSSRuleSourceData> ParsedStyleSheet::ruleSourceDataAt(unsigned index) const
127 if (!hasSourceData() || index >= m_sourceData->size())
130 return m_sourceData->at(index);
135 static PassOwnPtr<BisonCSSParser> createCSSParser(Document* document)
137 return adoptPtr(new BisonCSSParser(document ? CSSParserContext(*document, 0) : strictCSSParserContext()));
142 class StyleSheetHandler FINAL : public CSSParserObserver {
144 StyleSheetHandler(const String& parsedText, Document* document, StyleSheetContents* styleSheetContents, RuleSourceDataList* result)
145 : m_parsedText(parsedText)
146 , m_document(document)
147 , m_styleSheetContents(styleSheetContents)
149 , m_propertyRangeStart(UINT_MAX)
150 , m_selectorRangeStart(UINT_MAX)
151 , m_commentRangeStart(UINT_MAX)
157 virtual void startRuleHeader(CSSRuleSourceData::Type, unsigned) OVERRIDE;
158 virtual void endRuleHeader(unsigned) OVERRIDE;
159 virtual void startSelector(unsigned) OVERRIDE;
160 virtual void endSelector(unsigned) OVERRIDE;
161 virtual void startRuleBody(unsigned) OVERRIDE;
162 virtual void endRuleBody(unsigned, bool) OVERRIDE;
163 virtual void startEndUnknownRule() OVERRIDE { addNewRuleToSourceTree(CSSRuleSourceData::createUnknown()); }
164 virtual void startProperty(unsigned) OVERRIDE;
165 virtual void endProperty(bool, bool, unsigned, CSSParserError) OVERRIDE;
166 virtual void startComment(unsigned) OVERRIDE;
167 virtual void endComment(unsigned) OVERRIDE;
169 void addNewRuleToSourceTree(PassRefPtr<CSSRuleSourceData>);
170 PassRefPtr<CSSRuleSourceData> popRuleData();
171 template <typename CharacterType> inline void setRuleHeaderEnd(const CharacterType*, unsigned);
172 void fixUnparsedPropertyRanges(CSSRuleSourceData*);
174 const String& m_parsedText;
175 Document* m_document;
176 StyleSheetContents* m_styleSheetContents;
177 RuleSourceDataList* m_result;
178 RuleSourceDataList m_currentRuleDataStack;
179 RefPtr<CSSRuleSourceData> m_currentRuleData;
180 OwnPtr<BisonCSSParser> m_commentParser;
181 unsigned m_propertyRangeStart;
182 unsigned m_selectorRangeStart;
183 unsigned m_commentRangeStart;
186 void StyleSheetHandler::startRuleHeader(CSSRuleSourceData::Type type, unsigned offset)
188 // Pop off data for a previous invalid rule.
189 if (m_currentRuleData)
190 m_currentRuleDataStack.removeLast();
192 RefPtr<CSSRuleSourceData> data = CSSRuleSourceData::create(type);
193 data->ruleHeaderRange.start = offset;
194 m_currentRuleData = data;
195 m_currentRuleDataStack.append(data.release());
198 template <typename CharacterType>
199 inline void StyleSheetHandler::setRuleHeaderEnd(const CharacterType* dataStart, unsigned listEndOffset)
201 while (listEndOffset > 1) {
202 if (isHTMLSpace<CharacterType>(*(dataStart + listEndOffset - 1)))
208 m_currentRuleDataStack.last()->ruleHeaderRange.end = listEndOffset;
209 if (!m_currentRuleDataStack.last()->selectorRanges.isEmpty())
210 m_currentRuleDataStack.last()->selectorRanges.last().end = listEndOffset;
213 void StyleSheetHandler::endRuleHeader(unsigned offset)
215 ASSERT(!m_currentRuleDataStack.isEmpty());
217 if (m_parsedText.is8Bit())
218 setRuleHeaderEnd<LChar>(m_parsedText.characters8(), offset);
220 setRuleHeaderEnd<UChar>(m_parsedText.characters16(), offset);
223 void StyleSheetHandler::startSelector(unsigned offset)
225 m_selectorRangeStart = offset;
228 void StyleSheetHandler::endSelector(unsigned offset)
230 ASSERT(m_currentRuleDataStack.size());
231 m_currentRuleDataStack.last()->selectorRanges.append(SourceRange(m_selectorRangeStart, offset));
232 m_selectorRangeStart = UINT_MAX;
235 void StyleSheetHandler::startRuleBody(unsigned offset)
237 m_currentRuleData.clear();
238 ASSERT(!m_currentRuleDataStack.isEmpty());
239 if (m_parsedText[offset] == '{')
240 ++offset; // Skip the rule body opening brace.
241 m_currentRuleDataStack.last()->ruleBodyRange.start = offset;
244 void StyleSheetHandler::endRuleBody(unsigned offset, bool error)
246 ASSERT(!m_currentRuleDataStack.isEmpty());
247 m_currentRuleDataStack.last()->ruleBodyRange.end = offset;
248 m_propertyRangeStart = UINT_MAX;
249 RefPtr<CSSRuleSourceData> rule = popRuleData();
253 fixUnparsedPropertyRanges(rule.get());
254 addNewRuleToSourceTree(rule.release());
257 void StyleSheetHandler::addNewRuleToSourceTree(PassRefPtr<CSSRuleSourceData> rule)
259 if (m_currentRuleDataStack.isEmpty())
260 m_result->append(rule);
262 m_currentRuleDataStack.last()->childRules.append(rule);
265 PassRefPtr<CSSRuleSourceData> StyleSheetHandler::popRuleData()
267 ASSERT(!m_currentRuleDataStack.isEmpty());
268 m_currentRuleData.clear();
269 RefPtr<CSSRuleSourceData> data = m_currentRuleDataStack.last();
270 m_currentRuleDataStack.removeLast();
271 return data.release();
274 template <typename CharacterType>
275 static inline void fixUnparsedProperties(const CharacterType* characters, CSSRuleSourceData* ruleData)
277 Vector<CSSPropertySourceData>& propertyData = ruleData->styleSourceData->propertyData;
278 unsigned size = propertyData.size();
282 unsigned styleStart = ruleData->ruleBodyRange.start;
283 CSSPropertySourceData* nextData = &(propertyData.at(0));
284 for (unsigned i = 0; i < size; ++i) {
285 CSSPropertySourceData* currentData = nextData;
286 nextData = i < size - 1 ? &(propertyData.at(i + 1)) : 0;
288 if (currentData->parsedOk)
290 if (currentData->range.end > 0 && characters[styleStart + currentData->range.end - 1] == ';')
293 unsigned propertyEndInStyleSheet;
295 propertyEndInStyleSheet = ruleData->ruleBodyRange.end - 1;
297 propertyEndInStyleSheet = styleStart + nextData->range.start - 1;
299 while (isHTMLSpace<CharacterType>(characters[propertyEndInStyleSheet]))
300 --propertyEndInStyleSheet;
302 // propertyEndInStyleSheet points at the last property text character.
303 unsigned newPropertyEnd = propertyEndInStyleSheet - styleStart + 1; // Exclusive of the last property text character.
304 if (currentData->range.end != newPropertyEnd) {
305 currentData->range.end = newPropertyEnd;
306 unsigned valueStartInStyleSheet = styleStart + currentData->range.start + currentData->name.length();
307 while (valueStartInStyleSheet < propertyEndInStyleSheet && characters[valueStartInStyleSheet] != ':')
308 ++valueStartInStyleSheet;
309 if (valueStartInStyleSheet < propertyEndInStyleSheet)
310 ++valueStartInStyleSheet; // Shift past the ':'.
311 while (valueStartInStyleSheet < propertyEndInStyleSheet && isHTMLSpace<CharacterType>(characters[valueStartInStyleSheet]))
312 ++valueStartInStyleSheet;
313 // Need to exclude the trailing ';' from the property value.
314 currentData->value = String(characters + valueStartInStyleSheet, propertyEndInStyleSheet - valueStartInStyleSheet + (characters[propertyEndInStyleSheet] == ';' ? 0 : 1));
319 void StyleSheetHandler::fixUnparsedPropertyRanges(CSSRuleSourceData* ruleData)
321 if (!ruleData->styleSourceData)
324 if (m_parsedText.is8Bit()) {
325 fixUnparsedProperties<LChar>(m_parsedText.characters8(), ruleData);
329 fixUnparsedProperties<UChar>(m_parsedText.characters16(), ruleData);
332 void StyleSheetHandler::startProperty(unsigned offset)
334 if (m_currentRuleDataStack.isEmpty() || !m_currentRuleDataStack.last()->styleSourceData)
336 m_propertyRangeStart = offset;
339 void StyleSheetHandler::endProperty(bool isImportant, bool isParsed, unsigned offset, CSSParserError errorType)
341 // FIXME: This is the only place CSSParserError is every read!?
342 if (errorType != NoCSSError)
343 m_propertyRangeStart = UINT_MAX;
345 if (m_propertyRangeStart == UINT_MAX || m_currentRuleDataStack.isEmpty() || !m_currentRuleDataStack.last()->styleSourceData)
348 ASSERT(offset <= m_parsedText.length());
349 if (offset < m_parsedText.length() && m_parsedText[offset] == ';') // Include semicolon into the property text.
352 const unsigned start = m_propertyRangeStart;
353 const unsigned end = offset;
355 String propertyString = m_parsedText.substring(start, end - start).stripWhiteSpace();
356 if (propertyString.endsWith(';'))
357 propertyString = propertyString.left(propertyString.length() - 1);
358 size_t colonIndex = propertyString.find(':');
359 ASSERT(colonIndex != kNotFound);
361 String name = propertyString.left(colonIndex).stripWhiteSpace();
362 String value = propertyString.substring(colonIndex + 1, propertyString.length()).stripWhiteSpace();
363 // The property range is relative to the declaration start offset.
364 unsigned topRuleBodyRangeStart = m_currentRuleDataStack.last()->ruleBodyRange.start;
365 m_currentRuleDataStack.last()->styleSourceData->propertyData.append(
366 CSSPropertySourceData(name, value, isImportant, false, isParsed, SourceRange(start - topRuleBodyRangeStart, end - topRuleBodyRangeStart)));
367 m_propertyRangeStart = UINT_MAX;
370 void StyleSheetHandler::startComment(unsigned offset)
372 ASSERT(m_commentRangeStart == UINT_MAX);
373 m_commentRangeStart = offset;
376 void StyleSheetHandler::endComment(unsigned offset)
378 ASSERT(offset <= m_parsedText.length());
380 unsigned startOffset = m_commentRangeStart;
381 m_commentRangeStart = UINT_MAX;
382 if (m_propertyRangeStart != UINT_MAX) {
383 ASSERT(startOffset >= m_propertyRangeStart);
384 // startProperty() is called automatically at the start of a style declaration.
385 // Check if no text has been scanned yet, otherwise the comment is inside a property.
386 if (!m_parsedText.substring(m_propertyRangeStart, startOffset).stripWhiteSpace().isEmpty())
388 m_propertyRangeStart = UINT_MAX;
390 if (m_currentRuleDataStack.isEmpty() || !m_currentRuleDataStack.last()->ruleHeaderRange.end || !m_currentRuleDataStack.last()->styleSourceData)
393 // The lexer is not inside a property AND it is scanning a declaration-aware rule body.
394 String commentText = m_parsedText.substring(startOffset, offset - startOffset);
396 ASSERT(commentText.startsWith("/*"));
397 commentText = commentText.substring(2);
399 // Require well-formed comments.
400 if (!commentText.endsWith("*/"))
402 commentText = commentText.substring(0, commentText.length() - 2).stripWhiteSpace();
403 if (commentText.isEmpty())
406 // FIXME: Use the actual rule type rather than STYLE_RULE?
407 if (!m_commentParser)
408 m_commentParser = createCSSParser(m_document);
409 RuleSourceDataList sourceData;
411 // FIXME: Use another subclass of BisonCSSParser::SourceDataHandler and assert that
412 // no comments are encountered (will not need m_document and m_styleSheetContents).
413 StyleSheetHandler handler(commentText, m_document, m_styleSheetContents, &sourceData);
414 RefPtr<MutableStylePropertySet> tempMutableStyle = MutableStylePropertySet::create();
415 m_commentParser->parseDeclaration(tempMutableStyle.get(), commentText, &handler, m_styleSheetContents);
416 Vector<CSSPropertySourceData>& commentPropertyData = sourceData.first()->styleSourceData->propertyData;
417 if (commentPropertyData.size() != 1)
419 CSSPropertySourceData& propertyData = commentPropertyData.at(0);
420 if (propertyData.range.length() != commentText.length())
423 unsigned topRuleBodyRangeStart = m_currentRuleDataStack.last()->ruleBodyRange.start;
424 m_currentRuleDataStack.last()->styleSourceData->propertyData.append(
425 CSSPropertySourceData(propertyData.name, propertyData.value, false, true, true, SourceRange(startOffset - topRuleBodyRangeStart, offset - topRuleBodyRangeStart)));
430 enum MediaListSource {
431 MediaListSourceLinkedSheet,
432 MediaListSourceInlineSheet,
433 MediaListSourceMediaRule,
434 MediaListSourceImportRule
437 static PassRefPtr<TypeBuilder::CSS::SourceRange> buildSourceRangeObject(const SourceRange& range, Vector<unsigned>* lineEndings)
441 TextPosition start = TextPosition::fromOffsetAndLineEndings(range.start, *lineEndings);
442 TextPosition end = TextPosition::fromOffsetAndLineEndings(range.end, *lineEndings);
444 RefPtr<TypeBuilder::CSS::SourceRange> result = TypeBuilder::CSS::SourceRange::create()
445 .setStartLine(start.m_line.zeroBasedInt())
446 .setStartColumn(start.m_column.zeroBasedInt())
447 .setEndLine(end.m_line.zeroBasedInt())
448 .setEndColumn(end.m_column.zeroBasedInt());
449 return result.release();
452 static PassRefPtr<CSSRuleList> asCSSRuleList(CSSStyleSheet* styleSheet)
457 RefPtr<StaticCSSRuleList> list = StaticCSSRuleList::create();
458 Vector<RefPtr<CSSRule> >& listRules = list->rules();
459 for (unsigned i = 0, size = styleSheet->length(); i < size; ++i) {
460 CSSRule* item = styleSheet->item(i);
461 if (item->type() == CSSRule::CHARSET_RULE)
463 listRules.append(item);
465 return list.release();
468 static PassRefPtr<CSSRuleList> asCSSRuleList(CSSRule* rule)
473 if (rule->type() == CSSRule::MEDIA_RULE)
474 return toCSSMediaRule(rule)->cssRules();
476 if (rule->type() == CSSRule::KEYFRAMES_RULE)
477 return toCSSKeyframesRule(rule)->cssRules();
479 if (rule->type() == CSSRule::SUPPORTS_RULE)
480 return toCSSSupportsRule(rule)->cssRules();
485 PassRefPtr<InspectorStyle> InspectorStyle::create(const InspectorCSSId& styleId, PassRefPtr<CSSStyleDeclaration> style, InspectorStyleSheet* parentStyleSheet)
487 return adoptRef(new InspectorStyle(styleId, style, parentStyleSheet));
490 InspectorStyle::InspectorStyle(const InspectorCSSId& styleId, PassRefPtr<CSSStyleDeclaration> style, InspectorStyleSheet* parentStyleSheet)
493 , m_parentStyleSheet(parentStyleSheet)
494 , m_formatAcquired(false)
499 PassRefPtr<TypeBuilder::CSS::CSSStyle> InspectorStyle::buildObjectForStyle() const
501 RefPtr<TypeBuilder::CSS::CSSStyle> result = styleWithProperties();
502 if (!m_styleId.isEmpty())
503 result->setStyleId(m_styleId.asProtocolValue<TypeBuilder::CSS::CSSStyleId>());
505 RefPtr<CSSRuleSourceData> sourceData = extractSourceData();
507 result->setRange(buildSourceRangeObject(sourceData->ruleBodyRange, m_parentStyleSheet->lineEndings().get()));
509 return result.release();
512 PassRefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSComputedStyleProperty> > InspectorStyle::buildArrayForComputedStyle() const
514 RefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSComputedStyleProperty> > result = TypeBuilder::Array<TypeBuilder::CSS::CSSComputedStyleProperty>::create();
515 Vector<InspectorStyleProperty> properties;
516 populateAllProperties(properties);
518 for (Vector<InspectorStyleProperty>::iterator it = properties.begin(), itEnd = properties.end(); it != itEnd; ++it) {
519 const CSSPropertySourceData& propertyEntry = it->sourceData;
520 RefPtr<TypeBuilder::CSS::CSSComputedStyleProperty> entry = TypeBuilder::CSS::CSSComputedStyleProperty::create()
521 .setName(propertyEntry.name)
522 .setValue(propertyEntry.value);
523 result->addItem(entry);
526 return result.release();
529 bool InspectorStyle::verifyPropertyText(const String& propertyText, bool canOmitSemicolon)
531 DEFINE_STATIC_LOCAL(String, bogusPropertyName, ("-webkit-boguz-propertee"));
532 RefPtr<MutableStylePropertySet> tempMutableStyle = MutableStylePropertySet::create();
533 RuleSourceDataList sourceData;
534 RefPtr<StyleSheetContents> styleSheetContents = StyleSheetContents::create(strictCSSParserContext());
535 String declarationText = propertyText + (canOmitSemicolon ? ";" : " ") + bogusPropertyName + ": none";
536 StyleSheetHandler handler(declarationText, ownerDocument(), styleSheetContents.get(), &sourceData);
537 createCSSParser(ownerDocument())->parseDeclaration(tempMutableStyle.get(), declarationText, &handler, styleSheetContents.get());
538 Vector<CSSPropertySourceData>& propertyData = sourceData.first()->styleSourceData->propertyData;
539 unsigned propertyCount = propertyData.size();
541 // At least one property + the bogus property added just above should be present.
542 if (propertyCount < 2)
545 // Check for the proper propertyText termination (the parser could at least restore to the PROPERTY_NAME state).
546 if (propertyData.at(propertyCount - 1).name != bogusPropertyName)
552 bool InspectorStyle::setPropertyText(unsigned index, const String& propertyText, bool overwrite, String* oldText, ExceptionState& exceptionState)
554 ASSERT(m_parentStyleSheet);
556 if (!m_parentStyleSheet->ensureParsedDataReady()) {
557 exceptionState.throwDOMException(NotFoundError, "The parent style sheet's data hasn't been processed.");
561 if (!propertyText.stripWhiteSpace().isEmpty()) {
562 if (!verifyPropertyText(propertyText, false) && !verifyPropertyText(propertyText, true)) {
563 exceptionState.throwDOMException(SyntaxError, "The property '" + propertyText + "' could not be set.");
568 RefPtr<CSSRuleSourceData> sourceData = extractSourceData();
570 exceptionState.throwDOMException(NotFoundError, "The property '" + propertyText + "' could not be set.");
575 bool success = styleText(&text);
577 exceptionState.throwDOMException(NotFoundError, "The property '" + propertyText + "' could not be set.");
581 Vector<InspectorStyleProperty> allProperties;
582 populateAllProperties(allProperties);
584 InspectorStyleTextEditor editor(&allProperties, text, newLineAndWhitespaceDelimiters());
586 if (index >= allProperties.size()) {
587 exceptionState.throwDOMException(IndexSizeError, "The index provided (" + String::number(index) + ") is greater than or equal to the maximum bound (" + String::number(allProperties.size()) + ").");
590 *oldText = allProperties.at(index).rawText;
591 editor.replaceProperty(index, propertyText);
593 editor.insertProperty(index, propertyText, sourceData->ruleBodyRange.length());
595 return applyStyleText(editor.styleText());
598 bool InspectorStyle::styleText(String* result) const
600 RefPtr<CSSRuleSourceData> sourceData = extractSourceData();
604 String styleSheetText;
605 bool success = m_parentStyleSheet->getText(&styleSheetText);
609 SourceRange& bodyRange = sourceData->ruleBodyRange;
610 *result = styleSheetText.substring(bodyRange.start, bodyRange.end - bodyRange.start);
614 void InspectorStyle::populateAllProperties(Vector<InspectorStyleProperty>& result) const
616 HashSet<String> sourcePropertyNames;
618 RefPtr<CSSRuleSourceData> sourceData = extractSourceData();
620 String styleDeclaration;
621 bool isStyleTextKnown = styleText(&styleDeclaration);
622 ASSERT_UNUSED(isStyleTextKnown, isStyleTextKnown);
623 Vector<CSSPropertySourceData>& sourcePropertyData = sourceData->styleSourceData->propertyData;
624 for (Vector<CSSPropertySourceData>::const_iterator it = sourcePropertyData.begin(); it != sourcePropertyData.end(); ++it) {
625 InspectorStyleProperty p(*it, true);
626 p.setRawTextFromStyleDeclaration(styleDeclaration);
628 sourcePropertyNames.add(it->name.lower());
632 for (int i = 0, size = m_style->length(); i < size; ++i) {
633 String name = m_style->item(i);
634 if (!sourcePropertyNames.add(name.lower()).isNewEntry)
637 result.append(InspectorStyleProperty(CSSPropertySourceData(name, m_style->getPropertyValue(name), !m_style->getPropertyPriority(name).isEmpty(), false, true, SourceRange()), false));
641 PassRefPtr<TypeBuilder::CSS::CSSStyle> InspectorStyle::styleWithProperties() const
643 RefPtr<Array<TypeBuilder::CSS::CSSProperty> > propertiesObject = Array<TypeBuilder::CSS::CSSProperty>::create();
644 RefPtr<Array<TypeBuilder::CSS::ShorthandEntry> > shorthandEntries = Array<TypeBuilder::CSS::ShorthandEntry>::create();
645 HashMap<String, RefPtr<TypeBuilder::CSS::CSSProperty> > propertyNameToPreviousActiveProperty;
646 HashSet<String> foundShorthands;
647 String previousPriority;
648 String previousStatus;
649 OwnPtr<Vector<unsigned> > lineEndings(m_parentStyleSheet ? m_parentStyleSheet->lineEndings() : PassOwnPtr<Vector<unsigned> >());
650 RefPtr<CSSRuleSourceData> sourceData = extractSourceData();
651 unsigned ruleBodyRangeStart = sourceData ? sourceData->ruleBodyRange.start : 0;
653 Vector<InspectorStyleProperty> properties;
654 populateAllProperties(properties);
656 for (Vector<InspectorStyleProperty>::iterator it = properties.begin(), itEnd = properties.end(); it != itEnd; ++it) {
657 const CSSPropertySourceData& propertyEntry = it->sourceData;
658 const String& name = propertyEntry.name;
659 const bool disabled = it->sourceData.disabled;
661 TypeBuilder::CSS::CSSProperty::Status::Enum status = disabled ? TypeBuilder::CSS::CSSProperty::Status::Disabled : TypeBuilder::CSS::CSSProperty::Status::Active;
663 RefPtr<TypeBuilder::CSS::CSSProperty> property = TypeBuilder::CSS::CSSProperty::create()
665 .setValue(propertyEntry.value);
666 propertiesObject->addItem(property);
668 // Default "parsedOk" == true.
669 if (!propertyEntry.parsedOk)
670 property->setParsedOk(false);
671 if (it->hasRawText())
672 property->setText(it->rawText);
674 // Default "priority" == "".
675 if (propertyEntry.important)
676 property->setPriority("important");
678 // The property range is relative to the style body start.
679 // Should be converted into an absolute range (relative to the stylesheet start)
680 // for the proper conversion into line:column.
681 SourceRange absolutePropertyRange = propertyEntry.range;
682 absolutePropertyRange.start += ruleBodyRangeStart;
683 absolutePropertyRange.end += ruleBodyRangeStart;
684 property->setRange(buildSourceRangeObject(absolutePropertyRange, lineEndings.get()));
689 property->setImplicit(false);
691 // Parsed property overrides any property with the same name. Non-parsed property overrides
692 // previous non-parsed property with the same name (if any).
693 bool shouldInactivate = false;
694 CSSPropertyID propertyId = cssPropertyID(name);
695 // Canonicalize property names to treat non-prefixed and vendor-prefixed property names the same (opacity vs. -webkit-opacity).
696 String canonicalPropertyName = propertyId ? getPropertyNameString(propertyId) : name;
697 HashMap<String, RefPtr<TypeBuilder::CSS::CSSProperty> >::iterator activeIt = propertyNameToPreviousActiveProperty.find(canonicalPropertyName);
698 if (activeIt != propertyNameToPreviousActiveProperty.end()) {
699 if (propertyEntry.parsedOk) {
700 bool successPriority = activeIt->value->getString(TypeBuilder::CSS::CSSProperty::Priority, &previousPriority);
701 bool successStatus = activeIt->value->getString(TypeBuilder::CSS::CSSProperty::Status, &previousStatus);
702 if (successStatus && previousStatus != "inactive") {
703 if (propertyEntry.important || !successPriority) // Priority not set == "not important".
704 shouldInactivate = true;
705 else if (status == TypeBuilder::CSS::CSSProperty::Status::Active) {
706 // Inactivate a non-important property following the same-named important property.
707 status = TypeBuilder::CSS::CSSProperty::Status::Inactive;
711 bool previousParsedOk;
712 bool success = activeIt->value->getBoolean(TypeBuilder::CSS::CSSProperty::ParsedOk, &previousParsedOk);
713 if (success && !previousParsedOk)
714 shouldInactivate = true;
717 propertyNameToPreviousActiveProperty.set(canonicalPropertyName, property);
719 if (shouldInactivate) {
720 activeIt->value->setStatus(TypeBuilder::CSS::CSSProperty::Status::Inactive);
721 propertyNameToPreviousActiveProperty.set(canonicalPropertyName, property);
724 bool implicit = m_style->isPropertyImplicit(name);
725 // Default "implicit" == false.
727 property->setImplicit(true);
728 status = TypeBuilder::CSS::CSSProperty::Status::Style;
730 String shorthand = m_style->getPropertyShorthand(name);
731 if (!shorthand.isEmpty()) {
732 if (foundShorthands.add(shorthand).isNewEntry) {
733 RefPtr<TypeBuilder::CSS::ShorthandEntry> entry = TypeBuilder::CSS::ShorthandEntry::create()
735 .setValue(shorthandValue(shorthand));
736 shorthandEntries->addItem(entry);
742 // Default "status" == "style".
743 if (status != TypeBuilder::CSS::CSSProperty::Status::Style)
744 property->setStatus(status);
747 RefPtr<TypeBuilder::CSS::CSSStyle> result = TypeBuilder::CSS::CSSStyle::create()
748 .setCssProperties(propertiesObject)
749 .setShorthandEntries(shorthandEntries);
750 return result.release();
753 PassRefPtr<CSSRuleSourceData> InspectorStyle::extractSourceData() const
755 if (!m_parentStyleSheet || !m_parentStyleSheet->ensureParsedDataReady())
757 return m_parentStyleSheet->ruleSourceDataFor(m_style.get());
760 bool InspectorStyle::applyStyleText(const String& text)
762 return m_parentStyleSheet->setStyleText(m_style.get(), text);
765 String InspectorStyle::shorthandValue(const String& shorthandProperty) const
767 String value = m_style->getPropertyValue(shorthandProperty);
768 if (value.isEmpty()) {
769 StringBuilder builder;
771 for (unsigned i = 0; i < m_style->length(); ++i) {
772 String individualProperty = m_style->item(i);
773 if (m_style->getPropertyShorthand(individualProperty) != shorthandProperty)
775 if (m_style->isPropertyImplicit(individualProperty))
777 String individualValue = m_style->getPropertyValue(individualProperty);
778 if (individualValue == "initial")
780 if (!builder.isEmpty())
782 builder.append(individualValue);
785 return builder.toString();
790 String InspectorStyle::shorthandPriority(const String& shorthandProperty) const
792 String priority = m_style->getPropertyPriority(shorthandProperty);
793 if (priority.isEmpty()) {
794 for (unsigned i = 0; i < m_style->length(); ++i) {
795 String individualProperty = m_style->item(i);
796 if (m_style->getPropertyShorthand(individualProperty) != shorthandProperty)
798 priority = m_style->getPropertyPriority(individualProperty);
805 Vector<String> InspectorStyle::longhandProperties(const String& shorthandProperty) const
807 Vector<String> properties;
808 HashSet<String> foundProperties;
809 for (unsigned i = 0; i < m_style->length(); ++i) {
810 String individualProperty = m_style->item(i);
811 if (foundProperties.contains(individualProperty) || m_style->getPropertyShorthand(individualProperty) != shorthandProperty)
814 foundProperties.add(individualProperty);
815 properties.append(individualProperty);
820 NewLineAndWhitespace& InspectorStyle::newLineAndWhitespaceDelimiters() const
822 DEFINE_STATIC_LOCAL(String, defaultPrefix, (" "));
824 if (m_formatAcquired)
827 RefPtr<CSSRuleSourceData> sourceData = extractSourceData();
828 Vector<CSSPropertySourceData>* sourcePropertyData = sourceData ? &(sourceData->styleSourceData->propertyData) : 0;
830 if (!sourcePropertyData || !(propertyCount = sourcePropertyData->size())) {
831 m_format.first = "\n";
832 m_format.second = defaultPrefix;
833 return m_format; // Do not remember the default formatting and attempt to acquire it later.
837 bool success = styleText(&text);
838 ASSERT_UNUSED(success, success);
840 m_formatAcquired = true;
842 String candidatePrefix = defaultPrefix;
843 StringBuilder formatLineFeed;
844 StringBuilder prefix;
846 int propertyIndex = 0;
847 bool isFullPrefixScanned = false;
848 bool lineFeedTerminated = false;
849 while (propertyIndex < propertyCount) {
850 const WebCore::CSSPropertySourceData& currentProperty = sourcePropertyData->at(propertyIndex++);
852 bool processNextProperty = false;
853 int scanEnd = currentProperty.range.start;
854 for (int i = scanStart; i < scanEnd; ++i) {
856 bool isLineFeed = isHTMLLineBreak(ch);
858 if (!lineFeedTerminated)
859 formatLineFeed.append(ch);
861 } else if (isHTMLSpace<UChar>(ch))
864 candidatePrefix = prefix.toString();
866 scanStart = currentProperty.range.end;
868 processNextProperty = true;
871 if (!isLineFeed && formatLineFeed.length())
872 lineFeedTerminated = true;
874 if (!processNextProperty) {
875 isFullPrefixScanned = true;
880 m_format.first = formatLineFeed.toString();
881 m_format.second = isFullPrefixScanned ? prefix.toString() : candidatePrefix;
885 Document* InspectorStyle::ownerDocument() const
887 return m_parentStyleSheet->pageStyleSheet() ? m_parentStyleSheet->pageStyleSheet()->ownerDocument() : 0;
890 PassRefPtr<InspectorStyleSheet> InspectorStyleSheet::create(InspectorPageAgent* pageAgent, InspectorResourceAgent* resourceAgent, const String& id, PassRefPtr<CSSStyleSheet> pageStyleSheet, TypeBuilder::CSS::StyleSheetOrigin::Enum origin, const String& documentURL, Listener* listener)
892 return adoptRef(new InspectorStyleSheet(pageAgent, resourceAgent, id, pageStyleSheet, origin, documentURL, listener));
896 String InspectorStyleSheet::styleSheetURL(CSSStyleSheet* pageStyleSheet)
898 if (pageStyleSheet && !pageStyleSheet->contents()->baseURL().isEmpty())
899 return pageStyleSheet->contents()->baseURL().string();
900 return emptyString();
904 void InspectorStyleSheet::collectFlatRules(PassRefPtr<CSSRuleList> ruleList, CSSRuleVector* result)
909 for (unsigned i = 0, size = ruleList->length(); i < size; ++i) {
910 CSSRule* rule = ruleList->item(i);
912 // The result->append()'ed types should be exactly the same as in ParsedStyleSheet::flattenSourceData().
913 switch (rule->type()) {
914 case CSSRule::STYLE_RULE:
915 result->append(rule);
917 case CSSRule::IMPORT_RULE:
918 case CSSRule::MEDIA_RULE:
919 result->append(rule);
924 RefPtr<CSSRuleList> childRuleList = asCSSRuleList(rule);
926 collectFlatRules(childRuleList, result);
930 InspectorStyleSheet::InspectorStyleSheet(InspectorPageAgent* pageAgent, InspectorResourceAgent* resourceAgent, const String& id, PassRefPtr<CSSStyleSheet> pageStyleSheet, TypeBuilder::CSS::StyleSheetOrigin::Enum origin, const String& documentURL, Listener* listener)
931 : m_pageAgent(pageAgent)
932 , m_resourceAgent(resourceAgent)
934 , m_pageStyleSheet(pageStyleSheet)
936 , m_documentURL(documentURL)
937 , m_isRevalidating(false)
938 , m_listener(listener)
940 m_parsedStyleSheet = new ParsedStyleSheet();
943 InspectorStyleSheet::~InspectorStyleSheet()
945 delete m_parsedStyleSheet;
948 String InspectorStyleSheet::finalURL() const
950 String url = styleSheetURL(m_pageStyleSheet.get());
951 return url.isEmpty() ? m_documentURL : url;
954 void InspectorStyleSheet::reparseStyleSheet(const String& text)
957 m_listener->willReparseStyleSheet();
960 // Have a separate scope for clearRules() (bug 95324).
961 CSSStyleSheet::RuleMutationScope mutationScope(m_pageStyleSheet.get());
962 m_pageStyleSheet->contents()->clearRules();
963 m_pageStyleSheet->clearChildRuleCSSOMWrappers();
966 CSSStyleSheet::RuleMutationScope mutationScope(m_pageStyleSheet.get());
967 m_pageStyleSheet->contents()->parseString(text);
971 m_listener->didReparseStyleSheet();
972 fireStyleSheetChanged();
973 m_pageStyleSheet->ownerDocument()->styleResolverChanged(RecalcStyleImmediately, FullStyleUpdate);
976 bool InspectorStyleSheet::setText(const String& text, ExceptionState& exceptionState)
978 if (!checkPageStyleSheet(exceptionState))
980 if (!m_parsedStyleSheet)
983 m_parsedStyleSheet->setText(text);
989 String InspectorStyleSheet::ruleSelector(const InspectorCSSId& id, ExceptionState& exceptionState)
991 CSSStyleRule* rule = ruleForId(id);
993 exceptionState.throwDOMException(NotFoundError, "No rule was found for the given ID.");
996 return rule->selectorText();
999 bool InspectorStyleSheet::setRuleSelector(const InspectorCSSId& id, const String& selector, ExceptionState& exceptionState)
1001 if (!checkPageStyleSheet(exceptionState))
1003 CSSStyleRule* rule = ruleForId(id);
1005 exceptionState.throwDOMException(NotFoundError, "No rule was found for the given ID.");
1008 CSSStyleSheet* styleSheet = rule->parentStyleSheet();
1009 if (!styleSheet || !ensureParsedDataReady()) {
1010 exceptionState.throwDOMException(NotFoundError, "No stylesheet could be found in which to set the selector.");
1014 rule->setSelectorText(selector);
1015 RefPtr<CSSRuleSourceData> sourceData = ruleSourceDataFor(rule->style());
1017 exceptionState.throwDOMException(NotFoundError, "The selector '" + selector + "' could not be set.");
1021 String sheetText = m_parsedStyleSheet->text();
1022 sheetText.replace(sourceData->ruleHeaderRange.start, sourceData->ruleHeaderRange.length(), selector);
1023 m_parsedStyleSheet->setText(sheetText);
1024 fireStyleSheetChanged();
1028 static bool checkStyleRuleSelector(Document* document, const String& selector)
1030 CSSSelectorList selectorList;
1031 createCSSParser(document)->parseSelector(selector, selectorList);
1032 return selectorList.isValid();
1035 CSSStyleRule* InspectorStyleSheet::addRule(const String& selector, ExceptionState& exceptionState)
1037 if (!checkPageStyleSheet(exceptionState))
1039 if (!checkStyleRuleSelector(m_pageStyleSheet->ownerDocument(), selector)) {
1040 exceptionState.throwDOMException(SyntaxError, "The selector '" + selector + "' could not be added.");
1045 bool success = getText(&text);
1047 exceptionState.throwDOMException(NotFoundError, "The selector '" + selector + "' could not be added.");
1050 StringBuilder styleSheetText;
1051 styleSheetText.append(text);
1053 m_pageStyleSheet->addRule(selector, "", exceptionState);
1054 if (exceptionState.hadException())
1056 ASSERT(m_pageStyleSheet->length());
1057 unsigned lastRuleIndex = m_pageStyleSheet->length() - 1;
1058 CSSRule* rule = m_pageStyleSheet->item(lastRuleIndex);
1061 CSSStyleRule* styleRule = InspectorCSSAgent::asCSSStyleRule(rule);
1063 // What we just added has to be a CSSStyleRule - we cannot handle other types of rules yet.
1064 // If it is not a style rule, pretend we never touched the stylesheet.
1065 m_pageStyleSheet->deleteRule(lastRuleIndex, ASSERT_NO_EXCEPTION);
1066 exceptionState.throwDOMException(SyntaxError, "The selector '" + selector + "' could not be added.");
1070 if (!styleSheetText.isEmpty())
1071 styleSheetText.append('\n');
1073 styleSheetText.append(selector);
1074 styleSheetText.appendLiteral(" {}");
1075 // Using setText() as this operation changes the style sheet rule set.
1076 setText(styleSheetText.toString(), ASSERT_NO_EXCEPTION);
1078 fireStyleSheetChanged();
1083 bool InspectorStyleSheet::deleteRule(const InspectorCSSId& id, ExceptionState& exceptionState)
1085 if (!checkPageStyleSheet(exceptionState))
1087 RefPtr<CSSStyleRule> rule = ruleForId(id);
1089 exceptionState.throwDOMException(NotFoundError, "No style rule could be found for the provided ID.");
1092 CSSStyleSheet* styleSheet = rule->parentStyleSheet();
1093 if (!styleSheet || !ensureParsedDataReady()) {
1094 exceptionState.throwDOMException(NotFoundError, "No parent stylesheet could be found.");
1098 RefPtr<CSSRuleSourceData> sourceData = ruleSourceDataFor(rule->style());
1100 exceptionState.throwDOMException(NotFoundError, "No style rule could be found for the provided ID.");
1104 styleSheet->deleteRule(id.ordinal(), exceptionState);
1105 // |rule| MAY NOT be addressed after this line!
1107 if (exceptionState.hadException())
1110 String sheetText = m_parsedStyleSheet->text();
1111 sheetText.remove(sourceData->ruleHeaderRange.start, sourceData->ruleBodyRange.end - sourceData->ruleHeaderRange.start + 1);
1112 setText(sheetText, ASSERT_NO_EXCEPTION);
1113 fireStyleSheetChanged();
1117 CSSStyleRule* InspectorStyleSheet::ruleForId(const InspectorCSSId& id) const
1119 if (!m_pageStyleSheet)
1122 ASSERT(!id.isEmpty());
1124 return InspectorCSSAgent::asCSSStyleRule(id.ordinal() >= m_flatRules.size() ? 0 : m_flatRules.at(id.ordinal()).get());
1127 bool InspectorStyleSheet::fillObjectForStyleSheet(PassRefPtr<TypeBuilder::CSS::CSSStyleSheetBody> prpResult)
1129 CSSStyleSheet* styleSheet = pageStyleSheet();
1133 RefPtr<TypeBuilder::CSS::CSSStyleSheetBody> result = prpResult;
1135 String styleSheetText;
1136 bool success = getText(&styleSheetText);
1138 result->setText(styleSheetText);
1142 PassRefPtr<TypeBuilder::CSS::CSSStyleSheetHeader> InspectorStyleSheet::buildObjectForStyleSheetInfo() const
1144 CSSStyleSheet* styleSheet = pageStyleSheet();
1148 Document* document = styleSheet->ownerDocument();
1149 Frame* frame = document ? document->frame() : 0;
1151 RefPtr<TypeBuilder::CSS::CSSStyleSheetHeader> result = TypeBuilder::CSS::CSSStyleSheetHeader::create()
1152 .setStyleSheetId(id())
1153 .setOrigin(m_origin)
1154 .setDisabled(styleSheet->disabled())
1155 .setSourceURL(url())
1156 .setTitle(styleSheet->title())
1157 .setFrameId(m_pageAgent->frameId(frame))
1158 .setIsInline(styleSheet->isInline() && !startsAtZero())
1159 .setStartLine(styleSheet->startPositionInSource().m_line.zeroBasedInt())
1160 .setStartColumn(styleSheet->startPositionInSource().m_column.zeroBasedInt());
1163 result->setHasSourceURL(true);
1165 String sourceMapURLValue = sourceMapURL();
1166 if (!sourceMapURLValue.isEmpty())
1167 result->setSourceMapURL(sourceMapURLValue);
1168 return result.release();
1171 PassRefPtr<TypeBuilder::Array<TypeBuilder::CSS::Selector> > InspectorStyleSheet::selectorsFromSource(const CSSRuleSourceData* sourceData, const String& sheetText) const
1173 ScriptRegexp comment("/\\*[^]*?\\*/", TextCaseSensitive, MultilineEnabled);
1174 RefPtr<TypeBuilder::Array<TypeBuilder::CSS::Selector> > result = TypeBuilder::Array<TypeBuilder::CSS::Selector>::create();
1175 const SelectorRangeList& ranges = sourceData->selectorRanges;
1176 for (size_t i = 0, size = ranges.size(); i < size; ++i) {
1177 const SourceRange& range = ranges.at(i);
1178 String selector = sheetText.substring(range.start, range.length());
1180 // We don't want to see any comments in the selector components, only the meaningful parts.
1183 while ((offset = comment.match(selector, offset, &matchLength)) >= 0)
1184 selector.replace(offset, matchLength, "");
1186 RefPtr<TypeBuilder::CSS::Selector> simpleSelector = TypeBuilder::CSS::Selector::create()
1187 .setValue(selector.stripWhiteSpace());
1188 simpleSelector->setRange(buildSourceRangeObject(range, lineEndings().get()));
1189 result->addItem(simpleSelector.release());
1191 return result.release();
1194 PassRefPtr<TypeBuilder::CSS::SelectorList> InspectorStyleSheet::buildObjectForSelectorList(CSSStyleRule* rule)
1196 RefPtr<CSSRuleSourceData> sourceData;
1197 if (ensureParsedDataReady())
1198 sourceData = ruleSourceDataFor(rule->style());
1199 RefPtr<TypeBuilder::Array<TypeBuilder::CSS::Selector> > selectors;
1201 // This intentionally does not rely on the source data to avoid catching the trailing comments (before the declaration starting '{').
1202 String selectorText = rule->selectorText();
1205 selectors = selectorsFromSource(sourceData.get(), m_parsedStyleSheet->text());
1207 selectors = TypeBuilder::Array<TypeBuilder::CSS::Selector>::create();
1208 const CSSSelectorList& selectorList = rule->styleRule()->selectorList();
1209 for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(*selector))
1210 selectors->addItem(TypeBuilder::CSS::Selector::create().setValue(selector->selectorText()).release());
1212 RefPtr<TypeBuilder::CSS::SelectorList> result = TypeBuilder::CSS::SelectorList::create()
1213 .setSelectors(selectors)
1214 .setText(selectorText)
1216 return result.release();
1219 PassRefPtr<TypeBuilder::CSS::CSSRule> InspectorStyleSheet::buildObjectForRule(CSSStyleRule* rule, PassRefPtr<Array<TypeBuilder::CSS::CSSMedia> > mediaStack)
1221 CSSStyleSheet* styleSheet = pageStyleSheet();
1225 RefPtr<TypeBuilder::CSS::CSSRule> result = TypeBuilder::CSS::CSSRule::create()
1226 .setSelectorList(buildObjectForSelectorList(rule))
1227 .setOrigin(m_origin)
1228 .setStyle(buildObjectForStyle(rule->style()));
1230 String url = this->url();
1232 result->setSourceURL(url);
1235 InspectorCSSId id(ruleId(rule));
1237 result->setRuleId(id.asProtocolValue<TypeBuilder::CSS::CSSRuleId>());
1241 result->setMedia(mediaStack);
1243 return result.release();
1246 PassRefPtr<TypeBuilder::CSS::CSSStyle> InspectorStyleSheet::buildObjectForStyle(CSSStyleDeclaration* style)
1248 RefPtr<CSSRuleSourceData> sourceData;
1249 if (ensureParsedDataReady())
1250 sourceData = ruleSourceDataFor(style);
1252 InspectorCSSId id = ruleOrStyleId(style);
1254 // Any rule coming from User Agent and not from DefaultStyleSheet will not have id.
1255 // See InspectorCSSAgent::buildObjectForRule for details.
1256 RefPtr<InspectorStyle> inspectorStyle = InspectorStyle::create(id, style, this);
1257 return inspectorStyle->buildObjectForStyle();
1259 RefPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id);
1260 RefPtr<TypeBuilder::CSS::CSSStyle> result = inspectorStyle->buildObjectForStyle();
1262 // Style text cannot be retrieved without stylesheet, so set cssText here.
1265 bool success = getText(&sheetText);
1267 const SourceRange& bodyRange = sourceData->ruleBodyRange;
1268 result->setCssText(sheetText.substring(bodyRange.start, bodyRange.end - bodyRange.start));
1272 return result.release();
1275 bool InspectorStyleSheet::setStyleText(const InspectorCSSId& id, const String& text, String* oldText, ExceptionState& exceptionState)
1277 RefPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id);
1278 if (!inspectorStyle || !inspectorStyle->cssStyle()) {
1279 exceptionState.throwDOMException(NotFoundError, "No property could be found for the given ID.");
1283 bool success = inspectorStyle->styleText(oldText);
1285 exceptionState.throwDOMException(NotFoundError, "Style text could not be read for the given property.");
1289 success = setStyleText(inspectorStyle->cssStyle(), text);
1291 fireStyleSheetChanged();
1293 exceptionState.throwDOMException(SyntaxError, "The style text '" + text + "' is invalid.");
1297 bool InspectorStyleSheet::setPropertyText(const InspectorCSSId& id, unsigned propertyIndex, const String& text, bool overwrite, String* oldText, ExceptionState& exceptionState)
1299 RefPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id);
1300 if (!inspectorStyle) {
1301 exceptionState.throwDOMException(NotFoundError, "No property could be found for the given ID.");
1305 bool success = inspectorStyle->setPropertyText(propertyIndex, text, overwrite, oldText, exceptionState);
1307 fireStyleSheetChanged();
1311 bool InspectorStyleSheet::getText(String* result) const
1315 *result = m_parsedStyleSheet->text();
1319 CSSStyleDeclaration* InspectorStyleSheet::styleForId(const InspectorCSSId& id) const
1321 CSSStyleRule* rule = ruleForId(id);
1325 return rule->style();
1328 void InspectorStyleSheet::fireStyleSheetChanged()
1331 m_listener->styleSheetChanged(this);
1334 PassRefPtr<TypeBuilder::CSS::SourceRange> InspectorStyleSheet::ruleHeaderSourceRange(const CSSRule* rule)
1336 if (!ensureParsedDataReady())
1339 RefPtr<CSSRuleSourceData> sourceData = m_parsedStyleSheet->ruleSourceDataAt(ruleIndexByRule(rule));
1342 return buildSourceRangeObject(sourceData->ruleHeaderRange, lineEndings().get());
1345 PassRefPtr<InspectorStyle> InspectorStyleSheet::inspectorStyleForId(const InspectorCSSId& id)
1347 CSSStyleDeclaration* style = styleForId(id);
1351 return InspectorStyle::create(id, style, this);
1354 String InspectorStyleSheet::sourceURL() const
1356 if (!m_sourceURL.isNull())
1358 if (m_origin != TypeBuilder::CSS::StyleSheetOrigin::Regular) {
1363 String styleSheetText;
1364 bool success = getText(&styleSheetText);
1367 String commentValue = ContentSearchUtils::findSourceURL(styleSheetText, ContentSearchUtils::CSSMagicComment, &deprecated);
1368 if (!commentValue.isEmpty()) {
1369 // FIXME: add deprecated console message here.
1370 m_sourceURL = commentValue;
1371 return commentValue;
1378 String InspectorStyleSheet::url() const
1380 // "sourceURL" is present only for regular rules, otherwise "origin" should be used in the frontend.
1381 if (m_origin != TypeBuilder::CSS::StyleSheetOrigin::Regular)
1384 CSSStyleSheet* styleSheet = pageStyleSheet();
1391 if (styleSheet->isInline() && startsAtZero())
1397 bool InspectorStyleSheet::hasSourceURL() const
1399 return !sourceURL().isEmpty();
1402 bool InspectorStyleSheet::startsAtZero() const
1404 CSSStyleSheet* styleSheet = pageStyleSheet();
1408 return styleSheet->startPositionInSource() == TextPosition::minimumPosition();
1411 String InspectorStyleSheet::sourceMapURL() const
1413 if (m_origin != TypeBuilder::CSS::StyleSheetOrigin::Regular)
1416 String styleSheetText;
1417 bool success = getText(&styleSheetText);
1420 String commentValue = ContentSearchUtils::findSourceMapURL(styleSheetText, ContentSearchUtils::CSSMagicComment, &deprecated);
1421 if (!commentValue.isEmpty()) {
1422 // FIXME: add deprecated console message here.
1423 return commentValue;
1426 return m_pageAgent->resourceSourceMapURL(finalURL());
1429 InspectorCSSId InspectorStyleSheet::ruleOrStyleId(CSSStyleDeclaration* style) const
1431 unsigned index = ruleIndexByStyle(style);
1432 if (index != UINT_MAX)
1433 return InspectorCSSId(id(), index);
1434 return InspectorCSSId();
1437 Document* InspectorStyleSheet::ownerDocument() const
1439 return m_pageStyleSheet->ownerDocument();
1442 PassRefPtr<CSSRuleSourceData> InspectorStyleSheet::ruleSourceDataFor(CSSStyleDeclaration* style) const
1444 return m_parsedStyleSheet->ruleSourceDataAt(ruleIndexByStyle(style));
1447 PassOwnPtr<Vector<unsigned> > InspectorStyleSheet::lineEndings() const
1449 if (!m_parsedStyleSheet->hasText())
1450 return PassOwnPtr<Vector<unsigned> >();
1451 return WTF::lineEndings(m_parsedStyleSheet->text());
1454 unsigned InspectorStyleSheet::ruleIndexByStyle(CSSStyleDeclaration* pageStyle) const
1457 for (unsigned i = 0, size = m_flatRules.size(); i < size; ++i) {
1458 CSSStyleRule* styleRule = InspectorCSSAgent::asCSSStyleRule(m_flatRules.at(i).get());
1459 if (styleRule && styleRule->style() == pageStyle)
1465 unsigned InspectorStyleSheet::ruleIndexByRule(const CSSRule* rule) const
1468 size_t index = m_flatRules.find(rule);
1469 return index == kNotFound ? UINT_MAX : static_cast<unsigned>(index);
1472 bool InspectorStyleSheet::checkPageStyleSheet(ExceptionState& exceptionState) const
1474 if (!m_pageStyleSheet) {
1475 exceptionState.throwDOMException(NotSupportedError, "No stylesheet is available.");
1481 bool InspectorStyleSheet::ensureParsedDataReady()
1483 return ensureText() && ensureSourceData();
1486 bool InspectorStyleSheet::ensureText() const
1488 if (!m_parsedStyleSheet)
1490 if (m_parsedStyleSheet->hasText())
1494 bool success = originalStyleSheetText(&text);
1496 m_parsedStyleSheet->setText(text);
1497 // No need to clear m_flatRules here - it's empty.
1502 bool InspectorStyleSheet::ensureSourceData()
1504 if (m_parsedStyleSheet->hasSourceData())
1507 if (!m_parsedStyleSheet->hasText())
1510 RefPtr<StyleSheetContents> newStyleSheet = StyleSheetContents::create(strictCSSParserContext());
1511 OwnPtr<RuleSourceDataList> result = adoptPtr(new RuleSourceDataList());
1512 StyleSheetHandler handler(m_parsedStyleSheet->text(), m_pageStyleSheet->ownerDocument(), newStyleSheet.get(), result.get());
1513 createCSSParser(m_pageStyleSheet->ownerDocument())->parseSheet(newStyleSheet.get(), m_parsedStyleSheet->text(), TextPosition::minimumPosition(), &handler);
1514 m_parsedStyleSheet->setSourceData(result.release());
1515 return m_parsedStyleSheet->hasSourceData();
1518 void InspectorStyleSheet::ensureFlatRules() const
1520 // We are fine with redoing this for empty stylesheets as this will run fast.
1521 if (m_flatRules.isEmpty())
1522 collectFlatRules(asCSSRuleList(pageStyleSheet()), &m_flatRules);
1525 bool InspectorStyleSheet::setStyleText(CSSStyleDeclaration* style, const String& text)
1527 if (!m_pageStyleSheet)
1529 if (!ensureParsedDataReady())
1532 String patchedStyleSheetText;
1533 bool success = styleSheetTextWithChangedStyle(style, text, &patchedStyleSheetText);
1537 InspectorCSSId id = ruleOrStyleId(style);
1541 TrackExceptionState exceptionState;
1542 style->setCSSText(text, exceptionState);
1543 if (!exceptionState.hadException())
1544 m_parsedStyleSheet->setText(patchedStyleSheetText);
1546 return !exceptionState.hadException();
1549 bool InspectorStyleSheet::styleSheetTextWithChangedStyle(CSSStyleDeclaration* style, const String& newStyleText, String* result)
1554 if (!ensureParsedDataReady())
1557 RefPtr<CSSRuleSourceData> sourceData = ruleSourceDataFor(style);
1558 unsigned bodyStart = sourceData->ruleBodyRange.start;
1559 unsigned bodyEnd = sourceData->ruleBodyRange.end;
1560 ASSERT(bodyStart <= bodyEnd);
1562 String text = m_parsedStyleSheet->text();
1563 ASSERT_WITH_SECURITY_IMPLICATION(bodyEnd <= text.length()); // bodyEnd is exclusive
1565 text.replace(bodyStart, bodyEnd - bodyStart, newStyleText);
1570 InspectorCSSId InspectorStyleSheet::ruleId(CSSStyleRule* rule) const
1572 return ruleOrStyleId(rule->style());
1575 void InspectorStyleSheet::revalidateStyle(CSSStyleDeclaration* pageStyle)
1577 if (m_isRevalidating)
1580 m_isRevalidating = true;
1582 for (unsigned i = 0, size = m_flatRules.size(); i < size; ++i) {
1583 CSSStyleRule* parsedRule = InspectorCSSAgent::asCSSStyleRule(m_flatRules.at(i).get());
1584 if (parsedRule && parsedRule->style() == pageStyle) {
1585 if (parsedRule->styleRule()->properties()->asText() != pageStyle->cssText())
1586 setStyleText(pageStyle, pageStyle->cssText());
1590 m_isRevalidating = false;
1593 bool InspectorStyleSheet::originalStyleSheetText(String* result) const
1595 bool success = inlineStyleSheetText(result);
1597 success = resourceStyleSheetText(result);
1601 bool InspectorStyleSheet::resourceStyleSheetText(String* result) const
1603 if (m_origin == TypeBuilder::CSS::StyleSheetOrigin::User || m_origin == TypeBuilder::CSS::StyleSheetOrigin::User_agent)
1606 if (!m_pageStyleSheet || !ownerDocument() || !ownerDocument()->frame())
1610 bool success = m_resourceAgent->fetchResourceContent(ownerDocument()->frame(), KURL(ParsedURLString, m_pageStyleSheet->href()), result, &base64Encoded) && !base64Encoded;
1614 bool InspectorStyleSheet::inlineStyleSheetText(String* result) const
1616 if (!m_pageStyleSheet)
1619 Node* ownerNode = m_pageStyleSheet->ownerNode();
1620 if (!ownerNode || ownerNode->nodeType() != Node::ELEMENT_NODE)
1622 Element* ownerElement = toElement(ownerNode);
1624 if (!ownerElement->hasTagName(HTMLNames::styleTag) && !ownerElement->hasTagName(SVGNames::styleTag))
1626 *result = ownerElement->textContent();
1630 PassRefPtr<InspectorStyleSheetForInlineStyle> InspectorStyleSheetForInlineStyle::create(InspectorPageAgent* pageAgent, InspectorResourceAgent* resourceAgent, const String& id, PassRefPtr<Element> element, TypeBuilder::CSS::StyleSheetOrigin::Enum origin, Listener* listener)
1632 return adoptRef(new InspectorStyleSheetForInlineStyle(pageAgent, resourceAgent, id, element, origin, listener));
1635 InspectorStyleSheetForInlineStyle::InspectorStyleSheetForInlineStyle(InspectorPageAgent* pageAgent, InspectorResourceAgent* resourceAgent, const String& id, PassRefPtr<Element> element, TypeBuilder::CSS::StyleSheetOrigin::Enum origin, Listener* listener)
1636 : InspectorStyleSheet(pageAgent, resourceAgent, id, 0, origin, "", listener)
1637 , m_element(element)
1638 , m_ruleSourceData(0)
1639 , m_isStyleTextValid(false)
1642 m_inspectorStyle = InspectorStyle::create(InspectorCSSId(id, 0), inlineStyle(), this);
1643 m_styleText = m_element->isStyledElement() ? m_element->getAttribute("style").string() : String();
1646 void InspectorStyleSheetForInlineStyle::didModifyElementAttribute()
1648 m_isStyleTextValid = false;
1649 if (m_element->isStyledElement() && m_element->style() != m_inspectorStyle->cssStyle())
1650 m_inspectorStyle = InspectorStyle::create(InspectorCSSId(id(), 0), inlineStyle(), this);
1651 m_ruleSourceData.clear();
1654 void InspectorStyleSheetForInlineStyle::reparseStyleSheet(const String& text)
1656 fireStyleSheetChanged();
1659 bool InspectorStyleSheetForInlineStyle::setText(const String& text, ExceptionState& exceptionState)
1661 bool success = setStyleText(inlineStyle(), text);
1663 exceptionState.throwDOMException(SyntaxError, "Style sheet text is invalid.");
1667 bool InspectorStyleSheetForInlineStyle::getText(String* result) const
1669 if (!m_isStyleTextValid) {
1670 m_styleText = elementStyleText();
1671 m_isStyleTextValid = true;
1673 *result = m_styleText;
1677 bool InspectorStyleSheetForInlineStyle::setStyleText(CSSStyleDeclaration* style, const String& text)
1679 ASSERT_UNUSED(style, style == inlineStyle());
1680 TrackExceptionState exceptionState;
1683 InspectorCSSAgent::InlineStyleOverrideScope overrideScope(m_element->ownerDocument());
1684 m_element->setAttribute("style", AtomicString(text), exceptionState);
1688 m_isStyleTextValid = true;
1689 m_ruleSourceData.clear();
1690 return !exceptionState.hadException();
1693 PassOwnPtr<Vector<unsigned> > InspectorStyleSheetForInlineStyle::lineEndings() const
1695 return WTF::lineEndings(elementStyleText());
1698 Document* InspectorStyleSheetForInlineStyle::ownerDocument() const
1700 return &m_element->document();
1703 bool InspectorStyleSheetForInlineStyle::ensureParsedDataReady()
1705 // The "style" property value can get changed indirectly, e.g. via element.style.borderWidth = "2px".
1706 const String& currentStyleText = elementStyleText();
1707 if (m_styleText != currentStyleText) {
1708 m_ruleSourceData.clear();
1709 m_styleText = currentStyleText;
1710 m_isStyleTextValid = true;
1713 if (m_ruleSourceData)
1716 m_ruleSourceData = getStyleAttributeData();
1718 bool success = !!m_ruleSourceData;
1720 m_ruleSourceData = CSSRuleSourceData::create(CSSRuleSourceData::STYLE_RULE);
1727 PassRefPtr<InspectorStyle> InspectorStyleSheetForInlineStyle::inspectorStyleForId(const InspectorCSSId& id)
1729 ASSERT_UNUSED(id, !id.ordinal());
1730 return m_inspectorStyle;
1733 CSSStyleDeclaration* InspectorStyleSheetForInlineStyle::inlineStyle() const
1735 return m_element->style();
1738 const String& InspectorStyleSheetForInlineStyle::elementStyleText() const
1740 return m_element->getAttribute("style").string();
1743 PassRefPtr<CSSRuleSourceData> InspectorStyleSheetForInlineStyle::getStyleAttributeData() const
1745 if (!m_element->isStyledElement())
1748 if (m_styleText.isEmpty()) {
1749 RefPtr<CSSRuleSourceData> result = CSSRuleSourceData::create(CSSRuleSourceData::STYLE_RULE);
1750 result->ruleBodyRange.start = 0;
1751 result->ruleBodyRange.end = 0;
1752 return result.release();
1755 RefPtr<MutableStylePropertySet> tempDeclaration = MutableStylePropertySet::create();
1756 RuleSourceDataList ruleSourceDataResult;
1757 StyleSheetHandler handler(m_styleText, &m_element->document(), m_element->document().elementSheet()->contents(), &ruleSourceDataResult);
1758 createCSSParser(&m_element->document())->parseDeclaration(tempDeclaration.get(), m_styleText, &handler, m_element->document().elementSheet()->contents());
1759 return ruleSourceDataResult.first().release();
1762 } // namespace WebCore