Use "= default" to denote default constructor or destructor
[WebKit-https.git] / Source / WebCore / inspector / InspectorStyleSheet.cpp
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 "InspectorStyleSheet.h"
27
28 #include "CSSImportRule.h"
29 #include "CSSKeyframesRule.h"
30 #include "CSSMediaRule.h"
31 #include "CSSParser.h"
32 #include "CSSParserObserver.h"
33 #include "CSSPropertyNames.h"
34 #include "CSSPropertyParser.h"
35 #include "CSSPropertySourceData.h"
36 #include "CSSRule.h"
37 #include "CSSRuleList.h"
38 #include "CSSStyleRule.h"
39 #include "CSSStyleSheet.h"
40 #include "CSSSupportsRule.h"
41 #include "ContentSecurityPolicy.h"
42 #include "Document.h"
43 #include "Element.h"
44 #include "HTMLHeadElement.h"
45 #include "HTMLNames.h"
46 #include "HTMLParserIdioms.h"
47 #include "HTMLStyleElement.h"
48 #include "InspectorCSSAgent.h"
49 #include "InspectorPageAgent.h"
50 #include "MediaList.h"
51 #include "Node.h"
52 #include "SVGElement.h"
53 #include "SVGStyleElement.h"
54 #include "StyleProperties.h"
55 #include "StyleResolver.h"
56 #include "StyleRule.h"
57 #include "StyleRuleImport.h"
58 #include "StyleSheetContents.h"
59 #include "StyleSheetList.h"
60 #include <inspector/ContentSearchUtilities.h>
61 #include <wtf/text/StringBuilder.h>
62 #include <yarr/RegularExpression.h>
63
64 using Inspector::Protocol::Array;
65 using WebCore::RuleSourceDataList;
66 using WebCore::CSSRuleSourceData;
67
68 class ParsedStyleSheet {
69     WTF_MAKE_FAST_ALLOCATED;
70 public:
71     ParsedStyleSheet();
72
73     const String& text() const { ASSERT(m_hasText); return m_text; }
74     void setText(const String& text);
75     bool hasText() const { return m_hasText; }
76     RuleSourceDataList* sourceData() const { return m_sourceData.get(); }
77     void setSourceData(std::unique_ptr<RuleSourceDataList>);
78     bool hasSourceData() const { return m_sourceData != nullptr; }
79     WebCore::CSSRuleSourceData* ruleSourceDataAt(unsigned) const;
80
81 private:
82
83     String m_text;
84     bool m_hasText;
85     std::unique_ptr<RuleSourceDataList> m_sourceData;
86 };
87
88 ParsedStyleSheet::ParsedStyleSheet()
89     : m_hasText(false)
90 {
91 }
92
93 void ParsedStyleSheet::setText(const String& text)
94 {
95     m_hasText = true;
96     m_text = text;
97     setSourceData(nullptr);
98 }
99
100 static void flattenSourceData(RuleSourceDataList& dataList, RuleSourceDataList& target)
101 {
102     for (auto& data : dataList) {
103         if (data->type == WebCore::StyleRule::Style)
104             target.append(data.copyRef());
105         else if (data->type == WebCore::StyleRule::Media)
106             flattenSourceData(data->childRules, target);
107         else if (data->type == WebCore::StyleRule::Supports)
108             flattenSourceData(data->childRules, target);
109     }
110 }
111
112 void ParsedStyleSheet::setSourceData(std::unique_ptr<RuleSourceDataList> sourceData)
113 {
114     if (!sourceData) {
115         m_sourceData.reset();
116         return;
117     }
118
119     m_sourceData = std::make_unique<RuleSourceDataList>();
120
121     // FIXME: This is a temporary solution to retain the original flat sourceData structure
122     // containing only style rules, even though CSSParser now provides the full rule source data tree.
123     // Normally, we should just assign m_sourceData = sourceData;
124     flattenSourceData(*sourceData, *m_sourceData);
125 }
126
127 WebCore::CSSRuleSourceData* ParsedStyleSheet::ruleSourceDataAt(unsigned index) const
128 {
129     if (!hasSourceData() || index >= m_sourceData->size())
130         return nullptr;
131
132     return m_sourceData->at(index).ptr();
133 }
134
135
136 namespace WebCore {
137 using namespace Inspector;
138
139 static CSSParserContext parserContextForDocument(Document* document)
140 {
141     return document ? CSSParserContext(*document) : strictCSSParserContext();
142 }
143
144 class StyleSheetHandler : public CSSParserObserver {
145 public:
146     StyleSheetHandler(const String& parsedText, Document* document, RuleSourceDataList* result)
147         : m_parsedText(parsedText)
148         , m_document(document)
149         , m_ruleSourceDataResult(result)
150     {
151         ASSERT(m_ruleSourceDataResult);
152     }
153     
154 private:
155     void startRuleHeader(StyleRule::Type, unsigned) override;
156     void endRuleHeader(unsigned) override;
157     void observeSelector(unsigned startOffset, unsigned endOffset) override;
158     void startRuleBody(unsigned) override;
159     void endRuleBody(unsigned) override;
160     void observeProperty(unsigned startOffset, unsigned endOffset, bool isImportant, bool isParsed) override;
161     void observeComment(unsigned startOffset, unsigned endOffset) override;
162     
163     Ref<CSSRuleSourceData> popRuleData();
164     template <typename CharacterType> inline void setRuleHeaderEnd(const CharacterType*, unsigned);
165     void fixUnparsedPropertyRanges(CSSRuleSourceData*);
166     
167     const String& m_parsedText;
168     Document* m_document;
169     
170     RuleSourceDataList m_currentRuleDataStack;
171     RefPtr<CSSRuleSourceData> m_currentRuleData;
172     RuleSourceDataList* m_ruleSourceDataResult { nullptr };
173 };
174
175 void StyleSheetHandler::startRuleHeader(StyleRule::Type type, unsigned offset)
176 {
177     // Pop off data for a previous invalid rule.
178     if (m_currentRuleData)
179         m_currentRuleDataStack.removeLast();
180     
181     auto data = CSSRuleSourceData::create(type);
182     data->ruleHeaderRange.start = offset;
183     m_currentRuleData = data.copyRef();
184     m_currentRuleDataStack.append(WTFMove(data));
185 }
186
187 template <typename CharacterType> inline void StyleSheetHandler::setRuleHeaderEnd(const CharacterType* dataStart, unsigned listEndOffset)
188 {
189     while (listEndOffset > 1) {
190         if (isHTMLSpace<CharacterType>(*(dataStart + listEndOffset - 1)))
191             --listEndOffset;
192         else
193             break;
194     }
195
196     m_currentRuleDataStack.last()->ruleHeaderRange.end = listEndOffset;
197     if (!m_currentRuleDataStack.last()->selectorRanges.isEmpty())
198         m_currentRuleDataStack.last()->selectorRanges.last().end = listEndOffset;
199 }
200
201 void StyleSheetHandler::endRuleHeader(unsigned offset)
202 {
203     ASSERT(!m_currentRuleDataStack.isEmpty());
204     
205     if (m_parsedText.is8Bit())
206         setRuleHeaderEnd<LChar>(m_parsedText.characters8(), offset);
207     else
208         setRuleHeaderEnd<UChar>(m_parsedText.characters16(), offset);
209 }
210
211 void StyleSheetHandler::observeSelector(unsigned startOffset, unsigned endOffset)
212 {
213     ASSERT(m_currentRuleDataStack.size());
214     m_currentRuleDataStack.last()->selectorRanges.append(SourceRange(startOffset, endOffset));
215 }
216
217 void StyleSheetHandler::startRuleBody(unsigned offset)
218 {
219     m_currentRuleData = nullptr;
220     ASSERT(!m_currentRuleDataStack.isEmpty());
221     
222     // Skip the rule body opening brace.
223     if (m_parsedText[offset] == '{')
224         ++offset;
225
226     m_currentRuleDataStack.last()->ruleBodyRange.start = offset;
227 }
228
229 void StyleSheetHandler::endRuleBody(unsigned offset)
230 {
231     ASSERT(!m_currentRuleDataStack.isEmpty());
232     m_currentRuleDataStack.last()->ruleBodyRange.end = offset;
233     auto rule = popRuleData();
234     fixUnparsedPropertyRanges(rule.ptr());
235     if (m_currentRuleDataStack.isEmpty())
236         m_ruleSourceDataResult->append(WTFMove(rule));
237     else
238         m_currentRuleDataStack.last()->childRules.append(WTFMove(rule));
239 }
240
241 Ref<CSSRuleSourceData> StyleSheetHandler::popRuleData()
242 {
243     ASSERT(!m_currentRuleDataStack.isEmpty());
244     m_currentRuleData = nullptr;
245     auto data = WTFMove(m_currentRuleDataStack.last());
246     m_currentRuleDataStack.removeLast();
247     return data;
248 }
249
250 template <typename CharacterType>
251 static inline void fixUnparsedProperties(const CharacterType* characters, CSSRuleSourceData* ruleData)
252 {
253     Vector<CSSPropertySourceData>& propertyData = ruleData->styleSourceData->propertyData;
254     unsigned size = propertyData.size();
255     if (!size)
256         return;
257     
258     unsigned styleStart = ruleData->ruleBodyRange.start;
259     
260     CSSPropertySourceData* nextData = &(propertyData.at(0));
261     for (unsigned i = 0; i < size; ++i) {
262         CSSPropertySourceData* currentData = nextData;
263         nextData = i < size - 1 ? &(propertyData.at(i + 1)) : nullptr;
264         
265         if (currentData->parsedOk)
266             continue;
267         if (currentData->range.end > 0 && characters[styleStart + currentData->range.end - 1] == ';')
268             continue;
269         
270         unsigned propertyEnd;
271         if (!nextData)
272             propertyEnd = ruleData->ruleBodyRange.end - 1;
273         else
274             propertyEnd = styleStart + nextData->range.start - 1;
275         
276         while (isHTMLSpace<CharacterType>(characters[propertyEnd]))
277             --propertyEnd;
278         
279         // propertyEnd points at the last property text character.
280         unsigned newRangeEnd = (propertyEnd - styleStart) + 1;
281         if (currentData->range.end != newRangeEnd) {
282             currentData->range.end = newRangeEnd;
283             unsigned valueStart = styleStart + currentData->range.start + currentData->name.length();
284             while (valueStart < propertyEnd && characters[valueStart] != ':')
285                 ++valueStart;
286             
287             // Shift past the ':'.
288             if (valueStart < propertyEnd)
289                 ++valueStart;
290
291             while (valueStart < propertyEnd && isHTMLSpace<CharacterType>(characters[valueStart]))
292                 ++valueStart;
293             
294             // Need to exclude the trailing ';' from the property value.
295             currentData->value = String(characters + valueStart, propertyEnd - valueStart + (characters[propertyEnd] == ';' ? 0 : 1));
296         }
297     }
298 }
299
300 void StyleSheetHandler::fixUnparsedPropertyRanges(CSSRuleSourceData* ruleData)
301 {
302     if (!ruleData->styleSourceData)
303         return;
304     
305     if (m_parsedText.is8Bit()) {
306         fixUnparsedProperties<LChar>(m_parsedText.characters8(), ruleData);
307         return;
308     }
309     
310     fixUnparsedProperties<UChar>(m_parsedText.characters16(), ruleData);
311 }
312
313 void StyleSheetHandler::observeProperty(unsigned startOffset, unsigned endOffset, bool isImportant, bool isParsed)
314 {
315     if (m_currentRuleDataStack.isEmpty() || !m_currentRuleDataStack.last()->styleSourceData)
316         return;
317     
318     ASSERT(endOffset <= m_parsedText.length());
319     
320     // Include semicolon in the property text.
321     if (endOffset < m_parsedText.length() && m_parsedText[endOffset] == ';')
322         ++endOffset;
323     
324     ASSERT(startOffset < endOffset);
325     String propertyString = m_parsedText.substring(startOffset, endOffset - startOffset).stripWhiteSpace();
326     if (propertyString.endsWith(';'))
327         propertyString = propertyString.left(propertyString.length() - 1);
328     size_t colonIndex = propertyString.find(':');
329     ASSERT(colonIndex != notFound);
330
331     String name = propertyString.left(colonIndex).stripWhiteSpace();
332     String value = propertyString.substring(colonIndex + 1, propertyString.length()).stripWhiteSpace();
333     
334     // FIXME-NEWPARSER: The property range is relative to the declaration start offset, but no
335     // good reason for it, and it complicates fixUnparsedProperties.
336     SourceRange& topRuleBodyRange = m_currentRuleDataStack.last()->ruleBodyRange;
337     m_currentRuleDataStack.last()->styleSourceData->propertyData.append(CSSPropertySourceData(name, value, isImportant, false, isParsed, SourceRange(startOffset - topRuleBodyRange.start, endOffset - topRuleBodyRange.start)));
338 }
339
340 void StyleSheetHandler::observeComment(unsigned startOffset, unsigned endOffset)
341 {
342     ASSERT(endOffset <= m_parsedText.length());
343
344     if (m_currentRuleDataStack.isEmpty() || !m_currentRuleDataStack.last()->ruleHeaderRange.end || !m_currentRuleDataStack.last()->styleSourceData)
345         return;
346     
347     // The lexer is not inside a property AND it is scanning a declaration-aware
348     // rule body.
349     String commentText = m_parsedText.substring(startOffset, endOffset - startOffset);
350     
351     ASSERT(commentText.startsWith("/*"));
352     commentText = commentText.substring(2);
353     
354     // Require well-formed comments.
355     if (!commentText.endsWith("*/"))
356         return;
357     commentText = commentText.substring(0, commentText.length() - 2).stripWhiteSpace();
358     if (commentText.isEmpty())
359         return;
360     
361     // FIXME: Use the actual rule type rather than STYLE_RULE?
362     RuleSourceDataList sourceData;
363     
364     StyleSheetHandler handler(commentText, m_document, &sourceData);
365     CSSParser::parseDeclarationForInspector(parserContextForDocument(m_document), commentText, handler);
366     Vector<CSSPropertySourceData>& commentPropertyData = sourceData.first()->styleSourceData->propertyData;
367     if (commentPropertyData.size() != 1)
368         return;
369     CSSPropertySourceData& propertyData = commentPropertyData.at(0);
370     bool parsedOk = propertyData.parsedOk || propertyData.name.startsWith("-moz-") || propertyData.name.startsWith("-o-") || propertyData.name.startsWith("-webkit-") || propertyData.name.startsWith("-ms-");
371     if (!parsedOk || propertyData.range.length() != commentText.length())
372         return;
373     
374     // FIXME-NEWPARSER: The property range is relative to the declaration start offset, but no
375     // good reason for it, and it complicates fixUnparsedProperties.
376     SourceRange& topRuleBodyRange = m_currentRuleDataStack.last()->ruleBodyRange;
377     m_currentRuleDataStack.last()->styleSourceData->propertyData.append(CSSPropertySourceData(propertyData.name, propertyData.value, false, true, true, SourceRange(startOffset - topRuleBodyRange.start, endOffset - topRuleBodyRange.start)));
378 }
379
380 enum MediaListSource {
381     MediaListSourceLinkedSheet,
382     MediaListSourceInlineSheet,
383     MediaListSourceMediaRule,
384     MediaListSourceImportRule
385 };
386
387 static RefPtr<Inspector::Protocol::CSS::SourceRange> buildSourceRangeObject(const SourceRange& range, Vector<size_t>* lineEndings, int* endingLine = nullptr)
388 {
389     if (!lineEndings)
390         return nullptr;
391     TextPosition start = ContentSearchUtilities::textPositionFromOffset(range.start, *lineEndings);
392     TextPosition end = ContentSearchUtilities::textPositionFromOffset(range.end, *lineEndings);
393
394     if (endingLine)
395         *endingLine = end.m_line.zeroBasedInt();
396
397     return Inspector::Protocol::CSS::SourceRange::create()
398         .setStartLine(start.m_line.zeroBasedInt())
399         .setStartColumn(start.m_column.zeroBasedInt())
400         .setEndLine(end.m_line.zeroBasedInt())
401         .setEndColumn(end.m_column.zeroBasedInt())
402         .release();
403 }
404
405 static Ref<Inspector::Protocol::CSS::CSSMedia> buildMediaObject(const MediaList* media, MediaListSource mediaListSource, const String& sourceURL)
406 {
407     // Make certain compilers happy by initializing |source| up-front.
408     Inspector::Protocol::CSS::CSSMedia::Source source = Inspector::Protocol::CSS::CSSMedia::Source::InlineSheet;
409     switch (mediaListSource) {
410     case MediaListSourceMediaRule:
411         source = Inspector::Protocol::CSS::CSSMedia::Source::MediaRule;
412         break;
413     case MediaListSourceImportRule:
414         source = Inspector::Protocol::CSS::CSSMedia::Source::ImportRule;
415         break;
416     case MediaListSourceLinkedSheet:
417         source = Inspector::Protocol::CSS::CSSMedia::Source::LinkedSheet;
418         break;
419     case MediaListSourceInlineSheet:
420         source = Inspector::Protocol::CSS::CSSMedia::Source::InlineSheet;
421         break;
422     }
423
424     auto mediaObject = Inspector::Protocol::CSS::CSSMedia::create()
425         .setText(media->mediaText())
426         .setSource(source)
427         .release();
428
429     if (!sourceURL.isEmpty()) {
430         mediaObject->setSourceURL(sourceURL);
431         mediaObject->setSourceLine(media->queries()->lastLine());
432     }
433     return mediaObject;
434 }
435
436 static RefPtr<CSSRuleList> asCSSRuleList(CSSStyleSheet* styleSheet)
437 {
438     if (!styleSheet)
439         return nullptr;
440
441     RefPtr<StaticCSSRuleList> list = StaticCSSRuleList::create();
442     Vector<RefPtr<CSSRule>>& listRules = list->rules();
443     for (unsigned i = 0, size = styleSheet->length(); i < size; ++i)
444         listRules.append(styleSheet->item(i));
445     return WTFMove(list);
446 }
447
448 static RefPtr<CSSRuleList> asCSSRuleList(CSSRule* rule)
449 {
450     if (!rule)
451         return nullptr;
452
453     if (is<CSSMediaRule>(*rule))
454         return &downcast<CSSMediaRule>(*rule).cssRules();
455
456     if (is<CSSKeyframesRule>(*rule))
457         return &downcast<CSSKeyframesRule>(*rule).cssRules();
458
459     if (is<CSSSupportsRule>(*rule))
460         return &downcast<CSSSupportsRule>(*rule).cssRules();
461
462     return nullptr;
463 }
464
465 static void fillMediaListChain(CSSRule* rule, Array<Inspector::Protocol::CSS::CSSMedia>& mediaArray)
466 {
467     MediaList* mediaList;
468     CSSRule* parentRule = rule;
469     String sourceURL;
470     while (parentRule) {
471         CSSStyleSheet* parentStyleSheet = nullptr;
472         bool isMediaRule = true;
473         if (is<CSSMediaRule>(*parentRule)) {
474             CSSMediaRule& mediaRule = downcast<CSSMediaRule>(*parentRule);
475             mediaList = mediaRule.media();
476             parentStyleSheet = mediaRule.parentStyleSheet();
477         } else if (is<CSSImportRule>(*parentRule)) {
478             CSSImportRule& importRule = downcast<CSSImportRule>(*parentRule);
479             mediaList = &importRule.media();
480             parentStyleSheet = importRule.parentStyleSheet();
481             isMediaRule = false;
482         } else
483             mediaList = nullptr;
484
485         if (parentStyleSheet) {
486             sourceURL = parentStyleSheet->contents().baseURL();
487             if (sourceURL.isEmpty())
488                 sourceURL = InspectorDOMAgent::documentURLString(parentStyleSheet->ownerDocument());
489         } else
490             sourceURL = emptyString();
491
492         if (mediaList && mediaList->length())
493             mediaArray.addItem(buildMediaObject(mediaList, isMediaRule ? MediaListSourceMediaRule : MediaListSourceImportRule, sourceURL));
494
495         if (parentRule->parentRule())
496             parentRule = parentRule->parentRule();
497         else {
498             CSSStyleSheet* styleSheet = parentRule->parentStyleSheet();
499             while (styleSheet) {
500                 mediaList = styleSheet->media();
501                 if (mediaList && mediaList->length()) {
502                     Document* doc = styleSheet->ownerDocument();
503                     if (doc)
504                         sourceURL = doc->url();
505                     else if (!styleSheet->contents().baseURL().isEmpty())
506                         sourceURL = styleSheet->contents().baseURL();
507                     else
508                         sourceURL = emptyString();
509                     mediaArray.addItem(buildMediaObject(mediaList, styleSheet->ownerNode() ? MediaListSourceLinkedSheet : MediaListSourceInlineSheet, sourceURL));
510                 }
511                 parentRule = styleSheet->ownerRule();
512                 if (parentRule)
513                     break;
514                 styleSheet = styleSheet->parentStyleSheet();
515             }
516         }
517     }
518 }
519
520 Ref<InspectorStyle> InspectorStyle::create(const InspectorCSSId& styleId, Ref<CSSStyleDeclaration>&& style, InspectorStyleSheet* parentStyleSheet)
521 {
522     return adoptRef(*new InspectorStyle(styleId, WTFMove(style), parentStyleSheet));
523 }
524
525 InspectorStyle::InspectorStyle(const InspectorCSSId& styleId, Ref<CSSStyleDeclaration>&& style, InspectorStyleSheet* parentStyleSheet)
526     : m_styleId(styleId)
527     , m_style(WTFMove(style))
528     , m_parentStyleSheet(parentStyleSheet)
529 {
530 }
531
532 InspectorStyle::~InspectorStyle() = default;
533
534 RefPtr<Inspector::Protocol::CSS::CSSStyle> InspectorStyle::buildObjectForStyle() const
535 {
536     Ref<Inspector::Protocol::CSS::CSSStyle> result = styleWithProperties();
537     if (!m_styleId.isEmpty())
538         result->setStyleId(m_styleId.asProtocolValue<Inspector::Protocol::CSS::CSSStyleId>());
539
540     result->setWidth(m_style->getPropertyValue("width"));
541     result->setHeight(m_style->getPropertyValue("height"));
542
543     RefPtr<CSSRuleSourceData> sourceData = extractSourceData();
544     if (sourceData)
545         result->setRange(buildSourceRangeObject(sourceData->ruleBodyRange, m_parentStyleSheet->lineEndings().get()));
546
547     return WTFMove(result);
548 }
549
550 Ref<Inspector::Protocol::Array<Inspector::Protocol::CSS::CSSComputedStyleProperty>> InspectorStyle::buildArrayForComputedStyle() const
551 {
552     auto result = Inspector::Protocol::Array<Inspector::Protocol::CSS::CSSComputedStyleProperty>::create();
553     Vector<InspectorStyleProperty> properties;
554     populateAllProperties(&properties);
555
556     for (auto& property : properties) {
557         const CSSPropertySourceData& propertyEntry = property.sourceData;
558         auto entry = Inspector::Protocol::CSS::CSSComputedStyleProperty::create()
559             .setName(propertyEntry.name)
560             .setValue(propertyEntry.value)
561             .release();
562         result->addItem(WTFMove(entry));
563     }
564
565     return result;
566 }
567
568 ExceptionOr<String> InspectorStyle::text() const
569 {
570     // Precondition: m_parentStyleSheet->ensureParsedDataReady() has been called successfully.
571     auto sourceData = extractSourceData();
572     if (!sourceData)
573         return Exception { NotFoundError };
574
575     auto result = m_parentStyleSheet->text();
576     if (result.hasException())
577         return result.releaseException();
578
579     auto& bodyRange = sourceData->ruleBodyRange;
580     return result.releaseReturnValue().substring(bodyRange.start, bodyRange.end - bodyRange.start);
581 }
582
583 static String lowercasePropertyName(const String& name)
584 {
585     // Custom properties are case-sensitive.
586     if (name.startsWith("--"))
587         return name;
588     return name.convertToASCIILowercase();
589 }
590
591 void InspectorStyle::populateAllProperties(Vector<InspectorStyleProperty>* result) const
592 {
593     HashSet<String> sourcePropertyNames;
594
595     auto sourceData = extractSourceData();
596     auto* sourcePropertyData = sourceData ? &sourceData->styleSourceData->propertyData : nullptr;
597     if (sourcePropertyData) {
598         auto styleDeclarationOrException = text();
599         ASSERT(!styleDeclarationOrException.hasException());
600         String styleDeclaration = styleDeclarationOrException.hasException() ? emptyString() : styleDeclarationOrException.releaseReturnValue();
601         for (auto& sourceData : *sourcePropertyData) {
602             InspectorStyleProperty p(sourceData, true, sourceData.disabled);
603             p.setRawTextFromStyleDeclaration(styleDeclaration);
604             result->append(p);
605             sourcePropertyNames.add(lowercasePropertyName(sourceData.name));
606         }
607     }
608
609     for (int i = 0, size = m_style->length(); i < size; ++i) {
610         String name = m_style->item(i);
611         if (sourcePropertyNames.add(lowercasePropertyName(name)))
612             result->append(InspectorStyleProperty(CSSPropertySourceData(name, m_style->getPropertyValue(name), !m_style->getPropertyPriority(name).isEmpty(), false, true, SourceRange()), false, false));
613     }
614 }
615
616 Ref<Inspector::Protocol::CSS::CSSStyle> InspectorStyle::styleWithProperties() const
617 {
618     Vector<InspectorStyleProperty> properties;
619     populateAllProperties(&properties);
620
621     auto propertiesObject = Array<Inspector::Protocol::CSS::CSSProperty>::create();
622     auto shorthandEntries = Array<Inspector::Protocol::CSS::ShorthandEntry>::create();
623     HashMap<String, RefPtr<Inspector::Protocol::CSS::CSSProperty>> propertyNameToPreviousActiveProperty;
624     HashSet<String> foundShorthands;
625     String previousPriority;
626     String previousStatus;
627     std::unique_ptr<Vector<size_t>> lineEndings(m_parentStyleSheet ? m_parentStyleSheet->lineEndings() : nullptr);
628     auto sourceData = extractSourceData();
629     unsigned ruleBodyRangeStart = sourceData ? sourceData->ruleBodyRange.start : 0;
630
631     for (Vector<InspectorStyleProperty>::iterator it = properties.begin(), itEnd = properties.end(); it != itEnd; ++it) {
632         const CSSPropertySourceData& propertyEntry = it->sourceData;
633         const String& name = propertyEntry.name;
634
635         // Visual Studio disagrees with other compilers as to whether 'class' is needed here.
636 #if COMPILER(MSVC)
637         enum class Protocol::CSS::CSSPropertyStatus status;
638 #else
639         enum Inspector::Protocol::CSS::CSSPropertyStatus status;
640 #endif
641         status = it->disabled ? Inspector::Protocol::CSS::CSSPropertyStatus::Disabled : Inspector::Protocol::CSS::CSSPropertyStatus::Active;
642
643         RefPtr<Inspector::Protocol::CSS::CSSProperty> property = Inspector::Protocol::CSS::CSSProperty::create()
644             .setName(name.convertToASCIILowercase())
645             .setValue(propertyEntry.value)
646             .release();
647
648         propertiesObject->addItem(property.copyRef());
649
650         CSSPropertyID propertyId = cssPropertyID(name);
651
652         // Default "parsedOk" == true.
653         if (!propertyEntry.parsedOk || isInternalCSSProperty(propertyId))
654             property->setParsedOk(false);
655         if (it->hasRawText())
656             property->setText(it->rawText);
657
658         // Default "priority" == "".
659         if (propertyEntry.important)
660             property->setPriority("important");
661
662         if (it->hasSource) {
663             // The property range is relative to the style body start.
664             // Should be converted into an absolute range (relative to the stylesheet start)
665             // for the proper conversion into line:column.
666             SourceRange absolutePropertyRange = propertyEntry.range;
667             absolutePropertyRange.start += ruleBodyRangeStart;
668             absolutePropertyRange.end += ruleBodyRangeStart;
669             property->setRange(buildSourceRangeObject(absolutePropertyRange, lineEndings.get()));
670         }
671
672         if (!it->disabled) {
673             if (it->hasSource) {
674                 ASSERT(sourceData);
675                 property->setImplicit(false);
676
677                 // Parsed property overrides any property with the same name. Non-parsed property overrides
678                 // previous non-parsed property with the same name (if any).
679                 bool shouldInactivate = false;
680
681                 // Canonicalize property names to treat non-prefixed and vendor-prefixed property names the same (opacity vs. -webkit-opacity).
682                 String canonicalPropertyName = propertyId ? getPropertyNameString(propertyId) : name;
683                 HashMap<String, RefPtr<Inspector::Protocol::CSS::CSSProperty>>::iterator activeIt = propertyNameToPreviousActiveProperty.find(canonicalPropertyName);
684                 if (activeIt != propertyNameToPreviousActiveProperty.end()) {
685                     if (propertyEntry.parsedOk) {
686                         bool successPriority = activeIt->value->getString(Inspector::Protocol::CSS::CSSProperty::Priority, previousPriority);
687                         bool successStatus = activeIt->value->getString(Inspector::Protocol::CSS::CSSProperty::Status, previousStatus);
688                         if (successStatus && previousStatus != "inactive") {
689                             if (propertyEntry.important || !successPriority) // Priority not set == "not important".
690                                 shouldInactivate = true;
691                             else if (status == Inspector::Protocol::CSS::CSSPropertyStatus::Active) {
692                                 // Inactivate a non-important property following the same-named important property.
693                                 status = Inspector::Protocol::CSS::CSSPropertyStatus::Inactive;
694                             }
695                         }
696                     } else {
697                         bool previousParsedOk;
698                         bool success = activeIt->value->getBoolean(Inspector::Protocol::CSS::CSSProperty::ParsedOk, previousParsedOk);
699                         if (success && !previousParsedOk)
700                             shouldInactivate = true;
701                     }
702                 } else
703                     propertyNameToPreviousActiveProperty.set(canonicalPropertyName, property);
704
705                 if (shouldInactivate) {
706                     activeIt->value->setStatus(Inspector::Protocol::CSS::CSSPropertyStatus::Inactive);
707                     propertyNameToPreviousActiveProperty.set(canonicalPropertyName, property);
708                 }
709             } else {
710                 bool implicit = m_style->isPropertyImplicit(name);
711                 // Default "implicit" == false.
712                 if (implicit)
713                     property->setImplicit(true);
714                 status = Inspector::Protocol::CSS::CSSPropertyStatus::Style;
715
716                 String shorthand = m_style->getPropertyShorthand(name);
717                 if (!shorthand.isEmpty()) {
718                     if (!foundShorthands.contains(shorthand)) {
719                         foundShorthands.add(shorthand);
720                         auto entry = Inspector::Protocol::CSS::ShorthandEntry::create()
721                             .setName(shorthand)
722                             .setValue(shorthandValue(shorthand))
723                             .release();
724                         shorthandEntries->addItem(WTFMove(entry));
725                     }
726                 }
727             }
728         }
729
730         // Default "status" == "style".
731         if (status != Inspector::Protocol::CSS::CSSPropertyStatus::Style)
732             property->setStatus(status);
733     }
734
735     return Inspector::Protocol::CSS::CSSStyle::create()
736         .setCssProperties(WTFMove(propertiesObject))
737         .setShorthandEntries(WTFMove(shorthandEntries))
738         .release();
739 }
740
741 RefPtr<CSSRuleSourceData> InspectorStyle::extractSourceData() const
742 {
743     if (!m_parentStyleSheet || !m_parentStyleSheet->ensureParsedDataReady())
744         return nullptr;
745     return m_parentStyleSheet->ruleSourceDataFor(m_style.ptr());
746 }
747
748 ExceptionOr<void> InspectorStyle::setText(const String& text)
749 {
750     return m_parentStyleSheet->setStyleText(m_style.ptr(), text);
751 }
752
753 String InspectorStyle::shorthandValue(const String& shorthandProperty) const
754 {
755     String value = m_style->getPropertyValue(shorthandProperty);
756     if (!value.isEmpty())
757         return value;
758     StringBuilder builder;
759     for (unsigned i = 0; i < m_style->length(); ++i) {
760         String individualProperty = m_style->item(i);
761         if (m_style->getPropertyShorthand(individualProperty) != shorthandProperty)
762             continue;
763         if (m_style->isPropertyImplicit(individualProperty))
764             continue;
765         String individualValue = m_style->getPropertyValue(individualProperty);
766         if (individualValue == "initial")
767             continue;
768         if (!builder.isEmpty())
769             builder.append(' ');
770         builder.append(individualValue);
771     }
772     return builder.toString();
773 }
774
775 String InspectorStyle::shorthandPriority(const String& shorthandProperty) const
776 {
777     String priority = m_style->getPropertyPriority(shorthandProperty);
778     if (priority.isEmpty()) {
779         for (unsigned i = 0; i < m_style->length(); ++i) {
780             String individualProperty = m_style->item(i);
781             if (m_style->getPropertyShorthand(individualProperty) != shorthandProperty)
782                 continue;
783             priority = m_style->getPropertyPriority(individualProperty);
784             break;
785         }
786     }
787     return priority;
788 }
789
790 Vector<String> InspectorStyle::longhandProperties(const String& shorthandProperty) const
791 {
792     Vector<String> properties;
793     HashSet<String> foundProperties;
794     for (unsigned i = 0; i < m_style->length(); ++i) {
795         String individualProperty = m_style->item(i);
796         if (foundProperties.contains(individualProperty) || m_style->getPropertyShorthand(individualProperty) != shorthandProperty)
797             continue;
798
799         foundProperties.add(individualProperty);
800         properties.append(individualProperty);
801     }
802     return properties;
803 }
804
805 Ref<InspectorStyleSheet> InspectorStyleSheet::create(InspectorPageAgent* pageAgent, const String& id, RefPtr<CSSStyleSheet>&& pageStyleSheet, Inspector::Protocol::CSS::StyleSheetOrigin origin, const String& documentURL, Listener* listener)
806 {
807     return adoptRef(*new InspectorStyleSheet(pageAgent, id, WTFMove(pageStyleSheet), origin, documentURL, listener));
808 }
809
810 String InspectorStyleSheet::styleSheetURL(CSSStyleSheet* pageStyleSheet)
811 {
812     if (pageStyleSheet && !pageStyleSheet->contents().baseURL().isEmpty())
813         return pageStyleSheet->contents().baseURL().string();
814     return emptyString();
815 }
816
817 InspectorStyleSheet::InspectorStyleSheet(InspectorPageAgent* pageAgent, const String& id, RefPtr<CSSStyleSheet>&& pageStyleSheet, Inspector::Protocol::CSS::StyleSheetOrigin origin, const String& documentURL, Listener* listener)
818     : m_pageAgent(pageAgent)
819     , m_id(id)
820     , m_pageStyleSheet(WTFMove(pageStyleSheet))
821     , m_origin(origin)
822     , m_documentURL(documentURL)
823     , m_listener(listener)
824 {
825     m_parsedStyleSheet = new ParsedStyleSheet();
826 }
827
828 InspectorStyleSheet::~InspectorStyleSheet()
829 {
830     delete m_parsedStyleSheet;
831 }
832
833 String InspectorStyleSheet::finalURL() const
834 {
835     String url = styleSheetURL(m_pageStyleSheet.get());
836     return url.isEmpty() ? m_documentURL : url;
837 }
838
839 void InspectorStyleSheet::reparseStyleSheet(const String& text)
840 {
841     {
842         // Have a separate scope for clearRules() (bug 95324).
843         CSSStyleSheet::RuleMutationScope mutationScope(m_pageStyleSheet.get());
844         m_pageStyleSheet->contents().clearRules();
845     }
846     {
847         CSSStyleSheet::RuleMutationScope mutationScope(m_pageStyleSheet.get());
848         m_pageStyleSheet->contents().parseString(text);
849         m_pageStyleSheet->clearChildRuleCSSOMWrappers();
850         fireStyleSheetChanged();
851     }
852
853     // We just wiped the entire contents of the stylesheet. Clear the mutation flag.
854     m_pageStyleSheet->clearHadRulesMutation();
855 }
856
857 ExceptionOr<void> InspectorStyleSheet::setText(const String& text)
858 {
859     if (!m_pageStyleSheet)
860         return Exception { NotSupportedError };
861
862     m_parsedStyleSheet->setText(text);
863     m_flatRules.clear();
864
865     return { };
866 }
867
868 ExceptionOr<String> InspectorStyleSheet::ruleSelector(const InspectorCSSId& id)
869 {
870     CSSStyleRule* rule = ruleForId(id);
871     if (!rule)
872         return Exception { NotFoundError };
873     return rule->selectorText();
874 }
875
876 static bool isValidSelectorListString(const String& selector, Document* document)
877 {
878     CSSSelectorList selectorList;
879     CSSParser parser(parserContextForDocument(document));
880     parser.parseSelector(selector, selectorList);
881     return selectorList.isValid();
882 }
883
884 ExceptionOr<void> InspectorStyleSheet::setRuleSelector(const InspectorCSSId& id, const String& selector)
885 {
886     if (!m_pageStyleSheet)
887         return Exception { NotSupportedError };
888
889     // If the selector is invalid, do not proceed any further.
890     if (!isValidSelectorListString(selector, m_pageStyleSheet->ownerDocument()))
891         return Exception { SyntaxError };
892
893     CSSStyleRule* rule = ruleForId(id);
894     if (!rule)
895         return Exception { NotFoundError };
896
897     CSSStyleSheet* styleSheet = rule->parentStyleSheet();
898     if (!styleSheet || !ensureParsedDataReady())
899         return Exception { NotFoundError };
900
901     // If the stylesheet is already mutated at this point, that must mean that our data has been modified
902     // elsewhere. This should never happen as ensureParsedDataReady would return false in that case.
903     ASSERT(!styleSheetMutated());
904
905     rule->setSelectorText(selector);
906     auto sourceData = ruleSourceDataFor(&rule->style());
907     if (!sourceData)
908         return Exception { NotFoundError };
909
910     String sheetText = m_parsedStyleSheet->text();
911     sheetText.replace(sourceData->ruleHeaderRange.start, sourceData->ruleHeaderRange.length(), selector);
912     m_parsedStyleSheet->setText(sheetText);
913     m_pageStyleSheet->clearHadRulesMutation();
914     fireStyleSheetChanged();
915     return { };
916 }
917
918 ExceptionOr<CSSStyleRule*> InspectorStyleSheet::addRule(const String& selector)
919 {
920     if (!m_pageStyleSheet)
921         return Exception { NotSupportedError };
922
923     if (!isValidSelectorListString(selector, m_pageStyleSheet->ownerDocument()))
924         return Exception { SyntaxError };
925
926     auto text = this->text();
927     if (text.hasException())
928         return text.releaseException();
929
930     auto addRuleResult = m_pageStyleSheet->addRule(selector, emptyString(), std::nullopt);
931     if (addRuleResult.hasException())
932         return addRuleResult.releaseException();
933
934     StringBuilder styleSheetText;
935     styleSheetText.append(text.releaseReturnValue());
936
937     if (!styleSheetText.isEmpty())
938         styleSheetText.append('\n');
939
940     styleSheetText.append(selector);
941     styleSheetText.appendLiteral(" {}");
942
943     // Using setText() as this operation changes the stylesheet rule set.
944     setText(styleSheetText.toString());
945
946     // Inspector Style Sheets are always treated as though their parsed data is ready.
947     if (m_origin == Inspector::Protocol::CSS::StyleSheetOrigin::Inspector)
948         fireStyleSheetChanged();
949     else
950         reparseStyleSheet(styleSheetText.toString());
951
952     ASSERT(m_pageStyleSheet->length());
953     unsigned lastRuleIndex = m_pageStyleSheet->length() - 1;
954     CSSRule* rule = m_pageStyleSheet->item(lastRuleIndex);
955     ASSERT(rule);
956
957     CSSStyleRule* styleRule = InspectorCSSAgent::asCSSStyleRule(*rule);
958     if (!styleRule) {
959         // What we just added has to be a CSSStyleRule - we cannot handle other types of rules yet.
960         // If it is not a style rule, pretend we never touched the stylesheet.
961         m_pageStyleSheet->deleteRule(lastRuleIndex);
962         return Exception { SyntaxError };
963     }
964
965     return styleRule;
966 }
967
968 ExceptionOr<void> InspectorStyleSheet::deleteRule(const InspectorCSSId& id)
969 {
970     if (!m_pageStyleSheet)
971         return Exception { NotSupportedError };
972
973     RefPtr<CSSStyleRule> rule = ruleForId(id);
974     if (!rule)
975         return Exception { NotFoundError };
976     CSSStyleSheet* styleSheet = rule->parentStyleSheet();
977     if (!styleSheet || !ensureParsedDataReady())
978         return Exception { NotFoundError };
979
980     auto sourceData = ruleSourceDataFor(&rule->style());
981     if (!sourceData)
982         return Exception { NotFoundError };
983
984     auto deleteRuleResult = styleSheet->deleteRule(id.ordinal());
985     if (deleteRuleResult.hasException())
986         return deleteRuleResult.releaseException();
987
988     // |rule| MAY NOT be addressed after this!
989
990     String sheetText = m_parsedStyleSheet->text();
991     sheetText.remove(sourceData->ruleHeaderRange.start, sourceData->ruleBodyRange.end - sourceData->ruleHeaderRange.start + 1);
992     setText(sheetText);
993     fireStyleSheetChanged();
994     return { };
995 }
996
997 CSSStyleRule* InspectorStyleSheet::ruleForId(const InspectorCSSId& id) const
998 {
999     if (!m_pageStyleSheet)
1000         return nullptr;
1001
1002     ASSERT(!id.isEmpty());
1003     ensureFlatRules();
1004     return id.ordinal() >= m_flatRules.size() ? nullptr : m_flatRules.at(id.ordinal()).get();
1005 }
1006
1007 RefPtr<Inspector::Protocol::CSS::CSSStyleSheetBody> InspectorStyleSheet::buildObjectForStyleSheet()
1008 {
1009     CSSStyleSheet* styleSheet = pageStyleSheet();
1010     if (!styleSheet)
1011         return nullptr;
1012
1013     RefPtr<CSSRuleList> cssRuleList = asCSSRuleList(styleSheet);
1014
1015     auto result = Inspector::Protocol::CSS::CSSStyleSheetBody::create()
1016         .setStyleSheetId(id())
1017         .setRules(buildArrayForRuleList(cssRuleList.get()))
1018         .release();
1019
1020     auto styleSheetText = text();
1021     if (!styleSheetText.hasException())
1022         result->setText(styleSheetText.releaseReturnValue());
1023
1024     return WTFMove(result);
1025 }
1026
1027 RefPtr<Inspector::Protocol::CSS::CSSStyleSheetHeader> InspectorStyleSheet::buildObjectForStyleSheetInfo()
1028 {
1029     CSSStyleSheet* styleSheet = pageStyleSheet();
1030     if (!styleSheet)
1031         return nullptr;
1032
1033     Document* document = styleSheet->ownerDocument();
1034     Frame* frame = document ? document->frame() : nullptr;
1035     return Inspector::Protocol::CSS::CSSStyleSheetHeader::create()
1036         .setStyleSheetId(id())
1037         .setOrigin(m_origin)
1038         .setDisabled(styleSheet->disabled())
1039         .setSourceURL(finalURL())
1040         .setTitle(styleSheet->title())
1041         .setFrameId(m_pageAgent->frameId(frame))
1042         .setIsInline(styleSheet->isInline() && styleSheet->startPosition() != TextPosition())
1043         .setStartLine(styleSheet->startPosition().m_line.zeroBasedInt())
1044         .setStartColumn(styleSheet->startPosition().m_column.zeroBasedInt())
1045         .release();
1046 }
1047
1048 static bool hasDynamicSpecificity(const CSSSelector& simpleSelector)
1049 {
1050     // It is possible that these can have a static specificity if each selector in the list has
1051     // equal specificity, but lets always report that they can be dynamic.
1052     for (const CSSSelector* selector = &simpleSelector; selector; selector = selector->tagHistory()) {
1053         if (selector->match() == CSSSelector::PseudoClass) {
1054             CSSSelector::PseudoClassType pseudoClassType = selector->pseudoClassType();
1055             if (pseudoClassType == CSSSelector::PseudoClassMatches)
1056                 return true;
1057             if (pseudoClassType == CSSSelector::PseudoClassNthChild || pseudoClassType == CSSSelector::PseudoClassNthLastChild) {
1058                 if (selector->selectorList())
1059                     return true;
1060                 return false;
1061             }
1062         }
1063     }
1064
1065     return false;
1066 }
1067
1068 static Ref<Inspector::Protocol::CSS::CSSSelector> buildObjectForSelectorHelper(const String& selectorText, const CSSSelector& selector, Element* element)
1069 {
1070     auto inspectorSelector = Inspector::Protocol::CSS::CSSSelector::create()
1071         .setText(selectorText)
1072         .release();
1073
1074     if (element) {
1075         bool dynamic = hasDynamicSpecificity(selector);
1076         if (dynamic)
1077             inspectorSelector->setDynamic(true);
1078
1079         SelectorChecker::CheckingContext context(SelectorChecker::Mode::CollectingRules);
1080         SelectorChecker selectorChecker(element->document());
1081
1082         unsigned specificity;
1083         bool okay = selectorChecker.match(selector, *element, context, specificity);
1084         if (!okay)
1085             specificity = selector.staticSpecificity(okay);
1086
1087         if (okay) {
1088             auto tuple = Inspector::Protocol::Array<int>::create();
1089             tuple->addItem(static_cast<int>((specificity & CSSSelector::idMask) >> 16));
1090             tuple->addItem(static_cast<int>((specificity & CSSSelector::classMask) >> 8));
1091             tuple->addItem(static_cast<int>(specificity & CSSSelector::elementMask));
1092             inspectorSelector->setSpecificity(WTFMove(tuple));
1093         }
1094     }
1095
1096     return inspectorSelector;
1097 }
1098
1099 static Ref<Inspector::Protocol::Array<Inspector::Protocol::CSS::CSSSelector>> selectorsFromSource(const CSSRuleSourceData* sourceData, const String& sheetText, const CSSSelectorList& selectorList, Element* element)
1100 {
1101     static NeverDestroyed<JSC::Yarr::RegularExpression> comment("/\\*[^]*?\\*/", TextCaseSensitive, JSC::Yarr::MultilineEnabled);
1102
1103     auto result = Inspector::Protocol::Array<Inspector::Protocol::CSS::CSSSelector>::create();
1104     const CSSSelector* selector = selectorList.first();
1105     for (auto& range : sourceData->selectorRanges) {
1106         // If we don't have a selector, that means the SourceData for this CSSStyleSheet
1107         // no longer matches up with the actual rules in the CSSStyleSheet.
1108         ASSERT(selector);
1109         if (!selector)
1110             break;
1111
1112         String selectorText = sheetText.substring(range.start, range.length());
1113
1114         // We don't want to see any comments in the selector components, only the meaningful parts.
1115         replace(selectorText, comment, String());
1116         result->addItem(buildObjectForSelectorHelper(selectorText.stripWhiteSpace(), *selector, element));
1117
1118         selector = CSSSelectorList::next(selector);
1119     }
1120     return result;
1121 }
1122
1123 Ref<Inspector::Protocol::CSS::CSSSelector> InspectorStyleSheet::buildObjectForSelector(const CSSSelector* selector, Element* element)
1124 {
1125     return buildObjectForSelectorHelper(selector->selectorText(), *selector, element);
1126 }
1127
1128 Ref<Inspector::Protocol::CSS::SelectorList> InspectorStyleSheet::buildObjectForSelectorList(CSSStyleRule* rule, Element* element, int& endingLine)
1129 {
1130     RefPtr<CSSRuleSourceData> sourceData;
1131     if (ensureParsedDataReady())
1132         sourceData = ruleSourceDataFor(&rule->style());
1133     RefPtr<Inspector::Protocol::Array<Inspector::Protocol::CSS::CSSSelector>> selectors;
1134
1135     // This intentionally does not rely on the source data to avoid catching the trailing comments (before the declaration starting '{').
1136     String selectorText = rule->selectorText();
1137
1138     if (sourceData)
1139         selectors = selectorsFromSource(sourceData.get(), m_parsedStyleSheet->text(), rule->styleRule().selectorList(), element);
1140     else {
1141         selectors = Inspector::Protocol::Array<Inspector::Protocol::CSS::CSSSelector>::create();
1142         const CSSSelectorList& selectorList = rule->styleRule().selectorList();
1143         for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(selector))
1144             selectors->addItem(buildObjectForSelector(selector, element));
1145     }
1146     auto result = Inspector::Protocol::CSS::SelectorList::create()
1147         .setSelectors(WTFMove(selectors))
1148         .setText(selectorText)
1149         .release();
1150     if (sourceData)
1151         result->setRange(buildSourceRangeObject(sourceData->ruleHeaderRange, lineEndings().get(), &endingLine));
1152     return result;
1153 }
1154
1155 RefPtr<Inspector::Protocol::CSS::CSSRule> InspectorStyleSheet::buildObjectForRule(CSSStyleRule* rule, Element* element)
1156 {
1157     CSSStyleSheet* styleSheet = pageStyleSheet();
1158     if (!styleSheet)
1159         return nullptr;
1160
1161     int endingLine = 0;
1162     auto result = Inspector::Protocol::CSS::CSSRule::create()
1163         .setSelectorList(buildObjectForSelectorList(rule, element, endingLine))
1164         .setSourceLine(endingLine)
1165         .setOrigin(m_origin)
1166         .setStyle(buildObjectForStyle(&rule->style()))
1167         .release();
1168
1169     // "sourceURL" is present only for regular rules, otherwise "origin" should be used in the frontend.
1170     if (m_origin == Inspector::Protocol::CSS::StyleSheetOrigin::Regular)
1171         result->setSourceURL(finalURL());
1172
1173     if (canBind()) {
1174         InspectorCSSId id(ruleId(rule));
1175         if (!id.isEmpty())
1176             result->setRuleId(id.asProtocolValue<Inspector::Protocol::CSS::CSSRuleId>());
1177     }
1178
1179     auto mediaArray = Array<Inspector::Protocol::CSS::CSSMedia>::create();
1180
1181     fillMediaListChain(rule, mediaArray.get());
1182     if (mediaArray->length())
1183         result->setMedia(WTFMove(mediaArray));
1184
1185     return WTFMove(result);
1186 }
1187
1188 RefPtr<Inspector::Protocol::CSS::CSSStyle> InspectorStyleSheet::buildObjectForStyle(CSSStyleDeclaration* style)
1189 {
1190     RefPtr<CSSRuleSourceData> sourceData;
1191     if (ensureParsedDataReady())
1192         sourceData = ruleSourceDataFor(style);
1193
1194     InspectorCSSId id = ruleOrStyleId(style);
1195     if (id.isEmpty()) {
1196         return Inspector::Protocol::CSS::CSSStyle::create()
1197             .setCssProperties(Array<Inspector::Protocol::CSS::CSSProperty>::create())
1198             .setShorthandEntries(Array<Inspector::Protocol::CSS::ShorthandEntry>::create())
1199             .release();
1200     }
1201     RefPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id);
1202     RefPtr<Inspector::Protocol::CSS::CSSStyle> result = inspectorStyle->buildObjectForStyle();
1203
1204     // Style text cannot be retrieved without stylesheet, so set cssText here.
1205     if (sourceData) {
1206         auto sheetText = text();
1207         if (!sheetText.hasException()) {
1208             auto& bodyRange = sourceData->ruleBodyRange;
1209             result->setCssText(sheetText.releaseReturnValue().substring(bodyRange.start, bodyRange.end - bodyRange.start));
1210         }
1211     }
1212
1213     return result;
1214 }
1215
1216 ExceptionOr<void> InspectorStyleSheet::setStyleText(const InspectorCSSId& id, const String& text, String* oldText)
1217 {
1218     auto inspectorStyle = inspectorStyleForId(id);
1219     if (!inspectorStyle)
1220         return Exception { NotFoundError };
1221
1222     if (oldText) {
1223         auto result = inspectorStyle->text();
1224         if (result.hasException())
1225             return result.releaseException();
1226         *oldText = result.releaseReturnValue();
1227     }
1228
1229     auto result = inspectorStyle->setText(text);
1230     if (!result.hasException())
1231         fireStyleSheetChanged();
1232     return result;
1233 }
1234
1235 ExceptionOr<String> InspectorStyleSheet::text() const
1236 {
1237     if (!ensureText())
1238         return Exception { NotFoundError };
1239     return String { m_parsedStyleSheet->text() };
1240 }
1241
1242 CSSStyleDeclaration* InspectorStyleSheet::styleForId(const InspectorCSSId& id) const
1243 {
1244     CSSStyleRule* rule = ruleForId(id);
1245     if (!rule)
1246         return nullptr;
1247
1248     return &rule->style();
1249 }
1250
1251 void InspectorStyleSheet::fireStyleSheetChanged()
1252 {
1253     if (m_listener)
1254         m_listener->styleSheetChanged(this);
1255 }
1256
1257 RefPtr<InspectorStyle> InspectorStyleSheet::inspectorStyleForId(const InspectorCSSId& id)
1258 {
1259     CSSStyleDeclaration* style = styleForId(id);
1260     if (!style)
1261         return nullptr;
1262
1263     return InspectorStyle::create(id, *style, this);
1264 }
1265
1266 InspectorCSSId InspectorStyleSheet::ruleOrStyleId(CSSStyleDeclaration* style) const
1267 {
1268     unsigned index = ruleIndexByStyle(style);
1269     if (index != UINT_MAX)
1270         return InspectorCSSId(id(), index);
1271     return InspectorCSSId();
1272 }
1273
1274 Document* InspectorStyleSheet::ownerDocument() const
1275 {
1276     return m_pageStyleSheet->ownerDocument();
1277 }
1278
1279 RefPtr<CSSRuleSourceData> InspectorStyleSheet::ruleSourceDataFor(CSSStyleDeclaration* style) const
1280 {
1281     return m_parsedStyleSheet->ruleSourceDataAt(ruleIndexByStyle(style));
1282 }
1283
1284 std::unique_ptr<Vector<size_t>> InspectorStyleSheet::lineEndings() const
1285 {
1286     if (!m_parsedStyleSheet->hasText())
1287         return nullptr;
1288     return ContentSearchUtilities::lineEndings(m_parsedStyleSheet->text());
1289 }
1290
1291 unsigned InspectorStyleSheet::ruleIndexByStyle(CSSStyleDeclaration* pageStyle) const
1292 {
1293     ensureFlatRules();
1294     unsigned index = 0;
1295     for (auto& rule : m_flatRules) {
1296         if (&rule->style() == pageStyle)
1297             return index;
1298
1299         ++index;
1300     }
1301     return UINT_MAX;
1302 }
1303
1304 bool InspectorStyleSheet::styleSheetMutated() const
1305 {
1306     return m_pageStyleSheet && m_pageStyleSheet->hadRulesMutation();
1307 }
1308
1309 bool InspectorStyleSheet::ensureParsedDataReady()
1310 {
1311     bool allowParsedData = m_origin == Inspector::Protocol::CSS::StyleSheetOrigin::Inspector || !styleSheetMutated();
1312     return allowParsedData && ensureText() && ensureSourceData();
1313 }
1314
1315 bool InspectorStyleSheet::ensureText() const
1316 {
1317     if (!m_parsedStyleSheet)
1318         return false;
1319     if (m_parsedStyleSheet->hasText())
1320         return true;
1321
1322     String text;
1323     bool success = originalStyleSheetText(&text);
1324     if (success)
1325         m_parsedStyleSheet->setText(text);
1326     // No need to clear m_flatRules here - it's empty.
1327
1328     return success;
1329 }
1330
1331 bool InspectorStyleSheet::ensureSourceData()
1332 {
1333     if (m_parsedStyleSheet->hasSourceData())
1334         return true;
1335
1336     if (!m_parsedStyleSheet->hasText())
1337         return false;
1338
1339     RefPtr<StyleSheetContents> newStyleSheet = StyleSheetContents::create();
1340     auto ruleSourceDataResult = std::make_unique<RuleSourceDataList>();
1341     
1342     CSSParserContext context(parserContextForDocument(m_pageStyleSheet->ownerDocument()));
1343     StyleSheetHandler handler(m_parsedStyleSheet->text(), m_pageStyleSheet->ownerDocument(), ruleSourceDataResult.get());
1344     CSSParser::parseSheetForInspector(context, newStyleSheet.get(), m_parsedStyleSheet->text(), handler);
1345     m_parsedStyleSheet->setSourceData(WTFMove(ruleSourceDataResult));
1346     return m_parsedStyleSheet->hasSourceData();
1347 }
1348
1349 void InspectorStyleSheet::ensureFlatRules() const
1350 {
1351     // We are fine with redoing this for empty stylesheets as this will run fast.
1352     if (m_flatRules.isEmpty())
1353         collectFlatRules(asCSSRuleList(pageStyleSheet()), &m_flatRules);
1354 }
1355
1356 ExceptionOr<void> InspectorStyleSheet::setStyleText(CSSStyleDeclaration* style, const String& text)
1357 {
1358     if (!m_pageStyleSheet)
1359         return Exception { NotFoundError };
1360     if (!ensureParsedDataReady())
1361         return Exception { NotFoundError };
1362
1363     String patchedStyleSheetText;
1364     bool success = styleSheetTextWithChangedStyle(style, text, &patchedStyleSheetText);
1365     if (!success)
1366         return Exception { NotFoundError };
1367
1368     InspectorCSSId id = ruleOrStyleId(style);
1369     if (id.isEmpty())
1370         return Exception { NotFoundError };
1371
1372     auto setCssTextResult = style->setCssText(text);
1373     if (setCssTextResult.hasException())
1374         return setCssTextResult.releaseException();
1375
1376     m_parsedStyleSheet->setText(patchedStyleSheetText);
1377     return { };
1378 }
1379
1380 bool InspectorStyleSheet::styleSheetTextWithChangedStyle(CSSStyleDeclaration* style, const String& newStyleText, String* result)
1381 {
1382     if (!style)
1383         return false;
1384
1385     if (!ensureParsedDataReady())
1386         return false;
1387
1388     RefPtr<CSSRuleSourceData> sourceData = ruleSourceDataFor(style);
1389     unsigned bodyStart = sourceData->ruleBodyRange.start;
1390     unsigned bodyEnd = sourceData->ruleBodyRange.end;
1391     ASSERT(bodyStart <= bodyEnd);
1392
1393     String text = m_parsedStyleSheet->text();
1394     ASSERT_WITH_SECURITY_IMPLICATION(bodyEnd <= text.length()); // bodyEnd is exclusive
1395
1396     text.replace(bodyStart, bodyEnd - bodyStart, newStyleText);
1397     *result = text;
1398     return true;
1399 }
1400
1401 InspectorCSSId InspectorStyleSheet::ruleId(CSSStyleRule* rule) const
1402 {
1403     return ruleOrStyleId(&rule->style());
1404 }
1405
1406 bool InspectorStyleSheet::originalStyleSheetText(String* result) const
1407 {
1408     bool success = inlineStyleSheetText(result);
1409     if (!success)
1410         success = resourceStyleSheetText(result);
1411     return success;
1412 }
1413
1414 bool InspectorStyleSheet::resourceStyleSheetText(String* result) const
1415 {
1416     if (m_origin == Inspector::Protocol::CSS::StyleSheetOrigin::User || m_origin == Inspector::Protocol::CSS::StyleSheetOrigin::UserAgent)
1417         return false;
1418
1419     if (!m_pageStyleSheet || !ownerDocument() || !ownerDocument()->frame())
1420         return false;
1421
1422     String error;
1423     bool base64Encoded;
1424     InspectorPageAgent::resourceContent(error, ownerDocument()->frame(), URL(ParsedURLString, m_pageStyleSheet->href()), result, &base64Encoded);
1425     return error.isEmpty() && !base64Encoded;
1426 }
1427
1428 bool InspectorStyleSheet::inlineStyleSheetText(String* result) const
1429 {
1430     if (!m_pageStyleSheet)
1431         return false;
1432
1433     Node* ownerNode = m_pageStyleSheet->ownerNode();
1434     if (!is<Element>(ownerNode))
1435         return false;
1436     Element& ownerElement = downcast<Element>(*ownerNode);
1437
1438     if (!is<HTMLStyleElement>(ownerElement) && !is<SVGStyleElement>(ownerElement))
1439         return false;
1440     *result = ownerElement.textContent();
1441     return true;
1442 }
1443
1444 Ref<Inspector::Protocol::Array<Inspector::Protocol::CSS::CSSRule>> InspectorStyleSheet::buildArrayForRuleList(CSSRuleList* ruleList)
1445 {
1446     auto result = Inspector::Protocol::Array<Inspector::Protocol::CSS::CSSRule>::create();
1447     if (!ruleList)
1448         return result;
1449
1450     RefPtr<CSSRuleList> refRuleList = ruleList;
1451     CSSStyleRuleVector rules;
1452     collectFlatRules(WTFMove(refRuleList), &rules);
1453
1454     for (auto& rule : rules)
1455         result->addItem(buildObjectForRule(rule.get(), nullptr));
1456
1457     return result;
1458 }
1459
1460 void InspectorStyleSheet::collectFlatRules(RefPtr<CSSRuleList>&& ruleList, CSSStyleRuleVector* result)
1461 {
1462     if (!ruleList)
1463         return;
1464
1465     for (unsigned i = 0, size = ruleList->length(); i < size; ++i) {
1466         CSSRule* rule = ruleList->item(i);
1467         CSSStyleRule* styleRule = InspectorCSSAgent::asCSSStyleRule(*rule);
1468         if (styleRule)
1469             result->append(styleRule);
1470         else {
1471             RefPtr<CSSRuleList> childRuleList = asCSSRuleList(rule);
1472             if (childRuleList)
1473                 collectFlatRules(WTFMove(childRuleList), result);
1474         }
1475     }
1476 }
1477
1478 Ref<InspectorStyleSheetForInlineStyle> InspectorStyleSheetForInlineStyle::create(InspectorPageAgent* pageAgent, const String& id, Ref<StyledElement>&& element, Inspector::Protocol::CSS::StyleSheetOrigin origin, Listener* listener)
1479 {
1480     return adoptRef(*new InspectorStyleSheetForInlineStyle(pageAgent, id, WTFMove(element), origin, listener));
1481 }
1482
1483 InspectorStyleSheetForInlineStyle::InspectorStyleSheetForInlineStyle(InspectorPageAgent* pageAgent, const String& id, Ref<StyledElement>&& element, Inspector::Protocol::CSS::StyleSheetOrigin origin, Listener* listener)
1484     : InspectorStyleSheet(pageAgent, id, nullptr, origin, String(), listener)
1485     , m_element(WTFMove(element))
1486     , m_ruleSourceData(nullptr)
1487     , m_isStyleTextValid(false)
1488 {
1489     m_inspectorStyle = InspectorStyle::create(InspectorCSSId(id, 0), inlineStyle(), this);
1490     m_styleText = m_element->getAttribute("style").string();
1491 }
1492
1493 void InspectorStyleSheetForInlineStyle::didModifyElementAttribute()
1494 {
1495     m_isStyleTextValid = false;
1496     if (&m_element->cssomStyle() != &m_inspectorStyle->cssStyle())
1497         m_inspectorStyle = InspectorStyle::create(InspectorCSSId(id(), 0), inlineStyle(), this);
1498     m_ruleSourceData = nullptr;
1499 }
1500
1501 ExceptionOr<String> InspectorStyleSheetForInlineStyle::text() const
1502 {
1503     if (!m_isStyleTextValid) {
1504         m_styleText = elementStyleText();
1505         m_isStyleTextValid = true;
1506     }
1507     return String { m_styleText };
1508 }
1509
1510 ExceptionOr<void> InspectorStyleSheetForInlineStyle::setStyleText(CSSStyleDeclaration* style, const String& text)
1511 {
1512     ASSERT_UNUSED(style, style == &inlineStyle());
1513
1514     {
1515         InspectorCSSAgent::InlineStyleOverrideScope overrideScope(m_element->document());
1516         m_element->setAttribute(HTMLNames::styleAttr, text);
1517     }
1518
1519     m_styleText = text;
1520     m_isStyleTextValid = true;
1521     m_ruleSourceData = nullptr;
1522
1523     return { };
1524 }
1525
1526 std::unique_ptr<Vector<size_t>> InspectorStyleSheetForInlineStyle::lineEndings() const
1527 {
1528     return ContentSearchUtilities::lineEndings(elementStyleText());
1529 }
1530
1531 Document* InspectorStyleSheetForInlineStyle::ownerDocument() const
1532 {
1533     return &m_element->document();
1534 }
1535
1536 bool InspectorStyleSheetForInlineStyle::ensureParsedDataReady()
1537 {
1538     // The "style" property value can get changed indirectly, e.g. via element.style.borderWidth = "2px".
1539     const String& currentStyleText = elementStyleText();
1540     if (m_styleText != currentStyleText) {
1541         m_ruleSourceData = nullptr;
1542         m_styleText = currentStyleText;
1543         m_isStyleTextValid = true;
1544     }
1545
1546     if (m_ruleSourceData)
1547         return true;
1548
1549     m_ruleSourceData = ruleSourceData();
1550     return true;
1551 }
1552
1553 RefPtr<InspectorStyle> InspectorStyleSheetForInlineStyle::inspectorStyleForId(const InspectorCSSId& id)
1554 {
1555     ASSERT_UNUSED(id, !id.ordinal());
1556     return m_inspectorStyle.copyRef();
1557 }
1558
1559 CSSStyleDeclaration& InspectorStyleSheetForInlineStyle::inlineStyle() const
1560 {
1561     return m_element->cssomStyle();
1562 }
1563
1564 const String& InspectorStyleSheetForInlineStyle::elementStyleText() const
1565 {
1566     return m_element->getAttribute("style").string();
1567 }
1568
1569 Ref<CSSRuleSourceData> InspectorStyleSheetForInlineStyle::ruleSourceData() const
1570 {
1571     if (m_styleText.isEmpty()) {
1572         auto result = CSSRuleSourceData::create(StyleRule::Style);
1573         result->ruleBodyRange.start = 0;
1574         result->ruleBodyRange.end = 0;
1575         return result;
1576     }
1577
1578     CSSParserContext context(parserContextForDocument(&m_element->document()));
1579     RuleSourceDataList ruleSourceDataResult;
1580     StyleSheetHandler handler(m_styleText, &m_element->document(), &ruleSourceDataResult);
1581     CSSParser::parseDeclarationForInspector(context, m_styleText, handler);
1582     return WTFMove(ruleSourceDataResult.first());
1583 }
1584
1585 } // namespace WebCore