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 "bindings/core/v8/ExceptionState.h"
29 #include "bindings/core/v8/ExceptionStatePlaceholder.h"
30 #include "bindings/core/v8/ScriptRegexp.h"
31 #include "core/CSSPropertyNames.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 blink::TypeBuilder::Array;
57 using blink::RuleSourceDataList;
58 using blink::CSSRuleSourceData;
59 using blink::CSSStyleSheet;
63 using namespace blink;
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<blink::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();
432 m_sourceData = adoptPtrWillBeNoop(new RuleSourceDataList());
434 // FIXME: This is a temporary solution to retain the original flat sourceData structure
435 // containing only style rules, even though BisonCSSParser now provides the full rule source data tree.
436 // Normally, we should just assign m_sourceData = sourceData;
437 flattenSourceData(sourceData.get());
440 PassRefPtrWillBeRawPtr<blink::CSSRuleSourceData> ParsedStyleSheet::ruleSourceDataAt(unsigned index) const
442 if (!hasSourceData() || index >= m_sourceData->size())
445 return m_sourceData->at(index);
450 enum MediaListSource {
451 MediaListSourceLinkedSheet,
452 MediaListSourceInlineSheet,
453 MediaListSourceMediaRule,
454 MediaListSourceImportRule
457 static PassRefPtr<TypeBuilder::CSS::SourceRange> buildSourceRangeObject(const SourceRange& range, Vector<unsigned>* lineEndings)
461 TextPosition start = TextPosition::fromOffsetAndLineEndings(range.start, *lineEndings);
462 TextPosition end = TextPosition::fromOffsetAndLineEndings(range.end, *lineEndings);
464 RefPtr<TypeBuilder::CSS::SourceRange> result = TypeBuilder::CSS::SourceRange::create()
465 .setStartLine(start.m_line.zeroBasedInt())
466 .setStartColumn(start.m_column.zeroBasedInt())
467 .setEndLine(end.m_line.zeroBasedInt())
468 .setEndColumn(end.m_column.zeroBasedInt());
469 return result.release();
472 static PassRefPtrWillBeRawPtr<CSSRuleList> asCSSRuleList(CSSStyleSheet* styleSheet)
477 RefPtrWillBeRawPtr<StaticCSSRuleList> list = StaticCSSRuleList::create();
478 WillBeHeapVector<RefPtrWillBeMember<CSSRule> >& listRules = list->rules();
479 for (unsigned i = 0, size = styleSheet->length(); i < size; ++i) {
480 CSSRule* item = styleSheet->item(i);
481 if (item->type() == CSSRule::CHARSET_RULE)
483 listRules.append(item);
485 return list.release();
488 static PassRefPtrWillBeRawPtr<CSSRuleList> asCSSRuleList(CSSRule* rule)
493 if (rule->type() == CSSRule::MEDIA_RULE)
494 return toCSSMediaRule(rule)->cssRules();
496 if (rule->type() == CSSRule::KEYFRAMES_RULE)
497 return toCSSKeyframesRule(rule)->cssRules();
499 if (rule->type() == CSSRule::SUPPORTS_RULE)
500 return toCSSSupportsRule(rule)->cssRules();
505 PassRefPtrWillBeRawPtr<InspectorStyle> InspectorStyle::create(const InspectorCSSId& styleId, PassRefPtrWillBeRawPtr<CSSStyleDeclaration> style, InspectorStyleSheetBase* parentStyleSheet)
507 return adoptRefWillBeNoop(new InspectorStyle(styleId, style, parentStyleSheet));
510 InspectorStyle::InspectorStyle(const InspectorCSSId& styleId, PassRefPtrWillBeRawPtr<CSSStyleDeclaration> style, InspectorStyleSheetBase* parentStyleSheet)
513 , m_parentStyleSheet(parentStyleSheet)
514 , m_formatAcquired(false)
519 PassRefPtr<TypeBuilder::CSS::CSSStyle> InspectorStyle::buildObjectForStyle() const
521 RefPtr<TypeBuilder::CSS::CSSStyle> result = styleWithProperties();
522 if (!m_styleId.isEmpty())
523 result->setStyleSheetId(m_styleId.styleSheetId());
525 RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = extractSourceData();
527 result->setRange(buildSourceRangeObject(sourceData->ruleBodyRange, m_parentStyleSheet->lineEndings().get()));
529 return result.release();
532 PassRefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSComputedStyleProperty> > InspectorStyle::buildArrayForComputedStyle() const
534 RefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSComputedStyleProperty> > result = TypeBuilder::Array<TypeBuilder::CSS::CSSComputedStyleProperty>::create();
535 WillBeHeapVector<InspectorStyleProperty> properties;
536 populateAllProperties(properties);
538 for (WillBeHeapVector<InspectorStyleProperty>::iterator it = properties.begin(), itEnd = properties.end(); it != itEnd; ++it) {
539 const CSSPropertySourceData& propertyEntry = it->sourceData;
540 RefPtr<TypeBuilder::CSS::CSSComputedStyleProperty> entry = TypeBuilder::CSS::CSSComputedStyleProperty::create()
541 .setName(propertyEntry.name)
542 .setValue(propertyEntry.value);
543 result->addItem(entry);
546 return result.release();
549 bool InspectorStyle::verifyPropertyText(const String& propertyText, bool canOmitSemicolon)
551 DEFINE_STATIC_LOCAL(String, bogusPropertyName, ("-webkit-boguz-propertee"));
552 RefPtrWillBeRawPtr<MutableStylePropertySet> tempMutableStyle = MutableStylePropertySet::create();
553 RuleSourceDataList sourceData;
554 RefPtrWillBeRawPtr<StyleSheetContents> styleSheetContents = StyleSheetContents::create(strictCSSParserContext());
555 String declarationText = propertyText + (canOmitSemicolon ? ";" : " ") + bogusPropertyName + ": none";
556 StyleSheetHandler handler(declarationText, ownerDocument(), styleSheetContents.get(), &sourceData);
557 BisonCSSParser(parserContextForDocument(ownerDocument())).parseDeclaration(tempMutableStyle.get(), declarationText, &handler, styleSheetContents.get());
558 WillBeHeapVector<CSSPropertySourceData>& propertyData = sourceData.first()->styleSourceData->propertyData;
559 unsigned propertyCount = propertyData.size();
561 // At least one property + the bogus property added just above should be present.
562 if (propertyCount < 2)
565 // Check for the proper propertyText termination (the parser could at least restore to the PROPERTY_NAME state).
566 if (propertyData.at(propertyCount - 1).name != bogusPropertyName)
572 bool InspectorStyle::setPropertyText(unsigned index, const String& propertyText, bool overwrite, ExceptionState& exceptionState)
574 ASSERT(m_parentStyleSheet);
576 if (!m_parentStyleSheet->ensureParsedDataReady()) {
577 exceptionState.throwDOMException(NotFoundError, "The parent style sheet's data hasn't been processed.");
581 if (!propertyText.stripWhiteSpace().isEmpty()) {
582 if (!verifyPropertyText(propertyText, false) && !verifyPropertyText(propertyText, true)) {
583 exceptionState.throwDOMException(SyntaxError, "The property '" + propertyText + "' could not be set.");
588 RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = extractSourceData();
590 exceptionState.throwDOMException(NotFoundError, "The property '" + propertyText + "' could not be set.");
595 bool success = styleText(&text);
597 exceptionState.throwDOMException(NotFoundError, "The property '" + propertyText + "' could not be set.");
601 WillBeHeapVector<InspectorStyleProperty> allProperties;
602 populateAllProperties(allProperties);
604 InspectorStyleTextEditor editor(&allProperties, text, newLineAndWhitespaceDelimiters());
606 if (index >= allProperties.size()) {
607 exceptionState.throwDOMException(IndexSizeError, "The index provided (" + String::number(index) + ") is greater than or equal to the maximum bound (" + String::number(allProperties.size()) + ").");
610 editor.replaceProperty(index, propertyText);
612 editor.insertProperty(index, propertyText, sourceData->ruleBodyRange.length());
615 return m_parentStyleSheet->setStyleText(m_styleId, editor.styleText());
618 bool InspectorStyle::styleText(String* result) const
620 RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = extractSourceData();
624 String styleSheetText;
625 bool success = m_parentStyleSheet->getText(&styleSheetText);
629 SourceRange& bodyRange = sourceData->ruleBodyRange;
630 *result = styleSheetText.substring(bodyRange.start, bodyRange.end - bodyRange.start);
634 void InspectorStyle::populateAllProperties(WillBeHeapVector<InspectorStyleProperty>& result) const
636 HashSet<String> sourcePropertyNames;
638 RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = extractSourceData();
639 if (sourceData && sourceData->styleSourceData) {
640 String styleDeclaration;
641 bool isStyleTextKnown = styleText(&styleDeclaration);
642 ASSERT_UNUSED(isStyleTextKnown, isStyleTextKnown);
643 WillBeHeapVector<CSSPropertySourceData>& sourcePropertyData = sourceData->styleSourceData->propertyData;
644 for (WillBeHeapVector<CSSPropertySourceData>::const_iterator it = sourcePropertyData.begin(); it != sourcePropertyData.end(); ++it) {
645 InspectorStyleProperty p(*it, true);
646 p.setRawTextFromStyleDeclaration(styleDeclaration);
648 sourcePropertyNames.add(it->name.lower());
652 for (int i = 0, size = m_style->length(); i < size; ++i) {
653 String name = m_style->item(i);
654 if (!sourcePropertyNames.add(name.lower()).isNewEntry)
657 result.append(InspectorStyleProperty(CSSPropertySourceData(name, m_style->getPropertyValue(name), !m_style->getPropertyPriority(name).isEmpty(), false, true, SourceRange()), false));
661 PassRefPtr<TypeBuilder::CSS::CSSStyle> InspectorStyle::styleWithProperties() const
663 RefPtr<Array<TypeBuilder::CSS::CSSProperty> > propertiesObject = Array<TypeBuilder::CSS::CSSProperty>::create();
664 RefPtr<Array<TypeBuilder::CSS::ShorthandEntry> > shorthandEntries = Array<TypeBuilder::CSS::ShorthandEntry>::create();
665 HashSet<String> foundShorthands;
666 OwnPtr<Vector<unsigned> > lineEndings(m_parentStyleSheet ? m_parentStyleSheet->lineEndings() : PassOwnPtr<Vector<unsigned> >());
667 RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = extractSourceData();
668 unsigned ruleBodyRangeStart = sourceData ? sourceData->ruleBodyRange.start : 0;
670 WillBeHeapVector<InspectorStyleProperty> properties;
671 populateAllProperties(properties);
673 for (WillBeHeapVector<InspectorStyleProperty>::iterator it = properties.begin(), itEnd = properties.end(); it != itEnd; ++it) {
674 const CSSPropertySourceData& propertyEntry = it->sourceData;
675 const String& name = propertyEntry.name;
677 RefPtr<TypeBuilder::CSS::CSSProperty> property = TypeBuilder::CSS::CSSProperty::create()
679 .setValue(propertyEntry.value);
680 propertiesObject->addItem(property);
682 // Default "parsedOk" == true.
683 if (!propertyEntry.parsedOk)
684 property->setParsedOk(false);
685 if (it->hasRawText())
686 property->setText(it->rawText);
688 if (propertyEntry.important)
689 property->setImportant(true);
691 // The property range is relative to the style body start.
692 // Should be converted into an absolute range (relative to the stylesheet start)
693 // for the proper conversion into line:column.
694 SourceRange absolutePropertyRange = propertyEntry.range;
695 absolutePropertyRange.start += ruleBodyRangeStart;
696 absolutePropertyRange.end += ruleBodyRangeStart;
697 property->setRange(buildSourceRangeObject(absolutePropertyRange, lineEndings.get()));
698 if (!propertyEntry.disabled) {
700 property->setImplicit(false);
702 property->setDisabled(propertyEntry.disabled);
703 } else if (!propertyEntry.disabled) {
704 bool implicit = m_style->isPropertyImplicit(name);
705 // Default "implicit" == false.
707 property->setImplicit(true);
709 String shorthand = m_style->getPropertyShorthand(name);
710 if (!shorthand.isEmpty()) {
711 if (foundShorthands.add(shorthand).isNewEntry) {
712 RefPtr<TypeBuilder::CSS::ShorthandEntry> entry = TypeBuilder::CSS::ShorthandEntry::create()
714 .setValue(shorthandValue(shorthand));
715 shorthandEntries->addItem(entry);
721 RefPtr<TypeBuilder::CSS::CSSStyle> result = TypeBuilder::CSS::CSSStyle::create()
722 .setCssProperties(propertiesObject)
723 .setShorthandEntries(shorthandEntries);
724 return result.release();
727 PassRefPtrWillBeRawPtr<CSSRuleSourceData> InspectorStyle::extractSourceData() const
729 if (!m_parentStyleSheet || !m_parentStyleSheet->ensureParsedDataReady())
731 return m_parentStyleSheet->ruleSourceDataAt(m_styleId.ordinal());
734 String InspectorStyle::shorthandValue(const String& shorthandProperty) const
736 String value = m_style->getPropertyValue(shorthandProperty);
737 if (value.isEmpty()) {
738 StringBuilder builder;
740 for (unsigned i = 0; i < m_style->length(); ++i) {
741 String individualProperty = m_style->item(i);
742 if (m_style->getPropertyShorthand(individualProperty) != shorthandProperty)
744 if (m_style->isPropertyImplicit(individualProperty))
746 String individualValue = m_style->getPropertyValue(individualProperty);
747 if (individualValue == "initial")
749 if (!builder.isEmpty())
751 builder.append(individualValue);
754 return builder.toString();
759 NewLineAndWhitespace& InspectorStyle::newLineAndWhitespaceDelimiters() const
761 DEFINE_STATIC_LOCAL(String, defaultPrefix, (" "));
763 if (m_formatAcquired)
766 RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = extractSourceData();
767 WillBeHeapVector<CSSPropertySourceData>* sourcePropertyData = sourceData ? &(sourceData->styleSourceData->propertyData) : 0;
768 int propertyCount = sourcePropertyData ? sourcePropertyData->size() : 0;
769 if (!propertyCount) {
770 m_format.first = "\n";
771 m_format.second = defaultPrefix;
772 return m_format; // Do not remember the default formatting and attempt to acquire it later.
776 bool success = styleText(&text);
777 ASSERT_UNUSED(success, success);
779 m_formatAcquired = true;
781 String candidatePrefix = defaultPrefix;
782 StringBuilder formatLineFeed;
783 StringBuilder prefix;
785 int propertyIndex = 0;
786 bool isFullPrefixScanned = false;
787 bool lineFeedTerminated = false;
788 while (propertyIndex < propertyCount) {
789 const blink::CSSPropertySourceData& currentProperty = sourcePropertyData->at(propertyIndex++);
791 bool processNextProperty = false;
792 int scanEnd = currentProperty.range.start;
793 for (int i = scanStart; i < scanEnd; ++i) {
795 bool isLineFeed = isHTMLLineBreak(ch);
797 if (!lineFeedTerminated)
798 formatLineFeed.append(ch);
800 } else if (isHTMLSpace<UChar>(ch))
803 candidatePrefix = prefix.toString();
805 scanStart = currentProperty.range.end;
807 processNextProperty = true;
810 if (!isLineFeed && formatLineFeed.length())
811 lineFeedTerminated = true;
813 if (!processNextProperty) {
814 isFullPrefixScanned = true;
819 m_format.first = formatLineFeed.toString();
820 m_format.second = isFullPrefixScanned ? prefix.toString() : candidatePrefix;
824 Document* InspectorStyle::ownerDocument() const
826 return m_parentStyleSheet->ownerDocument();
829 void InspectorStyle::trace(Visitor* visitor)
831 visitor->trace(m_style);
832 visitor->trace(m_parentStyleSheet);
835 InspectorStyleSheetBase::InspectorStyleSheetBase(const String& id, Listener* listener)
837 , m_listener(listener)
841 bool InspectorStyleSheetBase::setPropertyText(const InspectorCSSId& id, unsigned propertyIndex, const String& text, bool overwrite, ExceptionState& exceptionState)
843 RefPtrWillBeRawPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id);
844 if (!inspectorStyle) {
845 exceptionState.throwDOMException(NotFoundError, "No property could be found for the given ID.");
848 return inspectorStyle->setPropertyText(propertyIndex, text, overwrite, exceptionState);
851 bool InspectorStyleSheetBase::getStyleText(const InspectorCSSId& id, String* text)
853 RefPtrWillBeRawPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id);
856 return inspectorStyle->styleText(text);
859 void InspectorStyleSheetBase::fireStyleSheetChanged()
862 listener()->styleSheetChanged(this);
865 PassRefPtr<TypeBuilder::CSS::CSSStyle> InspectorStyleSheetBase::buildObjectForStyle(CSSStyleDeclaration* style)
867 RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = nullptr;
868 if (ensureParsedDataReady())
869 sourceData = ruleSourceDataAt(styleId(style).ordinal());
871 InspectorCSSId id = styleId(style);
873 // Any rule coming from User Agent and not from DefaultStyleSheet will not have id.
874 // See InspectorCSSAgent::buildObjectForRule for details.
875 RefPtrWillBeRawPtr<InspectorStyle> inspectorStyle = InspectorStyle::create(id, style, this);
876 return inspectorStyle->buildObjectForStyle();
878 RefPtrWillBeRawPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id);
879 RefPtr<TypeBuilder::CSS::CSSStyle> result = inspectorStyle->buildObjectForStyle();
881 // Style text cannot be retrieved without stylesheet, so set cssText here.
884 bool success = getText(&sheetText);
886 const SourceRange& bodyRange = sourceData->ruleBodyRange;
887 result->setCssText(sheetText.substring(bodyRange.start, bodyRange.end - bodyRange.start));
891 return result.release();
894 PassOwnPtr<Vector<unsigned> > InspectorStyleSheetBase::lineEndings()
898 return PassOwnPtr<Vector<unsigned> >();
899 return WTF::lineEndings(text);
902 bool InspectorStyleSheetBase::lineNumberAndColumnToOffset(unsigned lineNumber, unsigned columnNumber, unsigned* offset)
904 OwnPtr<Vector<unsigned> > endings = lineEndings();
905 if (lineNumber >= endings->size())
907 unsigned charactersInLine = lineNumber > 0 ? endings->at(lineNumber) - endings->at(lineNumber - 1) - 1 : endings->at(0);
908 if (columnNumber > charactersInLine)
910 TextPosition position(OrdinalNumber::fromZeroBasedInt(lineNumber), OrdinalNumber::fromZeroBasedInt(columnNumber));
911 *offset = position.toOffset(*endings).zeroBasedInt();
915 bool InspectorStyleSheetBase::findPropertyByRange(const SourceRange& sourceRange, InspectorCSSId* ruleId, unsigned* propertyIndex, bool* overwrite)
917 if (!ensureParsedDataReady())
919 for (size_t i = 0; i < ruleCount(); ++i) {
920 RefPtrWillBeRawPtr<CSSRuleSourceData> ruleSourceData = ruleSourceDataAt(i);
921 RefPtrWillBeRawPtr<CSSStyleSourceData> styleSourceData = ruleSourceData->styleSourceData;
922 if (!styleSourceData)
924 if (ruleSourceData->ruleBodyRange.end < sourceRange.start || sourceRange.end < ruleSourceData->ruleBodyRange.start)
926 WillBeHeapVector<CSSPropertySourceData>& propertyData = styleSourceData->propertyData;
927 for (size_t j = 0; j < propertyData.size(); ++j) {
928 CSSPropertySourceData& property = propertyData.at(j);
929 unsigned styleStart = ruleSourceData->ruleBodyRange.start;
930 if (sourceRange.length() && property.range.start + styleStart == sourceRange.start && property.range.end + styleStart == sourceRange.end) {
931 *ruleId = InspectorCSSId(id(), i);
936 if (!sourceRange.length() && styleStart <= sourceRange.start && sourceRange.start <= property.range.start + styleStart) {
937 *ruleId = InspectorCSSId(id(), i);
943 if (!sourceRange.length() && ruleSourceData->ruleBodyRange.start <= sourceRange.start && sourceRange.start <= ruleSourceData->ruleBodyRange.end) {
944 *ruleId = InspectorCSSId(id(), i);
945 *propertyIndex = propertyData.size();
953 PassRefPtrWillBeRawPtr<InspectorStyleSheet> InspectorStyleSheet::create(InspectorPageAgent* pageAgent, InspectorResourceAgent* resourceAgent, const String& id, PassRefPtrWillBeRawPtr<CSSStyleSheet> pageStyleSheet, TypeBuilder::CSS::StyleSheetOrigin::Enum origin, const String& documentURL, Listener* listener)
955 return adoptRefWillBeNoop(new InspectorStyleSheet(pageAgent, resourceAgent, id, pageStyleSheet, origin, documentURL, listener));
958 InspectorStyleSheet::InspectorStyleSheet(InspectorPageAgent* pageAgent, InspectorResourceAgent* resourceAgent, const String& id, PassRefPtrWillBeRawPtr<CSSStyleSheet> pageStyleSheet, TypeBuilder::CSS::StyleSheetOrigin::Enum origin, const String& documentURL, Listener* listener)
959 : InspectorStyleSheetBase(id, listener)
960 , m_pageAgent(pageAgent)
961 , m_resourceAgent(resourceAgent)
962 , m_pageStyleSheet(pageStyleSheet)
964 , m_documentURL(documentURL)
966 m_parsedStyleSheet = adoptPtr(new ParsedStyleSheet(m_pageStyleSheet.get()));
969 InspectorStyleSheet::~InspectorStyleSheet()
973 void InspectorStyleSheet::trace(Visitor* visitor)
975 visitor->trace(m_pageAgent);
976 visitor->trace(m_resourceAgent);
977 visitor->trace(m_pageStyleSheet);
978 visitor->trace(m_flatRules);
979 InspectorStyleSheetBase::trace(visitor);
982 static String styleSheetURL(CSSStyleSheet* pageStyleSheet)
984 if (pageStyleSheet && !pageStyleSheet->contents()->baseURL().isEmpty())
985 return pageStyleSheet->contents()->baseURL().string();
986 return emptyString();
989 String InspectorStyleSheet::finalURL() const
991 String url = styleSheetURL(m_pageStyleSheet.get());
992 return url.isEmpty() ? m_documentURL : url;
995 bool InspectorStyleSheet::setText(const String& text, ExceptionState& exceptionState)
1001 listener()->willReparseStyleSheet();
1004 // Have a separate scope for clearRules() (bug 95324).
1005 CSSStyleSheet::RuleMutationScope mutationScope(m_pageStyleSheet.get());
1006 m_pageStyleSheet->contents()->clearRules();
1007 m_pageStyleSheet->clearChildRuleCSSOMWrappers();
1010 CSSStyleSheet::RuleMutationScope mutationScope(m_pageStyleSheet.get());
1011 m_pageStyleSheet->contents()->parseString(text);
1015 listener()->didReparseStyleSheet();
1016 fireStyleSheetChanged();
1017 m_pageStyleSheet->ownerDocument()->styleResolverChanged(FullStyleUpdate);
1021 String InspectorStyleSheet::ruleSelector(const InspectorCSSId& id, ExceptionState& exceptionState)
1023 CSSStyleRule* rule = ruleForId(id);
1025 exceptionState.throwDOMException(NotFoundError, "No rule was found for the given ID.");
1028 return rule->selectorText();
1031 bool InspectorStyleSheet::setRuleSelector(const InspectorCSSId& id, const String& selector, ExceptionState& exceptionState)
1033 CSSStyleRule* rule = ruleForId(id);
1035 exceptionState.throwDOMException(NotFoundError, "No rule was found for the given ID.");
1038 CSSStyleSheet* styleSheet = rule->parentStyleSheet();
1039 if (!styleSheet || !ensureParsedDataReady()) {
1040 exceptionState.throwDOMException(NotFoundError, "No stylesheet could be found in which to set the selector.");
1044 rule->setSelectorText(selector);
1045 RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = ruleSourceDataAt(id.ordinal());
1047 exceptionState.throwDOMException(NotFoundError, "The selector '" + selector + "' could not be set.");
1051 String sheetText = m_parsedStyleSheet->text();
1052 sheetText.replace(sourceData->ruleHeaderRange.start, sourceData->ruleHeaderRange.length(), selector);
1053 updateText(sheetText);
1054 fireStyleSheetChanged();
1058 unsigned InspectorStyleSheet::ruleIndexBySourceRange(const CSSMediaRule* parentMediaRule, const SourceRange& sourceRange)
1061 for (size_t i = 0; i < m_flatRules.size(); ++i) {
1062 RefPtrWillBeRawPtr<CSSRule> rule = m_flatRules.at(i);
1063 if (rule->parentRule() != parentMediaRule)
1065 RefPtrWillBeRawPtr<CSSRuleSourceData> ruleSourceData = m_parsedStyleSheet->ruleSourceDataAt(i);
1066 if (ruleSourceData->ruleBodyRange.end < sourceRange.start)
1072 CSSStyleRule* InspectorStyleSheet::insertCSSOMRuleInStyleSheet(const SourceRange& sourceRange, const String& ruleText, ExceptionState& exceptionState)
1074 unsigned index = ruleIndexBySourceRange(nullptr, sourceRange);
1075 m_pageStyleSheet->insertRule(ruleText, index, exceptionState);
1076 CSSRule* rule = m_pageStyleSheet->item(index);
1077 CSSStyleRule* styleRule = InspectorCSSAgent::asCSSStyleRule(rule);
1079 m_pageStyleSheet->deleteRule(index, ASSERT_NO_EXCEPTION);
1080 exceptionState.throwDOMException(SyntaxError, "The rule '" + ruleText + "' could not be added in style sheet.");
1086 CSSStyleRule* InspectorStyleSheet::insertCSSOMRuleInMediaRule(CSSMediaRule* mediaRule, const SourceRange& sourceRange, const String& ruleText, ExceptionState& exceptionState)
1088 unsigned index = ruleIndexBySourceRange(mediaRule, sourceRange);
1089 mediaRule->insertRule(ruleText, index, exceptionState);
1090 CSSRule* rule = mediaRule->item(index);
1091 CSSStyleRule* styleRule = InspectorCSSAgent::asCSSStyleRule(rule);
1093 mediaRule->deleteRule(index, ASSERT_NO_EXCEPTION);
1094 exceptionState.throwDOMException(SyntaxError, "The rule '" + ruleText + "' could not be added in media rule.");
1100 CSSStyleRule* InspectorStyleSheet::insertCSSOMRuleBySourceRange(const SourceRange& sourceRange, const String& ruleText, ExceptionState& exceptionState)
1102 int containingRuleIndex = -1;
1103 unsigned containingRuleLength = 0;
1104 for (size_t i = 0; i < m_parsedStyleSheet->ruleCount(); ++i) {
1105 RefPtrWillBeRawPtr<CSSRuleSourceData> ruleSourceData = m_parsedStyleSheet->ruleSourceDataAt(i);
1106 if (ruleSourceData->ruleHeaderRange.start < sourceRange.start && sourceRange.start < ruleSourceData->ruleBodyRange.start) {
1107 exceptionState.throwDOMException(NotFoundError, "Cannot insert rule inside rule selector.");
1110 if (sourceRange.start < ruleSourceData->ruleBodyRange.start || ruleSourceData->ruleBodyRange.end < sourceRange.start)
1112 if (containingRuleIndex == -1 || containingRuleLength > ruleSourceData->ruleBodyRange.length()) {
1113 containingRuleIndex = i;
1114 containingRuleLength = ruleSourceData->ruleBodyRange.length();
1117 if (containingRuleIndex == -1)
1118 return insertCSSOMRuleInStyleSheet(sourceRange, ruleText, exceptionState);
1119 RefPtrWillBeRawPtr<CSSRule> rule = m_flatRules.at(containingRuleIndex);
1120 if (rule->type() != CSSRule::MEDIA_RULE) {
1121 exceptionState.throwDOMException(NotFoundError, "Cannot insert rule in non-media rule.");
1124 return insertCSSOMRuleInMediaRule(toCSSMediaRule(rule.get()), sourceRange, ruleText, exceptionState);
1127 bool InspectorStyleSheet::verifyRuleText(const String& ruleText)
1129 DEFINE_STATIC_LOCAL(String, bogusPropertyName, ("-webkit-boguz-propertee"));
1130 RuleSourceDataList sourceData;
1131 RefPtrWillBeRawPtr<StyleSheetContents> styleSheetContents = StyleSheetContents::create(strictCSSParserContext());
1132 String text = ruleText + " div { " + bogusPropertyName + ": none; }";
1133 StyleSheetHandler handler(text, ownerDocument(), styleSheetContents.get(), &sourceData);
1134 BisonCSSParser(parserContextForDocument(ownerDocument())).parseSheet(styleSheetContents.get(), text, TextPosition::minimumPosition(), &handler);
1135 unsigned ruleCount = sourceData.size();
1137 // Exactly two rules should be parsed.
1141 // Added rule must be style rule.
1142 if (!sourceData.at(0)->styleSourceData)
1145 WillBeHeapVector<CSSPropertySourceData>& propertyData = sourceData.at(1)->styleSourceData->propertyData;
1146 unsigned propertyCount = propertyData.size();
1148 // Exactly one property should be in rule.
1149 if (propertyCount != 1)
1152 // Check for the property name.
1153 if (propertyData.at(0).name != bogusPropertyName)
1159 CSSStyleRule* InspectorStyleSheet::addRule(const String& ruleText, const SourceRange& location, ExceptionState& exceptionState)
1161 if (!ensureParsedDataReady()) {
1162 exceptionState.throwDOMException(NotFoundError, "Cannot parse style sheet.");
1166 if (location.start != location.end) {
1167 exceptionState.throwDOMException(NotFoundError, "Source range must be collapsed.");
1171 if (!verifyRuleText(ruleText)) {
1172 exceptionState.throwDOMException(SyntaxError, "Rule text is not valid.");
1177 bool success = getText(&text);
1179 exceptionState.throwDOMException(NotFoundError, "The rule '" + ruleText + "' could not be added.");
1184 CSSStyleRule* styleRule = insertCSSOMRuleBySourceRange(location, ruleText, exceptionState);
1185 if (exceptionState.hadException())
1188 text.insert(ruleText, location.start);
1190 m_parsedStyleSheet->setText(text);
1191 m_flatRules.clear();
1193 fireStyleSheetChanged();
1197 bool InspectorStyleSheet::deleteRule(const InspectorCSSId& id, const String& oldText, ExceptionState& exceptionState)
1199 RefPtrWillBeRawPtr<CSSStyleRule> rule = ruleForId(id);
1201 exceptionState.throwDOMException(NotFoundError, "No style rule could be found for the provided ID.");
1204 CSSStyleSheet* styleSheet = rule->parentStyleSheet();
1205 if (!styleSheet || !ensureParsedDataReady()) {
1206 exceptionState.throwDOMException(NotFoundError, "No parent stylesheet could be found.");
1210 RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = ruleSourceDataAt(id.ordinal());
1212 exceptionState.throwDOMException(NotFoundError, "No style rule could be found for the provided ID.");
1216 CSSRule* parentRule = rule->parentRule();
1218 if (parentRule->type() != CSSRule::MEDIA_RULE) {
1219 exceptionState.throwDOMException(NotFoundError, "Cannot remove rule from non-media rule.");
1222 CSSMediaRule* parentMediaRule = toCSSMediaRule(parentRule);
1224 while (index < parentMediaRule->length() && parentMediaRule->item(index) != rule)
1226 ASSERT(index < parentMediaRule->length());
1227 parentMediaRule->deleteRule(index, exceptionState);
1230 while (index < styleSheet->length() && styleSheet->item(index) != rule)
1232 ASSERT(index < styleSheet->length());
1233 styleSheet->deleteRule(index, exceptionState);
1235 // |rule| MAY NOT be addressed after this line!
1237 if (exceptionState.hadException())
1240 m_parsedStyleSheet->setText(oldText);
1241 m_flatRules.clear();
1242 fireStyleSheetChanged();
1246 void InspectorStyleSheet::updateText(const String& newText)
1248 Element* element = ownerStyleElement();
1250 m_pageAgent->addEditedResourceContent(finalURL(), newText);
1251 m_parsedStyleSheet->setText(newText);
1255 CSSStyleRule* InspectorStyleSheet::ruleForId(const InspectorCSSId& id) const
1257 ASSERT(!id.isEmpty());
1259 return InspectorCSSAgent::asCSSStyleRule(id.ordinal() >= m_flatRules.size() ? 0 : m_flatRules.at(id.ordinal()).get());
1262 PassRefPtr<TypeBuilder::CSS::CSSStyleSheetHeader> InspectorStyleSheet::buildObjectForStyleSheetInfo() const
1264 CSSStyleSheet* styleSheet = pageStyleSheet();
1268 Document* document = styleSheet->ownerDocument();
1269 LocalFrame* frame = document ? document->frame() : 0;
1271 RefPtr<TypeBuilder::CSS::CSSStyleSheetHeader> result = TypeBuilder::CSS::CSSStyleSheetHeader::create()
1272 .setStyleSheetId(id())
1273 .setOrigin(m_origin)
1274 .setDisabled(styleSheet->disabled())
1275 .setSourceURL(url())
1276 .setTitle(styleSheet->title())
1277 .setFrameId(m_pageAgent->frameId(frame))
1278 .setIsInline(styleSheet->isInline() && !startsAtZero())
1279 .setStartLine(styleSheet->startPositionInSource().m_line.zeroBasedInt())
1280 .setStartColumn(styleSheet->startPositionInSource().m_column.zeroBasedInt());
1283 result->setHasSourceURL(true);
1285 String sourceMapURLValue = sourceMapURL();
1286 if (!sourceMapURLValue.isEmpty())
1287 result->setSourceMapURL(sourceMapURLValue);
1288 return result.release();
1291 PassRefPtr<TypeBuilder::Array<TypeBuilder::CSS::Selector> > InspectorStyleSheet::selectorsFromSource(const CSSRuleSourceData* sourceData, const String& sheetText)
1293 ScriptRegexp comment("/\\*[^]*?\\*/", TextCaseSensitive, MultilineEnabled);
1294 RefPtr<TypeBuilder::Array<TypeBuilder::CSS::Selector> > result = TypeBuilder::Array<TypeBuilder::CSS::Selector>::create();
1295 const SelectorRangeList& ranges = sourceData->selectorRanges;
1296 for (size_t i = 0, size = ranges.size(); i < size; ++i) {
1297 const SourceRange& range = ranges.at(i);
1298 String selector = sheetText.substring(range.start, range.length());
1300 // We don't want to see any comments in the selector components, only the meaningful parts.
1303 while ((offset = comment.match(selector, offset, &matchLength)) >= 0)
1304 selector.replace(offset, matchLength, "");
1306 RefPtr<TypeBuilder::CSS::Selector> simpleSelector = TypeBuilder::CSS::Selector::create()
1307 .setValue(selector.stripWhiteSpace());
1308 simpleSelector->setRange(buildSourceRangeObject(range, lineEndings().get()));
1309 result->addItem(simpleSelector.release());
1311 return result.release();
1314 PassRefPtr<TypeBuilder::CSS::SelectorList> InspectorStyleSheet::buildObjectForSelectorList(CSSStyleRule* rule)
1316 RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = nullptr;
1317 if (ensureParsedDataReady())
1318 sourceData = ruleSourceDataAt(styleId(rule->style()).ordinal());
1319 RefPtr<TypeBuilder::Array<TypeBuilder::CSS::Selector> > selectors;
1321 // This intentionally does not rely on the source data to avoid catching the trailing comments (before the declaration starting '{').
1322 String selectorText = rule->selectorText();
1325 selectors = selectorsFromSource(sourceData.get(), m_parsedStyleSheet->text());
1327 selectors = TypeBuilder::Array<TypeBuilder::CSS::Selector>::create();
1328 const CSSSelectorList& selectorList = rule->styleRule()->selectorList();
1329 for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(*selector))
1330 selectors->addItem(TypeBuilder::CSS::Selector::create().setValue(selector->selectorText()).release());
1332 RefPtr<TypeBuilder::CSS::SelectorList> result = TypeBuilder::CSS::SelectorList::create()
1333 .setSelectors(selectors)
1334 .setText(selectorText)
1336 return result.release();
1339 static bool canBind(TypeBuilder::CSS::StyleSheetOrigin::Enum origin)
1341 return origin != TypeBuilder::CSS::StyleSheetOrigin::User_agent && origin != TypeBuilder::CSS::StyleSheetOrigin::User;
1344 PassRefPtr<TypeBuilder::CSS::CSSRule> InspectorStyleSheet::buildObjectForRule(CSSStyleRule* rule, PassRefPtr<Array<TypeBuilder::CSS::CSSMedia> > mediaStack)
1346 CSSStyleSheet* styleSheet = pageStyleSheet();
1350 RefPtr<TypeBuilder::CSS::CSSRule> result = TypeBuilder::CSS::CSSRule::create()
1351 .setSelectorList(buildObjectForSelectorList(rule))
1352 .setOrigin(m_origin)
1353 .setStyle(buildObjectForStyle(rule->style()));
1355 if (canBind(m_origin)) {
1356 InspectorCSSId id(ruleId(rule));
1358 result->setStyleSheetId(id.styleSheetId());
1362 result->setMedia(mediaStack);
1364 return result.release();
1367 bool InspectorStyleSheet::getText(String* result) const
1371 *result = m_parsedStyleSheet->text();
1375 CSSStyleDeclaration* InspectorStyleSheet::styleForId(const InspectorCSSId& id) const
1377 CSSStyleRule* rule = ruleForId(id);
1381 return rule->style();
1384 PassRefPtr<TypeBuilder::CSS::SourceRange> InspectorStyleSheet::ruleHeaderSourceRange(const CSSRule* rule)
1386 if (!ensureParsedDataReady())
1390 size_t index = m_flatRules.find(rule);
1391 // FIXME(lusnikov): m_flatRules are not always aligned with the m_parsedStyleSheet rule source
1392 // datas due to the CSSOM operations that add/remove rules without changing source.
1393 // This is a design issue. See crbug.com/178410
1394 if (index == kNotFound || index >= m_parsedStyleSheet->ruleCount())
1396 RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = m_parsedStyleSheet->ruleSourceDataAt(static_cast<unsigned>(index));
1397 return buildSourceRangeObject(sourceData->ruleHeaderRange, lineEndings().get());
1400 PassRefPtrWillBeRawPtr<InspectorStyle> InspectorStyleSheet::inspectorStyleForId(const InspectorCSSId& id)
1402 CSSStyleDeclaration* style = styleForId(id);
1406 return InspectorStyle::create(id, style, this);
1409 unsigned InspectorStyleSheet::ruleCount()
1411 return m_parsedStyleSheet->ruleCount();
1414 String InspectorStyleSheet::sourceURL() const
1416 if (!m_sourceURL.isNull())
1418 if (m_origin != TypeBuilder::CSS::StyleSheetOrigin::Regular) {
1423 String styleSheetText;
1424 bool success = getText(&styleSheetText);
1427 String commentValue = ContentSearchUtils::findSourceURL(styleSheetText, ContentSearchUtils::CSSMagicComment, &deprecated);
1428 if (!commentValue.isEmpty()) {
1429 // FIXME: add deprecated console message here.
1430 m_sourceURL = commentValue;
1431 return commentValue;
1438 String InspectorStyleSheet::url() const
1440 // "sourceURL" is present only for regular rules, otherwise "origin" should be used in the frontend.
1441 if (m_origin != TypeBuilder::CSS::StyleSheetOrigin::Regular)
1444 CSSStyleSheet* styleSheet = pageStyleSheet();
1451 if (styleSheet->isInline() && startsAtZero())
1457 bool InspectorStyleSheet::hasSourceURL() const
1459 return !sourceURL().isEmpty();
1462 bool InspectorStyleSheet::startsAtZero() const
1464 CSSStyleSheet* styleSheet = pageStyleSheet();
1468 return styleSheet->startPositionInSource() == TextPosition::minimumPosition();
1471 String InspectorStyleSheet::sourceMapURL() const
1473 if (m_origin != TypeBuilder::CSS::StyleSheetOrigin::Regular)
1476 String styleSheetText;
1477 bool success = getText(&styleSheetText);
1480 String commentValue = ContentSearchUtils::findSourceMapURL(styleSheetText, ContentSearchUtils::CSSMagicComment, &deprecated);
1481 if (!commentValue.isEmpty()) {
1482 // FIXME: add deprecated console message here.
1483 return commentValue;
1486 return m_pageAgent->resourceSourceMapURL(finalURL());
1489 InspectorCSSId InspectorStyleSheet::styleId(CSSStyleDeclaration* style) const
1491 unsigned index = ruleIndexByStyle(style);
1492 if (index != UINT_MAX)
1493 return InspectorCSSId(id(), index);
1494 return InspectorCSSId();
1497 bool InspectorStyleSheet::findRuleBySelectorRange(const SourceRange& sourceRange, InspectorCSSId* ruleId)
1499 if (!ensureParsedDataReady())
1501 for (size_t i = 0; i < ruleCount(); ++i) {
1502 RefPtrWillBeRawPtr<CSSRuleSourceData> ruleSourceData = ruleSourceDataAt(i);
1503 if (!ruleSourceData->styleSourceData)
1505 if (ruleSourceData->ruleHeaderRange.start == sourceRange.start && ruleSourceData->ruleHeaderRange.end == sourceRange.end) {
1506 *ruleId = InspectorCSSId(id(), i);
1513 const CSSRuleVector& InspectorStyleSheet::flatRules()
1519 Document* InspectorStyleSheet::ownerDocument() const
1521 return m_pageStyleSheet->ownerDocument();
1524 PassRefPtrWillBeRawPtr<CSSRuleSourceData> InspectorStyleSheet::ruleSourceDataAt(unsigned ruleIndex) const
1526 return m_parsedStyleSheet->ruleSourceDataAt(ruleIndex);
1529 unsigned InspectorStyleSheet::ruleIndexByStyle(CSSStyleDeclaration* pageStyle) const
1532 for (unsigned i = 0, size = m_flatRules.size(); i < size; ++i) {
1533 CSSStyleRule* styleRule = InspectorCSSAgent::asCSSStyleRule(m_flatRules.at(i).get());
1534 if (styleRule && styleRule->style() == pageStyle)
1540 bool InspectorStyleSheet::ensureParsedDataReady()
1542 return ensureText() && m_parsedStyleSheet->ensureSourceData();
1545 bool InspectorStyleSheet::ensureText() const
1547 if (m_parsedStyleSheet->hasText())
1551 bool success = originalStyleSheetText(&text);
1553 m_parsedStyleSheet->setText(text);
1554 // No need to clear m_flatRules here - it's empty.
1559 static void collectFlatRules(PassRefPtrWillBeRawPtr<CSSRuleList> ruleList, CSSRuleVector* result)
1564 for (unsigned i = 0, size = ruleList->length(); i < size; ++i) {
1565 CSSRule* rule = ruleList->item(i);
1567 // The result->append()'ed types should be exactly the same as in ParsedStyleSheet::flattenSourceData().
1568 switch (rule->type()) {
1569 case CSSRule::STYLE_RULE:
1570 result->append(rule);
1572 case CSSRule::IMPORT_RULE:
1573 case CSSRule::MEDIA_RULE:
1574 result->append(rule);
1579 RefPtrWillBeRawPtr<CSSRuleList> childRuleList = asCSSRuleList(rule);
1581 collectFlatRules(childRuleList, result);
1585 void InspectorStyleSheet::ensureFlatRules() const
1587 // We are fine with redoing this for empty stylesheets as this will run fast.
1588 if (m_flatRules.isEmpty())
1589 collectFlatRules(asCSSRuleList(pageStyleSheet()), &m_flatRules);
1592 bool InspectorStyleSheet::setStyleText(const InspectorCSSId& id, const String& text)
1594 CSSStyleDeclaration* style = styleForId(id);
1598 if (!ensureParsedDataReady())
1601 String patchedStyleSheetText;
1602 bool success = styleSheetTextWithChangedStyle(style, text, &patchedStyleSheetText);
1606 TrackExceptionState exceptionState;
1607 style->setCSSText(text, exceptionState);
1608 if (!exceptionState.hadException()) {
1609 updateText(patchedStyleSheetText);
1610 fireStyleSheetChanged();
1613 return !exceptionState.hadException();
1616 bool InspectorStyleSheet::styleSheetTextWithChangedStyle(CSSStyleDeclaration* style, const String& newStyleText, String* result)
1620 if (!ensureParsedDataReady())
1623 RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = ruleSourceDataAt(styleId(style).ordinal());
1624 unsigned bodyStart = sourceData->ruleBodyRange.start;
1625 unsigned bodyEnd = sourceData->ruleBodyRange.end;
1626 ASSERT(bodyStart <= bodyEnd);
1628 String text = m_parsedStyleSheet->text();
1629 ASSERT_WITH_SECURITY_IMPLICATION(bodyEnd <= text.length()); // bodyEnd is exclusive
1631 text.replace(bodyStart, bodyEnd - bodyStart, newStyleText);
1636 InspectorCSSId InspectorStyleSheet::ruleId(CSSStyleRule* rule) const
1638 return styleId(rule->style());
1641 bool InspectorStyleSheet::originalStyleSheetText(String* result) const
1643 bool success = inlineStyleSheetText(result);
1645 success = resourceStyleSheetText(result);
1649 bool InspectorStyleSheet::resourceStyleSheetText(String* result) const
1651 if (m_origin == TypeBuilder::CSS::StyleSheetOrigin::User || m_origin == TypeBuilder::CSS::StyleSheetOrigin::User_agent)
1654 if (!ownerDocument())
1657 KURL url(ParsedURLString, m_pageStyleSheet->href());
1658 if (m_pageAgent->getEditedResourceContent(url, result))
1662 bool success = m_resourceAgent->fetchResourceContent(ownerDocument(), url, result, &base64Encoded);
1663 return success && !base64Encoded;
1666 Element* InspectorStyleSheet::ownerStyleElement() const
1668 Node* ownerNode = m_pageStyleSheet->ownerNode();
1669 if (!ownerNode || !ownerNode->isElementNode())
1671 Element* ownerElement = toElement(ownerNode);
1673 if (!isHTMLStyleElement(ownerElement) && !isSVGStyleElement(ownerElement))
1675 return ownerElement;
1678 bool InspectorStyleSheet::inlineStyleSheetText(String* result) const
1680 Element* ownerElement = ownerStyleElement();
1683 *result = ownerElement->textContent();
1687 PassRefPtrWillBeRawPtr<InspectorStyleSheetForInlineStyle> InspectorStyleSheetForInlineStyle::create(const String& id, PassRefPtrWillBeRawPtr<Element> element, Listener* listener)
1689 return adoptRefWillBeNoop(new InspectorStyleSheetForInlineStyle(id, element, listener));
1692 InspectorStyleSheetForInlineStyle::InspectorStyleSheetForInlineStyle(const String& id, PassRefPtrWillBeRawPtr<Element> element, Listener* listener)
1693 : InspectorStyleSheetBase(id, listener)
1694 , m_element(element)
1695 , m_ruleSourceData(nullptr)
1696 , m_isStyleTextValid(false)
1699 m_inspectorStyle = InspectorStyle::create(InspectorCSSId(id, 0), inlineStyle(), this);
1700 m_styleText = m_element->isStyledElement() ? m_element->getAttribute("style").string() : String();
1703 void InspectorStyleSheetForInlineStyle::didModifyElementAttribute()
1705 m_isStyleTextValid = false;
1706 if (m_element->isStyledElement() && m_element->style() != m_inspectorStyle->cssStyle())
1707 m_inspectorStyle = InspectorStyle::create(InspectorCSSId(id(), 0), inlineStyle(), this);
1708 m_ruleSourceData.clear();
1711 bool InspectorStyleSheetForInlineStyle::setText(const String& text, ExceptionState& exceptionState)
1713 bool success = setStyleText(InspectorCSSId(id(), 0), text);
1715 exceptionState.throwDOMException(SyntaxError, "Style sheet text is invalid.");
1717 fireStyleSheetChanged();
1721 bool InspectorStyleSheetForInlineStyle::getText(String* result) const
1723 if (!m_isStyleTextValid) {
1724 m_styleText = elementStyleText();
1725 m_isStyleTextValid = true;
1727 *result = m_styleText;
1731 bool InspectorStyleSheetForInlineStyle::setStyleText(const InspectorCSSId& id, const String& text)
1733 CSSStyleDeclaration* style = styleForId(id);
1736 ASSERT_UNUSED(style, style == inlineStyle());
1737 TrackExceptionState exceptionState;
1740 InspectorCSSAgent::InlineStyleOverrideScope overrideScope(m_element->ownerDocument());
1741 m_element->setAttribute("style", AtomicString(text), exceptionState);
1743 if (!exceptionState.hadException()) {
1745 m_isStyleTextValid = true;
1746 m_ruleSourceData.clear();
1747 fireStyleSheetChanged();
1749 return !exceptionState.hadException();
1752 Document* InspectorStyleSheetForInlineStyle::ownerDocument() const
1754 return &m_element->document();
1757 bool InspectorStyleSheetForInlineStyle::ensureParsedDataReady()
1759 // The "style" property value can get changed indirectly, e.g. via element.style.borderWidth = "2px".
1760 const String& currentStyleText = elementStyleText();
1761 if (m_styleText != currentStyleText) {
1762 m_ruleSourceData.clear();
1763 m_styleText = currentStyleText;
1764 m_isStyleTextValid = true;
1767 if (m_ruleSourceData)
1770 m_ruleSourceData = getStyleAttributeData();
1772 bool success = !!m_ruleSourceData;
1774 m_ruleSourceData = CSSRuleSourceData::create(CSSRuleSourceData::STYLE_RULE);
1781 PassRefPtrWillBeRawPtr<InspectorStyle> InspectorStyleSheetForInlineStyle::inspectorStyleForId(const InspectorCSSId& id)
1783 ASSERT_UNUSED(id, !id.ordinal());
1784 return m_inspectorStyle;
1787 CSSStyleDeclaration* InspectorStyleSheetForInlineStyle::inlineStyle() const
1789 return m_element->style();
1792 const String& InspectorStyleSheetForInlineStyle::elementStyleText() const
1794 return m_element->getAttribute("style").string();
1797 PassRefPtrWillBeRawPtr<CSSRuleSourceData> InspectorStyleSheetForInlineStyle::getStyleAttributeData() const
1799 if (!m_element->isStyledElement())
1802 if (m_styleText.isEmpty()) {
1803 RefPtrWillBeRawPtr<CSSRuleSourceData> result = CSSRuleSourceData::create(CSSRuleSourceData::STYLE_RULE);
1804 result->ruleBodyRange.start = 0;
1805 result->ruleBodyRange.end = 0;
1806 return result.release();
1809 RefPtrWillBeRawPtr<MutableStylePropertySet> tempDeclaration = MutableStylePropertySet::create();
1810 RuleSourceDataList ruleSourceDataResult;
1811 StyleSheetHandler handler(m_styleText, &m_element->document(), m_element->document().elementSheet().contents(), &ruleSourceDataResult);
1812 BisonCSSParser(parserContextForDocument(&m_element->document())).parseDeclaration(tempDeclaration.get(), m_styleText, &handler, m_element->document().elementSheet().contents());
1813 return ruleSourceDataResult.first().release();
1816 void InspectorStyleSheetForInlineStyle::trace(Visitor* visitor)
1818 visitor->trace(m_element);
1819 visitor->trace(m_ruleSourceData);
1820 visitor->trace(m_inspectorStyle);
1821 InspectorStyleSheetBase::trace(visitor);
1824 } // namespace blink