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/CSSRuleList.h"
35 #include "core/css/CSSStyleRule.h"
36 #include "core/css/CSSStyleSheet.h"
37 #include "core/css/CSSSupportsRule.h"
38 #include "core/css/StylePropertySet.h"
39 #include "core/css/StyleRule.h"
40 #include "core/css/StyleSheetContents.h"
41 #include "core/css/parser/CSSParser.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 startProperty(unsigned) OVERRIDE;
93 virtual void endProperty(bool, bool, unsigned, CSSParserError) OVERRIDE;
94 virtual void startComment(unsigned) OVERRIDE;
95 virtual void endComment(unsigned) OVERRIDE;
97 void addNewRuleToSourceTree(PassRefPtrWillBeRawPtr<CSSRuleSourceData>);
98 PassRefPtrWillBeRawPtr<CSSRuleSourceData> popRuleData();
99 template <typename CharacterType> inline void setRuleHeaderEnd(const CharacterType*, unsigned);
100 void fixUnparsedPropertyRanges(CSSRuleSourceData*);
102 const String& m_parsedText;
103 Document* m_document;
104 StyleSheetContents* m_styleSheetContents;
105 RawPtrWillBeMember<RuleSourceDataList> m_result;
106 RuleSourceDataList m_currentRuleDataStack;
107 RefPtrWillBeMember<CSSRuleSourceData> m_currentRuleData;
108 CSSParser m_commentParser;
109 unsigned m_propertyRangeStart;
110 unsigned m_selectorRangeStart;
111 unsigned m_commentRangeStart;
114 void StyleSheetHandler::startRuleHeader(CSSRuleSourceData::Type type, unsigned offset)
116 // Pop off data for a previous invalid rule.
117 if (m_currentRuleData)
118 m_currentRuleDataStack.removeLast();
120 RefPtrWillBeRawPtr<CSSRuleSourceData> data = CSSRuleSourceData::create(type);
121 data->ruleHeaderRange.start = offset;
122 m_currentRuleData = data;
123 m_currentRuleDataStack.append(data.release());
126 template <typename CharacterType>
127 inline void StyleSheetHandler::setRuleHeaderEnd(const CharacterType* dataStart, unsigned listEndOffset)
129 while (listEndOffset > 1) {
130 if (isHTMLSpace<CharacterType>(*(dataStart + listEndOffset - 1)))
136 m_currentRuleDataStack.last()->ruleHeaderRange.end = listEndOffset;
137 if (!m_currentRuleDataStack.last()->selectorRanges.isEmpty())
138 m_currentRuleDataStack.last()->selectorRanges.last().end = listEndOffset;
141 void StyleSheetHandler::endRuleHeader(unsigned offset)
143 ASSERT(!m_currentRuleDataStack.isEmpty());
145 if (m_parsedText.is8Bit())
146 setRuleHeaderEnd<LChar>(m_parsedText.characters8(), offset);
148 setRuleHeaderEnd<UChar>(m_parsedText.characters16(), offset);
151 void StyleSheetHandler::startSelector(unsigned offset)
153 m_selectorRangeStart = offset;
156 void StyleSheetHandler::endSelector(unsigned offset)
158 ASSERT(m_currentRuleDataStack.size());
159 m_currentRuleDataStack.last()->selectorRanges.append(SourceRange(m_selectorRangeStart, offset));
160 m_selectorRangeStart = UINT_MAX;
163 void StyleSheetHandler::startRuleBody(unsigned offset)
165 m_currentRuleData.clear();
166 ASSERT(!m_currentRuleDataStack.isEmpty());
167 if (m_parsedText[offset] == '{')
168 ++offset; // Skip the rule body opening brace.
169 m_currentRuleDataStack.last()->ruleBodyRange.start = offset;
172 void StyleSheetHandler::endRuleBody(unsigned offset, bool error)
174 ASSERT(!m_currentRuleDataStack.isEmpty());
175 m_currentRuleDataStack.last()->ruleBodyRange.end = offset;
176 m_propertyRangeStart = UINT_MAX;
177 RefPtrWillBeRawPtr<CSSRuleSourceData> rule = popRuleData();
181 fixUnparsedPropertyRanges(rule.get());
182 addNewRuleToSourceTree(rule.release());
185 void StyleSheetHandler::addNewRuleToSourceTree(PassRefPtrWillBeRawPtr<CSSRuleSourceData> rule)
187 if (m_currentRuleDataStack.isEmpty())
188 m_result->append(rule);
190 m_currentRuleDataStack.last()->childRules.append(rule);
193 PassRefPtrWillBeRawPtr<CSSRuleSourceData> StyleSheetHandler::popRuleData()
195 ASSERT(!m_currentRuleDataStack.isEmpty());
196 m_currentRuleData.clear();
197 RefPtrWillBeRawPtr<CSSRuleSourceData> data = m_currentRuleDataStack.last();
198 m_currentRuleDataStack.removeLast();
199 return data.release();
202 template <typename CharacterType>
203 static inline void fixUnparsedProperties(const CharacterType* characters, CSSRuleSourceData* ruleData)
205 WillBeHeapVector<CSSPropertySourceData>& propertyData = ruleData->styleSourceData->propertyData;
206 unsigned size = propertyData.size();
210 CSSPropertySourceData* nextData = &(propertyData.at(0));
211 for (unsigned i = 0; i < size; ++i) {
212 CSSPropertySourceData* currentData = nextData;
213 nextData = i < size - 1 ? &(propertyData.at(i + 1)) : 0;
215 if (currentData->parsedOk)
217 if (currentData->range.end > 0 && characters[currentData->range.end - 1] == ';')
220 unsigned propertyEnd;
222 propertyEnd = ruleData->ruleBodyRange.end - 1;
224 propertyEnd = nextData->range.start - 1;
226 while (isHTMLSpace<CharacterType>(characters[propertyEnd]))
229 // propertyEnd points at the last property text character.
230 unsigned newPropertyEnd = propertyEnd + 1; // Exclusive of the last property text character.
231 if (currentData->range.end != newPropertyEnd) {
232 currentData->range.end = newPropertyEnd;
233 unsigned valueStart = currentData->range.start + currentData->name.length();
234 while (valueStart < propertyEnd && characters[valueStart] != ':')
236 if (valueStart < propertyEnd)
237 ++valueStart; // Shift past the ':'.
238 while (valueStart < propertyEnd && isHTMLSpace<CharacterType>(characters[valueStart]))
240 // Need to exclude the trailing ';' from the property value.
241 currentData->value = String(characters + valueStart, propertyEnd - valueStart + (characters[propertyEnd] == ';' ? 0 : 1));
246 void StyleSheetHandler::fixUnparsedPropertyRanges(CSSRuleSourceData* ruleData)
248 if (!ruleData->styleSourceData)
251 if (m_parsedText.is8Bit()) {
252 fixUnparsedProperties<LChar>(m_parsedText.characters8(), ruleData);
256 fixUnparsedProperties<UChar>(m_parsedText.characters16(), ruleData);
259 void StyleSheetHandler::startProperty(unsigned offset)
261 if (m_currentRuleDataStack.isEmpty() || !m_currentRuleDataStack.last()->styleSourceData)
263 m_propertyRangeStart = offset;
266 void StyleSheetHandler::endProperty(bool isImportant, bool isParsed, unsigned offset, CSSParserError errorType)
268 // FIXME: This is the only place CSSParserError is every read!?
269 if (errorType != NoCSSError)
270 m_propertyRangeStart = UINT_MAX;
272 if (m_propertyRangeStart == UINT_MAX || m_currentRuleDataStack.isEmpty() || !m_currentRuleDataStack.last()->styleSourceData)
275 ASSERT(offset <= m_parsedText.length());
276 if (offset < m_parsedText.length() && m_parsedText[offset] == ';') // Include semicolon into the property text.
279 const unsigned start = m_propertyRangeStart;
280 const unsigned end = offset;
282 String propertyString = m_parsedText.substring(start, end - start).stripWhiteSpace();
283 if (propertyString.endsWith(';'))
284 propertyString = propertyString.left(propertyString.length() - 1);
285 size_t colonIndex = propertyString.find(':');
286 ASSERT(colonIndex != kNotFound);
288 String name = propertyString.left(colonIndex).stripWhiteSpace();
289 String value = propertyString.substring(colonIndex + 1, propertyString.length()).stripWhiteSpace();
290 m_currentRuleDataStack.last()->styleSourceData->propertyData.append(
291 CSSPropertySourceData(name, value, isImportant, false, isParsed, SourceRange(start, end)));
292 m_propertyRangeStart = UINT_MAX;
295 void StyleSheetHandler::startComment(unsigned offset)
297 ASSERT(m_commentRangeStart == UINT_MAX);
298 m_commentRangeStart = offset;
301 void StyleSheetHandler::endComment(unsigned offset)
303 ASSERT(offset <= m_parsedText.length());
305 unsigned startOffset = m_commentRangeStart;
306 m_commentRangeStart = UINT_MAX;
307 if (m_propertyRangeStart != UINT_MAX) {
308 ASSERT(startOffset >= m_propertyRangeStart);
309 // startProperty() is called automatically at the start of a style declaration.
310 // Check if no text has been scanned yet, otherwise the comment is inside a property.
311 if (!m_parsedText.substring(m_propertyRangeStart, startOffset).stripWhiteSpace().isEmpty())
313 m_propertyRangeStart = UINT_MAX;
315 if (m_currentRuleDataStack.isEmpty() || !m_currentRuleDataStack.last()->ruleHeaderRange.end || !m_currentRuleDataStack.last()->styleSourceData)
318 // The lexer is not inside a property AND it is scanning a declaration-aware rule body.
319 String commentText = m_parsedText.substring(startOffset, offset - startOffset);
321 ASSERT(commentText.startsWith("/*"));
322 commentText = commentText.substring(2);
324 // Require well-formed comments.
325 if (!commentText.endsWith("*/"))
327 commentText = commentText.substring(0, commentText.length() - 2).stripWhiteSpace();
328 if (commentText.isEmpty())
331 // FIXME: Use the actual rule type rather than STYLE_RULE?
332 RuleSourceDataList sourceData;
334 // FIXME: Use another subclass of CSSParserObserver and assert that
335 // no comments are encountered (will not need m_document and m_styleSheetContents).
336 StyleSheetHandler handler(commentText, m_document, m_styleSheetContents, &sourceData);
337 RefPtrWillBeRawPtr<MutableStylePropertySet> tempMutableStyle = MutableStylePropertySet::create();
338 m_commentParser.parseDeclaration(tempMutableStyle.get(), commentText, &handler, m_styleSheetContents);
339 WillBeHeapVector<CSSPropertySourceData>& commentPropertyData = sourceData.first()->styleSourceData->propertyData;
340 if (commentPropertyData.size() != 1)
342 CSSPropertySourceData& propertyData = commentPropertyData.at(0);
343 if (propertyData.range.length() != commentText.length())
346 m_currentRuleDataStack.last()->styleSourceData->propertyData.append(
347 CSSPropertySourceData(propertyData.name, propertyData.value, false, true, true, SourceRange(startOffset, offset)));
352 class ParsedStyleSheet {
353 WTF_MAKE_FAST_ALLOCATED;
355 ParsedStyleSheet(CSSStyleSheet* pageStyleSheet);
357 const String& text() const { ASSERT(m_hasText); return m_text; }
358 void setText(const String&);
359 bool hasText() const { return m_hasText; }
360 bool ensureSourceData();
361 bool hasSourceData() const { return m_sourceData; }
362 PassRefPtrWillBeRawPtr<blink::CSSRuleSourceData> ruleSourceDataAt(unsigned) const;
363 unsigned ruleCount() { return m_sourceData->size(); }
366 void flattenSourceData(RuleSourceDataList*);
367 void setSourceData(PassOwnPtrWillBeRawPtr<RuleSourceDataList>);
371 OwnPtrWillBePersistent<RuleSourceDataList> m_sourceData;
372 RefPtrWillBePersistent<CSSStyleSheet> m_pageStyleSheet;
375 ParsedStyleSheet::ParsedStyleSheet(CSSStyleSheet* pageStyleSheet)
377 , m_pageStyleSheet(pageStyleSheet)
381 void ParsedStyleSheet::setText(const String& text)
385 setSourceData(nullptr);
388 void ParsedStyleSheet::flattenSourceData(RuleSourceDataList* dataList)
390 for (size_t i = 0; i < dataList->size(); ++i) {
391 RefPtrWillBeMember<CSSRuleSourceData>& data = dataList->at(i);
393 // The m_sourceData->append()'ed types should be exactly the same as in collectFlatRules().
394 switch (data->type) {
395 case CSSRuleSourceData::STYLE_RULE:
396 case CSSRuleSourceData::IMPORT_RULE:
397 case CSSRuleSourceData::CHARSET_RULE:
398 case CSSRuleSourceData::PAGE_RULE:
399 case CSSRuleSourceData::FONT_FACE_RULE:
400 case CSSRuleSourceData::VIEWPORT_RULE:
401 case CSSRuleSourceData::KEYFRAMES_RULE:
402 m_sourceData->append(data);
404 case CSSRuleSourceData::MEDIA_RULE:
405 case CSSRuleSourceData::SUPPORTS_RULE:
406 m_sourceData->append(data);
407 flattenSourceData(&data->childRules);
415 bool ParsedStyleSheet::ensureSourceData()
423 RefPtrWillBeRawPtr<StyleSheetContents> newStyleSheet = StyleSheetContents::create(strictCSSParserContext());
424 OwnPtrWillBeRawPtr<RuleSourceDataList> result = adoptPtrWillBeNoop(new RuleSourceDataList());
425 StyleSheetHandler handler(text(), m_pageStyleSheet->ownerDocument(), newStyleSheet.get(), result.get());
426 CSSParser::parseSheet(parserContextForDocument(m_pageStyleSheet->ownerDocument()), newStyleSheet.get(), text(), TextPosition::minimumPosition(), &handler);
427 setSourceData(result.release());
428 return hasSourceData();
431 void ParsedStyleSheet::setSourceData(PassOwnPtrWillBeRawPtr<RuleSourceDataList> sourceData)
434 m_sourceData.clear();
437 m_sourceData = adoptPtrWillBeNoop(new RuleSourceDataList());
439 // FIXME: This is a temporary solution to retain the original flat sourceData structure
440 // containing only style rules, even though BisonCSSParser now provides the full rule source data tree.
441 // Normally, we should just assign m_sourceData = sourceData;
442 flattenSourceData(sourceData.get());
445 PassRefPtrWillBeRawPtr<blink::CSSRuleSourceData> ParsedStyleSheet::ruleSourceDataAt(unsigned index) const
447 if (!hasSourceData() || index >= m_sourceData->size())
450 return m_sourceData->at(index);
455 enum MediaListSource {
456 MediaListSourceLinkedSheet,
457 MediaListSourceInlineSheet,
458 MediaListSourceMediaRule,
459 MediaListSourceImportRule
462 static PassRefPtr<TypeBuilder::CSS::SourceRange> buildSourceRangeObject(const SourceRange& range, Vector<unsigned>* lineEndings)
466 TextPosition start = TextPosition::fromOffsetAndLineEndings(range.start, *lineEndings);
467 TextPosition end = TextPosition::fromOffsetAndLineEndings(range.end, *lineEndings);
469 RefPtr<TypeBuilder::CSS::SourceRange> result = TypeBuilder::CSS::SourceRange::create()
470 .setStartLine(start.m_line.zeroBasedInt())
471 .setStartColumn(start.m_column.zeroBasedInt())
472 .setEndLine(end.m_line.zeroBasedInt())
473 .setEndColumn(end.m_column.zeroBasedInt());
474 return result.release();
477 static PassRefPtrWillBeRawPtr<CSSRuleList> asCSSRuleList(CSSRule* rule)
482 if (rule->type() == CSSRule::MEDIA_RULE)
483 return toCSSMediaRule(rule)->cssRules();
485 if (rule->type() == CSSRule::SUPPORTS_RULE)
486 return toCSSSupportsRule(rule)->cssRules();
491 PassRefPtrWillBeRawPtr<InspectorStyle> InspectorStyle::create(const InspectorCSSId& styleId, PassRefPtrWillBeRawPtr<CSSStyleDeclaration> style, InspectorStyleSheetBase* parentStyleSheet)
493 return adoptRefWillBeNoop(new InspectorStyle(styleId, style, parentStyleSheet));
496 InspectorStyle::InspectorStyle(const InspectorCSSId& styleId, PassRefPtrWillBeRawPtr<CSSStyleDeclaration> style, InspectorStyleSheetBase* parentStyleSheet)
499 , m_parentStyleSheet(parentStyleSheet)
500 , m_formatAcquired(false)
505 PassRefPtr<TypeBuilder::CSS::CSSStyle> InspectorStyle::buildObjectForStyle() const
507 RefPtr<TypeBuilder::CSS::CSSStyle> result = styleWithProperties();
508 if (!m_styleId.isEmpty())
509 result->setStyleSheetId(m_styleId.styleSheetId());
511 RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = extractSourceData();
513 result->setRange(buildSourceRangeObject(sourceData->ruleBodyRange, m_parentStyleSheet->lineEndings().get()));
515 return result.release();
518 PassRefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSComputedStyleProperty> > InspectorStyle::buildArrayForComputedStyle() const
520 RefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSComputedStyleProperty> > result = TypeBuilder::Array<TypeBuilder::CSS::CSSComputedStyleProperty>::create();
521 WillBeHeapVector<InspectorStyleProperty> properties;
522 populateAllProperties(properties);
524 for (WillBeHeapVector<InspectorStyleProperty>::iterator it = properties.begin(), itEnd = properties.end(); it != itEnd; ++it) {
525 const CSSPropertySourceData& propertyEntry = it->sourceData;
526 RefPtr<TypeBuilder::CSS::CSSComputedStyleProperty> entry = TypeBuilder::CSS::CSSComputedStyleProperty::create()
527 .setName(propertyEntry.name)
528 .setValue(propertyEntry.value);
529 result->addItem(entry);
532 return result.release();
535 bool InspectorStyle::verifyPropertyText(const String& propertyText, bool canOmitSemicolon)
537 DEFINE_STATIC_LOCAL(String, bogusPropertyName, ("-webkit-boguz-propertee"));
538 RefPtrWillBeRawPtr<MutableStylePropertySet> tempMutableStyle = MutableStylePropertySet::create();
539 RuleSourceDataList sourceData;
540 RefPtrWillBeRawPtr<StyleSheetContents> styleSheetContents = StyleSheetContents::create(strictCSSParserContext());
541 String declarationText = propertyText + (canOmitSemicolon ? ";" : " ") + bogusPropertyName + ": none";
542 StyleSheetHandler handler(declarationText, ownerDocument(), styleSheetContents.get(), &sourceData);
543 CSSParser(parserContextForDocument(ownerDocument())).parseDeclaration(tempMutableStyle.get(), declarationText, &handler, styleSheetContents.get());
544 WillBeHeapVector<CSSPropertySourceData>& propertyData = sourceData.first()->styleSourceData->propertyData;
545 unsigned propertyCount = propertyData.size();
547 // At least one property + the bogus property added just above should be present.
548 if (propertyCount < 2)
551 // Check for the proper propertyText termination (the parser could at least restore to the PROPERTY_NAME state).
552 if (propertyData.at(propertyCount - 1).name != bogusPropertyName)
558 bool InspectorStyle::setPropertyText(unsigned index, const String& propertyText, bool overwrite, ExceptionState& exceptionState)
560 ASSERT(m_parentStyleSheet);
562 if (!m_parentStyleSheet->ensureParsedDataReady()) {
563 exceptionState.throwDOMException(NotFoundError, "The parent style sheet's data hasn't been processed.");
567 if (!propertyText.stripWhiteSpace().isEmpty()) {
568 if (!verifyPropertyText(propertyText, false) && !verifyPropertyText(propertyText, true)) {
569 exceptionState.throwDOMException(SyntaxError, "The property '" + propertyText + "' could not be set.");
574 RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = extractSourceData();
576 exceptionState.throwDOMException(NotFoundError, "The property '" + propertyText + "' could not be set.");
581 bool success = styleText(&text);
583 exceptionState.throwDOMException(NotFoundError, "The property '" + propertyText + "' could not be set.");
587 WillBeHeapVector<InspectorStyleProperty> allProperties;
588 populateAllProperties(allProperties);
590 InspectorStyleTextEditor editor(&allProperties, text, sourceData->ruleBodyRange, newLineAndWhitespaceDelimiters());
592 if (index >= allProperties.size()) {
593 exceptionState.throwDOMException(IndexSizeError, "The index provided (" + String::number(index) + ") is greater than or equal to the maximum bound (" + String::number(allProperties.size()) + ").");
596 editor.replaceProperty(index, propertyText);
598 editor.insertProperty(index, propertyText);
601 return m_parentStyleSheet->setStyleText(m_styleId, editor.styleText());
604 bool InspectorStyle::styleText(String* result) const
606 RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = extractSourceData();
610 return textForRange(sourceData->ruleBodyRange, result);
613 bool InspectorStyle::textForRange(const SourceRange& range, String* result) const
615 String styleSheetText;
616 bool success = m_parentStyleSheet->getText(&styleSheetText);
620 ASSERT(0 <= range.start);
621 ASSERT(range.start <= range.end);
622 ASSERT(range.end <= styleSheetText.length());
623 *result = styleSheetText.substring(range.start, range.end - range.start);
627 void InspectorStyle::populateAllProperties(WillBeHeapVector<InspectorStyleProperty>& result) const
629 HashSet<String> sourcePropertyNames;
631 RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = extractSourceData();
632 if (sourceData && sourceData->styleSourceData) {
633 WillBeHeapVector<CSSPropertySourceData>& sourcePropertyData = sourceData->styleSourceData->propertyData;
634 for (WillBeHeapVector<CSSPropertySourceData>::const_iterator it = sourcePropertyData.begin(); it != sourcePropertyData.end(); ++it) {
635 InspectorStyleProperty p(*it, true);
636 bool isPropertyTextKnown = textForRange(p.sourceData.range, &p.rawText);
637 ASSERT_UNUSED(isPropertyTextKnown, isPropertyTextKnown);
639 sourcePropertyNames.add(it->name.lower());
643 for (int i = 0, size = m_style->length(); i < size; ++i) {
644 String name = m_style->item(i);
645 if (!sourcePropertyNames.add(name.lower()).isNewEntry)
648 String value = m_style->getPropertyValue(name);
651 result.append(InspectorStyleProperty(CSSPropertySourceData(name, value, !m_style->getPropertyPriority(name).isEmpty(), false, true, SourceRange()), false));
655 PassRefPtr<TypeBuilder::CSS::CSSStyle> InspectorStyle::styleWithProperties() const
657 RefPtr<Array<TypeBuilder::CSS::CSSProperty> > propertiesObject = Array<TypeBuilder::CSS::CSSProperty>::create();
658 RefPtr<Array<TypeBuilder::CSS::ShorthandEntry> > shorthandEntries = Array<TypeBuilder::CSS::ShorthandEntry>::create();
659 HashSet<String> foundShorthands;
660 OwnPtr<Vector<unsigned> > lineEndings(m_parentStyleSheet ? m_parentStyleSheet->lineEndings() : PassOwnPtr<Vector<unsigned> >());
662 WillBeHeapVector<InspectorStyleProperty> properties;
663 populateAllProperties(properties);
665 for (WillBeHeapVector<InspectorStyleProperty>::iterator it = properties.begin(), itEnd = properties.end(); it != itEnd; ++it) {
666 const CSSPropertySourceData& propertyEntry = it->sourceData;
667 const String& name = propertyEntry.name;
669 RefPtr<TypeBuilder::CSS::CSSProperty> property = TypeBuilder::CSS::CSSProperty::create()
671 .setValue(propertyEntry.value);
672 propertiesObject->addItem(property);
674 // Default "parsedOk" == true.
675 if (!propertyEntry.parsedOk)
676 property->setParsedOk(false);
677 if (it->hasRawText())
678 property->setText(it->rawText);
680 if (propertyEntry.important)
681 property->setImportant(true);
683 property->setRange(buildSourceRangeObject(propertyEntry.range, lineEndings.get()));
684 if (!propertyEntry.disabled)
685 property->setImplicit(false);
686 property->setDisabled(propertyEntry.disabled);
687 } else if (!propertyEntry.disabled) {
688 bool implicit = m_style->isPropertyImplicit(name);
689 // Default "implicit" == false.
691 property->setImplicit(true);
693 String shorthand = m_style->getPropertyShorthand(name);
694 if (!shorthand.isEmpty()) {
695 if (foundShorthands.add(shorthand).isNewEntry) {
696 RefPtr<TypeBuilder::CSS::ShorthandEntry> entry = TypeBuilder::CSS::ShorthandEntry::create()
698 .setValue(shorthandValue(shorthand));
699 shorthandEntries->addItem(entry);
705 RefPtr<TypeBuilder::CSS::CSSStyle> result = TypeBuilder::CSS::CSSStyle::create()
706 .setCssProperties(propertiesObject)
707 .setShorthandEntries(shorthandEntries);
708 return result.release();
711 PassRefPtrWillBeRawPtr<CSSRuleSourceData> InspectorStyle::extractSourceData() const
713 if (!m_parentStyleSheet || !m_parentStyleSheet->ensureParsedDataReady())
715 return m_parentStyleSheet->ruleSourceDataAt(m_styleId.ordinal());
718 String InspectorStyle::shorthandValue(const String& shorthandProperty) const
720 String value = m_style->getPropertyValue(shorthandProperty);
721 if (value.isEmpty()) {
722 StringBuilder builder;
724 for (unsigned i = 0; i < m_style->length(); ++i) {
725 String individualProperty = m_style->item(i);
726 if (m_style->getPropertyShorthand(individualProperty) != shorthandProperty)
728 if (m_style->isPropertyImplicit(individualProperty))
730 String individualValue = m_style->getPropertyValue(individualProperty);
731 if (individualValue == "initial")
733 if (!builder.isEmpty())
735 builder.append(individualValue);
738 return builder.toString();
743 NewLineAndWhitespace& InspectorStyle::newLineAndWhitespaceDelimiters() const
745 DEFINE_STATIC_LOCAL(String, defaultPrefix, (" "));
747 if (m_formatAcquired)
750 RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = extractSourceData();
751 WillBeHeapVector<CSSPropertySourceData>* sourcePropertyData = sourceData ? &(sourceData->styleSourceData->propertyData) : 0;
752 int propertyCount = sourcePropertyData ? sourcePropertyData->size() : 0;
753 if (!propertyCount) {
754 m_format.first = "\n";
755 m_format.second = defaultPrefix;
756 return m_format; // Do not remember the default formatting and attempt to acquire it later.
759 String styleSheetText;
760 bool success = m_parentStyleSheet->getText(&styleSheetText);
761 ASSERT_UNUSED(success, success);
763 m_formatAcquired = true;
765 String candidatePrefix = defaultPrefix;
766 StringBuilder formatLineFeed;
767 StringBuilder prefix;
768 int scanStart = sourceData->ruleBodyRange.start;
769 int propertyIndex = 0;
770 bool isFullPrefixScanned = false;
771 bool lineFeedTerminated = false;
772 while (propertyIndex < propertyCount) {
773 const blink::CSSPropertySourceData& currentProperty = sourcePropertyData->at(propertyIndex++);
775 bool processNextProperty = false;
776 int scanEnd = currentProperty.range.start;
777 for (int i = scanStart; i < scanEnd; ++i) {
778 UChar ch = styleSheetText[i];
779 bool isLineFeed = isHTMLLineBreak(ch);
781 if (!lineFeedTerminated)
782 formatLineFeed.append(ch);
784 } else if (isHTMLSpace<UChar>(ch))
787 candidatePrefix = prefix.toString();
789 scanStart = currentProperty.range.end;
791 processNextProperty = true;
794 if (!isLineFeed && formatLineFeed.length())
795 lineFeedTerminated = true;
797 if (!processNextProperty) {
798 isFullPrefixScanned = true;
803 m_format.first = formatLineFeed.toString();
804 m_format.second = isFullPrefixScanned ? prefix.toString() : candidatePrefix;
808 Document* InspectorStyle::ownerDocument() const
810 return m_parentStyleSheet->ownerDocument();
813 void InspectorStyle::trace(Visitor* visitor)
815 visitor->trace(m_style);
816 visitor->trace(m_parentStyleSheet);
819 InspectorStyleSheetBase::InspectorStyleSheetBase(const String& id, Listener* listener)
821 , m_listener(listener)
825 bool InspectorStyleSheetBase::setPropertyText(const InspectorCSSId& id, unsigned propertyIndex, const String& text, bool overwrite, ExceptionState& exceptionState)
827 RefPtrWillBeRawPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id);
828 if (!inspectorStyle) {
829 exceptionState.throwDOMException(NotFoundError, "No property could be found for the given ID.");
832 return inspectorStyle->setPropertyText(propertyIndex, text, overwrite, exceptionState);
835 bool InspectorStyleSheetBase::getStyleText(const InspectorCSSId& id, String* text)
837 RefPtrWillBeRawPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id);
840 return inspectorStyle->styleText(text);
843 void InspectorStyleSheetBase::fireStyleSheetChanged()
846 listener()->styleSheetChanged(this);
849 PassRefPtr<TypeBuilder::CSS::CSSStyle> InspectorStyleSheetBase::buildObjectForStyle(CSSStyleDeclaration* style)
851 RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = nullptr;
852 if (ensureParsedDataReady())
853 sourceData = ruleSourceDataAt(styleId(style).ordinal());
855 InspectorCSSId id = styleId(style);
857 // Any rule coming from User Agent and not from DefaultStyleSheet will not have id.
858 // See InspectorCSSAgent::buildObjectForRule for details.
859 RefPtrWillBeRawPtr<InspectorStyle> inspectorStyle = InspectorStyle::create(id, style, this);
860 return inspectorStyle->buildObjectForStyle();
862 RefPtrWillBeRawPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id);
863 RefPtr<TypeBuilder::CSS::CSSStyle> result = inspectorStyle->buildObjectForStyle();
865 // Style text cannot be retrieved without stylesheet, so set cssText here.
868 bool success = getText(&sheetText);
870 const SourceRange& bodyRange = sourceData->ruleBodyRange;
871 result->setCssText(sheetText.substring(bodyRange.start, bodyRange.end - bodyRange.start));
875 return result.release();
878 PassOwnPtr<Vector<unsigned> > InspectorStyleSheetBase::lineEndings()
882 return PassOwnPtr<Vector<unsigned> >();
883 return WTF::lineEndings(text);
886 bool InspectorStyleSheetBase::lineNumberAndColumnToOffset(unsigned lineNumber, unsigned columnNumber, unsigned* offset)
888 OwnPtr<Vector<unsigned> > endings = lineEndings();
889 if (lineNumber >= endings->size())
891 unsigned charactersInLine = lineNumber > 0 ? endings->at(lineNumber) - endings->at(lineNumber - 1) - 1 : endings->at(0);
892 if (columnNumber > charactersInLine)
894 TextPosition position(OrdinalNumber::fromZeroBasedInt(lineNumber), OrdinalNumber::fromZeroBasedInt(columnNumber));
895 *offset = position.toOffset(*endings).zeroBasedInt();
899 bool InspectorStyleSheetBase::findPropertyByRange(const SourceRange& sourceRange, InspectorCSSId* ruleId, unsigned* propertyIndex, bool* overwrite)
901 if (!ensureParsedDataReady())
903 for (size_t i = 0; i < ruleCount(); ++i) {
904 RefPtrWillBeRawPtr<CSSRuleSourceData> ruleSourceData = ruleSourceDataAt(i);
905 RefPtrWillBeRawPtr<CSSStyleSourceData> styleSourceData = ruleSourceData->styleSourceData;
906 if (!styleSourceData)
908 if (ruleSourceData->ruleBodyRange.end < sourceRange.start || sourceRange.end < ruleSourceData->ruleBodyRange.start)
910 WillBeHeapVector<CSSPropertySourceData>& propertyData = styleSourceData->propertyData;
911 for (size_t j = 0; j < propertyData.size(); ++j) {
912 CSSPropertySourceData& property = propertyData.at(j);
913 unsigned styleStart = ruleSourceData->ruleBodyRange.start;
914 if (sourceRange.length() && property.range.start == sourceRange.start && property.range.end == sourceRange.end) {
915 *ruleId = InspectorCSSId(id(), i);
920 if (!sourceRange.length() && styleStart <= sourceRange.start && sourceRange.start <= property.range.start) {
921 *ruleId = InspectorCSSId(id(), i);
927 if (!sourceRange.length() && ruleSourceData->ruleBodyRange.start <= sourceRange.start && sourceRange.start <= ruleSourceData->ruleBodyRange.end) {
928 *ruleId = InspectorCSSId(id(), i);
929 *propertyIndex = propertyData.size();
937 PassRefPtrWillBeRawPtr<InspectorStyleSheet> InspectorStyleSheet::create(InspectorPageAgent* pageAgent, InspectorResourceAgent* resourceAgent, const String& id, PassRefPtrWillBeRawPtr<CSSStyleSheet> pageStyleSheet, TypeBuilder::CSS::StyleSheetOrigin::Enum origin, const String& documentURL, Listener* listener)
939 return adoptRefWillBeNoop(new InspectorStyleSheet(pageAgent, resourceAgent, id, pageStyleSheet, origin, documentURL, listener));
942 InspectorStyleSheet::InspectorStyleSheet(InspectorPageAgent* pageAgent, InspectorResourceAgent* resourceAgent, const String& id, PassRefPtrWillBeRawPtr<CSSStyleSheet> pageStyleSheet, TypeBuilder::CSS::StyleSheetOrigin::Enum origin, const String& documentURL, Listener* listener)
943 : InspectorStyleSheetBase(id, listener)
944 , m_pageAgent(pageAgent)
945 , m_resourceAgent(resourceAgent)
946 , m_pageStyleSheet(pageStyleSheet)
948 , m_documentURL(documentURL)
950 m_parsedStyleSheet = adoptPtr(new ParsedStyleSheet(m_pageStyleSheet.get()));
953 InspectorStyleSheet::~InspectorStyleSheet()
957 void InspectorStyleSheet::trace(Visitor* visitor)
959 visitor->trace(m_pageAgent);
960 visitor->trace(m_resourceAgent);
961 visitor->trace(m_pageStyleSheet);
962 visitor->trace(m_flatRules);
963 InspectorStyleSheetBase::trace(visitor);
966 static String styleSheetURL(CSSStyleSheet* pageStyleSheet)
968 if (pageStyleSheet && !pageStyleSheet->contents()->baseURL().isEmpty())
969 return pageStyleSheet->contents()->baseURL().string();
970 return emptyString();
973 String InspectorStyleSheet::finalURL() const
975 String url = styleSheetURL(m_pageStyleSheet.get());
976 return url.isEmpty() ? m_documentURL : url;
979 bool InspectorStyleSheet::setText(const String& text, ExceptionState& exceptionState)
985 listener()->willReparseStyleSheet();
988 // Have a separate scope for clearRules() (bug 95324).
989 CSSStyleSheet::RuleMutationScope mutationScope(m_pageStyleSheet.get());
990 m_pageStyleSheet->contents()->clearRules();
991 m_pageStyleSheet->clearChildRuleCSSOMWrappers();
994 CSSStyleSheet::RuleMutationScope mutationScope(m_pageStyleSheet.get());
995 m_pageStyleSheet->contents()->parseString(text);
999 listener()->didReparseStyleSheet();
1000 fireStyleSheetChanged();
1001 m_pageStyleSheet->ownerDocument()->styleResolverChanged(FullStyleUpdate);
1005 String InspectorStyleSheet::ruleSelector(const InspectorCSSId& id, ExceptionState& exceptionState)
1007 CSSStyleRule* rule = ruleForId(id);
1009 exceptionState.throwDOMException(NotFoundError, "No rule was found for the given ID.");
1012 return rule->selectorText();
1015 bool InspectorStyleSheet::setRuleSelector(const InspectorCSSId& id, const String& selector, ExceptionState& exceptionState)
1017 CSSStyleRule* rule = ruleForId(id);
1019 exceptionState.throwDOMException(NotFoundError, "No rule was found for the given ID.");
1022 CSSStyleSheet* styleSheet = rule->parentStyleSheet();
1023 if (!styleSheet || !ensureParsedDataReady()) {
1024 exceptionState.throwDOMException(NotFoundError, "No stylesheet could be found in which to set the selector.");
1028 rule->setSelectorText(selector);
1029 RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = ruleSourceDataAt(id.ordinal());
1031 exceptionState.throwDOMException(NotFoundError, "The selector '" + selector + "' could not be set.");
1035 String sheetText = m_parsedStyleSheet->text();
1036 sheetText.replace(sourceData->ruleHeaderRange.start, sourceData->ruleHeaderRange.length(), selector);
1037 updateText(sheetText);
1038 fireStyleSheetChanged();
1042 unsigned InspectorStyleSheet::ruleIndexBySourceRange(const CSSMediaRule* parentMediaRule, const SourceRange& sourceRange)
1045 for (size_t i = 0; i < m_flatRules.size(); ++i) {
1046 RefPtrWillBeRawPtr<CSSRule> rule = m_flatRules.at(i);
1047 if (rule->parentRule() != parentMediaRule)
1049 RefPtrWillBeRawPtr<CSSRuleSourceData> ruleSourceData = m_parsedStyleSheet->ruleSourceDataAt(i);
1050 if (ruleSourceData->ruleBodyRange.end < sourceRange.start)
1056 CSSStyleRule* InspectorStyleSheet::insertCSSOMRuleInStyleSheet(const SourceRange& sourceRange, const String& ruleText, ExceptionState& exceptionState)
1058 unsigned index = ruleIndexBySourceRange(nullptr, sourceRange);
1059 m_pageStyleSheet->insertRule(ruleText, index, exceptionState);
1060 CSSRule* rule = m_pageStyleSheet->item(index);
1061 CSSStyleRule* styleRule = InspectorCSSAgent::asCSSStyleRule(rule);
1063 m_pageStyleSheet->deleteRule(index, ASSERT_NO_EXCEPTION);
1064 exceptionState.throwDOMException(SyntaxError, "The rule '" + ruleText + "' could not be added in style sheet.");
1070 CSSStyleRule* InspectorStyleSheet::insertCSSOMRuleInMediaRule(CSSMediaRule* mediaRule, const SourceRange& sourceRange, const String& ruleText, ExceptionState& exceptionState)
1072 unsigned index = ruleIndexBySourceRange(mediaRule, sourceRange);
1073 mediaRule->insertRule(ruleText, index, exceptionState);
1074 CSSRule* rule = mediaRule->item(index);
1075 CSSStyleRule* styleRule = InspectorCSSAgent::asCSSStyleRule(rule);
1077 mediaRule->deleteRule(index, ASSERT_NO_EXCEPTION);
1078 exceptionState.throwDOMException(SyntaxError, "The rule '" + ruleText + "' could not be added in media rule.");
1084 CSSStyleRule* InspectorStyleSheet::insertCSSOMRuleBySourceRange(const SourceRange& sourceRange, const String& ruleText, ExceptionState& exceptionState)
1086 int containingRuleIndex = -1;
1087 unsigned containingRuleLength = 0;
1088 for (size_t i = 0; i < m_parsedStyleSheet->ruleCount(); ++i) {
1089 RefPtrWillBeRawPtr<CSSRuleSourceData> ruleSourceData = m_parsedStyleSheet->ruleSourceDataAt(i);
1090 if (ruleSourceData->ruleHeaderRange.start < sourceRange.start && sourceRange.start < ruleSourceData->ruleBodyRange.start) {
1091 exceptionState.throwDOMException(NotFoundError, "Cannot insert rule inside rule selector.");
1094 if (sourceRange.start < ruleSourceData->ruleBodyRange.start || ruleSourceData->ruleBodyRange.end < sourceRange.start)
1096 if (containingRuleIndex == -1 || containingRuleLength > ruleSourceData->ruleBodyRange.length()) {
1097 containingRuleIndex = i;
1098 containingRuleLength = ruleSourceData->ruleBodyRange.length();
1101 if (containingRuleIndex == -1)
1102 return insertCSSOMRuleInStyleSheet(sourceRange, ruleText, exceptionState);
1103 RefPtrWillBeRawPtr<CSSRule> rule = m_flatRules.at(containingRuleIndex);
1104 if (rule->type() != CSSRule::MEDIA_RULE) {
1105 exceptionState.throwDOMException(NotFoundError, "Cannot insert rule in non-media rule.");
1108 return insertCSSOMRuleInMediaRule(toCSSMediaRule(rule.get()), sourceRange, ruleText, exceptionState);
1111 bool InspectorStyleSheet::verifyRuleText(const String& ruleText)
1113 DEFINE_STATIC_LOCAL(String, bogusPropertyName, ("-webkit-boguz-propertee"));
1114 RuleSourceDataList sourceData;
1115 RefPtrWillBeRawPtr<StyleSheetContents> styleSheetContents = StyleSheetContents::create(strictCSSParserContext());
1116 String text = ruleText + " div { " + bogusPropertyName + ": none; }";
1117 StyleSheetHandler handler(text, ownerDocument(), styleSheetContents.get(), &sourceData);
1118 CSSParser::parseSheet(parserContextForDocument(ownerDocument()), styleSheetContents.get(), text, TextPosition::minimumPosition(), &handler);
1119 unsigned ruleCount = sourceData.size();
1121 // Exactly two rules should be parsed.
1125 // Added rule must be style rule.
1126 if (!sourceData.at(0)->styleSourceData)
1129 WillBeHeapVector<CSSPropertySourceData>& propertyData = sourceData.at(1)->styleSourceData->propertyData;
1130 unsigned propertyCount = propertyData.size();
1132 // Exactly one property should be in rule.
1133 if (propertyCount != 1)
1136 // Check for the property name.
1137 if (propertyData.at(0).name != bogusPropertyName)
1143 CSSStyleRule* InspectorStyleSheet::addRule(const String& ruleText, const SourceRange& location, ExceptionState& exceptionState)
1145 if (!ensureParsedDataReady()) {
1146 exceptionState.throwDOMException(NotFoundError, "Cannot parse style sheet.");
1150 if (location.start != location.end) {
1151 exceptionState.throwDOMException(NotFoundError, "Source range must be collapsed.");
1155 if (!verifyRuleText(ruleText)) {
1156 exceptionState.throwDOMException(SyntaxError, "Rule text is not valid.");
1161 bool success = getText(&text);
1163 exceptionState.throwDOMException(NotFoundError, "The rule '" + ruleText + "' could not be added.");
1168 CSSStyleRule* styleRule = insertCSSOMRuleBySourceRange(location, ruleText, exceptionState);
1169 if (exceptionState.hadException())
1172 text.insert(ruleText, location.start);
1174 m_parsedStyleSheet->setText(text);
1175 m_flatRules.clear();
1177 fireStyleSheetChanged();
1181 bool InspectorStyleSheet::deleteRule(const InspectorCSSId& id, const String& oldText, ExceptionState& exceptionState)
1183 RefPtrWillBeRawPtr<CSSStyleRule> rule = ruleForId(id);
1185 exceptionState.throwDOMException(NotFoundError, "No style rule could be found for the provided ID.");
1188 CSSStyleSheet* styleSheet = rule->parentStyleSheet();
1189 if (!styleSheet || !ensureParsedDataReady()) {
1190 exceptionState.throwDOMException(NotFoundError, "No parent stylesheet could be found.");
1194 RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = ruleSourceDataAt(id.ordinal());
1196 exceptionState.throwDOMException(NotFoundError, "No style rule could be found for the provided ID.");
1200 CSSRule* parentRule = rule->parentRule();
1202 if (parentRule->type() != CSSRule::MEDIA_RULE) {
1203 exceptionState.throwDOMException(NotFoundError, "Cannot remove rule from non-media rule.");
1206 CSSMediaRule* parentMediaRule = toCSSMediaRule(parentRule);
1208 while (index < parentMediaRule->length() && parentMediaRule->item(index) != rule)
1210 ASSERT(index < parentMediaRule->length());
1211 parentMediaRule->deleteRule(index, exceptionState);
1214 while (index < styleSheet->length() && styleSheet->item(index) != rule)
1216 ASSERT(index < styleSheet->length());
1217 styleSheet->deleteRule(index, exceptionState);
1219 // |rule| MAY NOT be addressed after this line!
1221 if (exceptionState.hadException())
1224 m_parsedStyleSheet->setText(oldText);
1225 m_flatRules.clear();
1226 fireStyleSheetChanged();
1230 void InspectorStyleSheet::updateText(const String& newText)
1232 Element* element = ownerStyleElement();
1234 m_pageAgent->addEditedResourceContent(finalURL(), newText);
1235 m_parsedStyleSheet->setText(newText);
1239 CSSStyleRule* InspectorStyleSheet::ruleForId(const InspectorCSSId& id) const
1241 ASSERT(!id.isEmpty());
1243 return InspectorCSSAgent::asCSSStyleRule(id.ordinal() >= m_flatRules.size() ? 0 : m_flatRules.at(id.ordinal()).get());
1246 PassRefPtr<TypeBuilder::CSS::CSSStyleSheetHeader> InspectorStyleSheet::buildObjectForStyleSheetInfo() const
1248 CSSStyleSheet* styleSheet = pageStyleSheet();
1252 Document* document = styleSheet->ownerDocument();
1253 LocalFrame* frame = document ? document->frame() : 0;
1255 RefPtr<TypeBuilder::CSS::CSSStyleSheetHeader> result = TypeBuilder::CSS::CSSStyleSheetHeader::create()
1256 .setStyleSheetId(id())
1257 .setOrigin(m_origin)
1258 .setDisabled(styleSheet->disabled())
1259 .setSourceURL(url())
1260 .setTitle(styleSheet->title())
1261 .setFrameId(m_pageAgent->frameId(frame))
1262 .setIsInline(styleSheet->isInline() && !startsAtZero())
1263 .setStartLine(styleSheet->startPositionInSource().m_line.zeroBasedInt())
1264 .setStartColumn(styleSheet->startPositionInSource().m_column.zeroBasedInt());
1267 result->setHasSourceURL(true);
1269 String sourceMapURLValue = sourceMapURL();
1270 if (!sourceMapURLValue.isEmpty())
1271 result->setSourceMapURL(sourceMapURLValue);
1272 return result.release();
1275 PassRefPtr<TypeBuilder::Array<TypeBuilder::CSS::Selector> > InspectorStyleSheet::selectorsFromSource(const CSSRuleSourceData* sourceData, const String& sheetText)
1277 ScriptRegexp comment("/\\*[^]*?\\*/", TextCaseSensitive, MultilineEnabled);
1278 RefPtr<TypeBuilder::Array<TypeBuilder::CSS::Selector> > result = TypeBuilder::Array<TypeBuilder::CSS::Selector>::create();
1279 const SelectorRangeList& ranges = sourceData->selectorRanges;
1280 for (size_t i = 0, size = ranges.size(); i < size; ++i) {
1281 const SourceRange& range = ranges.at(i);
1282 String selector = sheetText.substring(range.start, range.length());
1284 // We don't want to see any comments in the selector components, only the meaningful parts.
1287 while ((offset = comment.match(selector, offset, &matchLength)) >= 0)
1288 selector.replace(offset, matchLength, "");
1290 RefPtr<TypeBuilder::CSS::Selector> simpleSelector = TypeBuilder::CSS::Selector::create()
1291 .setValue(selector.stripWhiteSpace());
1292 simpleSelector->setRange(buildSourceRangeObject(range, lineEndings().get()));
1293 result->addItem(simpleSelector.release());
1295 return result.release();
1298 PassRefPtr<TypeBuilder::CSS::SelectorList> InspectorStyleSheet::buildObjectForSelectorList(CSSStyleRule* rule)
1300 RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = nullptr;
1301 if (ensureParsedDataReady())
1302 sourceData = ruleSourceDataAt(styleId(rule->style()).ordinal());
1303 RefPtr<TypeBuilder::Array<TypeBuilder::CSS::Selector> > selectors;
1305 // This intentionally does not rely on the source data to avoid catching the trailing comments (before the declaration starting '{').
1306 String selectorText = rule->selectorText();
1309 selectors = selectorsFromSource(sourceData.get(), m_parsedStyleSheet->text());
1311 selectors = TypeBuilder::Array<TypeBuilder::CSS::Selector>::create();
1312 const CSSSelectorList& selectorList = rule->styleRule()->selectorList();
1313 for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(*selector))
1314 selectors->addItem(TypeBuilder::CSS::Selector::create().setValue(selector->selectorText()).release());
1316 RefPtr<TypeBuilder::CSS::SelectorList> result = TypeBuilder::CSS::SelectorList::create()
1317 .setSelectors(selectors)
1318 .setText(selectorText)
1320 return result.release();
1323 static bool canBind(TypeBuilder::CSS::StyleSheetOrigin::Enum origin)
1325 return origin != TypeBuilder::CSS::StyleSheetOrigin::User_agent && origin != TypeBuilder::CSS::StyleSheetOrigin::User;
1328 PassRefPtr<TypeBuilder::CSS::CSSRule> InspectorStyleSheet::buildObjectForRule(CSSStyleRule* rule, PassRefPtr<Array<TypeBuilder::CSS::CSSMedia> > mediaStack)
1330 CSSStyleSheet* styleSheet = pageStyleSheet();
1334 RefPtr<TypeBuilder::CSS::CSSRule> result = TypeBuilder::CSS::CSSRule::create()
1335 .setSelectorList(buildObjectForSelectorList(rule))
1336 .setOrigin(m_origin)
1337 .setStyle(buildObjectForStyle(rule->style()));
1339 if (canBind(m_origin)) {
1340 InspectorCSSId id(ruleId(rule));
1342 result->setStyleSheetId(id.styleSheetId());
1346 result->setMedia(mediaStack);
1348 return result.release();
1351 bool InspectorStyleSheet::getText(String* result) const
1355 *result = m_parsedStyleSheet->text();
1359 CSSStyleDeclaration* InspectorStyleSheet::styleForId(const InspectorCSSId& id) const
1361 CSSStyleRule* rule = ruleForId(id);
1365 return rule->style();
1368 PassRefPtr<TypeBuilder::CSS::SourceRange> InspectorStyleSheet::ruleHeaderSourceRange(const CSSRule* rule)
1370 if (!ensureParsedDataReady())
1374 size_t index = m_flatRules.find(rule);
1375 // FIXME(lusnikov): m_flatRules are not always aligned with the m_parsedStyleSheet rule source
1376 // datas due to the CSSOM operations that add/remove rules without changing source.
1377 // This is a design issue. See crbug.com/178410
1378 if (index == kNotFound || index >= m_parsedStyleSheet->ruleCount())
1380 RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = m_parsedStyleSheet->ruleSourceDataAt(static_cast<unsigned>(index));
1381 return buildSourceRangeObject(sourceData->ruleHeaderRange, lineEndings().get());
1384 PassRefPtrWillBeRawPtr<InspectorStyle> InspectorStyleSheet::inspectorStyleForId(const InspectorCSSId& id)
1386 CSSStyleDeclaration* style = styleForId(id);
1390 return InspectorStyle::create(id, style, this);
1393 unsigned InspectorStyleSheet::ruleCount()
1395 return m_parsedStyleSheet->ruleCount();
1398 String InspectorStyleSheet::sourceURL() const
1400 if (!m_sourceURL.isNull())
1402 if (m_origin != TypeBuilder::CSS::StyleSheetOrigin::Regular) {
1407 String styleSheetText;
1408 bool success = getText(&styleSheetText);
1411 String commentValue = ContentSearchUtils::findSourceURL(styleSheetText, ContentSearchUtils::CSSMagicComment, &deprecated);
1412 if (!commentValue.isEmpty()) {
1413 // FIXME: add deprecated console message here.
1414 m_sourceURL = commentValue;
1415 return commentValue;
1422 String InspectorStyleSheet::url() const
1424 // "sourceURL" is present only for regular rules, otherwise "origin" should be used in the frontend.
1425 if (m_origin != TypeBuilder::CSS::StyleSheetOrigin::Regular)
1428 CSSStyleSheet* styleSheet = pageStyleSheet();
1435 if (styleSheet->isInline() && startsAtZero())
1441 bool InspectorStyleSheet::hasSourceURL() const
1443 return !sourceURL().isEmpty();
1446 bool InspectorStyleSheet::startsAtZero() const
1448 CSSStyleSheet* styleSheet = pageStyleSheet();
1452 return styleSheet->startPositionInSource() == TextPosition::minimumPosition();
1455 String InspectorStyleSheet::sourceMapURL() const
1457 if (m_origin != TypeBuilder::CSS::StyleSheetOrigin::Regular)
1460 String styleSheetText;
1461 bool success = getText(&styleSheetText);
1464 String commentValue = ContentSearchUtils::findSourceMapURL(styleSheetText, ContentSearchUtils::CSSMagicComment, &deprecated);
1465 if (!commentValue.isEmpty()) {
1466 // FIXME: add deprecated console message here.
1467 return commentValue;
1470 return m_pageAgent->resourceSourceMapURL(finalURL());
1473 InspectorCSSId InspectorStyleSheet::styleId(CSSStyleDeclaration* style) const
1475 unsigned index = ruleIndexByStyle(style);
1476 if (index != UINT_MAX)
1477 return InspectorCSSId(id(), index);
1478 return InspectorCSSId();
1481 bool InspectorStyleSheet::findRuleBySelectorRange(const SourceRange& sourceRange, InspectorCSSId* ruleId)
1483 if (!ensureParsedDataReady())
1485 for (size_t i = 0; i < ruleCount(); ++i) {
1486 RefPtrWillBeRawPtr<CSSRuleSourceData> ruleSourceData = ruleSourceDataAt(i);
1487 if (!ruleSourceData->styleSourceData)
1489 if (ruleSourceData->ruleHeaderRange.start == sourceRange.start && ruleSourceData->ruleHeaderRange.end == sourceRange.end) {
1490 *ruleId = InspectorCSSId(id(), i);
1497 const CSSRuleVector& InspectorStyleSheet::flatRules()
1503 Document* InspectorStyleSheet::ownerDocument() const
1505 return m_pageStyleSheet->ownerDocument();
1508 PassRefPtrWillBeRawPtr<CSSRuleSourceData> InspectorStyleSheet::ruleSourceDataAt(unsigned ruleIndex) const
1510 return m_parsedStyleSheet->ruleSourceDataAt(ruleIndex);
1513 unsigned InspectorStyleSheet::ruleIndexByStyle(CSSStyleDeclaration* pageStyle) const
1516 for (unsigned i = 0, size = m_flatRules.size(); i < size; ++i) {
1517 CSSStyleRule* styleRule = InspectorCSSAgent::asCSSStyleRule(m_flatRules.at(i).get());
1518 if (styleRule && styleRule->style() == pageStyle)
1524 bool InspectorStyleSheet::ensureParsedDataReady()
1526 return ensureText() && m_parsedStyleSheet->ensureSourceData();
1529 bool InspectorStyleSheet::ensureText() const
1531 if (m_parsedStyleSheet->hasText())
1535 bool success = originalStyleSheetText(&text);
1537 m_parsedStyleSheet->setText(text);
1538 // No need to clear m_flatRules here - it's empty.
1543 template <typename RuleList>
1544 static void collectFlatRules(RuleList ruleList, CSSRuleVector* result)
1549 for (unsigned i = 0, size = ruleList->length(); i < size; ++i) {
1550 CSSRule* rule = ruleList->item(i);
1552 // The result->append()'ed types should be exactly the same as in ParsedStyleSheet::flattenSourceData().
1553 switch (rule->type()) {
1554 case CSSRule::STYLE_RULE:
1555 case CSSRule::IMPORT_RULE:
1556 case CSSRule::CHARSET_RULE:
1557 case CSSRule::PAGE_RULE:
1558 case CSSRule::FONT_FACE_RULE:
1559 case CSSRule::VIEWPORT_RULE:
1560 case CSSRule::KEYFRAMES_RULE:
1561 result->append(rule);
1563 case CSSRule::MEDIA_RULE:
1564 case CSSRule::SUPPORTS_RULE:
1565 result->append(rule);
1566 collectFlatRules(asCSSRuleList(rule), result);
1574 void InspectorStyleSheet::ensureFlatRules() const
1576 // We are fine with redoing this for empty stylesheets as this will run fast.
1577 if (m_flatRules.isEmpty())
1578 collectFlatRules(pageStyleSheet(), &m_flatRules);
1581 bool InspectorStyleSheet::setStyleText(const InspectorCSSId& id, const String& text)
1583 CSSStyleDeclaration* style = styleForId(id);
1587 if (!ensureParsedDataReady())
1590 String patchedStyleSheetText;
1591 bool success = styleSheetTextWithChangedStyle(style, text, &patchedStyleSheetText);
1595 TrackExceptionState exceptionState;
1596 style->setCSSText(text, exceptionState);
1597 if (!exceptionState.hadException()) {
1598 updateText(patchedStyleSheetText);
1599 fireStyleSheetChanged();
1602 return !exceptionState.hadException();
1605 bool InspectorStyleSheet::styleSheetTextWithChangedStyle(CSSStyleDeclaration* style, const String& newStyleText, String* result)
1609 if (!ensureParsedDataReady())
1612 RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = ruleSourceDataAt(styleId(style).ordinal());
1613 unsigned bodyStart = sourceData->ruleBodyRange.start;
1614 unsigned bodyEnd = sourceData->ruleBodyRange.end;
1615 ASSERT(bodyStart <= bodyEnd);
1617 String text = m_parsedStyleSheet->text();
1618 ASSERT_WITH_SECURITY_IMPLICATION(bodyEnd <= text.length()); // bodyEnd is exclusive
1620 text.replace(bodyStart, bodyEnd - bodyStart, newStyleText);
1625 InspectorCSSId InspectorStyleSheet::ruleId(CSSStyleRule* rule) const
1627 return styleId(rule->style());
1630 bool InspectorStyleSheet::originalStyleSheetText(String* result) const
1632 bool success = inlineStyleSheetText(result);
1634 success = resourceStyleSheetText(result);
1638 bool InspectorStyleSheet::resourceStyleSheetText(String* result) const
1640 if (m_origin == TypeBuilder::CSS::StyleSheetOrigin::User || m_origin == TypeBuilder::CSS::StyleSheetOrigin::User_agent)
1643 if (!ownerDocument())
1646 KURL url(ParsedURLString, m_pageStyleSheet->href());
1647 if (m_pageAgent->getEditedResourceContent(url, result))
1651 bool success = m_resourceAgent->fetchResourceContent(ownerDocument(), url, result, &base64Encoded);
1652 return success && !base64Encoded;
1655 Element* InspectorStyleSheet::ownerStyleElement() const
1657 Node* ownerNode = m_pageStyleSheet->ownerNode();
1658 if (!ownerNode || !ownerNode->isElementNode())
1660 Element* ownerElement = toElement(ownerNode);
1662 if (!isHTMLStyleElement(ownerElement) && !isSVGStyleElement(ownerElement))
1664 return ownerElement;
1667 bool InspectorStyleSheet::inlineStyleSheetText(String* result) const
1669 Element* ownerElement = ownerStyleElement();
1672 *result = ownerElement->textContent();
1676 PassRefPtrWillBeRawPtr<InspectorStyleSheetForInlineStyle> InspectorStyleSheetForInlineStyle::create(const String& id, PassRefPtrWillBeRawPtr<Element> element, Listener* listener)
1678 return adoptRefWillBeNoop(new InspectorStyleSheetForInlineStyle(id, element, listener));
1681 InspectorStyleSheetForInlineStyle::InspectorStyleSheetForInlineStyle(const String& id, PassRefPtrWillBeRawPtr<Element> element, Listener* listener)
1682 : InspectorStyleSheetBase(id, listener)
1683 , m_element(element)
1684 , m_ruleSourceData(nullptr)
1685 , m_isStyleTextValid(false)
1688 m_inspectorStyle = InspectorStyle::create(InspectorCSSId(id, 0), inlineStyle(), this);
1689 m_styleText = m_element->isStyledElement() ? m_element->getAttribute("style").string() : String();
1692 void InspectorStyleSheetForInlineStyle::didModifyElementAttribute()
1694 m_isStyleTextValid = false;
1695 if (m_element->isStyledElement() && m_element->style() != m_inspectorStyle->cssStyle())
1696 m_inspectorStyle = InspectorStyle::create(InspectorCSSId(id(), 0), inlineStyle(), this);
1697 m_ruleSourceData.clear();
1700 bool InspectorStyleSheetForInlineStyle::setText(const String& text, ExceptionState& exceptionState)
1702 bool success = setStyleText(InspectorCSSId(id(), 0), text);
1704 exceptionState.throwDOMException(SyntaxError, "Style sheet text is invalid.");
1706 fireStyleSheetChanged();
1710 bool InspectorStyleSheetForInlineStyle::getText(String* result) const
1712 if (!m_isStyleTextValid) {
1713 m_styleText = elementStyleText();
1714 m_isStyleTextValid = true;
1716 *result = m_styleText;
1720 bool InspectorStyleSheetForInlineStyle::setStyleText(const InspectorCSSId& id, const String& text)
1722 CSSStyleDeclaration* style = styleForId(id);
1725 ASSERT_UNUSED(style, style == inlineStyle());
1726 TrackExceptionState exceptionState;
1729 InspectorCSSAgent::InlineStyleOverrideScope overrideScope(m_element->ownerDocument());
1730 m_element->setAttribute("style", AtomicString(text), exceptionState);
1732 if (!exceptionState.hadException()) {
1734 m_isStyleTextValid = true;
1735 m_ruleSourceData.clear();
1736 fireStyleSheetChanged();
1738 return !exceptionState.hadException();
1741 Document* InspectorStyleSheetForInlineStyle::ownerDocument() const
1743 return &m_element->document();
1746 bool InspectorStyleSheetForInlineStyle::ensureParsedDataReady()
1748 // The "style" property value can get changed indirectly, e.g. via element.style.borderWidth = "2px".
1749 const String& currentStyleText = elementStyleText();
1750 if (m_styleText != currentStyleText) {
1751 m_ruleSourceData.clear();
1752 m_styleText = currentStyleText;
1753 m_isStyleTextValid = true;
1756 if (m_ruleSourceData)
1759 m_ruleSourceData = getStyleAttributeData();
1761 bool success = !!m_ruleSourceData;
1763 m_ruleSourceData = CSSRuleSourceData::create(CSSRuleSourceData::STYLE_RULE);
1770 PassRefPtrWillBeRawPtr<InspectorStyle> InspectorStyleSheetForInlineStyle::inspectorStyleForId(const InspectorCSSId& id)
1772 ASSERT_UNUSED(id, !id.ordinal());
1773 return m_inspectorStyle;
1776 CSSStyleDeclaration* InspectorStyleSheetForInlineStyle::inlineStyle() const
1778 return m_element->style();
1781 const String& InspectorStyleSheetForInlineStyle::elementStyleText() const
1783 return m_element->getAttribute("style").string();
1786 PassRefPtrWillBeRawPtr<CSSRuleSourceData> InspectorStyleSheetForInlineStyle::getStyleAttributeData() const
1788 if (!m_element->isStyledElement())
1791 if (m_styleText.isEmpty()) {
1792 RefPtrWillBeRawPtr<CSSRuleSourceData> result = CSSRuleSourceData::create(CSSRuleSourceData::STYLE_RULE);
1793 result->ruleBodyRange.start = 0;
1794 result->ruleBodyRange.end = 0;
1795 return result.release();
1798 RefPtrWillBeRawPtr<MutableStylePropertySet> tempDeclaration = MutableStylePropertySet::create();
1799 RuleSourceDataList ruleSourceDataResult;
1800 StyleSheetHandler handler(m_styleText, &m_element->document(), m_element->document().elementSheet().contents(), &ruleSourceDataResult);
1801 CSSParser(parserContextForDocument(&m_element->document())).parseDeclaration(tempDeclaration.get(), m_styleText, &handler, m_element->document().elementSheet().contents());
1802 return ruleSourceDataResult.first().release();
1805 void InspectorStyleSheetForInlineStyle::trace(Visitor* visitor)
1807 visitor->trace(m_element);
1808 visitor->trace(m_ruleSourceData);
1809 visitor->trace(m_inspectorStyle);
1810 InspectorStyleSheetBase::trace(visitor);
1813 } // namespace blink