• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010, Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
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.
12  *
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.
23  */
24 
25 #include "config.h"
26 #include "core/inspector/InspectorStyleSheet.h"
27 
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"
55 
56 using blink::TypeBuilder::Array;
57 using blink::RuleSourceDataList;
58 using blink::CSSRuleSourceData;
59 using blink::CSSStyleSheet;
60 
61 namespace {
62 
63 using namespace blink;
64 
parserContextForDocument(Document * document)65 static CSSParserContext parserContextForDocument(Document *document)
66 {
67     return document ? CSSParserContext(*document, 0) : strictCSSParserContext();
68 }
69 
70 class StyleSheetHandler FINAL : public CSSParserObserver {
71 public:
StyleSheetHandler(const String & parsedText,Document * document,StyleSheetContents * styleSheetContents,RuleSourceDataList * result)72     StyleSheetHandler(const String& parsedText, Document* document, StyleSheetContents* styleSheetContents, RuleSourceDataList* result)
73         : m_parsedText(parsedText)
74         , m_document(document)
75         , m_styleSheetContents(styleSheetContents)
76         , m_result(result)
77         , m_commentParser(parserContextForDocument(document))
78         , m_propertyRangeStart(UINT_MAX)
79         , m_selectorRangeStart(UINT_MAX)
80         , m_commentRangeStart(UINT_MAX)
81     {
82         ASSERT(m_result);
83     }
84 
85 private:
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;
96 
97     void addNewRuleToSourceTree(PassRefPtrWillBeRawPtr<CSSRuleSourceData>);
98     PassRefPtrWillBeRawPtr<CSSRuleSourceData> popRuleData();
99     template <typename CharacterType> inline void setRuleHeaderEnd(const CharacterType*, unsigned);
100     void fixUnparsedPropertyRanges(CSSRuleSourceData*);
101 
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;
112 };
113 
startRuleHeader(CSSRuleSourceData::Type type,unsigned offset)114 void StyleSheetHandler::startRuleHeader(CSSRuleSourceData::Type type, unsigned offset)
115 {
116     // Pop off data for a previous invalid rule.
117     if (m_currentRuleData)
118         m_currentRuleDataStack.removeLast();
119 
120     RefPtrWillBeRawPtr<CSSRuleSourceData> data = CSSRuleSourceData::create(type);
121     data->ruleHeaderRange.start = offset;
122     m_currentRuleData = data;
123     m_currentRuleDataStack.append(data.release());
124 }
125 
126 template <typename CharacterType>
setRuleHeaderEnd(const CharacterType * dataStart,unsigned listEndOffset)127 inline void StyleSheetHandler::setRuleHeaderEnd(const CharacterType* dataStart, unsigned listEndOffset)
128 {
129     while (listEndOffset > 1) {
130         if (isHTMLSpace<CharacterType>(*(dataStart + listEndOffset - 1)))
131             --listEndOffset;
132         else
133             break;
134     }
135 
136     m_currentRuleDataStack.last()->ruleHeaderRange.end = listEndOffset;
137     if (!m_currentRuleDataStack.last()->selectorRanges.isEmpty())
138         m_currentRuleDataStack.last()->selectorRanges.last().end = listEndOffset;
139 }
140 
endRuleHeader(unsigned offset)141 void StyleSheetHandler::endRuleHeader(unsigned offset)
142 {
143     ASSERT(!m_currentRuleDataStack.isEmpty());
144 
145     if (m_parsedText.is8Bit())
146         setRuleHeaderEnd<LChar>(m_parsedText.characters8(), offset);
147     else
148         setRuleHeaderEnd<UChar>(m_parsedText.characters16(), offset);
149 }
150 
startSelector(unsigned offset)151 void StyleSheetHandler::startSelector(unsigned offset)
152 {
153     m_selectorRangeStart = offset;
154 }
155 
endSelector(unsigned offset)156 void StyleSheetHandler::endSelector(unsigned offset)
157 {
158     ASSERT(m_currentRuleDataStack.size());
159     m_currentRuleDataStack.last()->selectorRanges.append(SourceRange(m_selectorRangeStart, offset));
160     m_selectorRangeStart = UINT_MAX;
161 }
162 
startRuleBody(unsigned offset)163 void StyleSheetHandler::startRuleBody(unsigned offset)
164 {
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;
170 }
171 
endRuleBody(unsigned offset,bool error)172 void StyleSheetHandler::endRuleBody(unsigned offset, bool error)
173 {
174     ASSERT(!m_currentRuleDataStack.isEmpty());
175     m_currentRuleDataStack.last()->ruleBodyRange.end = offset;
176     m_propertyRangeStart = UINT_MAX;
177     RefPtrWillBeRawPtr<CSSRuleSourceData> rule = popRuleData();
178     if (error)
179         return;
180 
181     fixUnparsedPropertyRanges(rule.get());
182     addNewRuleToSourceTree(rule.release());
183 }
184 
addNewRuleToSourceTree(PassRefPtrWillBeRawPtr<CSSRuleSourceData> rule)185 void StyleSheetHandler::addNewRuleToSourceTree(PassRefPtrWillBeRawPtr<CSSRuleSourceData> rule)
186 {
187     if (m_currentRuleDataStack.isEmpty())
188         m_result->append(rule);
189     else
190         m_currentRuleDataStack.last()->childRules.append(rule);
191 }
192 
popRuleData()193 PassRefPtrWillBeRawPtr<CSSRuleSourceData> StyleSheetHandler::popRuleData()
194 {
195     ASSERT(!m_currentRuleDataStack.isEmpty());
196     m_currentRuleData.clear();
197     RefPtrWillBeRawPtr<CSSRuleSourceData> data = m_currentRuleDataStack.last();
198     m_currentRuleDataStack.removeLast();
199     return data.release();
200 }
201 
202 template <typename CharacterType>
fixUnparsedProperties(const CharacterType * characters,CSSRuleSourceData * ruleData)203 static inline void fixUnparsedProperties(const CharacterType* characters, CSSRuleSourceData* ruleData)
204 {
205     WillBeHeapVector<CSSPropertySourceData>& propertyData = ruleData->styleSourceData->propertyData;
206     unsigned size = propertyData.size();
207     if (!size)
208         return;
209 
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;
214 
215         if (currentData->parsedOk)
216             continue;
217         if (currentData->range.end > 0 && characters[currentData->range.end - 1] == ';')
218             continue;
219 
220         unsigned propertyEnd;
221         if (!nextData)
222             propertyEnd = ruleData->ruleBodyRange.end - 1;
223         else
224             propertyEnd = nextData->range.start - 1;
225 
226         while (isHTMLSpace<CharacterType>(characters[propertyEnd]))
227             --propertyEnd;
228 
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] != ':')
235                 ++valueStart;
236             if (valueStart < propertyEnd)
237                 ++valueStart; // Shift past the ':'.
238             while (valueStart < propertyEnd && isHTMLSpace<CharacterType>(characters[valueStart]))
239                 ++valueStart;
240             // Need to exclude the trailing ';' from the property value.
241             currentData->value = String(characters + valueStart, propertyEnd - valueStart + (characters[propertyEnd] == ';' ? 0 : 1));
242         }
243     }
244 }
245 
fixUnparsedPropertyRanges(CSSRuleSourceData * ruleData)246 void StyleSheetHandler::fixUnparsedPropertyRanges(CSSRuleSourceData* ruleData)
247 {
248     if (!ruleData->styleSourceData)
249         return;
250 
251     if (m_parsedText.is8Bit()) {
252         fixUnparsedProperties<LChar>(m_parsedText.characters8(), ruleData);
253         return;
254     }
255 
256     fixUnparsedProperties<UChar>(m_parsedText.characters16(), ruleData);
257 }
258 
startProperty(unsigned offset)259 void StyleSheetHandler::startProperty(unsigned offset)
260 {
261     if (m_currentRuleDataStack.isEmpty() || !m_currentRuleDataStack.last()->styleSourceData)
262         return;
263     m_propertyRangeStart = offset;
264 }
265 
endProperty(bool isImportant,bool isParsed,unsigned offset,CSSParserError errorType)266 void StyleSheetHandler::endProperty(bool isImportant, bool isParsed, unsigned offset, CSSParserError errorType)
267 {
268     // FIXME: This is the only place CSSParserError is every read!?
269     if (errorType != NoCSSError)
270         m_propertyRangeStart = UINT_MAX;
271 
272     if (m_propertyRangeStart == UINT_MAX || m_currentRuleDataStack.isEmpty() || !m_currentRuleDataStack.last()->styleSourceData)
273         return;
274 
275     ASSERT(offset <= m_parsedText.length());
276     if (offset < m_parsedText.length() && m_parsedText[offset] == ';') // Include semicolon into the property text.
277         ++offset;
278 
279     const unsigned start = m_propertyRangeStart;
280     const unsigned end = offset;
281     ASSERT(start < end);
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);
287 
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;
293 }
294 
startComment(unsigned offset)295 void StyleSheetHandler::startComment(unsigned offset)
296 {
297     ASSERT(m_commentRangeStart == UINT_MAX);
298     m_commentRangeStart = offset;
299 }
300 
endComment(unsigned offset)301 void StyleSheetHandler::endComment(unsigned offset)
302 {
303     ASSERT(offset <= m_parsedText.length());
304 
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())
312             return;
313         m_propertyRangeStart = UINT_MAX;
314     }
315     if (m_currentRuleDataStack.isEmpty() || !m_currentRuleDataStack.last()->ruleHeaderRange.end || !m_currentRuleDataStack.last()->styleSourceData)
316         return;
317 
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);
320 
321     ASSERT(commentText.startsWith("/*"));
322     commentText = commentText.substring(2);
323 
324     // Require well-formed comments.
325     if (!commentText.endsWith("*/"))
326         return;
327     commentText = commentText.substring(0, commentText.length() - 2).stripWhiteSpace();
328     if (commentText.isEmpty())
329         return;
330 
331     // FIXME: Use the actual rule type rather than STYLE_RULE?
332     RuleSourceDataList sourceData;
333 
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)
341         return;
342     CSSPropertySourceData& propertyData = commentPropertyData.at(0);
343     if (propertyData.range.length() != commentText.length())
344         return;
345 
346     m_currentRuleDataStack.last()->styleSourceData->propertyData.append(
347         CSSPropertySourceData(propertyData.name, propertyData.value, false, true, true, SourceRange(startOffset, offset)));
348 }
349 
350 } // namespace
351 
352 class ParsedStyleSheet {
353     WTF_MAKE_FAST_ALLOCATED;
354 public:
355     ParsedStyleSheet(CSSStyleSheet* pageStyleSheet);
356 
text() const357     const String& text() const { ASSERT(m_hasText); return m_text; }
358     void setText(const String&);
hasText() const359     bool hasText() const { return m_hasText; }
360     bool ensureSourceData();
hasSourceData() const361     bool hasSourceData() const { return m_sourceData; }
362     PassRefPtrWillBeRawPtr<blink::CSSRuleSourceData> ruleSourceDataAt(unsigned) const;
ruleCount()363     unsigned ruleCount() { return m_sourceData->size(); }
364 
365 private:
366     void flattenSourceData(RuleSourceDataList*);
367     void setSourceData(PassOwnPtrWillBeRawPtr<RuleSourceDataList>);
368 
369     String m_text;
370     bool m_hasText;
371     OwnPtrWillBePersistent<RuleSourceDataList> m_sourceData;
372     RefPtrWillBePersistent<CSSStyleSheet> m_pageStyleSheet;
373 };
374 
ParsedStyleSheet(CSSStyleSheet * pageStyleSheet)375 ParsedStyleSheet::ParsedStyleSheet(CSSStyleSheet* pageStyleSheet)
376     : m_hasText(false)
377     , m_pageStyleSheet(pageStyleSheet)
378 {
379 }
380 
setText(const String & text)381 void ParsedStyleSheet::setText(const String& text)
382 {
383     m_hasText = true;
384     m_text = text;
385     setSourceData(nullptr);
386 }
387 
flattenSourceData(RuleSourceDataList * dataList)388 void ParsedStyleSheet::flattenSourceData(RuleSourceDataList* dataList)
389 {
390     for (size_t i = 0; i < dataList->size(); ++i) {
391         RefPtrWillBeMember<CSSRuleSourceData>& data = dataList->at(i);
392 
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);
403             break;
404         case CSSRuleSourceData::MEDIA_RULE:
405         case CSSRuleSourceData::SUPPORTS_RULE:
406             m_sourceData->append(data);
407             flattenSourceData(&data->childRules);
408             break;
409         default:
410             break;
411         }
412     }
413 }
414 
ensureSourceData()415 bool ParsedStyleSheet::ensureSourceData()
416 {
417     if (hasSourceData())
418         return true;
419 
420     if (!hasText())
421         return false;
422 
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();
429 }
430 
setSourceData(PassOwnPtrWillBeRawPtr<RuleSourceDataList> sourceData)431 void ParsedStyleSheet::setSourceData(PassOwnPtrWillBeRawPtr<RuleSourceDataList> sourceData)
432 {
433     if (!sourceData) {
434         m_sourceData.clear();
435         return;
436     }
437     m_sourceData = adoptPtrWillBeNoop(new RuleSourceDataList());
438 
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());
443 }
444 
ruleSourceDataAt(unsigned index) const445 PassRefPtrWillBeRawPtr<blink::CSSRuleSourceData> ParsedStyleSheet::ruleSourceDataAt(unsigned index) const
446 {
447     if (!hasSourceData() || index >= m_sourceData->size())
448         return nullptr;
449 
450     return m_sourceData->at(index);
451 }
452 
453 namespace blink {
454 
455 enum MediaListSource {
456     MediaListSourceLinkedSheet,
457     MediaListSourceInlineSheet,
458     MediaListSourceMediaRule,
459     MediaListSourceImportRule
460 };
461 
buildSourceRangeObject(const SourceRange & range,Vector<unsigned> * lineEndings)462 static PassRefPtr<TypeBuilder::CSS::SourceRange> buildSourceRangeObject(const SourceRange& range, Vector<unsigned>* lineEndings)
463 {
464     if (!lineEndings)
465         return nullptr;
466     TextPosition start = TextPosition::fromOffsetAndLineEndings(range.start, *lineEndings);
467     TextPosition end = TextPosition::fromOffsetAndLineEndings(range.end, *lineEndings);
468 
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();
475 }
476 
asCSSRuleList(CSSRule * rule)477 static PassRefPtrWillBeRawPtr<CSSRuleList> asCSSRuleList(CSSRule* rule)
478 {
479     if (!rule)
480         return nullptr;
481 
482     if (rule->type() == CSSRule::MEDIA_RULE)
483         return toCSSMediaRule(rule)->cssRules();
484 
485     if (rule->type() == CSSRule::SUPPORTS_RULE)
486         return toCSSSupportsRule(rule)->cssRules();
487 
488     return nullptr;
489 }
490 
create(const InspectorCSSId & styleId,PassRefPtrWillBeRawPtr<CSSStyleDeclaration> style,InspectorStyleSheetBase * parentStyleSheet)491 PassRefPtrWillBeRawPtr<InspectorStyle> InspectorStyle::create(const InspectorCSSId& styleId, PassRefPtrWillBeRawPtr<CSSStyleDeclaration> style, InspectorStyleSheetBase* parentStyleSheet)
492 {
493     return adoptRefWillBeNoop(new InspectorStyle(styleId, style, parentStyleSheet));
494 }
495 
InspectorStyle(const InspectorCSSId & styleId,PassRefPtrWillBeRawPtr<CSSStyleDeclaration> style,InspectorStyleSheetBase * parentStyleSheet)496 InspectorStyle::InspectorStyle(const InspectorCSSId& styleId, PassRefPtrWillBeRawPtr<CSSStyleDeclaration> style, InspectorStyleSheetBase* parentStyleSheet)
497     : m_styleId(styleId)
498     , m_style(style)
499     , m_parentStyleSheet(parentStyleSheet)
500     , m_formatAcquired(false)
501 {
502     ASSERT(m_style);
503 }
504 
buildObjectForStyle() const505 PassRefPtr<TypeBuilder::CSS::CSSStyle> InspectorStyle::buildObjectForStyle() const
506 {
507     RefPtr<TypeBuilder::CSS::CSSStyle> result = styleWithProperties();
508     if (!m_styleId.isEmpty())
509         result->setStyleSheetId(m_styleId.styleSheetId());
510 
511     RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = extractSourceData();
512     if (sourceData)
513         result->setRange(buildSourceRangeObject(sourceData->ruleBodyRange, m_parentStyleSheet->lineEndings().get()));
514 
515     return result.release();
516 }
517 
buildArrayForComputedStyle() const518 PassRefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSComputedStyleProperty> > InspectorStyle::buildArrayForComputedStyle() const
519 {
520     RefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSComputedStyleProperty> > result = TypeBuilder::Array<TypeBuilder::CSS::CSSComputedStyleProperty>::create();
521     WillBeHeapVector<InspectorStyleProperty> properties;
522     populateAllProperties(properties);
523 
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);
530     }
531 
532     return result.release();
533 }
534 
verifyPropertyText(const String & propertyText,bool canOmitSemicolon)535 bool InspectorStyle::verifyPropertyText(const String& propertyText, bool canOmitSemicolon)
536 {
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();
546 
547     // At least one property + the bogus property added just above should be present.
548     if (propertyCount < 2)
549         return false;
550 
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)
553         return false;
554 
555     return true;
556 }
557 
setPropertyText(unsigned index,const String & propertyText,bool overwrite,ExceptionState & exceptionState)558 bool InspectorStyle::setPropertyText(unsigned index, const String& propertyText, bool overwrite, ExceptionState& exceptionState)
559 {
560     ASSERT(m_parentStyleSheet);
561 
562     if (!m_parentStyleSheet->ensureParsedDataReady()) {
563         exceptionState.throwDOMException(NotFoundError, "The parent style sheet's data hasn't been processed.");
564         return false;
565     }
566 
567     if (!propertyText.stripWhiteSpace().isEmpty()) {
568         if (!verifyPropertyText(propertyText, false) && !verifyPropertyText(propertyText, true)) {
569             exceptionState.throwDOMException(SyntaxError, "The property '" + propertyText + "' could not be set.");
570             return false;
571         }
572     }
573 
574     RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = extractSourceData();
575     if (!sourceData) {
576         exceptionState.throwDOMException(NotFoundError, "The property '" + propertyText + "' could not be set.");
577         return false;
578     }
579 
580     String text;
581     bool success = styleText(&text);
582     if (!success) {
583         exceptionState.throwDOMException(NotFoundError, "The property '" + propertyText + "' could not be set.");
584         return false;
585     }
586 
587     WillBeHeapVector<InspectorStyleProperty> allProperties;
588     populateAllProperties(allProperties);
589 
590     InspectorStyleTextEditor editor(&allProperties, text, sourceData->ruleBodyRange, newLineAndWhitespaceDelimiters());
591     if (overwrite) {
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()) + ").");
594             return false;
595         }
596         editor.replaceProperty(index, propertyText);
597     } else {
598         editor.insertProperty(index, propertyText);
599     }
600 
601     return m_parentStyleSheet->setStyleText(m_styleId, editor.styleText());
602 }
603 
styleText(String * result) const604 bool InspectorStyle::styleText(String* result) const
605 {
606     RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = extractSourceData();
607     if (!sourceData)
608         return false;
609 
610     return textForRange(sourceData->ruleBodyRange, result);
611 }
612 
textForRange(const SourceRange & range,String * result) const613 bool InspectorStyle::textForRange(const SourceRange& range, String* result) const
614 {
615     String styleSheetText;
616     bool success = m_parentStyleSheet->getText(&styleSheetText);
617     if (!success)
618         return false;
619 
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);
624     return true;
625 }
626 
populateAllProperties(WillBeHeapVector<InspectorStyleProperty> & result) const627 void InspectorStyle::populateAllProperties(WillBeHeapVector<InspectorStyleProperty>& result) const
628 {
629     HashSet<String> sourcePropertyNames;
630 
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);
638             result.append(p);
639             sourcePropertyNames.add(it->name.lower());
640         }
641     }
642 
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)
646             continue;
647 
648         String value = m_style->getPropertyValue(name);
649         if (value.isEmpty())
650             continue;
651         result.append(InspectorStyleProperty(CSSPropertySourceData(name, value, !m_style->getPropertyPriority(name).isEmpty(), false, true, SourceRange()), false));
652     }
653 }
654 
styleWithProperties() const655 PassRefPtr<TypeBuilder::CSS::CSSStyle> InspectorStyle::styleWithProperties() const
656 {
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> >());
661 
662     WillBeHeapVector<InspectorStyleProperty> properties;
663     populateAllProperties(properties);
664 
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;
668 
669         RefPtr<TypeBuilder::CSS::CSSProperty> property = TypeBuilder::CSS::CSSProperty::create()
670             .setName(name)
671             .setValue(propertyEntry.value);
672         propertiesObject->addItem(property);
673 
674         // Default "parsedOk" == true.
675         if (!propertyEntry.parsedOk)
676             property->setParsedOk(false);
677         if (it->hasRawText())
678             property->setText(it->rawText);
679 
680         if (propertyEntry.important)
681             property->setImportant(true);
682         if (it->hasSource) {
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.
690             if (implicit)
691                 property->setImplicit(true);
692 
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()
697                         .setName(shorthand)
698                         .setValue(shorthandValue(shorthand));
699                     shorthandEntries->addItem(entry);
700                 }
701             }
702         }
703     }
704 
705     RefPtr<TypeBuilder::CSS::CSSStyle> result = TypeBuilder::CSS::CSSStyle::create()
706         .setCssProperties(propertiesObject)
707         .setShorthandEntries(shorthandEntries);
708     return result.release();
709 }
710 
extractSourceData() const711 PassRefPtrWillBeRawPtr<CSSRuleSourceData> InspectorStyle::extractSourceData() const
712 {
713     if (!m_parentStyleSheet || !m_parentStyleSheet->ensureParsedDataReady())
714         return nullptr;
715     return m_parentStyleSheet->ruleSourceDataAt(m_styleId.ordinal());
716 }
717 
shorthandValue(const String & shorthandProperty) const718 String InspectorStyle::shorthandValue(const String& shorthandProperty) const
719 {
720     String value = m_style->getPropertyValue(shorthandProperty);
721     if (value.isEmpty()) {
722         StringBuilder builder;
723 
724         for (unsigned i = 0; i < m_style->length(); ++i) {
725             String individualProperty = m_style->item(i);
726             if (m_style->getPropertyShorthand(individualProperty) != shorthandProperty)
727                 continue;
728             if (m_style->isPropertyImplicit(individualProperty))
729                 continue;
730             String individualValue = m_style->getPropertyValue(individualProperty);
731             if (individualValue == "initial")
732                 continue;
733             if (!builder.isEmpty())
734                 builder.append(' ');
735             builder.append(individualValue);
736         }
737 
738         return builder.toString();
739     }
740     return value;
741 }
742 
newLineAndWhitespaceDelimiters() const743 NewLineAndWhitespace& InspectorStyle::newLineAndWhitespaceDelimiters() const
744 {
745     DEFINE_STATIC_LOCAL(String, defaultPrefix, ("    "));
746 
747     if (m_formatAcquired)
748         return m_format;
749 
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.
757     }
758 
759     String styleSheetText;
760     bool success = m_parentStyleSheet->getText(&styleSheetText);
761     ASSERT_UNUSED(success, success);
762 
763     m_formatAcquired = true;
764 
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++);
774 
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);
780             if (isLineFeed) {
781                 if (!lineFeedTerminated)
782                     formatLineFeed.append(ch);
783                 prefix.clear();
784             } else if (isHTMLSpace<UChar>(ch))
785                 prefix.append(ch);
786             else {
787                 candidatePrefix = prefix.toString();
788                 prefix.clear();
789                 scanStart = currentProperty.range.end;
790                 ++propertyIndex;
791                 processNextProperty = true;
792                 break;
793             }
794             if (!isLineFeed && formatLineFeed.length())
795                 lineFeedTerminated = true;
796         }
797         if (!processNextProperty) {
798             isFullPrefixScanned = true;
799             break;
800         }
801     }
802 
803     m_format.first = formatLineFeed.toString();
804     m_format.second = isFullPrefixScanned ? prefix.toString() : candidatePrefix;
805     return m_format;
806 }
807 
ownerDocument() const808 Document* InspectorStyle::ownerDocument() const
809 {
810     return m_parentStyleSheet->ownerDocument();
811 }
812 
trace(Visitor * visitor)813 void InspectorStyle::trace(Visitor* visitor)
814 {
815     visitor->trace(m_style);
816     visitor->trace(m_parentStyleSheet);
817 }
818 
InspectorStyleSheetBase(const String & id,Listener * listener)819 InspectorStyleSheetBase::InspectorStyleSheetBase(const String& id, Listener* listener)
820     : m_id(id)
821     , m_listener(listener)
822 {
823 }
824 
setPropertyText(const InspectorCSSId & id,unsigned propertyIndex,const String & text,bool overwrite,ExceptionState & exceptionState)825 bool InspectorStyleSheetBase::setPropertyText(const InspectorCSSId& id, unsigned propertyIndex, const String& text, bool overwrite, ExceptionState& exceptionState)
826 {
827     RefPtrWillBeRawPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id);
828     if (!inspectorStyle) {
829         exceptionState.throwDOMException(NotFoundError, "No property could be found for the given ID.");
830         return false;
831     }
832     return inspectorStyle->setPropertyText(propertyIndex, text, overwrite, exceptionState);
833 }
834 
getStyleText(const InspectorCSSId & id,String * text)835 bool InspectorStyleSheetBase::getStyleText(const InspectorCSSId& id, String* text)
836 {
837     RefPtrWillBeRawPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id);
838     if (!inspectorStyle)
839         return false;
840     return inspectorStyle->styleText(text);
841 }
842 
fireStyleSheetChanged()843 void InspectorStyleSheetBase::fireStyleSheetChanged()
844 {
845     if (listener())
846         listener()->styleSheetChanged(this);
847 }
848 
buildObjectForStyle(CSSStyleDeclaration * style)849 PassRefPtr<TypeBuilder::CSS::CSSStyle> InspectorStyleSheetBase::buildObjectForStyle(CSSStyleDeclaration* style)
850 {
851     RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = nullptr;
852     if (ensureParsedDataReady())
853         sourceData = ruleSourceDataAt(styleId(style).ordinal());
854 
855     InspectorCSSId id = styleId(style);
856     if (id.isEmpty()) {
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();
861     }
862     RefPtrWillBeRawPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id);
863     RefPtr<TypeBuilder::CSS::CSSStyle> result = inspectorStyle->buildObjectForStyle();
864 
865     // Style text cannot be retrieved without stylesheet, so set cssText here.
866     if (sourceData) {
867         String sheetText;
868         bool success = getText(&sheetText);
869         if (success) {
870             const SourceRange& bodyRange = sourceData->ruleBodyRange;
871             result->setCssText(sheetText.substring(bodyRange.start, bodyRange.end - bodyRange.start));
872         }
873     }
874 
875     return result.release();
876 }
877 
lineEndings()878 PassOwnPtr<Vector<unsigned> > InspectorStyleSheetBase::lineEndings()
879 {
880     String text;
881     if (!getText(&text))
882         return PassOwnPtr<Vector<unsigned> >();
883     return WTF::lineEndings(text);
884 }
885 
lineNumberAndColumnToOffset(unsigned lineNumber,unsigned columnNumber,unsigned * offset)886 bool InspectorStyleSheetBase::lineNumberAndColumnToOffset(unsigned lineNumber, unsigned columnNumber, unsigned* offset)
887 {
888     OwnPtr<Vector<unsigned> > endings = lineEndings();
889     if (lineNumber >= endings->size())
890         return false;
891     unsigned charactersInLine = lineNumber > 0 ? endings->at(lineNumber) - endings->at(lineNumber - 1) - 1 : endings->at(0);
892     if (columnNumber > charactersInLine)
893         return false;
894     TextPosition position(OrdinalNumber::fromZeroBasedInt(lineNumber), OrdinalNumber::fromZeroBasedInt(columnNumber));
895     *offset = position.toOffset(*endings).zeroBasedInt();
896     return true;
897 }
898 
findPropertyByRange(const SourceRange & sourceRange,InspectorCSSId * ruleId,unsigned * propertyIndex,bool * overwrite)899 bool InspectorStyleSheetBase::findPropertyByRange(const SourceRange& sourceRange, InspectorCSSId* ruleId, unsigned* propertyIndex, bool* overwrite)
900 {
901     if (!ensureParsedDataReady())
902         return false;
903     for (size_t i = 0; i < ruleCount(); ++i) {
904         RefPtrWillBeRawPtr<CSSRuleSourceData> ruleSourceData = ruleSourceDataAt(i);
905         RefPtrWillBeRawPtr<CSSStyleSourceData> styleSourceData = ruleSourceData->styleSourceData;
906         if (!styleSourceData)
907             continue;
908         if (ruleSourceData->ruleBodyRange.end < sourceRange.start || sourceRange.end < ruleSourceData->ruleBodyRange.start)
909             continue;
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);
916                 *propertyIndex = j;
917                 *overwrite = true;
918                 return true;
919             }
920             if (!sourceRange.length() && styleStart <= sourceRange.start && sourceRange.start <= property.range.start) {
921                 *ruleId = InspectorCSSId(id(), i);
922                 *propertyIndex = j;
923                 *overwrite = false;
924                 return true;
925             }
926         }
927         if (!sourceRange.length() && ruleSourceData->ruleBodyRange.start <= sourceRange.start && sourceRange.start <= ruleSourceData->ruleBodyRange.end) {
928             *ruleId = InspectorCSSId(id(), i);
929             *propertyIndex = propertyData.size();
930             *overwrite = false;
931             return true;
932         }
933     }
934     return false;
935 }
936 
create(InspectorPageAgent * pageAgent,InspectorResourceAgent * resourceAgent,const String & id,PassRefPtrWillBeRawPtr<CSSStyleSheet> pageStyleSheet,TypeBuilder::CSS::StyleSheetOrigin::Enum origin,const String & documentURL,Listener * listener)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)
938 {
939     return adoptRefWillBeNoop(new InspectorStyleSheet(pageAgent, resourceAgent, id, pageStyleSheet, origin, documentURL, listener));
940 }
941 
InspectorStyleSheet(InspectorPageAgent * pageAgent,InspectorResourceAgent * resourceAgent,const String & id,PassRefPtrWillBeRawPtr<CSSStyleSheet> pageStyleSheet,TypeBuilder::CSS::StyleSheetOrigin::Enum origin,const String & documentURL,Listener * 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)
947     , m_origin(origin)
948     , m_documentURL(documentURL)
949 {
950     m_parsedStyleSheet = adoptPtr(new ParsedStyleSheet(m_pageStyleSheet.get()));
951 }
952 
~InspectorStyleSheet()953 InspectorStyleSheet::~InspectorStyleSheet()
954 {
955 }
956 
trace(Visitor * visitor)957 void InspectorStyleSheet::trace(Visitor* visitor)
958 {
959     visitor->trace(m_pageAgent);
960     visitor->trace(m_resourceAgent);
961     visitor->trace(m_pageStyleSheet);
962     visitor->trace(m_flatRules);
963     InspectorStyleSheetBase::trace(visitor);
964 }
965 
styleSheetURL(CSSStyleSheet * pageStyleSheet)966 static String styleSheetURL(CSSStyleSheet* pageStyleSheet)
967 {
968     if (pageStyleSheet && !pageStyleSheet->contents()->baseURL().isEmpty())
969         return pageStyleSheet->contents()->baseURL().string();
970     return emptyString();
971 }
972 
finalURL() const973 String InspectorStyleSheet::finalURL() const
974 {
975     String url = styleSheetURL(m_pageStyleSheet.get());
976     return url.isEmpty() ? m_documentURL : url;
977 }
978 
setText(const String & text,ExceptionState & exceptionState)979 bool InspectorStyleSheet::setText(const String& text, ExceptionState& exceptionState)
980 {
981     updateText(text);
982     m_flatRules.clear();
983 
984     if (listener())
985         listener()->willReparseStyleSheet();
986 
987     {
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();
992     }
993     {
994         CSSStyleSheet::RuleMutationScope mutationScope(m_pageStyleSheet.get());
995         m_pageStyleSheet->contents()->parseString(text);
996     }
997 
998     if (listener())
999         listener()->didReparseStyleSheet();
1000     fireStyleSheetChanged();
1001     m_pageStyleSheet->ownerDocument()->styleResolverChanged(FullStyleUpdate);
1002     return true;
1003 }
1004 
ruleSelector(const InspectorCSSId & id,ExceptionState & exceptionState)1005 String InspectorStyleSheet::ruleSelector(const InspectorCSSId& id, ExceptionState& exceptionState)
1006 {
1007     CSSStyleRule* rule = ruleForId(id);
1008     if (!rule) {
1009         exceptionState.throwDOMException(NotFoundError, "No rule was found for the given ID.");
1010         return "";
1011     }
1012     return rule->selectorText();
1013 }
1014 
setRuleSelector(const InspectorCSSId & id,const String & selector,ExceptionState & exceptionState)1015 bool InspectorStyleSheet::setRuleSelector(const InspectorCSSId& id, const String& selector, ExceptionState& exceptionState)
1016 {
1017     CSSStyleRule* rule = ruleForId(id);
1018     if (!rule) {
1019         exceptionState.throwDOMException(NotFoundError, "No rule was found for the given ID.");
1020         return false;
1021     }
1022     CSSStyleSheet* styleSheet = rule->parentStyleSheet();
1023     if (!styleSheet || !ensureParsedDataReady()) {
1024         exceptionState.throwDOMException(NotFoundError, "No stylesheet could be found in which to set the selector.");
1025         return false;
1026     }
1027 
1028     rule->setSelectorText(selector);
1029     RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = ruleSourceDataAt(id.ordinal());
1030     if (!sourceData) {
1031         exceptionState.throwDOMException(NotFoundError, "The selector '" + selector + "' could not be set.");
1032         return false;
1033     }
1034 
1035     String sheetText = m_parsedStyleSheet->text();
1036     sheetText.replace(sourceData->ruleHeaderRange.start, sourceData->ruleHeaderRange.length(), selector);
1037     updateText(sheetText);
1038     fireStyleSheetChanged();
1039     return true;
1040 }
1041 
ruleIndexBySourceRange(const CSSMediaRule * parentMediaRule,const SourceRange & sourceRange)1042 unsigned InspectorStyleSheet::ruleIndexBySourceRange(const CSSMediaRule* parentMediaRule, const SourceRange& sourceRange)
1043 {
1044     unsigned index = 0;
1045     for (size_t i = 0; i < m_flatRules.size(); ++i) {
1046         RefPtrWillBeRawPtr<CSSRule> rule = m_flatRules.at(i);
1047         if (rule->parentRule() != parentMediaRule)
1048             continue;
1049         RefPtrWillBeRawPtr<CSSRuleSourceData> ruleSourceData = m_parsedStyleSheet->ruleSourceDataAt(i);
1050         if (ruleSourceData->ruleBodyRange.end < sourceRange.start)
1051             ++index;
1052     }
1053     return index;
1054 }
1055 
insertCSSOMRuleInStyleSheet(const SourceRange & sourceRange,const String & ruleText,ExceptionState & exceptionState)1056 CSSStyleRule* InspectorStyleSheet::insertCSSOMRuleInStyleSheet(const SourceRange& sourceRange, const String& ruleText, ExceptionState& exceptionState)
1057 {
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);
1062     if (!styleRule) {
1063         m_pageStyleSheet->deleteRule(index, ASSERT_NO_EXCEPTION);
1064         exceptionState.throwDOMException(SyntaxError, "The rule '" + ruleText + "' could not be added in style sheet.");
1065         return 0;
1066     }
1067     return styleRule;
1068 }
1069 
insertCSSOMRuleInMediaRule(CSSMediaRule * mediaRule,const SourceRange & sourceRange,const String & ruleText,ExceptionState & exceptionState)1070 CSSStyleRule* InspectorStyleSheet::insertCSSOMRuleInMediaRule(CSSMediaRule* mediaRule, const SourceRange& sourceRange, const String& ruleText, ExceptionState& exceptionState)
1071 {
1072     unsigned index = ruleIndexBySourceRange(mediaRule, sourceRange);
1073     mediaRule->insertRule(ruleText, index, exceptionState);
1074     CSSRule* rule = mediaRule->item(index);
1075     CSSStyleRule* styleRule = InspectorCSSAgent::asCSSStyleRule(rule);
1076     if (!styleRule) {
1077         mediaRule->deleteRule(index, ASSERT_NO_EXCEPTION);
1078         exceptionState.throwDOMException(SyntaxError, "The rule '" + ruleText + "' could not be added in media rule.");
1079         return 0;
1080     }
1081     return styleRule;
1082 }
1083 
insertCSSOMRuleBySourceRange(const SourceRange & sourceRange,const String & ruleText,ExceptionState & exceptionState)1084 CSSStyleRule* InspectorStyleSheet::insertCSSOMRuleBySourceRange(const SourceRange& sourceRange, const String& ruleText, ExceptionState& exceptionState)
1085 {
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.");
1092             return 0;
1093         }
1094         if (sourceRange.start < ruleSourceData->ruleBodyRange.start || ruleSourceData->ruleBodyRange.end < sourceRange.start)
1095             continue;
1096         if (containingRuleIndex == -1 || containingRuleLength > ruleSourceData->ruleBodyRange.length()) {
1097             containingRuleIndex = i;
1098             containingRuleLength = ruleSourceData->ruleBodyRange.length();
1099         }
1100     }
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.");
1106         return 0;
1107     }
1108     return insertCSSOMRuleInMediaRule(toCSSMediaRule(rule.get()), sourceRange, ruleText, exceptionState);
1109 }
1110 
verifyRuleText(const String & ruleText)1111 bool InspectorStyleSheet::verifyRuleText(const String& ruleText)
1112 {
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();
1120 
1121     // Exactly two rules should be parsed.
1122     if (ruleCount != 2)
1123         return false;
1124 
1125     // Added rule must be style rule.
1126     if (!sourceData.at(0)->styleSourceData)
1127         return false;
1128 
1129     WillBeHeapVector<CSSPropertySourceData>& propertyData = sourceData.at(1)->styleSourceData->propertyData;
1130     unsigned propertyCount = propertyData.size();
1131 
1132     // Exactly one property should be in rule.
1133     if (propertyCount != 1)
1134         return false;
1135 
1136     // Check for the property name.
1137     if (propertyData.at(0).name != bogusPropertyName)
1138         return false;
1139 
1140     return true;
1141 }
1142 
addRule(const String & ruleText,const SourceRange & location,ExceptionState & exceptionState)1143 CSSStyleRule* InspectorStyleSheet::addRule(const String& ruleText, const SourceRange& location, ExceptionState& exceptionState)
1144 {
1145     if (!ensureParsedDataReady()) {
1146         exceptionState.throwDOMException(NotFoundError, "Cannot parse style sheet.");
1147         return 0;
1148     }
1149 
1150     if (location.start != location.end) {
1151         exceptionState.throwDOMException(NotFoundError, "Source range must be collapsed.");
1152         return 0;
1153     }
1154 
1155     if (!verifyRuleText(ruleText)) {
1156         exceptionState.throwDOMException(SyntaxError, "Rule text is not valid.");
1157         return 0;
1158     }
1159 
1160     String text;
1161     bool success = getText(&text);
1162     if (!success) {
1163         exceptionState.throwDOMException(NotFoundError, "The rule '" + ruleText + "' could not be added.");
1164         return 0;
1165     }
1166 
1167     ensureFlatRules();
1168     CSSStyleRule* styleRule = insertCSSOMRuleBySourceRange(location, ruleText, exceptionState);
1169     if (exceptionState.hadException())
1170         return 0;
1171 
1172     text.insert(ruleText, location.start);
1173 
1174     m_parsedStyleSheet->setText(text);
1175     m_flatRules.clear();
1176 
1177     fireStyleSheetChanged();
1178     return styleRule;
1179 }
1180 
deleteRule(const InspectorCSSId & id,const String & oldText,ExceptionState & exceptionState)1181 bool InspectorStyleSheet::deleteRule(const InspectorCSSId& id, const String& oldText, ExceptionState& exceptionState)
1182 {
1183     RefPtrWillBeRawPtr<CSSStyleRule> rule = ruleForId(id);
1184     if (!rule) {
1185         exceptionState.throwDOMException(NotFoundError, "No style rule could be found for the provided ID.");
1186         return false;
1187     }
1188     CSSStyleSheet* styleSheet = rule->parentStyleSheet();
1189     if (!styleSheet || !ensureParsedDataReady()) {
1190         exceptionState.throwDOMException(NotFoundError, "No parent stylesheet could be found.");
1191         return false;
1192     }
1193 
1194     RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = ruleSourceDataAt(id.ordinal());
1195     if (!sourceData) {
1196         exceptionState.throwDOMException(NotFoundError, "No style rule could be found for the provided ID.");
1197         return false;
1198     }
1199 
1200     CSSRule* parentRule = rule->parentRule();
1201     if (parentRule) {
1202         if (parentRule->type() != CSSRule::MEDIA_RULE) {
1203             exceptionState.throwDOMException(NotFoundError, "Cannot remove rule from non-media rule.");
1204             return false;
1205         }
1206         CSSMediaRule* parentMediaRule = toCSSMediaRule(parentRule);
1207         size_t index = 0;
1208         while (index < parentMediaRule->length() && parentMediaRule->item(index) != rule)
1209             ++index;
1210         ASSERT(index < parentMediaRule->length());
1211         parentMediaRule->deleteRule(index, exceptionState);
1212     } else {
1213         size_t index = 0;
1214         while (index < styleSheet->length() && styleSheet->item(index) != rule)
1215             ++index;
1216         ASSERT(index < styleSheet->length());
1217         styleSheet->deleteRule(index, exceptionState);
1218     }
1219     // |rule| MAY NOT be addressed after this line!
1220 
1221     if (exceptionState.hadException())
1222         return false;
1223 
1224     m_parsedStyleSheet->setText(oldText);
1225     m_flatRules.clear();
1226     fireStyleSheetChanged();
1227     return true;
1228 }
1229 
updateText(const String & newText)1230 void InspectorStyleSheet::updateText(const String& newText)
1231 {
1232     Element* element = ownerStyleElement();
1233     if (!element)
1234         m_pageAgent->addEditedResourceContent(finalURL(), newText);
1235     m_parsedStyleSheet->setText(newText);
1236 }
1237 
1238 
ruleForId(const InspectorCSSId & id) const1239 CSSStyleRule* InspectorStyleSheet::ruleForId(const InspectorCSSId& id) const
1240 {
1241     ASSERT(!id.isEmpty());
1242     ensureFlatRules();
1243     return InspectorCSSAgent::asCSSStyleRule(id.ordinal() >= m_flatRules.size() ? 0 : m_flatRules.at(id.ordinal()).get());
1244 }
1245 
buildObjectForStyleSheetInfo() const1246 PassRefPtr<TypeBuilder::CSS::CSSStyleSheetHeader> InspectorStyleSheet::buildObjectForStyleSheetInfo() const
1247 {
1248     CSSStyleSheet* styleSheet = pageStyleSheet();
1249     if (!styleSheet)
1250         return nullptr;
1251 
1252     Document* document = styleSheet->ownerDocument();
1253     LocalFrame* frame = document ? document->frame() : 0;
1254 
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());
1265 
1266     if (hasSourceURL())
1267         result->setHasSourceURL(true);
1268 
1269     String sourceMapURLValue = sourceMapURL();
1270     if (!sourceMapURLValue.isEmpty())
1271         result->setSourceMapURL(sourceMapURLValue);
1272     return result.release();
1273 }
1274 
selectorsFromSource(const CSSRuleSourceData * sourceData,const String & sheetText)1275 PassRefPtr<TypeBuilder::Array<TypeBuilder::CSS::Selector> > InspectorStyleSheet::selectorsFromSource(const CSSRuleSourceData* sourceData, const String& sheetText)
1276 {
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());
1283 
1284         // We don't want to see any comments in the selector components, only the meaningful parts.
1285         int matchLength;
1286         int offset = 0;
1287         while ((offset = comment.match(selector, offset, &matchLength)) >= 0)
1288             selector.replace(offset, matchLength, "");
1289 
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());
1294     }
1295     return result.release();
1296 }
1297 
buildObjectForSelectorList(CSSStyleRule * rule)1298 PassRefPtr<TypeBuilder::CSS::SelectorList> InspectorStyleSheet::buildObjectForSelectorList(CSSStyleRule* rule)
1299 {
1300     RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = nullptr;
1301     if (ensureParsedDataReady())
1302         sourceData = ruleSourceDataAt(styleId(rule->style()).ordinal());
1303     RefPtr<TypeBuilder::Array<TypeBuilder::CSS::Selector> > selectors;
1304 
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();
1307 
1308     if (sourceData)
1309         selectors = selectorsFromSource(sourceData.get(), m_parsedStyleSheet->text());
1310     else {
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());
1315     }
1316     RefPtr<TypeBuilder::CSS::SelectorList> result = TypeBuilder::CSS::SelectorList::create()
1317         .setSelectors(selectors)
1318         .setText(selectorText)
1319         .release();
1320     return result.release();
1321 }
1322 
canBind(TypeBuilder::CSS::StyleSheetOrigin::Enum origin)1323 static bool canBind(TypeBuilder::CSS::StyleSheetOrigin::Enum origin)
1324 {
1325     return origin != TypeBuilder::CSS::StyleSheetOrigin::User_agent && origin != TypeBuilder::CSS::StyleSheetOrigin::User;
1326 }
1327 
buildObjectForRule(CSSStyleRule * rule,PassRefPtr<Array<TypeBuilder::CSS::CSSMedia>> mediaStack)1328 PassRefPtr<TypeBuilder::CSS::CSSRule> InspectorStyleSheet::buildObjectForRule(CSSStyleRule* rule, PassRefPtr<Array<TypeBuilder::CSS::CSSMedia> > mediaStack)
1329 {
1330     CSSStyleSheet* styleSheet = pageStyleSheet();
1331     if (!styleSheet)
1332         return nullptr;
1333 
1334     RefPtr<TypeBuilder::CSS::CSSRule> result = TypeBuilder::CSS::CSSRule::create()
1335         .setSelectorList(buildObjectForSelectorList(rule))
1336         .setOrigin(m_origin)
1337         .setStyle(buildObjectForStyle(rule->style()));
1338 
1339     if (canBind(m_origin)) {
1340         InspectorCSSId id(ruleId(rule));
1341         if (!id.isEmpty())
1342             result->setStyleSheetId(id.styleSheetId());
1343     }
1344 
1345     if (mediaStack)
1346         result->setMedia(mediaStack);
1347 
1348     return result.release();
1349 }
1350 
getText(String * result) const1351 bool InspectorStyleSheet::getText(String* result) const
1352 {
1353     if (!ensureText())
1354         return false;
1355     *result = m_parsedStyleSheet->text();
1356     return true;
1357 }
1358 
styleForId(const InspectorCSSId & id) const1359 CSSStyleDeclaration* InspectorStyleSheet::styleForId(const InspectorCSSId& id) const
1360 {
1361     CSSStyleRule* rule = ruleForId(id);
1362     if (!rule)
1363         return 0;
1364 
1365     return rule->style();
1366 }
1367 
ruleHeaderSourceRange(const CSSRule * rule)1368 PassRefPtr<TypeBuilder::CSS::SourceRange> InspectorStyleSheet::ruleHeaderSourceRange(const CSSRule* rule)
1369 {
1370     if (!ensureParsedDataReady())
1371         return nullptr;
1372 
1373     ensureFlatRules();
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())
1379         return nullptr;
1380     RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = m_parsedStyleSheet->ruleSourceDataAt(static_cast<unsigned>(index));
1381     return buildSourceRangeObject(sourceData->ruleHeaderRange, lineEndings().get());
1382 }
1383 
inspectorStyleForId(const InspectorCSSId & id)1384 PassRefPtrWillBeRawPtr<InspectorStyle> InspectorStyleSheet::inspectorStyleForId(const InspectorCSSId& id)
1385 {
1386     CSSStyleDeclaration* style = styleForId(id);
1387     if (!style)
1388         return nullptr;
1389 
1390     return InspectorStyle::create(id, style, this);
1391 }
1392 
ruleCount()1393 unsigned InspectorStyleSheet::ruleCount()
1394 {
1395     return m_parsedStyleSheet->ruleCount();
1396 }
1397 
sourceURL() const1398 String InspectorStyleSheet::sourceURL() const
1399 {
1400     if (!m_sourceURL.isNull())
1401         return m_sourceURL;
1402     if (m_origin != TypeBuilder::CSS::StyleSheetOrigin::Regular) {
1403         m_sourceURL = "";
1404         return m_sourceURL;
1405     }
1406 
1407     String styleSheetText;
1408     bool success = getText(&styleSheetText);
1409     if (success) {
1410         bool deprecated;
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;
1416         }
1417     }
1418     m_sourceURL = "";
1419     return m_sourceURL;
1420 }
1421 
url() const1422 String InspectorStyleSheet::url() const
1423 {
1424     // "sourceURL" is present only for regular rules, otherwise "origin" should be used in the frontend.
1425     if (m_origin != TypeBuilder::CSS::StyleSheetOrigin::Regular)
1426         return String();
1427 
1428     CSSStyleSheet* styleSheet = pageStyleSheet();
1429     if (!styleSheet)
1430         return String();
1431 
1432     if (hasSourceURL())
1433         return sourceURL();
1434 
1435     if (styleSheet->isInline() && startsAtZero())
1436         return String();
1437 
1438     return finalURL();
1439 }
1440 
hasSourceURL() const1441 bool InspectorStyleSheet::hasSourceURL() const
1442 {
1443     return !sourceURL().isEmpty();
1444 }
1445 
startsAtZero() const1446 bool InspectorStyleSheet::startsAtZero() const
1447 {
1448     CSSStyleSheet* styleSheet = pageStyleSheet();
1449     if (!styleSheet)
1450         return true;
1451 
1452     return styleSheet->startPositionInSource() == TextPosition::minimumPosition();
1453 }
1454 
sourceMapURL() const1455 String InspectorStyleSheet::sourceMapURL() const
1456 {
1457     if (m_origin != TypeBuilder::CSS::StyleSheetOrigin::Regular)
1458         return String();
1459 
1460     String styleSheetText;
1461     bool success = getText(&styleSheetText);
1462     if (success) {
1463         bool deprecated;
1464         String commentValue = ContentSearchUtils::findSourceMapURL(styleSheetText, ContentSearchUtils::CSSMagicComment, &deprecated);
1465         if (!commentValue.isEmpty()) {
1466             // FIXME: add deprecated console message here.
1467             return commentValue;
1468         }
1469     }
1470     return m_pageAgent->resourceSourceMapURL(finalURL());
1471 }
1472 
styleId(CSSStyleDeclaration * style) const1473 InspectorCSSId InspectorStyleSheet::styleId(CSSStyleDeclaration* style) const
1474 {
1475     unsigned index = ruleIndexByStyle(style);
1476     if (index != UINT_MAX)
1477         return InspectorCSSId(id(), index);
1478     return InspectorCSSId();
1479 }
1480 
findRuleBySelectorRange(const SourceRange & sourceRange,InspectorCSSId * ruleId)1481 bool InspectorStyleSheet::findRuleBySelectorRange(const SourceRange& sourceRange, InspectorCSSId* ruleId)
1482 {
1483     if (!ensureParsedDataReady())
1484         return false;
1485     for (size_t i = 0; i < ruleCount(); ++i) {
1486         RefPtrWillBeRawPtr<CSSRuleSourceData> ruleSourceData = ruleSourceDataAt(i);
1487         if (!ruleSourceData->styleSourceData)
1488             continue;
1489         if (ruleSourceData->ruleHeaderRange.start == sourceRange.start && ruleSourceData->ruleHeaderRange.end == sourceRange.end) {
1490             *ruleId = InspectorCSSId(id(), i);
1491             return true;
1492         }
1493     }
1494     return false;
1495 }
1496 
flatRules()1497 const CSSRuleVector& InspectorStyleSheet::flatRules()
1498 {
1499     ensureFlatRules();
1500     return m_flatRules;
1501 }
1502 
ownerDocument() const1503 Document* InspectorStyleSheet::ownerDocument() const
1504 {
1505     return m_pageStyleSheet->ownerDocument();
1506 }
1507 
ruleSourceDataAt(unsigned ruleIndex) const1508 PassRefPtrWillBeRawPtr<CSSRuleSourceData> InspectorStyleSheet::ruleSourceDataAt(unsigned ruleIndex) const
1509 {
1510     return m_parsedStyleSheet->ruleSourceDataAt(ruleIndex);
1511 }
1512 
ruleIndexByStyle(CSSStyleDeclaration * pageStyle) const1513 unsigned InspectorStyleSheet::ruleIndexByStyle(CSSStyleDeclaration* pageStyle) const
1514 {
1515     ensureFlatRules();
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)
1519             return i;
1520     }
1521     return UINT_MAX;
1522 }
1523 
ensureParsedDataReady()1524 bool InspectorStyleSheet::ensureParsedDataReady()
1525 {
1526     return ensureText() && m_parsedStyleSheet->ensureSourceData();
1527 }
1528 
ensureText() const1529 bool InspectorStyleSheet::ensureText() const
1530 {
1531     if (m_parsedStyleSheet->hasText())
1532         return true;
1533 
1534     String text;
1535     bool success = originalStyleSheetText(&text);
1536     if (success)
1537         m_parsedStyleSheet->setText(text);
1538     // No need to clear m_flatRules here - it's empty.
1539 
1540     return success;
1541 }
1542 
1543 template <typename RuleList>
collectFlatRules(RuleList ruleList,CSSRuleVector * result)1544 static void collectFlatRules(RuleList ruleList, CSSRuleVector* result)
1545 {
1546     if (!ruleList)
1547         return;
1548 
1549     for (unsigned i = 0, size = ruleList->length(); i < size; ++i) {
1550         CSSRule* rule = ruleList->item(i);
1551 
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);
1562             break;
1563         case CSSRule::MEDIA_RULE:
1564         case CSSRule::SUPPORTS_RULE:
1565             result->append(rule);
1566             collectFlatRules(asCSSRuleList(rule), result);
1567             break;
1568         default:
1569             break;
1570         }
1571     }
1572 }
1573 
ensureFlatRules() const1574 void InspectorStyleSheet::ensureFlatRules() const
1575 {
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);
1579 }
1580 
setStyleText(const InspectorCSSId & id,const String & text)1581 bool InspectorStyleSheet::setStyleText(const InspectorCSSId& id, const String& text)
1582 {
1583     CSSStyleDeclaration* style = styleForId(id);
1584     if (!style)
1585         return false;
1586 
1587     if (!ensureParsedDataReady())
1588         return false;
1589 
1590     String patchedStyleSheetText;
1591     bool success = styleSheetTextWithChangedStyle(style, text, &patchedStyleSheetText);
1592     if (!success)
1593         return false;
1594 
1595     TrackExceptionState exceptionState;
1596     style->setCSSText(text, exceptionState);
1597     if (!exceptionState.hadException()) {
1598         updateText(patchedStyleSheetText);
1599         fireStyleSheetChanged();
1600     }
1601 
1602     return !exceptionState.hadException();
1603 }
1604 
styleSheetTextWithChangedStyle(CSSStyleDeclaration * style,const String & newStyleText,String * result)1605 bool InspectorStyleSheet::styleSheetTextWithChangedStyle(CSSStyleDeclaration* style, const String& newStyleText, String* result)
1606 {
1607     if (!style)
1608         return false;
1609     if (!ensureParsedDataReady())
1610         return false;
1611 
1612     RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = ruleSourceDataAt(styleId(style).ordinal());
1613     unsigned bodyStart = sourceData->ruleBodyRange.start;
1614     unsigned bodyEnd = sourceData->ruleBodyRange.end;
1615     ASSERT(bodyStart <= bodyEnd);
1616 
1617     String text = m_parsedStyleSheet->text();
1618     ASSERT_WITH_SECURITY_IMPLICATION(bodyEnd <= text.length()); // bodyEnd is exclusive
1619 
1620     text.replace(bodyStart, bodyEnd - bodyStart, newStyleText);
1621     *result = text;
1622     return true;
1623 }
1624 
ruleId(CSSStyleRule * rule) const1625 InspectorCSSId InspectorStyleSheet::ruleId(CSSStyleRule* rule) const
1626 {
1627     return styleId(rule->style());
1628 }
1629 
originalStyleSheetText(String * result) const1630 bool InspectorStyleSheet::originalStyleSheetText(String* result) const
1631 {
1632     bool success = inlineStyleSheetText(result);
1633     if (!success)
1634         success = resourceStyleSheetText(result);
1635     return success;
1636 }
1637 
resourceStyleSheetText(String * result) const1638 bool InspectorStyleSheet::resourceStyleSheetText(String* result) const
1639 {
1640     if (m_origin == TypeBuilder::CSS::StyleSheetOrigin::User || m_origin == TypeBuilder::CSS::StyleSheetOrigin::User_agent)
1641         return false;
1642 
1643     if (!ownerDocument())
1644         return false;
1645 
1646     KURL url(ParsedURLString, m_pageStyleSheet->href());
1647     if (m_pageAgent->getEditedResourceContent(url, result))
1648         return true;
1649 
1650     bool base64Encoded;
1651     bool success = m_resourceAgent->fetchResourceContent(ownerDocument(), url, result, &base64Encoded);
1652     return success && !base64Encoded;
1653 }
1654 
ownerStyleElement() const1655 Element* InspectorStyleSheet::ownerStyleElement() const
1656 {
1657     Node* ownerNode = m_pageStyleSheet->ownerNode();
1658     if (!ownerNode || !ownerNode->isElementNode())
1659         return 0;
1660     Element* ownerElement = toElement(ownerNode);
1661 
1662     if (!isHTMLStyleElement(ownerElement) && !isSVGStyleElement(ownerElement))
1663         return 0;
1664     return ownerElement;
1665 }
1666 
inlineStyleSheetText(String * result) const1667 bool InspectorStyleSheet::inlineStyleSheetText(String* result) const
1668 {
1669     Element* ownerElement = ownerStyleElement();
1670     if (!ownerElement)
1671         return false;
1672     *result = ownerElement->textContent();
1673     return true;
1674 }
1675 
create(const String & id,PassRefPtrWillBeRawPtr<Element> element,Listener * listener)1676 PassRefPtrWillBeRawPtr<InspectorStyleSheetForInlineStyle> InspectorStyleSheetForInlineStyle::create(const String& id, PassRefPtrWillBeRawPtr<Element> element, Listener* listener)
1677 {
1678     return adoptRefWillBeNoop(new InspectorStyleSheetForInlineStyle(id, element, listener));
1679 }
1680 
InspectorStyleSheetForInlineStyle(const String & id,PassRefPtrWillBeRawPtr<Element> element,Listener * 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)
1686 {
1687     ASSERT(m_element);
1688     m_inspectorStyle = InspectorStyle::create(InspectorCSSId(id, 0), inlineStyle(), this);
1689     m_styleText = m_element->isStyledElement() ? m_element->getAttribute("style").string() : String();
1690 }
1691 
didModifyElementAttribute()1692 void InspectorStyleSheetForInlineStyle::didModifyElementAttribute()
1693 {
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();
1698 }
1699 
setText(const String & text,ExceptionState & exceptionState)1700 bool InspectorStyleSheetForInlineStyle::setText(const String& text, ExceptionState& exceptionState)
1701 {
1702     bool success = setStyleText(InspectorCSSId(id(), 0), text);
1703     if (!success)
1704         exceptionState.throwDOMException(SyntaxError, "Style sheet text is invalid.");
1705     else
1706         fireStyleSheetChanged();
1707     return success;
1708 }
1709 
getText(String * result) const1710 bool InspectorStyleSheetForInlineStyle::getText(String* result) const
1711 {
1712     if (!m_isStyleTextValid) {
1713         m_styleText = elementStyleText();
1714         m_isStyleTextValid = true;
1715     }
1716     *result = m_styleText;
1717     return true;
1718 }
1719 
setStyleText(const InspectorCSSId & id,const String & text)1720 bool InspectorStyleSheetForInlineStyle::setStyleText(const InspectorCSSId& id, const String& text)
1721 {
1722     CSSStyleDeclaration* style = styleForId(id);
1723     if (!style)
1724         return false;
1725     ASSERT_UNUSED(style, style == inlineStyle());
1726     TrackExceptionState exceptionState;
1727 
1728     {
1729         InspectorCSSAgent::InlineStyleOverrideScope overrideScope(m_element->ownerDocument());
1730         m_element->setAttribute("style", AtomicString(text), exceptionState);
1731     }
1732     if (!exceptionState.hadException()) {
1733         m_styleText = text;
1734         m_isStyleTextValid = true;
1735         m_ruleSourceData.clear();
1736         fireStyleSheetChanged();
1737     }
1738     return !exceptionState.hadException();
1739 }
1740 
ownerDocument() const1741 Document* InspectorStyleSheetForInlineStyle::ownerDocument() const
1742 {
1743     return &m_element->document();
1744 }
1745 
ensureParsedDataReady()1746 bool InspectorStyleSheetForInlineStyle::ensureParsedDataReady()
1747 {
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;
1754     }
1755 
1756     if (m_ruleSourceData)
1757         return true;
1758 
1759     m_ruleSourceData = getStyleAttributeData();
1760 
1761     bool success = !!m_ruleSourceData;
1762     if (!success) {
1763         m_ruleSourceData = CSSRuleSourceData::create(CSSRuleSourceData::STYLE_RULE);
1764         return false;
1765     }
1766 
1767     return true;
1768 }
1769 
inspectorStyleForId(const InspectorCSSId & id)1770 PassRefPtrWillBeRawPtr<InspectorStyle> InspectorStyleSheetForInlineStyle::inspectorStyleForId(const InspectorCSSId& id)
1771 {
1772     ASSERT_UNUSED(id, !id.ordinal());
1773     return m_inspectorStyle;
1774 }
1775 
inlineStyle() const1776 CSSStyleDeclaration* InspectorStyleSheetForInlineStyle::inlineStyle() const
1777 {
1778     return m_element->style();
1779 }
1780 
elementStyleText() const1781 const String& InspectorStyleSheetForInlineStyle::elementStyleText() const
1782 {
1783     return m_element->getAttribute("style").string();
1784 }
1785 
getStyleAttributeData() const1786 PassRefPtrWillBeRawPtr<CSSRuleSourceData> InspectorStyleSheetForInlineStyle::getStyleAttributeData() const
1787 {
1788     if (!m_element->isStyledElement())
1789         return nullptr;
1790 
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();
1796     }
1797 
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();
1803 }
1804 
trace(Visitor * visitor)1805 void InspectorStyleSheetForInlineStyle::trace(Visitor* visitor)
1806 {
1807     visitor->trace(m_element);
1808     visitor->trace(m_ruleSourceData);
1809     visitor->trace(m_inspectorStyle);
1810     InspectorStyleSheetBase::trace(visitor);
1811 }
1812 
1813 } // namespace blink
1814 
1815