• 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 "CSSPropertyNames.h"
29 #include "HTMLNames.h"
30 #include "SVGNames.h"
31 #include "bindings/v8/ExceptionState.h"
32 #include "bindings/v8/ExceptionStatePlaceholder.h"
33 #include "bindings/v8/ScriptRegexp.h"
34 #include "core/css/CSSKeyframesRule.h"
35 #include "core/css/CSSMediaRule.h"
36 #include "core/css/CSSParser.h"
37 #include "core/css/CSSRule.h"
38 #include "core/css/CSSRuleList.h"
39 #include "core/css/CSSStyleRule.h"
40 #include "core/css/CSSStyleSheet.h"
41 #include "core/css/CSSSupportsRule.h"
42 #include "core/css/StylePropertySet.h"
43 #include "core/css/StyleRule.h"
44 #include "core/css/StyleSheetContents.h"
45 #include "core/dom/Document.h"
46 #include "core/dom/Element.h"
47 #include "core/dom/Node.h"
48 #include "core/html/parser/HTMLParserIdioms.h"
49 #include "core/inspector/ContentSearchUtils.h"
50 #include "core/inspector/InspectorCSSAgent.h"
51 #include "core/inspector/InspectorPageAgent.h"
52 #include "core/inspector/InspectorResourceAgent.h"
53 #include "core/page/Page.h"
54 #include "core/page/PageConsole.h"
55 #include "wtf/OwnPtr.h"
56 #include "wtf/PassOwnPtr.h"
57 #include "wtf/text/StringBuilder.h"
58 #include "wtf/text/TextPosition.h"
59 
60 using WebCore::TypeBuilder::Array;
61 using WebCore::RuleSourceDataList;
62 using WebCore::CSSRuleSourceData;
63 
64 class ParsedStyleSheet {
65     WTF_MAKE_FAST_ALLOCATED;
66 public:
67     ParsedStyleSheet();
68 
text() const69     const String& text() const { ASSERT(m_hasText); return m_text; }
70     void setText(const String& text);
hasText() const71     bool hasText() const { return m_hasText; }
72     void setSourceData(PassOwnPtr<RuleSourceDataList>);
hasSourceData() const73     bool hasSourceData() const { return m_sourceData; }
74     PassRefPtr<WebCore::CSSRuleSourceData> ruleSourceDataAt(unsigned) const;
75 
76 private:
77     void flattenSourceData(RuleSourceDataList*);
78 
79     String m_text;
80     bool m_hasText;
81     OwnPtr<RuleSourceDataList> m_sourceData;
82 };
83 
ParsedStyleSheet()84 ParsedStyleSheet::ParsedStyleSheet()
85     : m_hasText(false)
86 {
87 }
88 
setText(const String & text)89 void ParsedStyleSheet::setText(const String& text)
90 {
91     m_hasText = true;
92     m_text = text;
93     setSourceData(nullptr);
94 }
95 
flattenSourceData(RuleSourceDataList * dataList)96 void ParsedStyleSheet::flattenSourceData(RuleSourceDataList* dataList)
97 {
98     for (size_t i = 0; i < dataList->size(); ++i) {
99         RefPtr<CSSRuleSourceData>& data = dataList->at(i);
100         if (data->type == CSSRuleSourceData::STYLE_RULE) {
101             m_sourceData->append(data);
102         } else if (data->type == CSSRuleSourceData::IMPORT_RULE) {
103             m_sourceData->append(data);
104         } else if (data->type == CSSRuleSourceData::MEDIA_RULE) {
105             m_sourceData->append(data);
106             flattenSourceData(&data->childRules);
107         } else if (data->type == CSSRuleSourceData::SUPPORTS_RULE) {
108             flattenSourceData(&data->childRules);
109         }
110     }
111 }
112 
setSourceData(PassOwnPtr<RuleSourceDataList> sourceData)113 void ParsedStyleSheet::setSourceData(PassOwnPtr<RuleSourceDataList> sourceData)
114 {
115     if (!sourceData) {
116         m_sourceData.clear();
117         return;
118     }
119 
120     m_sourceData = adoptPtr(new RuleSourceDataList());
121 
122     // FIXME: This is a temporary solution to retain the original flat sourceData structure
123     // containing only style rules, even though CSSParser now provides the full rule source data tree.
124     // Normally, we should just assign m_sourceData = sourceData;
125     flattenSourceData(sourceData.get());
126 }
127 
ruleSourceDataAt(unsigned index) const128 PassRefPtr<WebCore::CSSRuleSourceData> ParsedStyleSheet::ruleSourceDataAt(unsigned index) const
129 {
130     if (!hasSourceData() || index >= m_sourceData->size())
131         return 0;
132 
133     return m_sourceData->at(index);
134 }
135 
136 namespace WebCore {
137 
createCSSParser(Document * document)138 static PassOwnPtr<CSSParser> createCSSParser(Document* document)
139 {
140     UseCounter* counter = 0;
141     return adoptPtr(new CSSParser(document ? CSSParserContext(*document) : strictCSSParserContext(), counter));
142 }
143 
144 namespace {
145 
146 class StyleSheetHandler : public CSSParser::SourceDataHandler {
147 public:
StyleSheetHandler(const String & parsedText,Document * document,StyleSheetContents * styleSheetContents,RuleSourceDataList * result)148     StyleSheetHandler(const String& parsedText, Document* document, StyleSheetContents* styleSheetContents, RuleSourceDataList* result)
149         : m_parsedText(parsedText)
150         , m_document(document)
151         , m_styleSheetContents(styleSheetContents)
152         , m_result(result)
153         , m_propertyRangeStart(UINT_MAX)
154         , m_selectorRangeStart(UINT_MAX)
155         , m_commentRangeStart(UINT_MAX)
156     {
157         ASSERT(m_result);
158     }
159 
160 private:
161     virtual void startRuleHeader(CSSRuleSourceData::Type, unsigned) OVERRIDE;
162     virtual void endRuleHeader(unsigned) OVERRIDE;
163     virtual void startSelector(unsigned) OVERRIDE;
164     virtual void endSelector(unsigned) OVERRIDE;
165     virtual void startRuleBody(unsigned) OVERRIDE;
166     virtual void endRuleBody(unsigned, bool) OVERRIDE;
startEndUnknownRule()167     virtual void startEndUnknownRule() OVERRIDE { addNewRuleToSourceTree(CSSRuleSourceData::createUnknown()); }
168     virtual void startProperty(unsigned) OVERRIDE;
169     virtual void endProperty(bool, bool, unsigned, CSSParser::ErrorType) OVERRIDE;
170     virtual void startComment(unsigned) OVERRIDE;
171     virtual void endComment(unsigned) OVERRIDE;
172 
173     void addNewRuleToSourceTree(PassRefPtr<CSSRuleSourceData>);
174     PassRefPtr<CSSRuleSourceData> popRuleData();
175     template <typename CharacterType> inline void setRuleHeaderEnd(const CharacterType*, unsigned);
176     void fixUnparsedPropertyRanges(CSSRuleSourceData*);
177 
178     const String& m_parsedText;
179     Document* m_document;
180     StyleSheetContents* m_styleSheetContents;
181     RuleSourceDataList* m_result;
182     RuleSourceDataList m_currentRuleDataStack;
183     RefPtr<CSSRuleSourceData> m_currentRuleData;
184     OwnPtr<CSSParser> m_commentParser;
185     unsigned m_propertyRangeStart;
186     unsigned m_selectorRangeStart;
187     unsigned m_commentRangeStart;
188 };
189 
startRuleHeader(CSSRuleSourceData::Type type,unsigned offset)190 void StyleSheetHandler::startRuleHeader(CSSRuleSourceData::Type type, unsigned offset)
191 {
192     // Pop off data for a previous invalid rule.
193     if (m_currentRuleData)
194         m_currentRuleDataStack.removeLast();
195 
196     RefPtr<CSSRuleSourceData> data = CSSRuleSourceData::create(type);
197     data->ruleHeaderRange.start = offset;
198     m_currentRuleData = data;
199     m_currentRuleDataStack.append(data.release());
200 }
201 
202 template <typename CharacterType>
setRuleHeaderEnd(const CharacterType * dataStart,unsigned listEndOffset)203 inline void StyleSheetHandler::setRuleHeaderEnd(const CharacterType* dataStart, unsigned listEndOffset)
204 {
205     while (listEndOffset > 1) {
206         if (isHTMLSpace<CharacterType>(*(dataStart + listEndOffset - 1)))
207             --listEndOffset;
208         else
209             break;
210     }
211 
212     m_currentRuleDataStack.last()->ruleHeaderRange.end = listEndOffset;
213     if (!m_currentRuleDataStack.last()->selectorRanges.isEmpty())
214         m_currentRuleDataStack.last()->selectorRanges.last().end = listEndOffset;
215 }
216 
endRuleHeader(unsigned offset)217 void StyleSheetHandler::endRuleHeader(unsigned offset)
218 {
219     ASSERT(!m_currentRuleDataStack.isEmpty());
220 
221     if (m_parsedText.is8Bit())
222         setRuleHeaderEnd<LChar>(m_parsedText.characters8(), offset);
223     else
224         setRuleHeaderEnd<UChar>(m_parsedText.characters16(), offset);
225 }
226 
startSelector(unsigned offset)227 void StyleSheetHandler::startSelector(unsigned offset)
228 {
229     m_selectorRangeStart = offset;
230 }
231 
endSelector(unsigned offset)232 void StyleSheetHandler::endSelector(unsigned offset)
233 {
234     ASSERT(m_currentRuleDataStack.size());
235     m_currentRuleDataStack.last()->selectorRanges.append(SourceRange(m_selectorRangeStart, offset));
236     m_selectorRangeStart = UINT_MAX;
237 }
238 
startRuleBody(unsigned offset)239 void StyleSheetHandler::startRuleBody(unsigned offset)
240 {
241     m_currentRuleData.clear();
242     ASSERT(!m_currentRuleDataStack.isEmpty());
243     if (m_parsedText[offset] == '{')
244         ++offset; // Skip the rule body opening brace.
245     m_currentRuleDataStack.last()->ruleBodyRange.start = offset;
246 }
247 
endRuleBody(unsigned offset,bool error)248 void StyleSheetHandler::endRuleBody(unsigned offset, bool error)
249 {
250     ASSERT(!m_currentRuleDataStack.isEmpty());
251     m_currentRuleDataStack.last()->ruleBodyRange.end = offset;
252     m_propertyRangeStart = UINT_MAX;
253     RefPtr<CSSRuleSourceData> rule = popRuleData();
254     if (error)
255         return;
256 
257     fixUnparsedPropertyRanges(rule.get());
258     addNewRuleToSourceTree(rule.release());
259 }
260 
addNewRuleToSourceTree(PassRefPtr<CSSRuleSourceData> rule)261 void StyleSheetHandler::addNewRuleToSourceTree(PassRefPtr<CSSRuleSourceData> rule)
262 {
263     if (m_currentRuleDataStack.isEmpty())
264         m_result->append(rule);
265     else
266         m_currentRuleDataStack.last()->childRules.append(rule);
267 }
268 
popRuleData()269 PassRefPtr<CSSRuleSourceData> StyleSheetHandler::popRuleData()
270 {
271     ASSERT(!m_currentRuleDataStack.isEmpty());
272     m_currentRuleData.clear();
273     RefPtr<CSSRuleSourceData> data = m_currentRuleDataStack.last();
274     m_currentRuleDataStack.removeLast();
275     return data.release();
276 }
277 
278 template <typename CharacterType>
fixUnparsedProperties(const CharacterType * characters,CSSRuleSourceData * ruleData)279 static inline void fixUnparsedProperties(const CharacterType* characters, CSSRuleSourceData* ruleData)
280 {
281     Vector<CSSPropertySourceData>& propertyData = ruleData->styleSourceData->propertyData;
282     unsigned size = propertyData.size();
283     if (!size)
284         return;
285 
286     unsigned styleStart = ruleData->ruleBodyRange.start;
287     CSSPropertySourceData* nextData = &(propertyData.at(0));
288     for (unsigned i = 0; i < size; ++i) {
289         CSSPropertySourceData* currentData = nextData;
290         nextData = i < size - 1 ? &(propertyData.at(i + 1)) : 0;
291 
292         if (currentData->parsedOk)
293             continue;
294         if (currentData->range.end > 0 && characters[styleStart + currentData->range.end - 1] == ';')
295             continue;
296 
297         unsigned propertyEndInStyleSheet;
298         if (!nextData)
299             propertyEndInStyleSheet = ruleData->ruleBodyRange.end - 1;
300         else
301             propertyEndInStyleSheet = styleStart + nextData->range.start - 1;
302 
303         while (isHTMLSpace<CharacterType>(characters[propertyEndInStyleSheet]))
304             --propertyEndInStyleSheet;
305 
306         // propertyEndInStyleSheet points at the last property text character.
307         unsigned newPropertyEnd = propertyEndInStyleSheet - styleStart + 1; // Exclusive of the last property text character.
308         if (currentData->range.end != newPropertyEnd) {
309             currentData->range.end = newPropertyEnd;
310             unsigned valueStartInStyleSheet = styleStart + currentData->range.start + currentData->name.length();
311             while (valueStartInStyleSheet < propertyEndInStyleSheet && characters[valueStartInStyleSheet] != ':')
312                 ++valueStartInStyleSheet;
313             if (valueStartInStyleSheet < propertyEndInStyleSheet)
314                 ++valueStartInStyleSheet; // Shift past the ':'.
315             while (valueStartInStyleSheet < propertyEndInStyleSheet && isHTMLSpace<CharacterType>(characters[valueStartInStyleSheet]))
316                 ++valueStartInStyleSheet;
317             // Need to exclude the trailing ';' from the property value.
318             currentData->value = String(characters + valueStartInStyleSheet, propertyEndInStyleSheet - valueStartInStyleSheet + (characters[propertyEndInStyleSheet] == ';' ? 0 : 1));
319         }
320     }
321 }
322 
fixUnparsedPropertyRanges(CSSRuleSourceData * ruleData)323 void StyleSheetHandler::fixUnparsedPropertyRanges(CSSRuleSourceData* ruleData)
324 {
325     if (!ruleData->styleSourceData)
326         return;
327 
328     if (m_parsedText.is8Bit()) {
329         fixUnparsedProperties<LChar>(m_parsedText.characters8(), ruleData);
330         return;
331     }
332 
333     fixUnparsedProperties<UChar>(m_parsedText.characters16(), ruleData);
334 }
335 
startProperty(unsigned offset)336 void StyleSheetHandler::startProperty(unsigned offset)
337 {
338     if (m_currentRuleDataStack.isEmpty() || !m_currentRuleDataStack.last()->styleSourceData)
339         return;
340     m_propertyRangeStart = offset;
341 }
342 
endProperty(bool isImportant,bool isParsed,unsigned offset,CSSParser::ErrorType errorType)343 void StyleSheetHandler::endProperty(bool isImportant, bool isParsed, unsigned offset, CSSParser::ErrorType errorType)
344 {
345     if (errorType != CSSParser::NoError)
346         m_propertyRangeStart = UINT_MAX;
347 
348     if (m_propertyRangeStart == UINT_MAX || m_currentRuleDataStack.isEmpty() || !m_currentRuleDataStack.last()->styleSourceData)
349         return;
350 
351     ASSERT(offset <= m_parsedText.length());
352     if (offset < m_parsedText.length() && m_parsedText[offset] == ';') // Include semicolon into the property text.
353         ++offset;
354 
355     const unsigned start = m_propertyRangeStart;
356     const unsigned end = offset;
357     ASSERT(start < end);
358     String propertyString = m_parsedText.substring(start, end - start).stripWhiteSpace();
359     if (propertyString.endsWith(';'))
360         propertyString = propertyString.left(propertyString.length() - 1);
361     size_t colonIndex = propertyString.find(':');
362     ASSERT(colonIndex != kNotFound);
363 
364     String name = propertyString.left(colonIndex).stripWhiteSpace();
365     String value = propertyString.substring(colonIndex + 1, propertyString.length()).stripWhiteSpace();
366     // The property range is relative to the declaration start offset.
367     unsigned topRuleBodyRangeStart = m_currentRuleDataStack.last()->ruleBodyRange.start;
368     m_currentRuleDataStack.last()->styleSourceData->propertyData.append(
369         CSSPropertySourceData(name, value, isImportant, false, isParsed, SourceRange(start - topRuleBodyRangeStart, end - topRuleBodyRangeStart)));
370     m_propertyRangeStart = UINT_MAX;
371 }
372 
startComment(unsigned offset)373 void StyleSheetHandler::startComment(unsigned offset)
374 {
375     ASSERT(m_commentRangeStart == UINT_MAX);
376     m_commentRangeStart = offset;
377 }
378 
endComment(unsigned offset)379 void StyleSheetHandler::endComment(unsigned offset)
380 {
381     ASSERT(offset <= m_parsedText.length());
382 
383     unsigned startOffset = m_commentRangeStart;
384     m_commentRangeStart = UINT_MAX;
385     if (m_propertyRangeStart != UINT_MAX) {
386         ASSERT(startOffset >= m_propertyRangeStart);
387         // startProperty() is called automatically at the start of a style declaration.
388         // Check if no text has been scanned yet, otherwise the comment is inside a property.
389         if (!m_parsedText.substring(m_propertyRangeStart, startOffset).stripWhiteSpace().isEmpty())
390             return;
391         m_propertyRangeStart = UINT_MAX;
392     }
393     if (m_currentRuleDataStack.isEmpty() || !m_currentRuleDataStack.last()->ruleHeaderRange.end || !m_currentRuleDataStack.last()->styleSourceData)
394         return;
395 
396     // The lexer is not inside a property AND it is scanning a declaration-aware rule body.
397     String commentText = m_parsedText.substring(startOffset, offset - startOffset);
398 
399     ASSERT(commentText.startsWith("/*"));
400     commentText = commentText.substring(2);
401 
402     // Require well-formed comments.
403     if (!commentText.endsWith("*/"))
404         return;
405     commentText = commentText.substring(0, commentText.length() - 2).stripWhiteSpace();
406     if (commentText.isEmpty())
407         return;
408 
409     // FIXME: Use the actual rule type rather than STYLE_RULE?
410     if (!m_commentParser)
411         m_commentParser = createCSSParser(m_document);
412     RuleSourceDataList sourceData;
413 
414     // FIXME: Use another subclass of CSSParser::SourceDataHandler and assert that
415     // no comments are encountered (will not need m_document and m_styleSheetContents).
416     StyleSheetHandler handler(commentText, m_document, m_styleSheetContents, &sourceData);
417     RefPtr<MutableStylePropertySet> tempMutableStyle = MutableStylePropertySet::create();
418     m_commentParser->parseDeclaration(tempMutableStyle.get(), commentText, &handler, m_styleSheetContents);
419     Vector<CSSPropertySourceData>& commentPropertyData = sourceData.first()->styleSourceData->propertyData;
420     if (commentPropertyData.size() != 1)
421         return;
422     CSSPropertySourceData& propertyData = commentPropertyData.at(0);
423     if (propertyData.range.length() != commentText.length())
424         return;
425 
426     unsigned topRuleBodyRangeStart = m_currentRuleDataStack.last()->ruleBodyRange.start;
427     m_currentRuleDataStack.last()->styleSourceData->propertyData.append(
428         CSSPropertySourceData(propertyData.name, propertyData.value, false, true, true, SourceRange(startOffset - topRuleBodyRangeStart, offset - topRuleBodyRangeStart)));
429 }
430 
431 } // namespace
432 
433 enum MediaListSource {
434     MediaListSourceLinkedSheet,
435     MediaListSourceInlineSheet,
436     MediaListSourceMediaRule,
437     MediaListSourceImportRule
438 };
439 
buildSourceRangeObject(const SourceRange & range,Vector<unsigned> * lineEndings)440 static PassRefPtr<TypeBuilder::CSS::SourceRange> buildSourceRangeObject(const SourceRange& range, Vector<unsigned>* lineEndings)
441 {
442     if (!lineEndings)
443         return 0;
444     TextPosition start = TextPosition::fromOffsetAndLineEndings(range.start, *lineEndings);
445     TextPosition end = TextPosition::fromOffsetAndLineEndings(range.end, *lineEndings);
446 
447     RefPtr<TypeBuilder::CSS::SourceRange> result = TypeBuilder::CSS::SourceRange::create()
448         .setStartLine(start.m_line.zeroBasedInt())
449         .setStartColumn(start.m_column.zeroBasedInt())
450         .setEndLine(end.m_line.zeroBasedInt())
451         .setEndColumn(end.m_column.zeroBasedInt());
452     return result.release();
453 }
454 
asCSSRuleList(CSSStyleSheet * styleSheet)455 static PassRefPtr<CSSRuleList> asCSSRuleList(CSSStyleSheet* styleSheet)
456 {
457     if (!styleSheet)
458         return 0;
459 
460     RefPtr<StaticCSSRuleList> list = StaticCSSRuleList::create();
461     Vector<RefPtr<CSSRule> >& listRules = list->rules();
462     for (unsigned i = 0, size = styleSheet->length(); i < size; ++i) {
463         CSSRule* item = styleSheet->item(i);
464         if (item->type() == CSSRule::CHARSET_RULE)
465             continue;
466         listRules.append(item);
467     }
468     return list.release();
469 }
470 
asCSSRuleList(CSSRule * rule)471 static PassRefPtr<CSSRuleList> asCSSRuleList(CSSRule* rule)
472 {
473     if (!rule)
474         return 0;
475 
476     if (rule->type() == CSSRule::MEDIA_RULE)
477         return toCSSMediaRule(rule)->cssRules();
478 
479     if (rule->type() == CSSRule::KEYFRAMES_RULE)
480         return toCSSKeyframesRule(rule)->cssRules();
481 
482     if (rule->type() == CSSRule::SUPPORTS_RULE)
483         return toCSSSupportsRule(rule)->cssRules();
484 
485     return 0;
486 }
487 
create(const InspectorCSSId & styleId,PassRefPtr<CSSStyleDeclaration> style,InspectorStyleSheet * parentStyleSheet)488 PassRefPtr<InspectorStyle> InspectorStyle::create(const InspectorCSSId& styleId, PassRefPtr<CSSStyleDeclaration> style, InspectorStyleSheet* parentStyleSheet)
489 {
490     return adoptRef(new InspectorStyle(styleId, style, parentStyleSheet));
491 }
492 
InspectorStyle(const InspectorCSSId & styleId,PassRefPtr<CSSStyleDeclaration> style,InspectorStyleSheet * parentStyleSheet)493 InspectorStyle::InspectorStyle(const InspectorCSSId& styleId, PassRefPtr<CSSStyleDeclaration> style, InspectorStyleSheet* parentStyleSheet)
494     : m_styleId(styleId)
495     , m_style(style)
496     , m_parentStyleSheet(parentStyleSheet)
497     , m_formatAcquired(false)
498 {
499     ASSERT(m_style);
500 }
501 
buildObjectForStyle() const502 PassRefPtr<TypeBuilder::CSS::CSSStyle> InspectorStyle::buildObjectForStyle() const
503 {
504     RefPtr<TypeBuilder::CSS::CSSStyle> result = styleWithProperties();
505     if (!m_styleId.isEmpty())
506         result->setStyleId(m_styleId.asProtocolValue<TypeBuilder::CSS::CSSStyleId>());
507 
508     result->setWidth(m_style->getPropertyValue("width"));
509     result->setHeight(m_style->getPropertyValue("height"));
510 
511     RefPtr<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     Vector<InspectorStyleProperty> properties;
522     populateAllProperties(properties);
523 
524     for (Vector<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 
535 // This method does the following preprocessing of |propertyText| with |overwrite| == false and |index| past the last active property:
536 // - If the last property (if present) has no closing ";", the ";" is prepended to the current |propertyText| value.
537 // - A heuristic formatting is attempted to retain the style structure.
538 //
539 // The propertyText (if not empty) is checked to be a valid style declaration (containing at least one property). If not,
540 // the method returns false (denoting an error).
setPropertyText(unsigned index,const String & propertyText,bool overwrite,String * oldText,ExceptionState & exceptionState)541 bool InspectorStyle::setPropertyText(unsigned index, const String& propertyText, bool overwrite, String* oldText, ExceptionState& exceptionState)
542 {
543     ASSERT(m_parentStyleSheet);
544     DEFINE_STATIC_LOCAL(String, bogusPropertyName, ("-webkit-boguz-propertee"));
545 
546     if (!m_parentStyleSheet->ensureParsedDataReady()) {
547         exceptionState.throwUninformativeAndGenericDOMException(NotFoundError);
548         return false;
549     }
550 
551     if (!propertyText.stripWhiteSpace().isEmpty()) {
552         RefPtr<MutableStylePropertySet> tempMutableStyle = MutableStylePropertySet::create();
553         String declarationText = propertyText + " " + bogusPropertyName + ": none";
554         RuleSourceDataList sourceData;
555         StyleSheetHandler handler(declarationText, ownerDocument(), m_style->parentStyleSheet()->contents(), &sourceData);
556         createCSSParser(ownerDocument())->parseDeclaration(tempMutableStyle.get(), declarationText, &handler, m_style->parentStyleSheet()->contents());
557         Vector<CSSPropertySourceData>& propertyData = sourceData.first()->styleSourceData->propertyData;
558         unsigned propertyCount = propertyData.size();
559 
560         // At least one property + the bogus property added just above should be present.
561         if (propertyCount < 2) {
562             exceptionState.throwUninformativeAndGenericDOMException(SyntaxError);
563             return false;
564         }
565 
566         // Check for the proper propertyText termination (the parser could at least restore to the PROPERTY_NAME state).
567         if (propertyData.at(propertyCount - 1).name != bogusPropertyName) {
568             exceptionState.throwUninformativeAndGenericDOMException(SyntaxError);
569             return false;
570         }
571     }
572 
573     RefPtr<CSSRuleSourceData> sourceData = extractSourceData();
574     if (!sourceData) {
575         exceptionState.throwUninformativeAndGenericDOMException(NotFoundError);
576         return false;
577     }
578 
579     String text;
580     bool success = styleText(&text);
581     if (!success) {
582         exceptionState.throwUninformativeAndGenericDOMException(NotFoundError);
583         return false;
584     }
585 
586     Vector<InspectorStyleProperty> allProperties;
587     populateAllProperties(allProperties);
588 
589     InspectorStyleTextEditor editor(&allProperties, text, newLineAndWhitespaceDelimiters());
590     if (overwrite) {
591         if (index >= allProperties.size()) {
592             exceptionState.throwUninformativeAndGenericDOMException(IndexSizeError);
593             return false;
594         }
595         *oldText = allProperties.at(index).rawText;
596         editor.replaceProperty(index, propertyText);
597     } else
598         editor.insertProperty(index, propertyText, sourceData->ruleBodyRange.length());
599 
600     return applyStyleText(editor.styleText());
601 }
602 
toggleProperty(unsigned index,bool disable,ExceptionState & exceptionState)603 bool InspectorStyle::toggleProperty(unsigned index, bool disable, ExceptionState& exceptionState)
604 {
605     ASSERT(m_parentStyleSheet);
606     if (!m_parentStyleSheet->ensureParsedDataReady()) {
607         exceptionState.throwUninformativeAndGenericDOMException(NoModificationAllowedError);
608         return false;
609     }
610 
611     RefPtr<CSSRuleSourceData> sourceData = extractSourceData();
612     if (!sourceData) {
613         exceptionState.throwUninformativeAndGenericDOMException(NotFoundError);
614         return false;
615     }
616 
617     String text;
618     bool success = styleText(&text);
619     if (!success) {
620         exceptionState.throwUninformativeAndGenericDOMException(NotFoundError);
621         return false;
622     }
623 
624     Vector<InspectorStyleProperty> allProperties;
625     populateAllProperties(allProperties);
626     if (index >= allProperties.size()) {
627         exceptionState.throwUninformativeAndGenericDOMException(IndexSizeError);
628         return false;
629     }
630 
631     InspectorStyleProperty& property = allProperties.at(index);
632     if (property.sourceData.disabled == disable)
633         return true; // Idempotent operation.
634 
635     InspectorStyleTextEditor editor(&allProperties, text, newLineAndWhitespaceDelimiters());
636     if (disable)
637         editor.disableProperty(index);
638     else
639         editor.enableProperty(index);
640 
641     return applyStyleText(editor.styleText());
642 }
643 
styleText(String * result) const644 bool InspectorStyle::styleText(String* result) const
645 {
646     RefPtr<CSSRuleSourceData> sourceData = extractSourceData();
647     if (!sourceData)
648         return false;
649 
650     String styleSheetText;
651     bool success = m_parentStyleSheet->getText(&styleSheetText);
652     if (!success)
653         return false;
654 
655     SourceRange& bodyRange = sourceData->ruleBodyRange;
656     *result = styleSheetText.substring(bodyRange.start, bodyRange.end - bodyRange.start);
657     return true;
658 }
659 
populateAllProperties(Vector<InspectorStyleProperty> & result) const660 void InspectorStyle::populateAllProperties(Vector<InspectorStyleProperty>& result) const
661 {
662     HashSet<String> sourcePropertyNames;
663 
664     RefPtr<CSSRuleSourceData> sourceData = extractSourceData();
665     if (sourceData) {
666         String styleDeclaration;
667         bool isStyleTextKnown = styleText(&styleDeclaration);
668         ASSERT_UNUSED(isStyleTextKnown, isStyleTextKnown);
669         Vector<CSSPropertySourceData>& sourcePropertyData = sourceData->styleSourceData->propertyData;
670         for (Vector<CSSPropertySourceData>::const_iterator it = sourcePropertyData.begin(); it != sourcePropertyData.end(); ++it) {
671             InspectorStyleProperty p(*it, true);
672             p.setRawTextFromStyleDeclaration(styleDeclaration);
673             result.append(p);
674             sourcePropertyNames.add(it->name.lower());
675         }
676     }
677 
678     for (int i = 0, size = m_style->length(); i < size; ++i) {
679         String name = m_style->item(i);
680         if (!sourcePropertyNames.add(name.lower()).isNewEntry)
681             continue;
682 
683         result.append(InspectorStyleProperty(CSSPropertySourceData(name, m_style->getPropertyValue(name), !m_style->getPropertyPriority(name).isEmpty(), false, true, SourceRange()), false));
684     }
685 }
686 
styleWithProperties() const687 PassRefPtr<TypeBuilder::CSS::CSSStyle> InspectorStyle::styleWithProperties() const
688 {
689     RefPtr<Array<TypeBuilder::CSS::CSSProperty> > propertiesObject = Array<TypeBuilder::CSS::CSSProperty>::create();
690     RefPtr<Array<TypeBuilder::CSS::ShorthandEntry> > shorthandEntries = Array<TypeBuilder::CSS::ShorthandEntry>::create();
691     HashMap<String, RefPtr<TypeBuilder::CSS::CSSProperty> > propertyNameToPreviousActiveProperty;
692     HashSet<String> foundShorthands;
693     String previousPriority;
694     String previousStatus;
695     OwnPtr<Vector<unsigned> > lineEndings(m_parentStyleSheet ? m_parentStyleSheet->lineEndings() : PassOwnPtr<Vector<unsigned> >());
696     RefPtr<CSSRuleSourceData> sourceData = extractSourceData();
697     unsigned ruleBodyRangeStart = sourceData ? sourceData->ruleBodyRange.start : 0;
698 
699     Vector<InspectorStyleProperty> properties;
700     populateAllProperties(properties);
701 
702     for (Vector<InspectorStyleProperty>::iterator it = properties.begin(), itEnd = properties.end(); it != itEnd; ++it) {
703         const CSSPropertySourceData& propertyEntry = it->sourceData;
704         const String& name = propertyEntry.name;
705         const bool disabled = it->sourceData.disabled;
706 
707         TypeBuilder::CSS::CSSProperty::Status::Enum status = disabled ? TypeBuilder::CSS::CSSProperty::Status::Disabled : TypeBuilder::CSS::CSSProperty::Status::Active;
708 
709         RefPtr<TypeBuilder::CSS::CSSProperty> property = TypeBuilder::CSS::CSSProperty::create()
710             .setName(name)
711             .setValue(propertyEntry.value);
712         propertiesObject->addItem(property);
713 
714         // Default "parsedOk" == true.
715         if (!propertyEntry.parsedOk)
716             property->setParsedOk(false);
717         if (it->hasRawText())
718             property->setText(it->rawText);
719 
720         // Default "priority" == "".
721         if (propertyEntry.important)
722             property->setPriority("important");
723         if (it->hasSource) {
724             // The property range is relative to the style body start.
725             // Should be converted into an absolute range (relative to the stylesheet start)
726             // for the proper conversion into line:column.
727             SourceRange absolutePropertyRange = propertyEntry.range;
728             absolutePropertyRange.start += ruleBodyRangeStart;
729             absolutePropertyRange.end += ruleBodyRangeStart;
730             property->setRange(buildSourceRangeObject(absolutePropertyRange, lineEndings.get()));
731         }
732         if (!disabled) {
733             if (it->hasSource) {
734                 ASSERT(sourceData);
735                 property->setImplicit(false);
736 
737                 // Parsed property overrides any property with the same name. Non-parsed property overrides
738                 // previous non-parsed property with the same name (if any).
739                 bool shouldInactivate = false;
740                 CSSPropertyID propertyId = cssPropertyID(name);
741                 // Canonicalize property names to treat non-prefixed and vendor-prefixed property names the same (opacity vs. -webkit-opacity).
742                 String canonicalPropertyName = propertyId ? getPropertyNameString(propertyId) : name;
743                 HashMap<String, RefPtr<TypeBuilder::CSS::CSSProperty> >::iterator activeIt = propertyNameToPreviousActiveProperty.find(canonicalPropertyName);
744                 if (activeIt != propertyNameToPreviousActiveProperty.end()) {
745                     if (propertyEntry.parsedOk) {
746                         bool successPriority = activeIt->value->getString(TypeBuilder::CSS::CSSProperty::Priority, &previousPriority);
747                         bool successStatus = activeIt->value->getString(TypeBuilder::CSS::CSSProperty::Status, &previousStatus);
748                         if (successStatus && previousStatus != "inactive") {
749                             if (propertyEntry.important || !successPriority) // Priority not set == "not important".
750                                 shouldInactivate = true;
751                             else if (status == TypeBuilder::CSS::CSSProperty::Status::Active) {
752                                 // Inactivate a non-important property following the same-named important property.
753                                 status = TypeBuilder::CSS::CSSProperty::Status::Inactive;
754                             }
755                         }
756                     } else {
757                         bool previousParsedOk;
758                         bool success = activeIt->value->getBoolean(TypeBuilder::CSS::CSSProperty::ParsedOk, &previousParsedOk);
759                         if (success && !previousParsedOk)
760                             shouldInactivate = true;
761                     }
762                 } else
763                     propertyNameToPreviousActiveProperty.set(canonicalPropertyName, property);
764 
765                 if (shouldInactivate) {
766                     activeIt->value->setStatus(TypeBuilder::CSS::CSSProperty::Status::Inactive);
767                     propertyNameToPreviousActiveProperty.set(canonicalPropertyName, property);
768                 }
769             } else {
770                 bool implicit = m_style->isPropertyImplicit(name);
771                 // Default "implicit" == false.
772                 if (implicit)
773                     property->setImplicit(true);
774                 status = TypeBuilder::CSS::CSSProperty::Status::Style;
775 
776                 String shorthand = m_style->getPropertyShorthand(name);
777                 if (!shorthand.isEmpty()) {
778                     if (foundShorthands.add(shorthand).isNewEntry) {
779                         RefPtr<TypeBuilder::CSS::ShorthandEntry> entry = TypeBuilder::CSS::ShorthandEntry::create()
780                             .setName(shorthand)
781                             .setValue(shorthandValue(shorthand));
782                         shorthandEntries->addItem(entry);
783                     }
784                 }
785             }
786         }
787 
788         // Default "status" == "style".
789         if (status != TypeBuilder::CSS::CSSProperty::Status::Style)
790             property->setStatus(status);
791     }
792 
793     RefPtr<TypeBuilder::CSS::CSSStyle> result = TypeBuilder::CSS::CSSStyle::create()
794         .setCssProperties(propertiesObject)
795         .setShorthandEntries(shorthandEntries);
796     return result.release();
797 }
798 
extractSourceData() const799 PassRefPtr<CSSRuleSourceData> InspectorStyle::extractSourceData() const
800 {
801     if (!m_parentStyleSheet || !m_parentStyleSheet->ensureParsedDataReady())
802         return 0;
803     return m_parentStyleSheet->ruleSourceDataFor(m_style.get());
804 }
805 
applyStyleText(const String & text)806 bool InspectorStyle::applyStyleText(const String& text)
807 {
808     return m_parentStyleSheet->setStyleText(m_style.get(), text);
809 }
810 
shorthandValue(const String & shorthandProperty) const811 String InspectorStyle::shorthandValue(const String& shorthandProperty) const
812 {
813     String value = m_style->getPropertyValue(shorthandProperty);
814     if (value.isEmpty()) {
815         StringBuilder builder;
816 
817         for (unsigned i = 0; i < m_style->length(); ++i) {
818             String individualProperty = m_style->item(i);
819             if (m_style->getPropertyShorthand(individualProperty) != shorthandProperty)
820                 continue;
821             if (m_style->isPropertyImplicit(individualProperty))
822                 continue;
823             String individualValue = m_style->getPropertyValue(individualProperty);
824             if (individualValue == "initial")
825                 continue;
826             if (!builder.isEmpty())
827                 builder.append(" ");
828             builder.append(individualValue);
829         }
830 
831         return builder.toString();
832     }
833     return value;
834 }
835 
shorthandPriority(const String & shorthandProperty) const836 String InspectorStyle::shorthandPriority(const String& shorthandProperty) const
837 {
838     String priority = m_style->getPropertyPriority(shorthandProperty);
839     if (priority.isEmpty()) {
840         for (unsigned i = 0; i < m_style->length(); ++i) {
841             String individualProperty = m_style->item(i);
842             if (m_style->getPropertyShorthand(individualProperty) != shorthandProperty)
843                 continue;
844             priority = m_style->getPropertyPriority(individualProperty);
845             break;
846         }
847     }
848     return priority;
849 }
850 
longhandProperties(const String & shorthandProperty) const851 Vector<String> InspectorStyle::longhandProperties(const String& shorthandProperty) const
852 {
853     Vector<String> properties;
854     HashSet<String> foundProperties;
855     for (unsigned i = 0; i < m_style->length(); ++i) {
856         String individualProperty = m_style->item(i);
857         if (foundProperties.contains(individualProperty) || m_style->getPropertyShorthand(individualProperty) != shorthandProperty)
858             continue;
859 
860         foundProperties.add(individualProperty);
861         properties.append(individualProperty);
862     }
863     return properties;
864 }
865 
newLineAndWhitespaceDelimiters() const866 NewLineAndWhitespace& InspectorStyle::newLineAndWhitespaceDelimiters() const
867 {
868     DEFINE_STATIC_LOCAL(String, defaultPrefix, ("    "));
869 
870     if (m_formatAcquired)
871         return m_format;
872 
873     RefPtr<CSSRuleSourceData> sourceData = extractSourceData();
874     Vector<CSSPropertySourceData>* sourcePropertyData = sourceData ? &(sourceData->styleSourceData->propertyData) : 0;
875     int propertyCount;
876     if (!sourcePropertyData || !(propertyCount = sourcePropertyData->size())) {
877         m_format.first = "\n";
878         m_format.second = defaultPrefix;
879         return m_format; // Do not remember the default formatting and attempt to acquire it later.
880     }
881 
882     String text;
883     bool success = styleText(&text);
884     ASSERT_UNUSED(success, success);
885 
886     m_formatAcquired = true;
887 
888     String candidatePrefix = defaultPrefix;
889     StringBuilder formatLineFeed;
890     StringBuilder prefix;
891     int scanStart = 0;
892     int propertyIndex = 0;
893     bool isFullPrefixScanned = false;
894     bool lineFeedTerminated = false;
895     while (propertyIndex < propertyCount) {
896         const WebCore::CSSPropertySourceData& currentProperty = sourcePropertyData->at(propertyIndex++);
897 
898         bool processNextProperty = false;
899         int scanEnd = currentProperty.range.start;
900         for (int i = scanStart; i < scanEnd; ++i) {
901             UChar ch = text[i];
902             bool isLineFeed = isHTMLLineBreak(ch);
903             if (isLineFeed) {
904                 if (!lineFeedTerminated)
905                     formatLineFeed.append(ch);
906                 prefix.clear();
907             } else if (isHTMLSpace<UChar>(ch))
908                 prefix.append(ch);
909             else {
910                 candidatePrefix = prefix.toString();
911                 prefix.clear();
912                 scanStart = currentProperty.range.end;
913                 ++propertyIndex;
914                 processNextProperty = true;
915                 break;
916             }
917             if (!isLineFeed && formatLineFeed.length())
918                 lineFeedTerminated = true;
919         }
920         if (!processNextProperty) {
921             isFullPrefixScanned = true;
922             break;
923         }
924     }
925 
926     m_format.first = formatLineFeed.toString();
927     m_format.second = isFullPrefixScanned ? prefix.toString() : candidatePrefix;
928     return m_format;
929 }
930 
ownerDocument() const931 Document* InspectorStyle::ownerDocument() const
932 {
933     return m_parentStyleSheet->pageStyleSheet() ? m_parentStyleSheet->pageStyleSheet()->ownerDocument() : 0;
934 }
935 
create(InspectorPageAgent * pageAgent,InspectorResourceAgent * resourceAgent,const String & id,PassRefPtr<CSSStyleSheet> pageStyleSheet,TypeBuilder::CSS::StyleSheetOrigin::Enum origin,const String & documentURL,Listener * listener)936 PassRefPtr<InspectorStyleSheet> InspectorStyleSheet::create(InspectorPageAgent* pageAgent, InspectorResourceAgent* resourceAgent, const String& id, PassRefPtr<CSSStyleSheet> pageStyleSheet, TypeBuilder::CSS::StyleSheetOrigin::Enum origin, const String& documentURL, Listener* listener)
937 {
938     return adoptRef(new InspectorStyleSheet(pageAgent, resourceAgent, id, pageStyleSheet, origin, documentURL, listener));
939 }
940 
941 // static
styleSheetURL(CSSStyleSheet * pageStyleSheet)942 String InspectorStyleSheet::styleSheetURL(CSSStyleSheet* pageStyleSheet)
943 {
944     if (pageStyleSheet && !pageStyleSheet->contents()->baseURL().isEmpty())
945         return pageStyleSheet->contents()->baseURL().string();
946     return emptyString();
947 }
948 
949 // static
collectFlatRules(PassRefPtr<CSSRuleList> ruleList,CSSRuleVector * result)950 void InspectorStyleSheet::collectFlatRules(PassRefPtr<CSSRuleList> ruleList, CSSRuleVector* result)
951 {
952     if (!ruleList)
953         return;
954 
955     for (unsigned i = 0, size = ruleList->length(); i < size; ++i) {
956         CSSRule* rule = ruleList->item(i);
957 
958         // The result->append()'ed types should be exactly the same as in ParsedStyleSheet::flattenSourceData().
959         switch (rule->type()) {
960         case CSSRule::STYLE_RULE:
961             result->append(rule);
962             continue;
963         case CSSRule::IMPORT_RULE:
964         case CSSRule::MEDIA_RULE:
965             result->append(rule);
966             break;
967         default:
968             break;
969         }
970         RefPtr<CSSRuleList> childRuleList = asCSSRuleList(rule);
971         if (childRuleList)
972             collectFlatRules(childRuleList, result);
973     }
974 }
975 
InspectorStyleSheet(InspectorPageAgent * pageAgent,InspectorResourceAgent * resourceAgent,const String & id,PassRefPtr<CSSStyleSheet> pageStyleSheet,TypeBuilder::CSS::StyleSheetOrigin::Enum origin,const String & documentURL,Listener * listener)976 InspectorStyleSheet::InspectorStyleSheet(InspectorPageAgent* pageAgent, InspectorResourceAgent* resourceAgent, const String& id, PassRefPtr<CSSStyleSheet> pageStyleSheet, TypeBuilder::CSS::StyleSheetOrigin::Enum origin, const String& documentURL, Listener* listener)
977     : m_pageAgent(pageAgent)
978     , m_resourceAgent(resourceAgent)
979     , m_id(id)
980     , m_pageStyleSheet(pageStyleSheet)
981     , m_origin(origin)
982     , m_documentURL(documentURL)
983     , m_isRevalidating(false)
984     , m_listener(listener)
985 {
986     m_parsedStyleSheet = new ParsedStyleSheet();
987 }
988 
~InspectorStyleSheet()989 InspectorStyleSheet::~InspectorStyleSheet()
990 {
991     delete m_parsedStyleSheet;
992 }
993 
finalURL() const994 String InspectorStyleSheet::finalURL() const
995 {
996     String url = styleSheetURL(m_pageStyleSheet.get());
997     return url.isEmpty() ? m_documentURL : url;
998 }
999 
reparseStyleSheet(const String & text)1000 void InspectorStyleSheet::reparseStyleSheet(const String& text)
1001 {
1002     if (m_listener)
1003         m_listener->willReparseStyleSheet();
1004 
1005     {
1006         // Have a separate scope for clearRules() (bug 95324).
1007         CSSStyleSheet::RuleMutationScope mutationScope(m_pageStyleSheet.get());
1008         m_pageStyleSheet->contents()->clearRules();
1009         m_pageStyleSheet->clearChildRuleCSSOMWrappers();
1010     }
1011     {
1012         CSSStyleSheet::RuleMutationScope mutationScope(m_pageStyleSheet.get());
1013         m_pageStyleSheet->contents()->parseString(text);
1014     }
1015 
1016     if (m_listener)
1017         m_listener->didReparseStyleSheet();
1018     fireStyleSheetChanged();
1019     m_pageStyleSheet->ownerDocument()->styleResolverChanged(RecalcStyleImmediately, FullStyleUpdate);
1020 }
1021 
setText(const String & text,ExceptionState & exceptionState)1022 bool InspectorStyleSheet::setText(const String& text, ExceptionState& exceptionState)
1023 {
1024     if (!checkPageStyleSheet(exceptionState))
1025         return false;
1026     if (!m_parsedStyleSheet)
1027         return false;
1028 
1029     m_parsedStyleSheet->setText(text);
1030     m_flatRules.clear();
1031 
1032     return true;
1033 }
1034 
ruleSelector(const InspectorCSSId & id,ExceptionState & exceptionState)1035 String InspectorStyleSheet::ruleSelector(const InspectorCSSId& id, ExceptionState& exceptionState)
1036 {
1037     CSSStyleRule* rule = ruleForId(id);
1038     if (!rule) {
1039         exceptionState.throwUninformativeAndGenericDOMException(NotFoundError);
1040         return "";
1041     }
1042     return rule->selectorText();
1043 }
1044 
setRuleSelector(const InspectorCSSId & id,const String & selector,ExceptionState & exceptionState)1045 bool InspectorStyleSheet::setRuleSelector(const InspectorCSSId& id, const String& selector, ExceptionState& exceptionState)
1046 {
1047     if (!checkPageStyleSheet(exceptionState))
1048         return false;
1049     CSSStyleRule* rule = ruleForId(id);
1050     if (!rule) {
1051         exceptionState.throwUninformativeAndGenericDOMException(NotFoundError);
1052         return false;
1053     }
1054     CSSStyleSheet* styleSheet = rule->parentStyleSheet();
1055     if (!styleSheet || !ensureParsedDataReady()) {
1056         exceptionState.throwUninformativeAndGenericDOMException(NotFoundError);
1057         return false;
1058     }
1059 
1060     rule->setSelectorText(selector);
1061     RefPtr<CSSRuleSourceData> sourceData = ruleSourceDataFor(rule->style());
1062     if (!sourceData) {
1063         exceptionState.throwUninformativeAndGenericDOMException(NotFoundError);
1064         return false;
1065     }
1066 
1067     String sheetText = m_parsedStyleSheet->text();
1068     sheetText.replace(sourceData->ruleHeaderRange.start, sourceData->ruleHeaderRange.length(), selector);
1069     m_parsedStyleSheet->setText(sheetText);
1070     fireStyleSheetChanged();
1071     return true;
1072 }
1073 
checkStyleRuleSelector(Document * document,const String & selector)1074 static bool checkStyleRuleSelector(Document* document, const String& selector)
1075 {
1076     CSSSelectorList selectorList;
1077     createCSSParser(document)->parseSelector(selector, selectorList);
1078     return selectorList.isValid();
1079 }
1080 
addRule(const String & selector,ExceptionState & exceptionState)1081 CSSStyleRule* InspectorStyleSheet::addRule(const String& selector, ExceptionState& exceptionState)
1082 {
1083     if (!checkPageStyleSheet(exceptionState))
1084         return 0;
1085     if (!checkStyleRuleSelector(m_pageStyleSheet->ownerDocument(), selector)) {
1086         exceptionState.throwUninformativeAndGenericDOMException(SyntaxError);
1087         return 0;
1088     }
1089 
1090     String text;
1091     bool success = getText(&text);
1092     if (!success) {
1093         exceptionState.throwUninformativeAndGenericDOMException(NotFoundError);
1094         return 0;
1095     }
1096     StringBuilder styleSheetText;
1097     styleSheetText.append(text);
1098 
1099     m_pageStyleSheet->addRule(selector, "", exceptionState);
1100     if (exceptionState.hadException())
1101         return 0;
1102     ASSERT(m_pageStyleSheet->length());
1103     unsigned lastRuleIndex = m_pageStyleSheet->length() - 1;
1104     CSSRule* rule = m_pageStyleSheet->item(lastRuleIndex);
1105     ASSERT(rule);
1106 
1107     CSSStyleRule* styleRule = InspectorCSSAgent::asCSSStyleRule(rule);
1108     if (!styleRule) {
1109         // What we just added has to be a CSSStyleRule - we cannot handle other types of rules yet.
1110         // If it is not a style rule, pretend we never touched the stylesheet.
1111         m_pageStyleSheet->deleteRule(lastRuleIndex, ASSERT_NO_EXCEPTION);
1112         exceptionState.throwUninformativeAndGenericDOMException(SyntaxError);
1113         return 0;
1114     }
1115 
1116     if (!styleSheetText.isEmpty())
1117         styleSheetText.append('\n');
1118 
1119     styleSheetText.append(selector);
1120     styleSheetText.appendLiteral(" {}");
1121     // Using setText() as this operation changes the style sheet rule set.
1122     setText(styleSheetText.toString(), ASSERT_NO_EXCEPTION);
1123 
1124     fireStyleSheetChanged();
1125 
1126     return styleRule;
1127 }
1128 
deleteRule(const InspectorCSSId & id,ExceptionState & exceptionState)1129 bool InspectorStyleSheet::deleteRule(const InspectorCSSId& id, ExceptionState& exceptionState)
1130 {
1131     if (!checkPageStyleSheet(exceptionState))
1132         return false;
1133     RefPtr<CSSStyleRule> rule = ruleForId(id);
1134     if (!rule) {
1135         exceptionState.throwUninformativeAndGenericDOMException(NotFoundError);
1136         return false;
1137     }
1138     CSSStyleSheet* styleSheet = rule->parentStyleSheet();
1139     if (!styleSheet || !ensureParsedDataReady()) {
1140         exceptionState.throwUninformativeAndGenericDOMException(NotFoundError);
1141         return false;
1142     }
1143 
1144     RefPtr<CSSRuleSourceData> sourceData = ruleSourceDataFor(rule->style());
1145     if (!sourceData) {
1146         exceptionState.throwUninformativeAndGenericDOMException(NotFoundError);
1147         return false;
1148     }
1149 
1150     styleSheet->deleteRule(id.ordinal(), exceptionState);
1151     // |rule| MAY NOT be addressed after this line!
1152 
1153     if (exceptionState.hadException())
1154         return false;
1155 
1156     String sheetText = m_parsedStyleSheet->text();
1157     sheetText.remove(sourceData->ruleHeaderRange.start, sourceData->ruleBodyRange.end - sourceData->ruleHeaderRange.start + 1);
1158     setText(sheetText, ASSERT_NO_EXCEPTION);
1159     fireStyleSheetChanged();
1160     return true;
1161 }
1162 
ruleForId(const InspectorCSSId & id) const1163 CSSStyleRule* InspectorStyleSheet::ruleForId(const InspectorCSSId& id) const
1164 {
1165     if (!m_pageStyleSheet)
1166         return 0;
1167 
1168     ASSERT(!id.isEmpty());
1169     ensureFlatRules();
1170     return InspectorCSSAgent::asCSSStyleRule(id.ordinal() >= m_flatRules.size() ? 0 : m_flatRules.at(id.ordinal()).get());
1171 }
1172 
fillObjectForStyleSheet(PassRefPtr<TypeBuilder::CSS::CSSStyleSheetBody> prpResult)1173 bool InspectorStyleSheet::fillObjectForStyleSheet(PassRefPtr<TypeBuilder::CSS::CSSStyleSheetBody> prpResult)
1174 {
1175     CSSStyleSheet* styleSheet = pageStyleSheet();
1176     if (!styleSheet)
1177         return false;
1178 
1179     RefPtr<TypeBuilder::CSS::CSSStyleSheetBody> result = prpResult;
1180 
1181     String styleSheetText;
1182     bool success = getText(&styleSheetText);
1183     if (success)
1184         result->setText(styleSheetText);
1185     return success;
1186 }
1187 
buildObjectForStyleSheetInfo() const1188 PassRefPtr<TypeBuilder::CSS::CSSStyleSheetHeader> InspectorStyleSheet::buildObjectForStyleSheetInfo() const
1189 {
1190     CSSStyleSheet* styleSheet = pageStyleSheet();
1191     if (!styleSheet)
1192         return 0;
1193 
1194     Document* document = styleSheet->ownerDocument();
1195     Frame* frame = document ? document->frame() : 0;
1196 
1197     RefPtr<TypeBuilder::CSS::CSSStyleSheetHeader> result = TypeBuilder::CSS::CSSStyleSheetHeader::create()
1198         .setStyleSheetId(id())
1199         .setOrigin(m_origin)
1200         .setDisabled(styleSheet->disabled())
1201         .setSourceURL(url())
1202         .setTitle(styleSheet->title())
1203         .setFrameId(m_pageAgent->frameId(frame))
1204         .setIsInline(styleSheet->isInline() && !startsAtZero())
1205         .setStartLine(styleSheet->startPositionInSource().m_line.zeroBasedInt())
1206         .setStartColumn(styleSheet->startPositionInSource().m_column.zeroBasedInt());
1207 
1208     if (hasSourceURL())
1209         result->setHasSourceURL(true);
1210 
1211     String sourceMapURLValue = sourceMapURL();
1212     if (!sourceMapURLValue.isEmpty())
1213         result->setSourceMapURL(sourceMapURLValue);
1214     return result.release();
1215 }
1216 
selectorsFromSource(const CSSRuleSourceData * sourceData,const String & sheetText) const1217 PassRefPtr<TypeBuilder::Array<TypeBuilder::CSS::Selector> > InspectorStyleSheet::selectorsFromSource(const CSSRuleSourceData* sourceData, const String& sheetText) const
1218 {
1219     ScriptRegexp comment("/\\*[^]*?\\*/", TextCaseSensitive, MultilineEnabled);
1220     RefPtr<TypeBuilder::Array<TypeBuilder::CSS::Selector> > result = TypeBuilder::Array<TypeBuilder::CSS::Selector>::create();
1221     const SelectorRangeList& ranges = sourceData->selectorRanges;
1222     for (size_t i = 0, size = ranges.size(); i < size; ++i) {
1223         const SourceRange& range = ranges.at(i);
1224         String selector = sheetText.substring(range.start, range.length());
1225 
1226         // We don't want to see any comments in the selector components, only the meaningful parts.
1227         int matchLength;
1228         int offset = 0;
1229         while ((offset = comment.match(selector, offset, &matchLength)) >= 0)
1230             selector.replace(offset, matchLength, "");
1231 
1232         RefPtr<TypeBuilder::CSS::Selector> simpleSelector = TypeBuilder::CSS::Selector::create()
1233             .setValue(selector.stripWhiteSpace());
1234         simpleSelector->setRange(buildSourceRangeObject(range, lineEndings().get()));
1235         result->addItem(simpleSelector.release());
1236     }
1237     return result.release();
1238 }
1239 
buildObjectForSelectorList(CSSStyleRule * rule)1240 PassRefPtr<TypeBuilder::CSS::SelectorList> InspectorStyleSheet::buildObjectForSelectorList(CSSStyleRule* rule)
1241 {
1242     RefPtr<CSSRuleSourceData> sourceData;
1243     if (ensureParsedDataReady())
1244         sourceData = ruleSourceDataFor(rule->style());
1245     RefPtr<TypeBuilder::Array<TypeBuilder::CSS::Selector> > selectors;
1246 
1247     // This intentionally does not rely on the source data to avoid catching the trailing comments (before the declaration starting '{').
1248     String selectorText = rule->selectorText();
1249 
1250     if (sourceData)
1251         selectors = selectorsFromSource(sourceData.get(), m_parsedStyleSheet->text());
1252     else {
1253         selectors = TypeBuilder::Array<TypeBuilder::CSS::Selector>::create();
1254         const CSSSelectorList& selectorList = rule->styleRule()->selectorList();
1255         for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(selector))
1256             selectors->addItem(TypeBuilder::CSS::Selector::create().setValue(selector->selectorText()).release());
1257     }
1258     RefPtr<TypeBuilder::CSS::SelectorList> result = TypeBuilder::CSS::SelectorList::create()
1259         .setSelectors(selectors)
1260         .setText(selectorText)
1261         .release();
1262     return result.release();
1263 }
1264 
buildObjectForRule(CSSStyleRule * rule,PassRefPtr<Array<TypeBuilder::CSS::CSSMedia>> mediaStack)1265 PassRefPtr<TypeBuilder::CSS::CSSRule> InspectorStyleSheet::buildObjectForRule(CSSStyleRule* rule, PassRefPtr<Array<TypeBuilder::CSS::CSSMedia> > mediaStack)
1266 {
1267     CSSStyleSheet* styleSheet = pageStyleSheet();
1268     if (!styleSheet)
1269         return 0;
1270 
1271     RefPtr<TypeBuilder::CSS::CSSRule> result = TypeBuilder::CSS::CSSRule::create()
1272         .setSelectorList(buildObjectForSelectorList(rule))
1273         .setOrigin(m_origin)
1274         .setStyle(buildObjectForStyle(rule->style()));
1275 
1276     String url = this->url();
1277     if (!url.isEmpty())
1278         result->setSourceURL(url);
1279 
1280     if (canBind()) {
1281         InspectorCSSId id(ruleId(rule));
1282         if (!id.isEmpty())
1283             result->setRuleId(id.asProtocolValue<TypeBuilder::CSS::CSSRuleId>());
1284     }
1285 
1286     RefPtr<Array<TypeBuilder::CSS::CSSMedia> > mediaArray = Array<TypeBuilder::CSS::CSSMedia>::create();
1287 
1288     if (mediaStack)
1289         result->setMedia(mediaStack);
1290 
1291     return result.release();
1292 }
1293 
buildObjectForStyle(CSSStyleDeclaration * style)1294 PassRefPtr<TypeBuilder::CSS::CSSStyle> InspectorStyleSheet::buildObjectForStyle(CSSStyleDeclaration* style)
1295 {
1296     RefPtr<CSSRuleSourceData> sourceData;
1297     if (ensureParsedDataReady())
1298         sourceData = ruleSourceDataFor(style);
1299 
1300     InspectorCSSId id = ruleOrStyleId(style);
1301     if (id.isEmpty()) {
1302         RefPtr<TypeBuilder::CSS::CSSStyle> bogusStyle = TypeBuilder::CSS::CSSStyle::create()
1303             .setCssProperties(Array<TypeBuilder::CSS::CSSProperty>::create())
1304             .setShorthandEntries(Array<TypeBuilder::CSS::ShorthandEntry>::create());
1305         return bogusStyle.release();
1306     }
1307     RefPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id);
1308     RefPtr<TypeBuilder::CSS::CSSStyle> result = inspectorStyle->buildObjectForStyle();
1309 
1310     // Style text cannot be retrieved without stylesheet, so set cssText here.
1311     if (sourceData) {
1312         String sheetText;
1313         bool success = getText(&sheetText);
1314         if (success) {
1315             const SourceRange& bodyRange = sourceData->ruleBodyRange;
1316             result->setCssText(sheetText.substring(bodyRange.start, bodyRange.end - bodyRange.start));
1317         }
1318     }
1319 
1320     return result.release();
1321 }
1322 
setStyleText(const InspectorCSSId & id,const String & text,String * oldText,ExceptionState & exceptionState)1323 bool InspectorStyleSheet::setStyleText(const InspectorCSSId& id, const String& text, String* oldText, ExceptionState& exceptionState)
1324 {
1325     RefPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id);
1326     if (!inspectorStyle || !inspectorStyle->cssStyle()) {
1327         exceptionState.throwUninformativeAndGenericDOMException(NotFoundError);
1328         return false;
1329     }
1330 
1331     bool success = inspectorStyle->styleText(oldText);
1332     if (!success) {
1333         exceptionState.throwUninformativeAndGenericDOMException(NotFoundError);
1334         return false;
1335     }
1336 
1337     success = setStyleText(inspectorStyle->cssStyle(), text);
1338     if (success)
1339         fireStyleSheetChanged();
1340     else
1341         exceptionState.throwUninformativeAndGenericDOMException(SyntaxError);
1342     return success;
1343 }
1344 
setPropertyText(const InspectorCSSId & id,unsigned propertyIndex,const String & text,bool overwrite,String * oldText,ExceptionState & exceptionState)1345 bool InspectorStyleSheet::setPropertyText(const InspectorCSSId& id, unsigned propertyIndex, const String& text, bool overwrite, String* oldText, ExceptionState& exceptionState)
1346 {
1347     RefPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id);
1348     if (!inspectorStyle) {
1349         exceptionState.throwUninformativeAndGenericDOMException(NotFoundError);
1350         return false;
1351     }
1352 
1353     bool success = inspectorStyle->setPropertyText(propertyIndex, text, overwrite, oldText, exceptionState);
1354     if (success)
1355         fireStyleSheetChanged();
1356     return success;
1357 }
1358 
toggleProperty(const InspectorCSSId & id,unsigned propertyIndex,bool disable,ExceptionState & exceptionState)1359 bool InspectorStyleSheet::toggleProperty(const InspectorCSSId& id, unsigned propertyIndex, bool disable, ExceptionState& exceptionState)
1360 {
1361     RefPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id);
1362     if (!inspectorStyle) {
1363         exceptionState.throwUninformativeAndGenericDOMException(NotFoundError);
1364         return false;
1365     }
1366 
1367     bool success = inspectorStyle->toggleProperty(propertyIndex, disable, exceptionState);
1368     if (success)
1369         fireStyleSheetChanged();
1370     return success;
1371 }
1372 
getText(String * result) const1373 bool InspectorStyleSheet::getText(String* result) const
1374 {
1375     if (!ensureText())
1376         return false;
1377     *result = m_parsedStyleSheet->text();
1378     return true;
1379 }
1380 
styleForId(const InspectorCSSId & id) const1381 CSSStyleDeclaration* InspectorStyleSheet::styleForId(const InspectorCSSId& id) const
1382 {
1383     CSSStyleRule* rule = ruleForId(id);
1384     if (!rule)
1385         return 0;
1386 
1387     return rule->style();
1388 }
1389 
fireStyleSheetChanged()1390 void InspectorStyleSheet::fireStyleSheetChanged()
1391 {
1392     if (m_listener)
1393         m_listener->styleSheetChanged(this);
1394 }
1395 
ruleHeaderSourceRange(const CSSRule * rule)1396 PassRefPtr<TypeBuilder::CSS::SourceRange> InspectorStyleSheet::ruleHeaderSourceRange(const CSSRule* rule)
1397 {
1398     if (!ensureParsedDataReady())
1399         return 0;
1400 
1401     RefPtr<CSSRuleSourceData> sourceData = m_parsedStyleSheet->ruleSourceDataAt(ruleIndexByRule(rule));
1402     if (!sourceData)
1403         return 0;
1404     return buildSourceRangeObject(sourceData->ruleHeaderRange, lineEndings().get());
1405 }
1406 
inspectorStyleForId(const InspectorCSSId & id)1407 PassRefPtr<InspectorStyle> InspectorStyleSheet::inspectorStyleForId(const InspectorCSSId& id)
1408 {
1409     CSSStyleDeclaration* style = styleForId(id);
1410     if (!style)
1411         return 0;
1412 
1413     return InspectorStyle::create(id, style, this);
1414 }
1415 
sourceURL() const1416 String InspectorStyleSheet::sourceURL() const
1417 {
1418     if (!m_sourceURL.isNull())
1419         return m_sourceURL;
1420     if (m_origin != TypeBuilder::CSS::StyleSheetOrigin::Regular) {
1421         m_sourceURL = "";
1422         return m_sourceURL;
1423     }
1424 
1425     String styleSheetText;
1426     bool success = getText(&styleSheetText);
1427     if (success) {
1428         bool deprecated;
1429         String commentValue = ContentSearchUtils::findSourceURL(styleSheetText, ContentSearchUtils::CSSMagicComment, &deprecated);
1430         if (!commentValue.isEmpty()) {
1431             // FIXME: add deprecated console message here.
1432             m_sourceURL = commentValue;
1433             return commentValue;
1434         }
1435     }
1436     m_sourceURL = "";
1437     return m_sourceURL;
1438 }
1439 
url() const1440 String InspectorStyleSheet::url() const
1441 {
1442     // "sourceURL" is present only for regular rules, otherwise "origin" should be used in the frontend.
1443     if (m_origin != TypeBuilder::CSS::StyleSheetOrigin::Regular)
1444         return String();
1445 
1446     CSSStyleSheet* styleSheet = pageStyleSheet();
1447     if (!styleSheet)
1448         return String();
1449 
1450     if (hasSourceURL())
1451         return sourceURL();
1452 
1453     if (styleSheet->isInline() && startsAtZero())
1454         return String();
1455 
1456     return finalURL();
1457 }
1458 
hasSourceURL() const1459 bool InspectorStyleSheet::hasSourceURL() const
1460 {
1461     return !sourceURL().isEmpty();
1462 }
1463 
startsAtZero() const1464 bool InspectorStyleSheet::startsAtZero() const
1465 {
1466     CSSStyleSheet* styleSheet = pageStyleSheet();
1467     if (!styleSheet)
1468         return true;
1469 
1470     return styleSheet->startPositionInSource() == TextPosition::minimumPosition();
1471 }
1472 
sourceMapURL() const1473 String InspectorStyleSheet::sourceMapURL() const
1474 {
1475     if (m_origin != TypeBuilder::CSS::StyleSheetOrigin::Regular)
1476         return String();
1477 
1478     String styleSheetText;
1479     bool success = getText(&styleSheetText);
1480     if (success) {
1481         bool deprecated;
1482         String commentValue = ContentSearchUtils::findSourceMapURL(styleSheetText, ContentSearchUtils::CSSMagicComment, &deprecated);
1483         if (!commentValue.isEmpty()) {
1484             // FIXME: add deprecated console message here.
1485             return commentValue;
1486         }
1487     }
1488     return m_pageAgent->resourceSourceMapURL(finalURL());
1489 }
1490 
ruleOrStyleId(CSSStyleDeclaration * style) const1491 InspectorCSSId InspectorStyleSheet::ruleOrStyleId(CSSStyleDeclaration* style) const
1492 {
1493     unsigned index = ruleIndexByStyle(style);
1494     if (index != UINT_MAX)
1495         return InspectorCSSId(id(), index);
1496     return InspectorCSSId();
1497 }
1498 
ownerDocument() const1499 Document* InspectorStyleSheet::ownerDocument() const
1500 {
1501     return m_pageStyleSheet->ownerDocument();
1502 }
1503 
ruleSourceDataFor(CSSStyleDeclaration * style) const1504 PassRefPtr<CSSRuleSourceData> InspectorStyleSheet::ruleSourceDataFor(CSSStyleDeclaration* style) const
1505 {
1506     return m_parsedStyleSheet->ruleSourceDataAt(ruleIndexByStyle(style));
1507 }
1508 
lineEndings() const1509 PassOwnPtr<Vector<unsigned> > InspectorStyleSheet::lineEndings() const
1510 {
1511     if (!m_parsedStyleSheet->hasText())
1512         return PassOwnPtr<Vector<unsigned> >();
1513     return WTF::lineEndings(m_parsedStyleSheet->text());
1514 }
1515 
ruleIndexByStyle(CSSStyleDeclaration * pageStyle) const1516 unsigned InspectorStyleSheet::ruleIndexByStyle(CSSStyleDeclaration* pageStyle) const
1517 {
1518     ensureFlatRules();
1519     for (unsigned i = 0, size = m_flatRules.size(); i < size; ++i) {
1520         CSSStyleRule* styleRule = InspectorCSSAgent::asCSSStyleRule(m_flatRules.at(i).get());
1521         if (styleRule && styleRule->style() == pageStyle)
1522             return i;
1523     }
1524     return UINT_MAX;
1525 }
1526 
ruleIndexByRule(const CSSRule * rule) const1527 unsigned InspectorStyleSheet::ruleIndexByRule(const CSSRule* rule) const
1528 {
1529     ensureFlatRules();
1530     size_t index = m_flatRules.find(rule);
1531     return index == kNotFound ? UINT_MAX : static_cast<unsigned>(index);
1532 }
1533 
checkPageStyleSheet(ExceptionState & exceptionState) const1534 bool InspectorStyleSheet::checkPageStyleSheet(ExceptionState& exceptionState) const
1535 {
1536     if (!m_pageStyleSheet) {
1537         exceptionState.throwUninformativeAndGenericDOMException(NotSupportedError);
1538         return false;
1539     }
1540     return true;
1541 }
1542 
ensureParsedDataReady()1543 bool InspectorStyleSheet::ensureParsedDataReady()
1544 {
1545     return ensureText() && ensureSourceData();
1546 }
1547 
ensureText() const1548 bool InspectorStyleSheet::ensureText() const
1549 {
1550     if (!m_parsedStyleSheet)
1551         return false;
1552     if (m_parsedStyleSheet->hasText())
1553         return true;
1554 
1555     String text;
1556     bool success = originalStyleSheetText(&text);
1557     if (success)
1558         m_parsedStyleSheet->setText(text);
1559     // No need to clear m_flatRules here - it's empty.
1560 
1561     return success;
1562 }
1563 
ensureSourceData()1564 bool InspectorStyleSheet::ensureSourceData()
1565 {
1566     if (m_parsedStyleSheet->hasSourceData())
1567         return true;
1568 
1569     if (!m_parsedStyleSheet->hasText())
1570         return false;
1571 
1572     RefPtr<StyleSheetContents> newStyleSheet = StyleSheetContents::create();
1573     OwnPtr<RuleSourceDataList> result = adoptPtr(new RuleSourceDataList());
1574     StyleSheetHandler handler(m_parsedStyleSheet->text(), m_pageStyleSheet->ownerDocument(), newStyleSheet.get(), result.get());
1575     createCSSParser(m_pageStyleSheet->ownerDocument())->parseSheet(newStyleSheet.get(), m_parsedStyleSheet->text(), TextPosition::minimumPosition(), &handler);
1576     m_parsedStyleSheet->setSourceData(result.release());
1577     return m_parsedStyleSheet->hasSourceData();
1578 }
1579 
ensureFlatRules() const1580 void InspectorStyleSheet::ensureFlatRules() const
1581 {
1582     // We are fine with redoing this for empty stylesheets as this will run fast.
1583     if (m_flatRules.isEmpty())
1584         collectFlatRules(asCSSRuleList(pageStyleSheet()), &m_flatRules);
1585 }
1586 
setStyleText(CSSStyleDeclaration * style,const String & text)1587 bool InspectorStyleSheet::setStyleText(CSSStyleDeclaration* style, const String& text)
1588 {
1589     if (!m_pageStyleSheet)
1590         return false;
1591     if (!ensureParsedDataReady())
1592         return false;
1593 
1594     String patchedStyleSheetText;
1595     bool success = styleSheetTextWithChangedStyle(style, text, &patchedStyleSheetText);
1596     if (!success)
1597         return false;
1598 
1599     InspectorCSSId id = ruleOrStyleId(style);
1600     if (id.isEmpty())
1601         return false;
1602 
1603     TrackExceptionState exceptionState;
1604     style->setCSSText(text, exceptionState);
1605     if (!exceptionState.hadException())
1606         m_parsedStyleSheet->setText(patchedStyleSheetText);
1607 
1608     return !exceptionState.hadException();
1609 }
1610 
styleSheetTextWithChangedStyle(CSSStyleDeclaration * style,const String & newStyleText,String * result)1611 bool InspectorStyleSheet::styleSheetTextWithChangedStyle(CSSStyleDeclaration* style, const String& newStyleText, String* result)
1612 {
1613     if (!style)
1614         return false;
1615 
1616     if (!ensureParsedDataReady())
1617         return false;
1618 
1619     RefPtr<CSSRuleSourceData> sourceData = ruleSourceDataFor(style);
1620     unsigned bodyStart = sourceData->ruleBodyRange.start;
1621     unsigned bodyEnd = sourceData->ruleBodyRange.end;
1622     ASSERT(bodyStart <= bodyEnd);
1623 
1624     String text = m_parsedStyleSheet->text();
1625     ASSERT_WITH_SECURITY_IMPLICATION(bodyEnd <= text.length()); // bodyEnd is exclusive
1626 
1627     text.replace(bodyStart, bodyEnd - bodyStart, newStyleText);
1628     *result = text;
1629     return true;
1630 }
1631 
ruleId(CSSStyleRule * rule) const1632 InspectorCSSId InspectorStyleSheet::ruleId(CSSStyleRule* rule) const
1633 {
1634     return ruleOrStyleId(rule->style());
1635 }
1636 
revalidateStyle(CSSStyleDeclaration * pageStyle)1637 void InspectorStyleSheet::revalidateStyle(CSSStyleDeclaration* pageStyle)
1638 {
1639     if (m_isRevalidating)
1640         return;
1641 
1642     m_isRevalidating = true;
1643     ensureFlatRules();
1644     for (unsigned i = 0, size = m_flatRules.size(); i < size; ++i) {
1645         CSSStyleRule* parsedRule = InspectorCSSAgent::asCSSStyleRule(m_flatRules.at(i).get());
1646         if (parsedRule && parsedRule->style() == pageStyle) {
1647             if (parsedRule->styleRule()->properties()->asText() != pageStyle->cssText())
1648                 setStyleText(pageStyle, pageStyle->cssText());
1649             break;
1650         }
1651     }
1652     m_isRevalidating = false;
1653 }
1654 
originalStyleSheetText(String * result) const1655 bool InspectorStyleSheet::originalStyleSheetText(String* result) const
1656 {
1657     bool success = inlineStyleSheetText(result);
1658     if (!success)
1659         success = resourceStyleSheetText(result);
1660     return success;
1661 }
1662 
resourceStyleSheetText(String * result) const1663 bool InspectorStyleSheet::resourceStyleSheetText(String* result) const
1664 {
1665     if (m_origin == TypeBuilder::CSS::StyleSheetOrigin::User || m_origin == TypeBuilder::CSS::StyleSheetOrigin::User_agent)
1666         return false;
1667 
1668     if (!m_pageStyleSheet || !ownerDocument() || !ownerDocument()->frame())
1669         return false;
1670 
1671     bool base64Encoded;
1672     bool success = m_resourceAgent->fetchResourceContent(ownerDocument()->frame(), KURL(ParsedURLString, m_pageStyleSheet->href()), result, &base64Encoded) && !base64Encoded;
1673     return success;
1674 }
1675 
inlineStyleSheetText(String * result) const1676 bool InspectorStyleSheet::inlineStyleSheetText(String* result) const
1677 {
1678     if (!m_pageStyleSheet)
1679         return false;
1680 
1681     Node* ownerNode = m_pageStyleSheet->ownerNode();
1682     if (!ownerNode || ownerNode->nodeType() != Node::ELEMENT_NODE)
1683         return false;
1684     Element* ownerElement = toElement(ownerNode);
1685 
1686     if (!ownerElement->hasTagName(HTMLNames::styleTag) && !ownerElement->hasTagName(SVGNames::styleTag))
1687         return false;
1688     *result = ownerElement->textContent();
1689     return true;
1690 }
1691 
create(InspectorPageAgent * pageAgent,InspectorResourceAgent * resourceAgent,const String & id,PassRefPtr<Element> element,TypeBuilder::CSS::StyleSheetOrigin::Enum origin,Listener * listener)1692 PassRefPtr<InspectorStyleSheetForInlineStyle> InspectorStyleSheetForInlineStyle::create(InspectorPageAgent* pageAgent, InspectorResourceAgent* resourceAgent, const String& id, PassRefPtr<Element> element, TypeBuilder::CSS::StyleSheetOrigin::Enum origin, Listener* listener)
1693 {
1694     return adoptRef(new InspectorStyleSheetForInlineStyle(pageAgent, resourceAgent, id, element, origin, listener));
1695 }
1696 
InspectorStyleSheetForInlineStyle(InspectorPageAgent * pageAgent,InspectorResourceAgent * resourceAgent,const String & id,PassRefPtr<Element> element,TypeBuilder::CSS::StyleSheetOrigin::Enum origin,Listener * listener)1697 InspectorStyleSheetForInlineStyle::InspectorStyleSheetForInlineStyle(InspectorPageAgent* pageAgent, InspectorResourceAgent* resourceAgent, const String& id, PassRefPtr<Element> element, TypeBuilder::CSS::StyleSheetOrigin::Enum origin, Listener* listener)
1698     : InspectorStyleSheet(pageAgent, resourceAgent, id, 0, origin, "", listener)
1699     , m_element(element)
1700     , m_ruleSourceData(0)
1701     , m_isStyleTextValid(false)
1702 {
1703     ASSERT(m_element);
1704     m_inspectorStyle = InspectorStyle::create(InspectorCSSId(id, 0), inlineStyle(), this);
1705     m_styleText = m_element->isStyledElement() ? m_element->getAttribute("style").string() : String();
1706 }
1707 
didModifyElementAttribute()1708 void InspectorStyleSheetForInlineStyle::didModifyElementAttribute()
1709 {
1710     m_isStyleTextValid = false;
1711     if (m_element->isStyledElement() && m_element->style() != m_inspectorStyle->cssStyle())
1712         m_inspectorStyle = InspectorStyle::create(InspectorCSSId(id(), 0), inlineStyle(), this);
1713     m_ruleSourceData.clear();
1714 }
1715 
getText(String * result) const1716 bool InspectorStyleSheetForInlineStyle::getText(String* result) const
1717 {
1718     if (!m_isStyleTextValid) {
1719         m_styleText = elementStyleText();
1720         m_isStyleTextValid = true;
1721     }
1722     *result = m_styleText;
1723     return true;
1724 }
1725 
setStyleText(CSSStyleDeclaration * style,const String & text)1726 bool InspectorStyleSheetForInlineStyle::setStyleText(CSSStyleDeclaration* style, const String& text)
1727 {
1728     ASSERT_UNUSED(style, style == inlineStyle());
1729     TrackExceptionState exceptionState;
1730 
1731     {
1732         InspectorCSSAgent::InlineStyleOverrideScope overrideScope(m_element->ownerDocument());
1733         m_element->setAttribute("style", AtomicString(text), exceptionState);
1734     }
1735 
1736     m_styleText = text;
1737     m_isStyleTextValid = true;
1738     m_ruleSourceData.clear();
1739     return !exceptionState.hadException();
1740 }
1741 
lineEndings() const1742 PassOwnPtr<Vector<unsigned> > InspectorStyleSheetForInlineStyle::lineEndings() const
1743 {
1744     return WTF::lineEndings(elementStyleText());
1745 }
1746 
ownerDocument() const1747 Document* InspectorStyleSheetForInlineStyle::ownerDocument() const
1748 {
1749     return &m_element->document();
1750 }
1751 
ensureParsedDataReady()1752 bool InspectorStyleSheetForInlineStyle::ensureParsedDataReady()
1753 {
1754     // The "style" property value can get changed indirectly, e.g. via element.style.borderWidth = "2px".
1755     const String& currentStyleText = elementStyleText();
1756     if (m_styleText != currentStyleText) {
1757         m_ruleSourceData.clear();
1758         m_styleText = currentStyleText;
1759         m_isStyleTextValid = true;
1760     }
1761 
1762     if (m_ruleSourceData)
1763         return true;
1764 
1765     m_ruleSourceData = getStyleAttributeData();
1766 
1767     bool success = !!m_ruleSourceData;
1768     if (!success) {
1769         m_ruleSourceData = CSSRuleSourceData::create(CSSRuleSourceData::STYLE_RULE);
1770         return false;
1771     }
1772 
1773     return true;
1774 }
1775 
inspectorStyleForId(const InspectorCSSId & id)1776 PassRefPtr<InspectorStyle> InspectorStyleSheetForInlineStyle::inspectorStyleForId(const InspectorCSSId& id)
1777 {
1778     ASSERT_UNUSED(id, !id.ordinal());
1779     return m_inspectorStyle;
1780 }
1781 
inlineStyle() const1782 CSSStyleDeclaration* InspectorStyleSheetForInlineStyle::inlineStyle() const
1783 {
1784     return m_element->style();
1785 }
1786 
elementStyleText() const1787 const String& InspectorStyleSheetForInlineStyle::elementStyleText() const
1788 {
1789     return m_element->getAttribute("style").string();
1790 }
1791 
getStyleAttributeData() const1792 PassRefPtr<CSSRuleSourceData> InspectorStyleSheetForInlineStyle::getStyleAttributeData() const
1793 {
1794     if (!m_element->isStyledElement())
1795         return 0;
1796 
1797     if (m_styleText.isEmpty()) {
1798         RefPtr<CSSRuleSourceData> result = CSSRuleSourceData::create(CSSRuleSourceData::STYLE_RULE);
1799         result->ruleBodyRange.start = 0;
1800         result->ruleBodyRange.end = 0;
1801         return result.release();
1802     }
1803 
1804     RefPtr<MutableStylePropertySet> tempDeclaration = MutableStylePropertySet::create();
1805     RuleSourceDataList ruleSourceDataResult;
1806     StyleSheetHandler handler(m_styleText, &m_element->document(), m_element->document().elementSheet()->contents(), &ruleSourceDataResult);
1807     createCSSParser(&m_element->document())->parseDeclaration(tempDeclaration.get(), m_styleText, &handler, m_element->document().elementSheet()->contents());
1808     return ruleSourceDataResult.first().release();
1809 }
1810 
1811 } // namespace WebCore
1812 
1813