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