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