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