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 "bindings/v8/ExceptionState.h"
30 #include "bindings/v8/ExceptionStatePlaceholder.h"
31 #include "bindings/v8/ScriptRegexp.h"
32 #include "core/css/CSSKeyframesRule.h"
33 #include "core/css/CSSMediaRule.h"
34 #include "core/css/parser/BisonCSSParser.h"
35 #include "core/css/CSSRuleList.h"
36 #include "core/css/CSSStyleRule.h"
37 #include "core/css/CSSStyleSheet.h"
38 #include "core/css/CSSSupportsRule.h"
39 #include "core/css/StylePropertySet.h"
40 #include "core/css/StyleRule.h"
41 #include "core/css/StyleSheetContents.h"
42 #include "core/dom/Document.h"
43 #include "core/dom/Element.h"
44 #include "core/html/HTMLStyleElement.h"
45 #include "core/html/parser/HTMLParserIdioms.h"
46 #include "core/inspector/ContentSearchUtils.h"
47 #include "core/inspector/InspectorCSSAgent.h"
48 #include "core/inspector/InspectorPageAgent.h"
49 #include "core/inspector/InspectorResourceAgent.h"
50 #include "core/svg/SVGStyleElement.h"
51 #include "wtf/OwnPtr.h"
52 #include "wtf/PassOwnPtr.h"
53 #include "wtf/text/StringBuilder.h"
54 #include "wtf/text/TextPosition.h"
56 using WebCore::TypeBuilder::Array;
57 using WebCore::RuleSourceDataList;
58 using WebCore::CSSRuleSourceData;
59 using WebCore::CSSStyleSheet;
63 using namespace WebCore;
65 static CSSParserContext parserContextForDocument(Document *document)
67 return document ? CSSParserContext(*document, 0) : strictCSSParserContext();
70 class StyleSheetHandler FINAL : public CSSParserObserver {
72 StyleSheetHandler(const String& parsedText, Document* document, StyleSheetContents* styleSheetContents, RuleSourceDataList* result)
73 : m_parsedText(parsedText)
74 , m_document(document)
75 , m_styleSheetContents(styleSheetContents)
77 , m_commentParser(parserContextForDocument(document))
78 , m_propertyRangeStart(UINT_MAX)
79 , m_selectorRangeStart(UINT_MAX)
80 , m_commentRangeStart(UINT_MAX)
86 virtual void startRuleHeader(CSSRuleSourceData::Type, unsigned) OVERRIDE;
87 virtual void endRuleHeader(unsigned) OVERRIDE;
88 virtual void startSelector(unsigned) OVERRIDE;
89 virtual void endSelector(unsigned) OVERRIDE;
90 virtual void startRuleBody(unsigned) OVERRIDE;
91 virtual void endRuleBody(unsigned, bool) OVERRIDE;
92 virtual void startEndUnknownRule() OVERRIDE { addNewRuleToSourceTree(CSSRuleSourceData::createUnknown()); }
93 virtual void startProperty(unsigned) OVERRIDE;
94 virtual void endProperty(bool, bool, unsigned, CSSParserError) OVERRIDE;
95 virtual void startComment(unsigned) OVERRIDE;
96 virtual void endComment(unsigned) OVERRIDE;
98 void addNewRuleToSourceTree(PassRefPtrWillBeRawPtr<CSSRuleSourceData>);
99 PassRefPtrWillBeRawPtr<CSSRuleSourceData> popRuleData();
100 template <typename CharacterType> inline void setRuleHeaderEnd(const CharacterType*, unsigned);
101 void fixUnparsedPropertyRanges(CSSRuleSourceData*);
103 const String& m_parsedText;
104 Document* m_document;
105 StyleSheetContents* m_styleSheetContents;
106 RawPtrWillBeMember<RuleSourceDataList> m_result;
107 RuleSourceDataList m_currentRuleDataStack;
108 RefPtrWillBeMember<CSSRuleSourceData> m_currentRuleData;
109 BisonCSSParser m_commentParser;
110 unsigned m_propertyRangeStart;
111 unsigned m_selectorRangeStart;
112 unsigned m_commentRangeStart;
115 void StyleSheetHandler::startRuleHeader(CSSRuleSourceData::Type type, unsigned offset)
117 // Pop off data for a previous invalid rule.
118 if (m_currentRuleData)
119 m_currentRuleDataStack.removeLast();
121 RefPtrWillBeRawPtr<CSSRuleSourceData> data = CSSRuleSourceData::create(type);
122 data->ruleHeaderRange.start = offset;
123 m_currentRuleData = data;
124 m_currentRuleDataStack.append(data.release());
127 template <typename CharacterType>
128 inline void StyleSheetHandler::setRuleHeaderEnd(const CharacterType* dataStart, unsigned listEndOffset)
130 while (listEndOffset > 1) {
131 if (isHTMLSpace<CharacterType>(*(dataStart + listEndOffset - 1)))
137 m_currentRuleDataStack.last()->ruleHeaderRange.end = listEndOffset;
138 if (!m_currentRuleDataStack.last()->selectorRanges.isEmpty())
139 m_currentRuleDataStack.last()->selectorRanges.last().end = listEndOffset;
142 void StyleSheetHandler::endRuleHeader(unsigned offset)
144 ASSERT(!m_currentRuleDataStack.isEmpty());
146 if (m_parsedText.is8Bit())
147 setRuleHeaderEnd<LChar>(m_parsedText.characters8(), offset);
149 setRuleHeaderEnd<UChar>(m_parsedText.characters16(), offset);
152 void StyleSheetHandler::startSelector(unsigned offset)
154 m_selectorRangeStart = offset;
157 void StyleSheetHandler::endSelector(unsigned offset)
159 ASSERT(m_currentRuleDataStack.size());
160 m_currentRuleDataStack.last()->selectorRanges.append(SourceRange(m_selectorRangeStart, offset));
161 m_selectorRangeStart = UINT_MAX;
164 void StyleSheetHandler::startRuleBody(unsigned offset)
166 m_currentRuleData.clear();
167 ASSERT(!m_currentRuleDataStack.isEmpty());
168 if (m_parsedText[offset] == '{')
169 ++offset; // Skip the rule body opening brace.
170 m_currentRuleDataStack.last()->ruleBodyRange.start = offset;
173 void StyleSheetHandler::endRuleBody(unsigned offset, bool error)
175 ASSERT(!m_currentRuleDataStack.isEmpty());
176 m_currentRuleDataStack.last()->ruleBodyRange.end = offset;
177 m_propertyRangeStart = UINT_MAX;
178 RefPtrWillBeRawPtr<CSSRuleSourceData> rule = popRuleData();
182 fixUnparsedPropertyRanges(rule.get());
183 addNewRuleToSourceTree(rule.release());
186 void StyleSheetHandler::addNewRuleToSourceTree(PassRefPtrWillBeRawPtr<CSSRuleSourceData> rule)
188 if (m_currentRuleDataStack.isEmpty())
189 m_result->append(rule);
191 m_currentRuleDataStack.last()->childRules.append(rule);
194 PassRefPtrWillBeRawPtr<CSSRuleSourceData> StyleSheetHandler::popRuleData()
196 ASSERT(!m_currentRuleDataStack.isEmpty());
197 m_currentRuleData.clear();
198 RefPtrWillBeRawPtr<CSSRuleSourceData> data = m_currentRuleDataStack.last();
199 m_currentRuleDataStack.removeLast();
200 return data.release();
203 template <typename CharacterType>
204 static inline void fixUnparsedProperties(const CharacterType* characters, CSSRuleSourceData* ruleData)
206 WillBeHeapVector<CSSPropertySourceData>& propertyData = ruleData->styleSourceData->propertyData;
207 unsigned size = propertyData.size();
211 unsigned styleStart = ruleData->ruleBodyRange.start;
212 CSSPropertySourceData* nextData = &(propertyData.at(0));
213 for (unsigned i = 0; i < size; ++i) {
214 CSSPropertySourceData* currentData = nextData;
215 nextData = i < size - 1 ? &(propertyData.at(i + 1)) : 0;
217 if (currentData->parsedOk)
219 if (currentData->range.end > 0 && characters[styleStart + currentData->range.end - 1] == ';')
222 unsigned propertyEndInStyleSheet;
224 propertyEndInStyleSheet = ruleData->ruleBodyRange.end - 1;
226 propertyEndInStyleSheet = styleStart + nextData->range.start - 1;
228 while (isHTMLSpace<CharacterType>(characters[propertyEndInStyleSheet]))
229 --propertyEndInStyleSheet;
231 // propertyEndInStyleSheet points at the last property text character.
232 unsigned newPropertyEnd = propertyEndInStyleSheet - styleStart + 1; // Exclusive of the last property text character.
233 if (currentData->range.end != newPropertyEnd) {
234 currentData->range.end = newPropertyEnd;
235 unsigned valueStartInStyleSheet = styleStart + currentData->range.start + currentData->name.length();
236 while (valueStartInStyleSheet < propertyEndInStyleSheet && characters[valueStartInStyleSheet] != ':')
237 ++valueStartInStyleSheet;
238 if (valueStartInStyleSheet < propertyEndInStyleSheet)
239 ++valueStartInStyleSheet; // Shift past the ':'.
240 while (valueStartInStyleSheet < propertyEndInStyleSheet && isHTMLSpace<CharacterType>(characters[valueStartInStyleSheet]))
241 ++valueStartInStyleSheet;
242 // Need to exclude the trailing ';' from the property value.
243 currentData->value = String(characters + valueStartInStyleSheet, propertyEndInStyleSheet - valueStartInStyleSheet + (characters[propertyEndInStyleSheet] == ';' ? 0 : 1));
248 void StyleSheetHandler::fixUnparsedPropertyRanges(CSSRuleSourceData* ruleData)
250 if (!ruleData->styleSourceData)
253 if (m_parsedText.is8Bit()) {
254 fixUnparsedProperties<LChar>(m_parsedText.characters8(), ruleData);
258 fixUnparsedProperties<UChar>(m_parsedText.characters16(), ruleData);
261 void StyleSheetHandler::startProperty(unsigned offset)
263 if (m_currentRuleDataStack.isEmpty() || !m_currentRuleDataStack.last()->styleSourceData)
265 m_propertyRangeStart = offset;
268 void StyleSheetHandler::endProperty(bool isImportant, bool isParsed, unsigned offset, CSSParserError errorType)
270 // FIXME: This is the only place CSSParserError is every read!?
271 if (errorType != NoCSSError)
272 m_propertyRangeStart = UINT_MAX;
274 if (m_propertyRangeStart == UINT_MAX || m_currentRuleDataStack.isEmpty() || !m_currentRuleDataStack.last()->styleSourceData)
277 ASSERT(offset <= m_parsedText.length());
278 if (offset < m_parsedText.length() && m_parsedText[offset] == ';') // Include semicolon into the property text.
281 const unsigned start = m_propertyRangeStart;
282 const unsigned end = offset;
284 String propertyString = m_parsedText.substring(start, end - start).stripWhiteSpace();
285 if (propertyString.endsWith(';'))
286 propertyString = propertyString.left(propertyString.length() - 1);
287 size_t colonIndex = propertyString.find(':');
288 ASSERT(colonIndex != kNotFound);
290 String name = propertyString.left(colonIndex).stripWhiteSpace();
291 String value = propertyString.substring(colonIndex + 1, propertyString.length()).stripWhiteSpace();
292 // The property range is relative to the declaration start offset.
293 unsigned topRuleBodyRangeStart = m_currentRuleDataStack.last()->ruleBodyRange.start;
294 m_currentRuleDataStack.last()->styleSourceData->propertyData.append(
295 CSSPropertySourceData(name, value, isImportant, false, isParsed, SourceRange(start - topRuleBodyRangeStart, end - topRuleBodyRangeStart)));
296 m_propertyRangeStart = UINT_MAX;
299 void StyleSheetHandler::startComment(unsigned offset)
301 ASSERT(m_commentRangeStart == UINT_MAX);
302 m_commentRangeStart = offset;
305 void StyleSheetHandler::endComment(unsigned offset)
307 ASSERT(offset <= m_parsedText.length());
309 unsigned startOffset = m_commentRangeStart;
310 m_commentRangeStart = UINT_MAX;
311 if (m_propertyRangeStart != UINT_MAX) {
312 ASSERT(startOffset >= m_propertyRangeStart);
313 // startProperty() is called automatically at the start of a style declaration.
314 // Check if no text has been scanned yet, otherwise the comment is inside a property.
315 if (!m_parsedText.substring(m_propertyRangeStart, startOffset).stripWhiteSpace().isEmpty())
317 m_propertyRangeStart = UINT_MAX;
319 if (m_currentRuleDataStack.isEmpty() || !m_currentRuleDataStack.last()->ruleHeaderRange.end || !m_currentRuleDataStack.last()->styleSourceData)
322 // The lexer is not inside a property AND it is scanning a declaration-aware rule body.
323 String commentText = m_parsedText.substring(startOffset, offset - startOffset);
325 ASSERT(commentText.startsWith("/*"));
326 commentText = commentText.substring(2);
328 // Require well-formed comments.
329 if (!commentText.endsWith("*/"))
331 commentText = commentText.substring(0, commentText.length() - 2).stripWhiteSpace();
332 if (commentText.isEmpty())
335 // FIXME: Use the actual rule type rather than STYLE_RULE?
336 RuleSourceDataList sourceData;
338 // FIXME: Use another subclass of BisonCSSParser::SourceDataHandler and assert that
339 // no comments are encountered (will not need m_document and m_styleSheetContents).
340 StyleSheetHandler handler(commentText, m_document, m_styleSheetContents, &sourceData);
341 RefPtrWillBeRawPtr<MutableStylePropertySet> tempMutableStyle = MutableStylePropertySet::create();
342 m_commentParser.parseDeclaration(tempMutableStyle.get(), commentText, &handler, m_styleSheetContents);
343 WillBeHeapVector<CSSPropertySourceData>& commentPropertyData = sourceData.first()->styleSourceData->propertyData;
344 if (commentPropertyData.size() != 1)
346 CSSPropertySourceData& propertyData = commentPropertyData.at(0);
347 if (propertyData.range.length() != commentText.length())
350 unsigned topRuleBodyRangeStart = m_currentRuleDataStack.last()->ruleBodyRange.start;
351 m_currentRuleDataStack.last()->styleSourceData->propertyData.append(
352 CSSPropertySourceData(propertyData.name, propertyData.value, false, true, true, SourceRange(startOffset - topRuleBodyRangeStart, offset - topRuleBodyRangeStart)));
357 class ParsedStyleSheet {
358 WTF_MAKE_FAST_ALLOCATED;
360 ParsedStyleSheet(CSSStyleSheet* pageStyleSheet);
362 const String& text() const { ASSERT(m_hasText); return m_text; }
363 void setText(const String&);
364 bool hasText() const { return m_hasText; }
365 bool ensureSourceData();
366 bool hasSourceData() const { return m_sourceData; }
367 PassRefPtrWillBeRawPtr<WebCore::CSSRuleSourceData> ruleSourceDataAt(unsigned) const;
368 unsigned ruleCount() { return m_sourceData->size(); }
371 void flattenSourceData(RuleSourceDataList*);
372 void setSourceData(PassOwnPtrWillBeRawPtr<RuleSourceDataList>);
376 OwnPtrWillBePersistent<RuleSourceDataList> m_sourceData;
377 RefPtrWillBePersistent<CSSStyleSheet> m_pageStyleSheet;
380 ParsedStyleSheet::ParsedStyleSheet(CSSStyleSheet* pageStyleSheet)
382 , m_pageStyleSheet(pageStyleSheet)
386 void ParsedStyleSheet::setText(const String& text)
390 setSourceData(nullptr);
393 void ParsedStyleSheet::flattenSourceData(RuleSourceDataList* dataList)
395 for (size_t i = 0; i < dataList->size(); ++i) {
396 RefPtrWillBeMember<CSSRuleSourceData>& data = dataList->at(i);
397 if (data->type == CSSRuleSourceData::STYLE_RULE) {
398 m_sourceData->append(data);
399 } else if (data->type == CSSRuleSourceData::IMPORT_RULE) {
400 m_sourceData->append(data);
401 } else if (data->type == CSSRuleSourceData::MEDIA_RULE) {
402 m_sourceData->append(data);
403 flattenSourceData(&data->childRules);
404 } else if (data->type == CSSRuleSourceData::SUPPORTS_RULE) {
405 flattenSourceData(&data->childRules);
410 bool ParsedStyleSheet::ensureSourceData()
418 RefPtrWillBeRawPtr<StyleSheetContents> newStyleSheet = StyleSheetContents::create(strictCSSParserContext());
419 OwnPtrWillBeRawPtr<RuleSourceDataList> result = adoptPtrWillBeNoop(new RuleSourceDataList());
420 StyleSheetHandler handler(text(), m_pageStyleSheet->ownerDocument(), newStyleSheet.get(), result.get());
421 BisonCSSParser(parserContextForDocument(m_pageStyleSheet->ownerDocument())).parseSheet(newStyleSheet.get(), text(), TextPosition::minimumPosition(), &handler);
422 setSourceData(result.release());
423 return hasSourceData();
426 void ParsedStyleSheet::setSourceData(PassOwnPtrWillBeRawPtr<RuleSourceDataList> sourceData)
429 m_sourceData.clear();
433 m_sourceData = adoptPtrWillBeNoop(new RuleSourceDataList());
435 // FIXME: This is a temporary solution to retain the original flat sourceData structure
436 // containing only style rules, even though BisonCSSParser now provides the full rule source data tree.
437 // Normally, we should just assign m_sourceData = sourceData;
438 flattenSourceData(sourceData.get());
441 PassRefPtrWillBeRawPtr<WebCore::CSSRuleSourceData> ParsedStyleSheet::ruleSourceDataAt(unsigned index) const
443 if (!hasSourceData() || index >= m_sourceData->size())
446 return m_sourceData->at(index);
451 enum MediaListSource {
452 MediaListSourceLinkedSheet,
453 MediaListSourceInlineSheet,
454 MediaListSourceMediaRule,
455 MediaListSourceImportRule
458 static PassRefPtr<TypeBuilder::CSS::SourceRange> buildSourceRangeObject(const SourceRange& range, Vector<unsigned>* lineEndings)
462 TextPosition start = TextPosition::fromOffsetAndLineEndings(range.start, *lineEndings);
463 TextPosition end = TextPosition::fromOffsetAndLineEndings(range.end, *lineEndings);
465 RefPtr<TypeBuilder::CSS::SourceRange> result = TypeBuilder::CSS::SourceRange::create()
466 .setStartLine(start.m_line.zeroBasedInt())
467 .setStartColumn(start.m_column.zeroBasedInt())
468 .setEndLine(end.m_line.zeroBasedInt())
469 .setEndColumn(end.m_column.zeroBasedInt());
470 return result.release();
473 static PassRefPtrWillBeRawPtr<CSSRuleList> asCSSRuleList(CSSStyleSheet* styleSheet)
478 RefPtrWillBeRawPtr<StaticCSSRuleList> list = StaticCSSRuleList::create();
479 WillBeHeapVector<RefPtrWillBeMember<CSSRule> >& listRules = list->rules();
480 for (unsigned i = 0, size = styleSheet->length(); i < size; ++i) {
481 CSSRule* item = styleSheet->item(i);
482 if (item->type() == CSSRule::CHARSET_RULE)
484 listRules.append(item);
486 return list.release();
489 static PassRefPtrWillBeRawPtr<CSSRuleList> asCSSRuleList(CSSRule* rule)
494 if (rule->type() == CSSRule::MEDIA_RULE)
495 return toCSSMediaRule(rule)->cssRules();
497 if (rule->type() == CSSRule::KEYFRAMES_RULE)
498 return toCSSKeyframesRule(rule)->cssRules();
500 if (rule->type() == CSSRule::SUPPORTS_RULE)
501 return toCSSSupportsRule(rule)->cssRules();
506 PassRefPtr<InspectorStyle> InspectorStyle::create(const InspectorCSSId& styleId, PassRefPtrWillBeRawPtr<CSSStyleDeclaration> style, InspectorStyleSheetBase* parentStyleSheet)
508 return adoptRef(new InspectorStyle(styleId, style, parentStyleSheet));
511 InspectorStyle::InspectorStyle(const InspectorCSSId& styleId, PassRefPtrWillBeRawPtr<CSSStyleDeclaration> style, InspectorStyleSheetBase* parentStyleSheet)
514 , m_parentStyleSheet(parentStyleSheet)
515 , m_formatAcquired(false)
520 PassRefPtr<TypeBuilder::CSS::CSSStyle> InspectorStyle::buildObjectForStyle() const
522 RefPtr<TypeBuilder::CSS::CSSStyle> result = styleWithProperties();
523 if (!m_styleId.isEmpty())
524 result->setStyleSheetId(m_styleId.styleSheetId());
526 RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = extractSourceData();
528 result->setRange(buildSourceRangeObject(sourceData->ruleBodyRange, m_parentStyleSheet->lineEndings().get()));
530 return result.release();
533 PassRefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSComputedStyleProperty> > InspectorStyle::buildArrayForComputedStyle() const
535 RefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSComputedStyleProperty> > result = TypeBuilder::Array<TypeBuilder::CSS::CSSComputedStyleProperty>::create();
536 WillBeHeapVector<InspectorStyleProperty> properties;
537 populateAllProperties(properties);
539 for (WillBeHeapVector<InspectorStyleProperty>::iterator it = properties.begin(), itEnd = properties.end(); it != itEnd; ++it) {
540 const CSSPropertySourceData& propertyEntry = it->sourceData;
541 RefPtr<TypeBuilder::CSS::CSSComputedStyleProperty> entry = TypeBuilder::CSS::CSSComputedStyleProperty::create()
542 .setName(propertyEntry.name)
543 .setValue(propertyEntry.value);
544 result->addItem(entry);
547 return result.release();
550 bool InspectorStyle::verifyPropertyText(const String& propertyText, bool canOmitSemicolon)
552 DEFINE_STATIC_LOCAL(String, bogusPropertyName, ("-webkit-boguz-propertee"));
553 RefPtrWillBeRawPtr<MutableStylePropertySet> tempMutableStyle = MutableStylePropertySet::create();
554 RuleSourceDataList sourceData;
555 RefPtrWillBeRawPtr<StyleSheetContents> styleSheetContents = StyleSheetContents::create(strictCSSParserContext());
556 String declarationText = propertyText + (canOmitSemicolon ? ";" : " ") + bogusPropertyName + ": none";
557 StyleSheetHandler handler(declarationText, ownerDocument(), styleSheetContents.get(), &sourceData);
558 BisonCSSParser(parserContextForDocument(ownerDocument())).parseDeclaration(tempMutableStyle.get(), declarationText, &handler, styleSheetContents.get());
559 WillBeHeapVector<CSSPropertySourceData>& propertyData = sourceData.first()->styleSourceData->propertyData;
560 unsigned propertyCount = propertyData.size();
562 // At least one property + the bogus property added just above should be present.
563 if (propertyCount < 2)
566 // Check for the proper propertyText termination (the parser could at least restore to the PROPERTY_NAME state).
567 if (propertyData.at(propertyCount - 1).name != bogusPropertyName)
573 bool InspectorStyle::setPropertyText(unsigned index, const String& propertyText, bool overwrite, ExceptionState& exceptionState)
575 ASSERT(m_parentStyleSheet);
577 if (!m_parentStyleSheet->ensureParsedDataReady()) {
578 exceptionState.throwDOMException(NotFoundError, "The parent style sheet's data hasn't been processed.");
582 if (!propertyText.stripWhiteSpace().isEmpty()) {
583 if (!verifyPropertyText(propertyText, false) && !verifyPropertyText(propertyText, true)) {
584 exceptionState.throwDOMException(SyntaxError, "The property '" + propertyText + "' could not be set.");
589 RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = extractSourceData();
591 exceptionState.throwDOMException(NotFoundError, "The property '" + propertyText + "' could not be set.");
596 bool success = styleText(&text);
598 exceptionState.throwDOMException(NotFoundError, "The property '" + propertyText + "' could not be set.");
602 WillBeHeapVector<InspectorStyleProperty> allProperties;
603 populateAllProperties(allProperties);
605 InspectorStyleTextEditor editor(&allProperties, text, newLineAndWhitespaceDelimiters());
607 if (index >= allProperties.size()) {
608 exceptionState.throwDOMException(IndexSizeError, "The index provided (" + String::number(index) + ") is greater than or equal to the maximum bound (" + String::number(allProperties.size()) + ").");
611 editor.replaceProperty(index, propertyText);
613 editor.insertProperty(index, propertyText, sourceData->ruleBodyRange.length());
616 return m_parentStyleSheet->setStyleText(m_styleId, editor.styleText());
619 bool InspectorStyle::styleText(String* result) const
621 RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = extractSourceData();
625 String styleSheetText;
626 bool success = m_parentStyleSheet->getText(&styleSheetText);
630 SourceRange& bodyRange = sourceData->ruleBodyRange;
631 *result = styleSheetText.substring(bodyRange.start, bodyRange.end - bodyRange.start);
635 void InspectorStyle::populateAllProperties(WillBeHeapVector<InspectorStyleProperty>& result) const
637 HashSet<String> sourcePropertyNames;
639 RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = extractSourceData();
641 String styleDeclaration;
642 bool isStyleTextKnown = styleText(&styleDeclaration);
643 ASSERT_UNUSED(isStyleTextKnown, isStyleTextKnown);
644 WillBeHeapVector<CSSPropertySourceData>& sourcePropertyData = sourceData->styleSourceData->propertyData;
645 for (WillBeHeapVector<CSSPropertySourceData>::const_iterator it = sourcePropertyData.begin(); it != sourcePropertyData.end(); ++it) {
646 InspectorStyleProperty p(*it, true);
647 p.setRawTextFromStyleDeclaration(styleDeclaration);
649 sourcePropertyNames.add(it->name.lower());
653 for (int i = 0, size = m_style->length(); i < size; ++i) {
654 String name = m_style->item(i);
655 if (!sourcePropertyNames.add(name.lower()).isNewEntry)
658 result.append(InspectorStyleProperty(CSSPropertySourceData(name, m_style->getPropertyValue(name), !m_style->getPropertyPriority(name).isEmpty(), false, true, SourceRange()), false));
662 PassRefPtr<TypeBuilder::CSS::CSSStyle> InspectorStyle::styleWithProperties() const
664 RefPtr<Array<TypeBuilder::CSS::CSSProperty> > propertiesObject = Array<TypeBuilder::CSS::CSSProperty>::create();
665 RefPtr<Array<TypeBuilder::CSS::ShorthandEntry> > shorthandEntries = Array<TypeBuilder::CSS::ShorthandEntry>::create();
666 HashSet<String> foundShorthands;
667 OwnPtr<Vector<unsigned> > lineEndings(m_parentStyleSheet ? m_parentStyleSheet->lineEndings() : PassOwnPtr<Vector<unsigned> >());
668 RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = extractSourceData();
669 unsigned ruleBodyRangeStart = sourceData ? sourceData->ruleBodyRange.start : 0;
671 WillBeHeapVector<InspectorStyleProperty> properties;
672 populateAllProperties(properties);
674 for (WillBeHeapVector<InspectorStyleProperty>::iterator it = properties.begin(), itEnd = properties.end(); it != itEnd; ++it) {
675 const CSSPropertySourceData& propertyEntry = it->sourceData;
676 const String& name = propertyEntry.name;
678 RefPtr<TypeBuilder::CSS::CSSProperty> property = TypeBuilder::CSS::CSSProperty::create()
680 .setValue(propertyEntry.value);
681 propertiesObject->addItem(property);
683 // Default "parsedOk" == true.
684 if (!propertyEntry.parsedOk)
685 property->setParsedOk(false);
686 if (it->hasRawText())
687 property->setText(it->rawText);
689 if (propertyEntry.important)
690 property->setImportant(true);
692 // The property range is relative to the style body start.
693 // Should be converted into an absolute range (relative to the stylesheet start)
694 // for the proper conversion into line:column.
695 SourceRange absolutePropertyRange = propertyEntry.range;
696 absolutePropertyRange.start += ruleBodyRangeStart;
697 absolutePropertyRange.end += ruleBodyRangeStart;
698 property->setRange(buildSourceRangeObject(absolutePropertyRange, lineEndings.get()));
699 if (!propertyEntry.disabled) {
701 property->setImplicit(false);
703 property->setDisabled(propertyEntry.disabled);
704 } else if (!propertyEntry.disabled) {
705 bool implicit = m_style->isPropertyImplicit(name);
706 // Default "implicit" == false.
708 property->setImplicit(true);
710 String shorthand = m_style->getPropertyShorthand(name);
711 if (!shorthand.isEmpty()) {
712 if (foundShorthands.add(shorthand).isNewEntry) {
713 RefPtr<TypeBuilder::CSS::ShorthandEntry> entry = TypeBuilder::CSS::ShorthandEntry::create()
715 .setValue(shorthandValue(shorthand));
716 shorthandEntries->addItem(entry);
722 RefPtr<TypeBuilder::CSS::CSSStyle> result = TypeBuilder::CSS::CSSStyle::create()
723 .setCssProperties(propertiesObject)
724 .setShorthandEntries(shorthandEntries);
725 return result.release();
728 PassRefPtrWillBeRawPtr<CSSRuleSourceData> InspectorStyle::extractSourceData() const
730 if (!m_parentStyleSheet || !m_parentStyleSheet->ensureParsedDataReady())
732 return m_parentStyleSheet->ruleSourceDataAt(m_styleId.ordinal());
735 String InspectorStyle::shorthandValue(const String& shorthandProperty) const
737 String value = m_style->getPropertyValue(shorthandProperty);
738 if (value.isEmpty()) {
739 StringBuilder builder;
741 for (unsigned i = 0; i < m_style->length(); ++i) {
742 String individualProperty = m_style->item(i);
743 if (m_style->getPropertyShorthand(individualProperty) != shorthandProperty)
745 if (m_style->isPropertyImplicit(individualProperty))
747 String individualValue = m_style->getPropertyValue(individualProperty);
748 if (individualValue == "initial")
750 if (!builder.isEmpty())
752 builder.append(individualValue);
755 return builder.toString();
760 NewLineAndWhitespace& InspectorStyle::newLineAndWhitespaceDelimiters() const
762 DEFINE_STATIC_LOCAL(String, defaultPrefix, (" "));
764 if (m_formatAcquired)
767 RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = extractSourceData();
768 WillBeHeapVector<CSSPropertySourceData>* sourcePropertyData = sourceData ? &(sourceData->styleSourceData->propertyData) : 0;
770 if (!sourcePropertyData || !(propertyCount = sourcePropertyData->size())) {
771 m_format.first = "\n";
772 m_format.second = defaultPrefix;
773 return m_format; // Do not remember the default formatting and attempt to acquire it later.
777 bool success = styleText(&text);
778 ASSERT_UNUSED(success, success);
780 m_formatAcquired = true;
782 String candidatePrefix = defaultPrefix;
783 StringBuilder formatLineFeed;
784 StringBuilder prefix;
786 int propertyIndex = 0;
787 bool isFullPrefixScanned = false;
788 bool lineFeedTerminated = false;
789 while (propertyIndex < propertyCount) {
790 const WebCore::CSSPropertySourceData& currentProperty = sourcePropertyData->at(propertyIndex++);
792 bool processNextProperty = false;
793 int scanEnd = currentProperty.range.start;
794 for (int i = scanStart; i < scanEnd; ++i) {
796 bool isLineFeed = isHTMLLineBreak(ch);
798 if (!lineFeedTerminated)
799 formatLineFeed.append(ch);
801 } else if (isHTMLSpace<UChar>(ch))
804 candidatePrefix = prefix.toString();
806 scanStart = currentProperty.range.end;
808 processNextProperty = true;
811 if (!isLineFeed && formatLineFeed.length())
812 lineFeedTerminated = true;
814 if (!processNextProperty) {
815 isFullPrefixScanned = true;
820 m_format.first = formatLineFeed.toString();
821 m_format.second = isFullPrefixScanned ? prefix.toString() : candidatePrefix;
825 Document* InspectorStyle::ownerDocument() const
827 return m_parentStyleSheet->ownerDocument();
830 InspectorStyleSheetBase::InspectorStyleSheetBase(const String& id, Listener* listener)
832 , m_listener(listener)
836 bool InspectorStyleSheetBase::setPropertyText(const InspectorCSSId& id, unsigned propertyIndex, const String& text, bool overwrite, ExceptionState& exceptionState)
838 RefPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id);
839 if (!inspectorStyle) {
840 exceptionState.throwDOMException(NotFoundError, "No property could be found for the given ID.");
843 return inspectorStyle->setPropertyText(propertyIndex, text, overwrite, exceptionState);
846 bool InspectorStyleSheetBase::getStyleText(const InspectorCSSId& id, String* text)
848 RefPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id);
851 return inspectorStyle->styleText(text);
854 void InspectorStyleSheetBase::fireStyleSheetChanged()
857 listener()->styleSheetChanged(this);
860 PassRefPtr<TypeBuilder::CSS::CSSStyle> InspectorStyleSheetBase::buildObjectForStyle(CSSStyleDeclaration* style)
862 RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = nullptr;
863 if (ensureParsedDataReady())
864 sourceData = ruleSourceDataAt(styleId(style).ordinal());
866 InspectorCSSId id = styleId(style);
868 // Any rule coming from User Agent and not from DefaultStyleSheet will not have id.
869 // See InspectorCSSAgent::buildObjectForRule for details.
870 RefPtr<InspectorStyle> inspectorStyle = InspectorStyle::create(id, style, this);
871 return inspectorStyle->buildObjectForStyle();
873 RefPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id);
874 RefPtr<TypeBuilder::CSS::CSSStyle> result = inspectorStyle->buildObjectForStyle();
876 // Style text cannot be retrieved without stylesheet, so set cssText here.
879 bool success = getText(&sheetText);
881 const SourceRange& bodyRange = sourceData->ruleBodyRange;
882 result->setCssText(sheetText.substring(bodyRange.start, bodyRange.end - bodyRange.start));
886 return result.release();
889 PassOwnPtr<Vector<unsigned> > InspectorStyleSheetBase::lineEndings()
893 return PassOwnPtr<Vector<unsigned> >();
894 return WTF::lineEndings(text);
897 bool InspectorStyleSheetBase::lineNumberAndColumnToOffset(unsigned lineNumber, unsigned columnNumber, unsigned* offset)
899 OwnPtr<Vector<unsigned> > endings = lineEndings();
900 if (lineNumber >= endings->size())
902 unsigned charactersInLine = lineNumber > 0 ? endings->at(lineNumber) - endings->at(lineNumber - 1) - 1 : endings->at(0);
903 if (columnNumber > charactersInLine)
905 TextPosition position(OrdinalNumber::fromZeroBasedInt(lineNumber), OrdinalNumber::fromZeroBasedInt(columnNumber));
906 *offset = position.toOffset(*endings).zeroBasedInt();
910 bool InspectorStyleSheetBase::findPropertyByRange(const SourceRange& sourceRange, InspectorCSSId* ruleId, unsigned* propertyIndex, bool* overwrite)
912 if (!ensureParsedDataReady())
914 for (size_t i = 0; i < ruleCount(); ++i) {
915 RefPtrWillBeRawPtr<CSSRuleSourceData> ruleSourceData = ruleSourceDataAt(i);
916 RefPtrWillBeRawPtr<CSSStyleSourceData> styleSourceData = ruleSourceData->styleSourceData;
917 if (!styleSourceData)
919 if (ruleSourceData->ruleBodyRange.end < sourceRange.start || sourceRange.end < ruleSourceData->ruleBodyRange.start)
921 WillBeHeapVector<CSSPropertySourceData>& propertyData = styleSourceData->propertyData;
922 for (size_t j = 0; j < propertyData.size(); ++j) {
923 CSSPropertySourceData& property = propertyData.at(j);
924 unsigned styleStart = ruleSourceData->ruleBodyRange.start;
925 if (sourceRange.length() && property.range.start + styleStart == sourceRange.start && property.range.end + styleStart == sourceRange.end) {
926 *ruleId = InspectorCSSId(id(), i);
931 if (!sourceRange.length() && styleStart <= sourceRange.start && sourceRange.start <= property.range.start + styleStart) {
932 *ruleId = InspectorCSSId(id(), i);
938 if (!sourceRange.length() && ruleSourceData->ruleBodyRange.start <= sourceRange.start && sourceRange.start <= ruleSourceData->ruleBodyRange.end) {
939 *ruleId = InspectorCSSId(id(), i);
940 *propertyIndex = propertyData.size();
948 PassRefPtr<InspectorStyleSheet> InspectorStyleSheet::create(InspectorPageAgent* pageAgent, InspectorResourceAgent* resourceAgent, const String& id, PassRefPtrWillBeRawPtr<CSSStyleSheet> pageStyleSheet, TypeBuilder::CSS::StyleSheetOrigin::Enum origin, const String& documentURL, Listener* listener)
950 return adoptRef(new InspectorStyleSheet(pageAgent, resourceAgent, id, pageStyleSheet, origin, documentURL, listener));
953 InspectorStyleSheet::InspectorStyleSheet(InspectorPageAgent* pageAgent, InspectorResourceAgent* resourceAgent, const String& id, PassRefPtrWillBeRawPtr<CSSStyleSheet> pageStyleSheet, TypeBuilder::CSS::StyleSheetOrigin::Enum origin, const String& documentURL, Listener* listener)
954 : InspectorStyleSheetBase(id, listener)
955 , m_pageAgent(pageAgent)
956 , m_resourceAgent(resourceAgent)
957 , m_pageStyleSheet(pageStyleSheet)
959 , m_documentURL(documentURL)
961 m_parsedStyleSheet = adoptPtr(new ParsedStyleSheet(m_pageStyleSheet.get()));
964 InspectorStyleSheet::~InspectorStyleSheet()
968 static String styleSheetURL(CSSStyleSheet* pageStyleSheet)
970 if (pageStyleSheet && !pageStyleSheet->contents()->baseURL().isEmpty())
971 return pageStyleSheet->contents()->baseURL().string();
972 return emptyString();
975 String InspectorStyleSheet::finalURL() const
977 String url = styleSheetURL(m_pageStyleSheet.get());
978 return url.isEmpty() ? m_documentURL : url;
981 bool InspectorStyleSheet::setText(const String& text, ExceptionState& exceptionState)
983 m_parsedStyleSheet->setText(text);
987 listener()->willReparseStyleSheet();
990 // Have a separate scope for clearRules() (bug 95324).
991 CSSStyleSheet::RuleMutationScope mutationScope(m_pageStyleSheet.get());
992 m_pageStyleSheet->contents()->clearRules();
993 m_pageStyleSheet->clearChildRuleCSSOMWrappers();
996 CSSStyleSheet::RuleMutationScope mutationScope(m_pageStyleSheet.get());
997 m_pageStyleSheet->contents()->parseString(text);
1001 listener()->didReparseStyleSheet();
1002 fireStyleSheetChanged();
1003 m_pageStyleSheet->ownerDocument()->styleResolverChanged(RecalcStyleImmediately, FullStyleUpdate);
1007 String InspectorStyleSheet::ruleSelector(const InspectorCSSId& id, ExceptionState& exceptionState)
1009 CSSStyleRule* rule = ruleForId(id);
1011 exceptionState.throwDOMException(NotFoundError, "No rule was found for the given ID.");
1014 return rule->selectorText();
1017 bool InspectorStyleSheet::setRuleSelector(const InspectorCSSId& id, const String& selector, ExceptionState& exceptionState)
1019 CSSStyleRule* rule = ruleForId(id);
1021 exceptionState.throwDOMException(NotFoundError, "No rule was found for the given ID.");
1024 CSSStyleSheet* styleSheet = rule->parentStyleSheet();
1025 if (!styleSheet || !ensureParsedDataReady()) {
1026 exceptionState.throwDOMException(NotFoundError, "No stylesheet could be found in which to set the selector.");
1030 rule->setSelectorText(selector);
1031 RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = ruleSourceDataAt(id.ordinal());
1033 exceptionState.throwDOMException(NotFoundError, "The selector '" + selector + "' could not be set.");
1037 String sheetText = m_parsedStyleSheet->text();
1038 sheetText.replace(sourceData->ruleHeaderRange.start, sourceData->ruleHeaderRange.length(), selector);
1039 m_parsedStyleSheet->setText(sheetText);
1040 fireStyleSheetChanged();
1044 static bool checkStyleRuleSelector(Document* document, const String& selector)
1046 CSSSelectorList selectorList;
1047 BisonCSSParser(parserContextForDocument(document)).parseSelector(selector, selectorList);
1048 return selectorList.isValid();
1051 CSSStyleRule* InspectorStyleSheet::addRule(const String& selector, ExceptionState& exceptionState)
1053 if (!checkStyleRuleSelector(m_pageStyleSheet->ownerDocument(), selector)) {
1054 exceptionState.throwDOMException(SyntaxError, "The selector '" + selector + "' could not be added.");
1059 bool success = getText(&text);
1061 exceptionState.throwDOMException(NotFoundError, "The selector '" + selector + "' could not be added.");
1064 StringBuilder styleSheetText;
1065 styleSheetText.append(text);
1067 m_pageStyleSheet->addRule(selector, "", exceptionState);
1068 if (exceptionState.hadException())
1070 ASSERT(m_pageStyleSheet->length());
1071 unsigned lastRuleIndex = m_pageStyleSheet->length() - 1;
1072 CSSRule* rule = m_pageStyleSheet->item(lastRuleIndex);
1075 CSSStyleRule* styleRule = InspectorCSSAgent::asCSSStyleRule(rule);
1077 // What we just added has to be a CSSStyleRule - we cannot handle other types of rules yet.
1078 // If it is not a style rule, pretend we never touched the stylesheet.
1079 m_pageStyleSheet->deleteRule(lastRuleIndex, ASSERT_NO_EXCEPTION);
1080 exceptionState.throwDOMException(SyntaxError, "The selector '" + selector + "' could not be added.");
1084 if (!styleSheetText.isEmpty())
1085 styleSheetText.append('\n');
1087 styleSheetText.append(selector);
1088 styleSheetText.appendLiteral(" {}");
1089 m_parsedStyleSheet->setText(styleSheetText.toString());
1090 m_flatRules.clear();
1092 fireStyleSheetChanged();
1097 bool InspectorStyleSheet::deleteRule(const InspectorCSSId& id, ExceptionState& exceptionState)
1099 RefPtrWillBeRawPtr<CSSStyleRule> rule = ruleForId(id);
1101 exceptionState.throwDOMException(NotFoundError, "No style rule could be found for the provided ID.");
1104 CSSStyleSheet* styleSheet = rule->parentStyleSheet();
1105 if (!styleSheet || !ensureParsedDataReady()) {
1106 exceptionState.throwDOMException(NotFoundError, "No parent stylesheet could be found.");
1110 RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = ruleSourceDataAt(id.ordinal());
1112 exceptionState.throwDOMException(NotFoundError, "No style rule could be found for the provided ID.");
1116 styleSheet->deleteRule(id.ordinal(), exceptionState);
1117 // |rule| MAY NOT be addressed after this line!
1119 if (exceptionState.hadException())
1122 String sheetText = m_parsedStyleSheet->text();
1123 sheetText.remove(sourceData->ruleHeaderRange.start, sourceData->ruleBodyRange.end - sourceData->ruleHeaderRange.start + 1);
1124 m_parsedStyleSheet->setText(sheetText);
1125 m_flatRules.clear();
1126 fireStyleSheetChanged();
1130 CSSStyleRule* InspectorStyleSheet::ruleForId(const InspectorCSSId& id) const
1132 ASSERT(!id.isEmpty());
1134 return InspectorCSSAgent::asCSSStyleRule(id.ordinal() >= m_flatRules.size() ? 0 : m_flatRules.at(id.ordinal()).get());
1137 PassRefPtr<TypeBuilder::CSS::CSSStyleSheetHeader> InspectorStyleSheet::buildObjectForStyleSheetInfo() const
1139 CSSStyleSheet* styleSheet = pageStyleSheet();
1143 Document* document = styleSheet->ownerDocument();
1144 LocalFrame* frame = document ? document->frame() : 0;
1146 RefPtr<TypeBuilder::CSS::CSSStyleSheetHeader> result = TypeBuilder::CSS::CSSStyleSheetHeader::create()
1147 .setStyleSheetId(id())
1148 .setOrigin(m_origin)
1149 .setDisabled(styleSheet->disabled())
1150 .setSourceURL(url())
1151 .setTitle(styleSheet->title())
1152 .setFrameId(m_pageAgent->frameId(frame))
1153 .setIsInline(styleSheet->isInline() && !startsAtZero())
1154 .setStartLine(styleSheet->startPositionInSource().m_line.zeroBasedInt())
1155 .setStartColumn(styleSheet->startPositionInSource().m_column.zeroBasedInt());
1158 result->setHasSourceURL(true);
1160 String sourceMapURLValue = sourceMapURL();
1161 if (!sourceMapURLValue.isEmpty())
1162 result->setSourceMapURL(sourceMapURLValue);
1163 return result.release();
1166 PassRefPtr<TypeBuilder::Array<TypeBuilder::CSS::Selector> > InspectorStyleSheet::selectorsFromSource(const CSSRuleSourceData* sourceData, const String& sheetText)
1168 ScriptRegexp comment("/\\*[^]*?\\*/", TextCaseSensitive, MultilineEnabled);
1169 RefPtr<TypeBuilder::Array<TypeBuilder::CSS::Selector> > result = TypeBuilder::Array<TypeBuilder::CSS::Selector>::create();
1170 const SelectorRangeList& ranges = sourceData->selectorRanges;
1171 for (size_t i = 0, size = ranges.size(); i < size; ++i) {
1172 const SourceRange& range = ranges.at(i);
1173 String selector = sheetText.substring(range.start, range.length());
1175 // We don't want to see any comments in the selector components, only the meaningful parts.
1178 while ((offset = comment.match(selector, offset, &matchLength)) >= 0)
1179 selector.replace(offset, matchLength, "");
1181 RefPtr<TypeBuilder::CSS::Selector> simpleSelector = TypeBuilder::CSS::Selector::create()
1182 .setValue(selector.stripWhiteSpace());
1183 simpleSelector->setRange(buildSourceRangeObject(range, lineEndings().get()));
1184 result->addItem(simpleSelector.release());
1186 return result.release();
1189 PassRefPtr<TypeBuilder::CSS::SelectorList> InspectorStyleSheet::buildObjectForSelectorList(CSSStyleRule* rule)
1191 RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = nullptr;
1192 if (ensureParsedDataReady())
1193 sourceData = ruleSourceDataAt(styleId(rule->style()).ordinal());
1194 RefPtr<TypeBuilder::Array<TypeBuilder::CSS::Selector> > selectors;
1196 // This intentionally does not rely on the source data to avoid catching the trailing comments (before the declaration starting '{').
1197 String selectorText = rule->selectorText();
1200 selectors = selectorsFromSource(sourceData.get(), m_parsedStyleSheet->text());
1202 selectors = TypeBuilder::Array<TypeBuilder::CSS::Selector>::create();
1203 const CSSSelectorList& selectorList = rule->styleRule()->selectorList();
1204 for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(*selector))
1205 selectors->addItem(TypeBuilder::CSS::Selector::create().setValue(selector->selectorText()).release());
1207 RefPtr<TypeBuilder::CSS::SelectorList> result = TypeBuilder::CSS::SelectorList::create()
1208 .setSelectors(selectors)
1209 .setText(selectorText)
1211 return result.release();
1214 static bool canBind(TypeBuilder::CSS::StyleSheetOrigin::Enum origin)
1216 return origin != TypeBuilder::CSS::StyleSheetOrigin::User_agent && origin != TypeBuilder::CSS::StyleSheetOrigin::User;
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 if (canBind(m_origin)) {
1231 InspectorCSSId id(ruleId(rule));
1233 result->setStyleSheetId(id.styleSheetId());
1237 result->setMedia(mediaStack);
1239 return result.release();
1242 bool InspectorStyleSheet::getText(String* result) const
1246 *result = m_parsedStyleSheet->text();
1250 CSSStyleDeclaration* InspectorStyleSheet::styleForId(const InspectorCSSId& id) const
1252 CSSStyleRule* rule = ruleForId(id);
1256 return rule->style();
1259 PassRefPtr<TypeBuilder::CSS::SourceRange> InspectorStyleSheet::ruleHeaderSourceRange(const CSSRule* rule)
1261 if (!ensureParsedDataReady())
1265 size_t index = m_flatRules.find(rule);
1266 if (index == kNotFound)
1268 RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = m_parsedStyleSheet->ruleSourceDataAt(static_cast<unsigned>(index));
1269 return buildSourceRangeObject(sourceData->ruleHeaderRange, lineEndings().get());
1272 PassRefPtr<InspectorStyle> InspectorStyleSheet::inspectorStyleForId(const InspectorCSSId& id)
1274 CSSStyleDeclaration* style = styleForId(id);
1278 return InspectorStyle::create(id, style, this);
1281 unsigned InspectorStyleSheet::ruleCount()
1283 return m_parsedStyleSheet->ruleCount();
1286 String InspectorStyleSheet::sourceURL() const
1288 if (!m_sourceURL.isNull())
1290 if (m_origin != TypeBuilder::CSS::StyleSheetOrigin::Regular) {
1295 String styleSheetText;
1296 bool success = getText(&styleSheetText);
1299 String commentValue = ContentSearchUtils::findSourceURL(styleSheetText, ContentSearchUtils::CSSMagicComment, &deprecated);
1300 if (!commentValue.isEmpty()) {
1301 // FIXME: add deprecated console message here.
1302 m_sourceURL = commentValue;
1303 return commentValue;
1310 String InspectorStyleSheet::url() const
1312 // "sourceURL" is present only for regular rules, otherwise "origin" should be used in the frontend.
1313 if (m_origin != TypeBuilder::CSS::StyleSheetOrigin::Regular)
1316 CSSStyleSheet* styleSheet = pageStyleSheet();
1323 if (styleSheet->isInline() && startsAtZero())
1329 bool InspectorStyleSheet::hasSourceURL() const
1331 return !sourceURL().isEmpty();
1334 bool InspectorStyleSheet::startsAtZero() const
1336 CSSStyleSheet* styleSheet = pageStyleSheet();
1340 return styleSheet->startPositionInSource() == TextPosition::minimumPosition();
1343 String InspectorStyleSheet::sourceMapURL() const
1345 if (m_origin != TypeBuilder::CSS::StyleSheetOrigin::Regular)
1348 String styleSheetText;
1349 bool success = getText(&styleSheetText);
1352 String commentValue = ContentSearchUtils::findSourceMapURL(styleSheetText, ContentSearchUtils::CSSMagicComment, &deprecated);
1353 if (!commentValue.isEmpty()) {
1354 // FIXME: add deprecated console message here.
1355 return commentValue;
1358 return m_pageAgent->resourceSourceMapURL(finalURL());
1361 InspectorCSSId InspectorStyleSheet::styleId(CSSStyleDeclaration* style) const
1363 unsigned index = ruleIndexByStyle(style);
1364 if (index != UINT_MAX)
1365 return InspectorCSSId(id(), index);
1366 return InspectorCSSId();
1369 bool InspectorStyleSheet::findRuleBySelectorRange(const SourceRange& sourceRange, InspectorCSSId* ruleId)
1371 if (!ensureParsedDataReady())
1373 for (size_t i = 0; i < ruleCount(); ++i) {
1374 RefPtrWillBeRawPtr<CSSRuleSourceData> ruleSourceData = ruleSourceDataAt(i);
1375 if (!ruleSourceData->styleSourceData)
1377 if (ruleSourceData->ruleHeaderRange.start == sourceRange.start && ruleSourceData->ruleHeaderRange.end == sourceRange.end) {
1378 *ruleId = InspectorCSSId(id(), i);
1385 Document* InspectorStyleSheet::ownerDocument() const
1387 return m_pageStyleSheet->ownerDocument();
1390 PassRefPtrWillBeRawPtr<CSSRuleSourceData> InspectorStyleSheet::ruleSourceDataAt(unsigned ruleIndex) const
1392 return m_parsedStyleSheet->ruleSourceDataAt(ruleIndex);
1395 unsigned InspectorStyleSheet::ruleIndexByStyle(CSSStyleDeclaration* pageStyle) const
1398 for (unsigned i = 0, size = m_flatRules.size(); i < size; ++i) {
1399 CSSStyleRule* styleRule = InspectorCSSAgent::asCSSStyleRule(m_flatRules.at(i).get());
1400 if (styleRule && styleRule->style() == pageStyle)
1406 bool InspectorStyleSheet::ensureParsedDataReady()
1408 return ensureText() && m_parsedStyleSheet->ensureSourceData();
1411 bool InspectorStyleSheet::ensureText() const
1413 if (m_parsedStyleSheet->hasText())
1417 bool success = originalStyleSheetText(&text);
1419 m_parsedStyleSheet->setText(text);
1420 // No need to clear m_flatRules here - it's empty.
1425 static void collectFlatRules(PassRefPtrWillBeRawPtr<CSSRuleList> ruleList, CSSRuleVector* result)
1430 for (unsigned i = 0, size = ruleList->length(); i < size; ++i) {
1431 CSSRule* rule = ruleList->item(i);
1433 // The result->append()'ed types should be exactly the same as in ParsedStyleSheet::flattenSourceData().
1434 switch (rule->type()) {
1435 case CSSRule::STYLE_RULE:
1436 result->append(rule);
1438 case CSSRule::IMPORT_RULE:
1439 case CSSRule::MEDIA_RULE:
1440 result->append(rule);
1445 RefPtrWillBeRawPtr<CSSRuleList> childRuleList = asCSSRuleList(rule);
1447 collectFlatRules(childRuleList, result);
1451 void InspectorStyleSheet::ensureFlatRules() const
1453 // We are fine with redoing this for empty stylesheets as this will run fast.
1454 if (m_flatRules.isEmpty())
1455 collectFlatRules(asCSSRuleList(pageStyleSheet()), &m_flatRules);
1458 bool InspectorStyleSheet::setStyleText(const InspectorCSSId& id, const String& text)
1460 CSSStyleDeclaration* style = styleForId(id);
1464 if (!ensureParsedDataReady())
1467 String patchedStyleSheetText;
1468 bool success = styleSheetTextWithChangedStyle(style, text, &patchedStyleSheetText);
1472 TrackExceptionState exceptionState;
1473 style->setCSSText(text, exceptionState);
1474 if (!exceptionState.hadException()) {
1475 m_parsedStyleSheet->setText(patchedStyleSheetText);
1476 fireStyleSheetChanged();
1479 return !exceptionState.hadException();
1482 bool InspectorStyleSheet::styleSheetTextWithChangedStyle(CSSStyleDeclaration* style, const String& newStyleText, String* result)
1486 if (!ensureParsedDataReady())
1489 RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = ruleSourceDataAt(styleId(style).ordinal());
1490 unsigned bodyStart = sourceData->ruleBodyRange.start;
1491 unsigned bodyEnd = sourceData->ruleBodyRange.end;
1492 ASSERT(bodyStart <= bodyEnd);
1494 String text = m_parsedStyleSheet->text();
1495 ASSERT_WITH_SECURITY_IMPLICATION(bodyEnd <= text.length()); // bodyEnd is exclusive
1497 text.replace(bodyStart, bodyEnd - bodyStart, newStyleText);
1502 InspectorCSSId InspectorStyleSheet::ruleId(CSSStyleRule* rule) const
1504 return styleId(rule->style());
1507 bool InspectorStyleSheet::originalStyleSheetText(String* result) const
1509 bool success = inlineStyleSheetText(result);
1511 success = resourceStyleSheetText(result);
1515 bool InspectorStyleSheet::resourceStyleSheetText(String* result) const
1517 if (m_origin == TypeBuilder::CSS::StyleSheetOrigin::User || m_origin == TypeBuilder::CSS::StyleSheetOrigin::User_agent)
1520 if (!ownerDocument() || !ownerDocument()->frame())
1524 bool success = m_resourceAgent->fetchResourceContent(ownerDocument()->frame(), KURL(ParsedURLString, m_pageStyleSheet->href()), result, &base64Encoded) && !base64Encoded;
1528 bool InspectorStyleSheet::inlineStyleSheetText(String* result) const
1530 Node* ownerNode = m_pageStyleSheet->ownerNode();
1531 if (!ownerNode || ownerNode->nodeType() != Node::ELEMENT_NODE)
1533 Element& ownerElement = toElement(*ownerNode);
1535 if (!isHTMLStyleElement(ownerElement) && !isSVGStyleElement(ownerElement))
1537 *result = ownerElement.textContent();
1541 PassRefPtr<InspectorStyleSheetForInlineStyle> InspectorStyleSheetForInlineStyle::create(const String& id, PassRefPtr<Element> element, Listener* listener)
1543 return adoptRef(new InspectorStyleSheetForInlineStyle(id, element, listener));
1546 InspectorStyleSheetForInlineStyle::InspectorStyleSheetForInlineStyle(const String& id, PassRefPtr<Element> element, Listener* listener)
1547 : InspectorStyleSheetBase(id, listener)
1548 , m_element(element)
1549 , m_ruleSourceData(nullptr)
1550 , m_isStyleTextValid(false)
1553 m_inspectorStyle = InspectorStyle::create(InspectorCSSId(id, 0), inlineStyle(), this);
1554 m_styleText = m_element->isStyledElement() ? m_element->getAttribute("style").string() : String();
1557 void InspectorStyleSheetForInlineStyle::didModifyElementAttribute()
1559 m_isStyleTextValid = false;
1560 if (m_element->isStyledElement() && m_element->style() != m_inspectorStyle->cssStyle())
1561 m_inspectorStyle = InspectorStyle::create(InspectorCSSId(id(), 0), inlineStyle(), this);
1562 m_ruleSourceData.clear();
1565 bool InspectorStyleSheetForInlineStyle::setText(const String& text, ExceptionState& exceptionState)
1567 bool success = setStyleText(InspectorCSSId(id(), 0), text);
1569 exceptionState.throwDOMException(SyntaxError, "Style sheet text is invalid.");
1571 fireStyleSheetChanged();
1575 bool InspectorStyleSheetForInlineStyle::getText(String* result) const
1577 if (!m_isStyleTextValid) {
1578 m_styleText = elementStyleText();
1579 m_isStyleTextValid = true;
1581 *result = m_styleText;
1585 bool InspectorStyleSheetForInlineStyle::setStyleText(const InspectorCSSId& id, const String& text)
1587 CSSStyleDeclaration* style = styleForId(id);
1590 ASSERT_UNUSED(style, style == inlineStyle());
1591 TrackExceptionState exceptionState;
1594 InspectorCSSAgent::InlineStyleOverrideScope overrideScope(m_element->ownerDocument());
1595 m_element->setAttribute("style", AtomicString(text), exceptionState);
1597 if (!exceptionState.hadException()) {
1599 m_isStyleTextValid = true;
1600 m_ruleSourceData.clear();
1601 fireStyleSheetChanged();
1603 return !exceptionState.hadException();
1606 Document* InspectorStyleSheetForInlineStyle::ownerDocument() const
1608 return &m_element->document();
1611 bool InspectorStyleSheetForInlineStyle::ensureParsedDataReady()
1613 // The "style" property value can get changed indirectly, e.g. via element.style.borderWidth = "2px".
1614 const String& currentStyleText = elementStyleText();
1615 if (m_styleText != currentStyleText) {
1616 m_ruleSourceData.clear();
1617 m_styleText = currentStyleText;
1618 m_isStyleTextValid = true;
1621 if (m_ruleSourceData)
1624 m_ruleSourceData = getStyleAttributeData();
1626 bool success = !!m_ruleSourceData;
1628 m_ruleSourceData = CSSRuleSourceData::create(CSSRuleSourceData::STYLE_RULE);
1635 PassRefPtr<InspectorStyle> InspectorStyleSheetForInlineStyle::inspectorStyleForId(const InspectorCSSId& id)
1637 ASSERT_UNUSED(id, !id.ordinal());
1638 return m_inspectorStyle;
1641 CSSStyleDeclaration* InspectorStyleSheetForInlineStyle::inlineStyle() const
1643 return m_element->style();
1646 const String& InspectorStyleSheetForInlineStyle::elementStyleText() const
1648 return m_element->getAttribute("style").string();
1651 PassRefPtrWillBeRawPtr<CSSRuleSourceData> InspectorStyleSheetForInlineStyle::getStyleAttributeData() const
1653 if (!m_element->isStyledElement())
1656 if (m_styleText.isEmpty()) {
1657 RefPtrWillBeRawPtr<CSSRuleSourceData> result = CSSRuleSourceData::create(CSSRuleSourceData::STYLE_RULE);
1658 result->ruleBodyRange.start = 0;
1659 result->ruleBodyRange.end = 0;
1660 return result.release();
1663 RefPtrWillBeRawPtr<MutableStylePropertySet> tempDeclaration = MutableStylePropertySet::create();
1664 RuleSourceDataList ruleSourceDataResult;
1665 StyleSheetHandler handler(m_styleText, &m_element->document(), m_element->document().elementSheet().contents(), &ruleSourceDataResult);
1666 BisonCSSParser(parserContextForDocument(&m_element->document())).parseDeclaration(tempDeclaration.get(), m_styleText, &handler, m_element->document().elementSheet().contents());
1667 return ruleSourceDataResult.first().release();
1670 } // namespace WebCore