[Mac] Regression: WebContent process's display name is no longer set
[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         auto status = it->disabled ? Inspector::Protocol::CSS::CSSPropertyStatus::Disabled : Inspector::Protocol::CSS::CSSPropertyStatus::Active;
638
639         RefPtr<Inspector::Protocol::CSS::CSSProperty> property = Inspector::Protocol::CSS::CSSProperty::create()
640             .setName(name.convertToASCIILowercase())
641             .setValue(propertyEntry.value)
642             .release();
643
644         propertiesObject->addItem(property.copyRef());
645
646         CSSPropertyID propertyId = cssPropertyID(name);
647
648         // Default "parsedOk" == true.
649         if (!propertyEntry.parsedOk || isInternalCSSProperty(propertyId))
650             property->setParsedOk(false);
651         if (it->hasRawText())
652             property->setText(it->rawText);
653
654         // Default "priority" == "".
655         if (propertyEntry.important)
656             property->setPriority("important");
657
658         if (it->hasSource) {
659             // The property range is relative to the style body start.
660             // Should be converted into an absolute range (relative to the stylesheet start)
661             // for the proper conversion into line:column.
662             SourceRange absolutePropertyRange = propertyEntry.range;
663             absolutePropertyRange.start += ruleBodyRangeStart;
664             absolutePropertyRange.end += ruleBodyRangeStart;
665             property->setRange(buildSourceRangeObject(absolutePropertyRange, lineEndings));
666         }
667
668         if (!it->disabled) {
669             if (it->hasSource) {
670                 ASSERT(sourceData);
671                 property->setImplicit(false);
672
673                 // Parsed property overrides any property with the same name. Non-parsed property overrides
674                 // previous non-parsed property with the same name (if any).
675                 bool shouldInactivate = false;
676
677                 // Canonicalize property names to treat non-prefixed and vendor-prefixed property names the same (opacity vs. -webkit-opacity).
678                 String canonicalPropertyName = propertyId ? getPropertyNameString(propertyId) : name;
679                 HashMap<String, RefPtr<Inspector::Protocol::CSS::CSSProperty>>::iterator activeIt = propertyNameToPreviousActiveProperty.find(canonicalPropertyName);
680                 if (activeIt != propertyNameToPreviousActiveProperty.end()) {
681                     if (propertyEntry.parsedOk) {
682                         bool successPriority = activeIt->value->getString(Inspector::Protocol::CSS::CSSProperty::Priority, previousPriority);
683                         bool successStatus = activeIt->value->getString(Inspector::Protocol::CSS::CSSProperty::Status, previousStatus);
684                         if (successStatus && previousStatus != "inactive") {
685                             if (propertyEntry.important || !successPriority) // Priority not set == "not important".
686                                 shouldInactivate = true;
687                             else if (status == Inspector::Protocol::CSS::CSSPropertyStatus::Active) {
688                                 // Inactivate a non-important property following the same-named important property.
689                                 status = Inspector::Protocol::CSS::CSSPropertyStatus::Inactive;
690                             }
691                         }
692                     } else {
693                         bool previousParsedOk;
694                         bool success = activeIt->value->getBoolean(Inspector::Protocol::CSS::CSSProperty::ParsedOk, previousParsedOk);
695                         if (success && !previousParsedOk)
696                             shouldInactivate = true;
697                     }
698                 } else
699                     propertyNameToPreviousActiveProperty.set(canonicalPropertyName, property);
700
701                 if (shouldInactivate) {
702                     activeIt->value->setStatus(Inspector::Protocol::CSS::CSSPropertyStatus::Inactive);
703                     propertyNameToPreviousActiveProperty.set(canonicalPropertyName, property);
704                 }
705             } else {
706                 bool implicit = m_style->isPropertyImplicit(name);
707                 // Default "implicit" == false.
708                 if (implicit)
709                     property->setImplicit(true);
710                 status = Inspector::Protocol::CSS::CSSPropertyStatus::Style;
711
712                 String shorthand = m_style->getPropertyShorthand(name);
713                 if (!shorthand.isEmpty()) {
714                     if (!foundShorthands.contains(shorthand)) {
715                         foundShorthands.add(shorthand);
716                         auto entry = Inspector::Protocol::CSS::ShorthandEntry::create()
717                             .setName(shorthand)
718                             .setValue(shorthandValue(shorthand))
719                             .release();
720                         shorthandEntries->addItem(WTFMove(entry));
721                     }
722                 }
723             }
724         }
725
726         // Default "status" == "style".
727         if (status != Inspector::Protocol::CSS::CSSPropertyStatus::Style)
728             property->setStatus(status);
729     }
730
731     return Inspector::Protocol::CSS::CSSStyle::create()
732         .setCssProperties(WTFMove(propertiesObject))
733         .setShorthandEntries(WTFMove(shorthandEntries))
734         .release();
735 }
736
737 RefPtr<CSSRuleSourceData> InspectorStyle::extractSourceData() const
738 {
739     if (!m_parentStyleSheet || !m_parentStyleSheet->ensureParsedDataReady())
740         return nullptr;
741     return m_parentStyleSheet->ruleSourceDataFor(m_style.ptr());
742 }
743
744 ExceptionOr<void> InspectorStyle::setText(const String& text)
745 {
746     return m_parentStyleSheet->setStyleText(m_style.ptr(), text);
747 }
748
749 String InspectorStyle::shorthandValue(const String& shorthandProperty) const
750 {
751     String value = m_style->getPropertyValue(shorthandProperty);
752     if (!value.isEmpty())
753         return value;
754     StringBuilder builder;
755     for (unsigned i = 0; i < m_style->length(); ++i) {
756         String individualProperty = m_style->item(i);
757         if (m_style->getPropertyShorthand(individualProperty) != shorthandProperty)
758             continue;
759         if (m_style->isPropertyImplicit(individualProperty))
760             continue;
761         String individualValue = m_style->getPropertyValue(individualProperty);
762         if (individualValue == "initial")
763             continue;
764         if (!builder.isEmpty())
765             builder.append(' ');
766         builder.append(individualValue);
767     }
768     return builder.toString();
769 }
770
771 String InspectorStyle::shorthandPriority(const String& shorthandProperty) const
772 {
773     String priority = m_style->getPropertyPriority(shorthandProperty);
774     if (priority.isEmpty()) {
775         for (unsigned i = 0; i < m_style->length(); ++i) {
776             String individualProperty = m_style->item(i);
777             if (m_style->getPropertyShorthand(individualProperty) != shorthandProperty)
778                 continue;
779             priority = m_style->getPropertyPriority(individualProperty);
780             break;
781         }
782     }
783     return priority;
784 }
785
786 Vector<String> InspectorStyle::longhandProperties(const String& shorthandProperty) const
787 {
788     Vector<String> properties;
789     HashSet<String> foundProperties;
790     for (unsigned i = 0; i < m_style->length(); ++i) {
791         String individualProperty = m_style->item(i);
792         if (foundProperties.contains(individualProperty) || m_style->getPropertyShorthand(individualProperty) != shorthandProperty)
793             continue;
794
795         foundProperties.add(individualProperty);
796         properties.append(individualProperty);
797     }
798     return properties;
799 }
800
801 Ref<InspectorStyleSheet> InspectorStyleSheet::create(InspectorPageAgent* pageAgent, const String& id, RefPtr<CSSStyleSheet>&& pageStyleSheet, Inspector::Protocol::CSS::StyleSheetOrigin origin, const String& documentURL, Listener* listener)
802 {
803     return adoptRef(*new InspectorStyleSheet(pageAgent, id, WTFMove(pageStyleSheet), origin, documentURL, listener));
804 }
805
806 String InspectorStyleSheet::styleSheetURL(CSSStyleSheet* pageStyleSheet)
807 {
808     if (pageStyleSheet && !pageStyleSheet->contents().baseURL().isEmpty())
809         return pageStyleSheet->contents().baseURL().string();
810     return emptyString();
811 }
812
813 InspectorStyleSheet::InspectorStyleSheet(InspectorPageAgent* pageAgent, const String& id, RefPtr<CSSStyleSheet>&& pageStyleSheet, Inspector::Protocol::CSS::StyleSheetOrigin origin, const String& documentURL, Listener* listener)
814     : m_pageAgent(pageAgent)
815     , m_id(id)
816     , m_pageStyleSheet(WTFMove(pageStyleSheet))
817     , m_origin(origin)
818     , m_documentURL(documentURL)
819     , m_listener(listener)
820 {
821     m_parsedStyleSheet = new ParsedStyleSheet();
822 }
823
824 InspectorStyleSheet::~InspectorStyleSheet()
825 {
826     delete m_parsedStyleSheet;
827 }
828
829 String InspectorStyleSheet::finalURL() const
830 {
831     String url = styleSheetURL(m_pageStyleSheet.get());
832     return url.isEmpty() ? m_documentURL : url;
833 }
834
835 void InspectorStyleSheet::reparseStyleSheet(const String& text)
836 {
837     {
838         // Have a separate scope for clearRules() (bug 95324).
839         CSSStyleSheet::RuleMutationScope mutationScope(m_pageStyleSheet.get());
840         m_pageStyleSheet->contents().clearRules();
841     }
842     {
843         CSSStyleSheet::RuleMutationScope mutationScope(m_pageStyleSheet.get());
844         m_pageStyleSheet->contents().parseString(text);
845         m_pageStyleSheet->clearChildRuleCSSOMWrappers();
846         fireStyleSheetChanged();
847     }
848
849     // We just wiped the entire contents of the stylesheet. Clear the mutation flag.
850     m_pageStyleSheet->clearHadRulesMutation();
851 }
852
853 ExceptionOr<void> InspectorStyleSheet::setText(const String& text)
854 {
855     if (!m_pageStyleSheet)
856         return Exception { NotSupportedError };
857
858     m_parsedStyleSheet->setText(text);
859     m_flatRules.clear();
860
861     return { };
862 }
863
864 ExceptionOr<String> InspectorStyleSheet::ruleSelector(const InspectorCSSId& id)
865 {
866     CSSStyleRule* rule = ruleForId(id);
867     if (!rule)
868         return Exception { NotFoundError };
869     return rule->selectorText();
870 }
871
872 static bool isValidSelectorListString(const String& selector, Document* document)
873 {
874     CSSSelectorList selectorList;
875     CSSParser parser(parserContextForDocument(document));
876     parser.parseSelector(selector, selectorList);
877     return selectorList.isValid();
878 }
879
880 ExceptionOr<void> InspectorStyleSheet::setRuleSelector(const InspectorCSSId& id, const String& selector)
881 {
882     if (!m_pageStyleSheet)
883         return Exception { NotSupportedError };
884
885     // If the selector is invalid, do not proceed any further.
886     if (!isValidSelectorListString(selector, m_pageStyleSheet->ownerDocument()))
887         return Exception { SyntaxError };
888
889     CSSStyleRule* rule = ruleForId(id);
890     if (!rule)
891         return Exception { NotFoundError };
892
893     CSSStyleSheet* styleSheet = rule->parentStyleSheet();
894     if (!styleSheet || !ensureParsedDataReady())
895         return Exception { NotFoundError };
896
897     // If the stylesheet is already mutated at this point, that must mean that our data has been modified
898     // elsewhere. This should never happen as ensureParsedDataReady would return false in that case.
899     ASSERT(!styleSheetMutated());
900
901     rule->setSelectorText(selector);
902     auto sourceData = ruleSourceDataFor(&rule->style());
903     if (!sourceData)
904         return Exception { NotFoundError };
905
906     String sheetText = m_parsedStyleSheet->text();
907     sheetText.replace(sourceData->ruleHeaderRange.start, sourceData->ruleHeaderRange.length(), selector);
908     m_parsedStyleSheet->setText(sheetText);
909     m_pageStyleSheet->clearHadRulesMutation();
910     fireStyleSheetChanged();
911     return { };
912 }
913
914 ExceptionOr<CSSStyleRule*> InspectorStyleSheet::addRule(const String& selector)
915 {
916     if (!m_pageStyleSheet)
917         return Exception { NotSupportedError };
918
919     if (!isValidSelectorListString(selector, m_pageStyleSheet->ownerDocument()))
920         return Exception { SyntaxError };
921
922     auto text = this->text();
923     if (text.hasException())
924         return text.releaseException();
925
926     auto addRuleResult = m_pageStyleSheet->addRule(selector, emptyString(), std::nullopt);
927     if (addRuleResult.hasException())
928         return addRuleResult.releaseException();
929
930     StringBuilder styleSheetText;
931     styleSheetText.append(text.releaseReturnValue());
932
933     if (!styleSheetText.isEmpty())
934         styleSheetText.append('\n');
935
936     styleSheetText.append(selector);
937     styleSheetText.appendLiteral(" {}");
938
939     // Using setText() as this operation changes the stylesheet rule set.
940     setText(styleSheetText.toString());
941
942     // Inspector Style Sheets are always treated as though their parsed data is ready.
943     if (m_origin == Inspector::Protocol::CSS::StyleSheetOrigin::Inspector)
944         fireStyleSheetChanged();
945     else
946         reparseStyleSheet(styleSheetText.toString());
947
948     ASSERT(m_pageStyleSheet->length());
949     unsigned lastRuleIndex = m_pageStyleSheet->length() - 1;
950     CSSRule* rule = m_pageStyleSheet->item(lastRuleIndex);
951     ASSERT(rule);
952
953     CSSStyleRule* styleRule = InspectorCSSAgent::asCSSStyleRule(*rule);
954     if (!styleRule) {
955         // What we just added has to be a CSSStyleRule - we cannot handle other types of rules yet.
956         // If it is not a style rule, pretend we never touched the stylesheet.
957         m_pageStyleSheet->deleteRule(lastRuleIndex);
958         return Exception { SyntaxError };
959     }
960
961     return styleRule;
962 }
963
964 ExceptionOr<void> InspectorStyleSheet::deleteRule(const InspectorCSSId& id)
965 {
966     if (!m_pageStyleSheet)
967         return Exception { NotSupportedError };
968
969     RefPtr<CSSStyleRule> rule = ruleForId(id);
970     if (!rule)
971         return Exception { NotFoundError };
972     CSSStyleSheet* styleSheet = rule->parentStyleSheet();
973     if (!styleSheet || !ensureParsedDataReady())
974         return Exception { NotFoundError };
975
976     auto sourceData = ruleSourceDataFor(&rule->style());
977     if (!sourceData)
978         return Exception { NotFoundError };
979
980     auto deleteRuleResult = styleSheet->deleteRule(id.ordinal());
981     if (deleteRuleResult.hasException())
982         return deleteRuleResult.releaseException();
983
984     // |rule| MAY NOT be addressed after this!
985
986     String sheetText = m_parsedStyleSheet->text();
987     sheetText.remove(sourceData->ruleHeaderRange.start, sourceData->ruleBodyRange.end - sourceData->ruleHeaderRange.start + 1);
988     setText(sheetText);
989     fireStyleSheetChanged();
990     return { };
991 }
992
993 CSSStyleRule* InspectorStyleSheet::ruleForId(const InspectorCSSId& id) const
994 {
995     if (!m_pageStyleSheet)
996         return nullptr;
997
998     ASSERT(!id.isEmpty());
999     ensureFlatRules();
1000     return id.ordinal() >= m_flatRules.size() ? nullptr : m_flatRules.at(id.ordinal()).get();
1001 }
1002
1003 RefPtr<Inspector::Protocol::CSS::CSSStyleSheetBody> InspectorStyleSheet::buildObjectForStyleSheet()
1004 {
1005     CSSStyleSheet* styleSheet = pageStyleSheet();
1006     if (!styleSheet)
1007         return nullptr;
1008
1009     RefPtr<CSSRuleList> cssRuleList = asCSSRuleList(styleSheet);
1010
1011     auto result = Inspector::Protocol::CSS::CSSStyleSheetBody::create()
1012         .setStyleSheetId(id())
1013         .setRules(buildArrayForRuleList(cssRuleList.get()))
1014         .release();
1015
1016     auto styleSheetText = text();
1017     if (!styleSheetText.hasException())
1018         result->setText(styleSheetText.releaseReturnValue());
1019
1020     return WTFMove(result);
1021 }
1022
1023 RefPtr<Inspector::Protocol::CSS::CSSStyleSheetHeader> InspectorStyleSheet::buildObjectForStyleSheetInfo()
1024 {
1025     CSSStyleSheet* styleSheet = pageStyleSheet();
1026     if (!styleSheet)
1027         return nullptr;
1028
1029     Document* document = styleSheet->ownerDocument();
1030     Frame* frame = document ? document->frame() : nullptr;
1031     return Inspector::Protocol::CSS::CSSStyleSheetHeader::create()
1032         .setStyleSheetId(id())
1033         .setOrigin(m_origin)
1034         .setDisabled(styleSheet->disabled())
1035         .setSourceURL(finalURL())
1036         .setTitle(styleSheet->title())
1037         .setFrameId(m_pageAgent->frameId(frame))
1038         .setIsInline(styleSheet->isInline() && styleSheet->startPosition() != TextPosition())
1039         .setStartLine(styleSheet->startPosition().m_line.zeroBasedInt())
1040         .setStartColumn(styleSheet->startPosition().m_column.zeroBasedInt())
1041         .release();
1042 }
1043
1044 static bool hasDynamicSpecificity(const CSSSelector& simpleSelector)
1045 {
1046     // It is possible that these can have a static specificity if each selector in the list has
1047     // equal specificity, but lets always report that they can be dynamic.
1048     for (const CSSSelector* selector = &simpleSelector; selector; selector = selector->tagHistory()) {
1049         if (selector->match() == CSSSelector::PseudoClass) {
1050             CSSSelector::PseudoClassType pseudoClassType = selector->pseudoClassType();
1051             if (pseudoClassType == CSSSelector::PseudoClassMatches)
1052                 return true;
1053             if (pseudoClassType == CSSSelector::PseudoClassNthChild || pseudoClassType == CSSSelector::PseudoClassNthLastChild) {
1054                 if (selector->selectorList())
1055                     return true;
1056                 return false;
1057             }
1058         }
1059     }
1060
1061     return false;
1062 }
1063
1064 static Ref<Inspector::Protocol::CSS::CSSSelector> buildObjectForSelectorHelper(const String& selectorText, const CSSSelector& selector, Element* element)
1065 {
1066     auto inspectorSelector = Inspector::Protocol::CSS::CSSSelector::create()
1067         .setText(selectorText)
1068         .release();
1069
1070     if (element) {
1071         bool dynamic = hasDynamicSpecificity(selector);
1072         if (dynamic)
1073             inspectorSelector->setDynamic(true);
1074
1075         SelectorChecker::CheckingContext context(SelectorChecker::Mode::CollectingRules);
1076         SelectorChecker selectorChecker(element->document());
1077
1078         unsigned specificity;
1079         bool okay = selectorChecker.match(selector, *element, context, specificity);
1080         if (!okay)
1081             specificity = selector.staticSpecificity(okay);
1082
1083         if (okay) {
1084             auto tuple = JSON::ArrayOf<int>::create();
1085             tuple->addItem(static_cast<int>((specificity & CSSSelector::idMask) >> 16));
1086             tuple->addItem(static_cast<int>((specificity & CSSSelector::classMask) >> 8));
1087             tuple->addItem(static_cast<int>(specificity & CSSSelector::elementMask));
1088             inspectorSelector->setSpecificity(WTFMove(tuple));
1089         }
1090     }
1091
1092     return inspectorSelector;
1093 }
1094
1095 static Ref<JSON::ArrayOf<Inspector::Protocol::CSS::CSSSelector>> selectorsFromSource(const CSSRuleSourceData* sourceData, const String& sheetText, const CSSSelectorList& selectorList, Element* element)
1096 {
1097     static NeverDestroyed<JSC::Yarr::RegularExpression> comment("/\\*[^]*?\\*/", JSC::Yarr::TextCaseSensitive, JSC::Yarr::MultilineEnabled);
1098
1099     auto result = JSON::ArrayOf<Inspector::Protocol::CSS::CSSSelector>::create();
1100     const CSSSelector* selector = selectorList.first();
1101     for (auto& range : sourceData->selectorRanges) {
1102         // If we don't have a selector, that means the SourceData for this CSSStyleSheet
1103         // no longer matches up with the actual rules in the CSSStyleSheet.
1104         ASSERT(selector);
1105         if (!selector)
1106             break;
1107
1108         String selectorText = sheetText.substring(range.start, range.length());
1109
1110         // We don't want to see any comments in the selector components, only the meaningful parts.
1111         replace(selectorText, comment, String());
1112         result->addItem(buildObjectForSelectorHelper(selectorText.stripWhiteSpace(), *selector, element));
1113
1114         selector = CSSSelectorList::next(selector);
1115     }
1116     return result;
1117 }
1118
1119 Ref<Inspector::Protocol::CSS::CSSSelector> InspectorStyleSheet::buildObjectForSelector(const CSSSelector* selector, Element* element)
1120 {
1121     return buildObjectForSelectorHelper(selector->selectorText(), *selector, element);
1122 }
1123
1124 Ref<Inspector::Protocol::CSS::SelectorList> InspectorStyleSheet::buildObjectForSelectorList(CSSStyleRule* rule, Element* element, int& endingLine)
1125 {
1126     RefPtr<CSSRuleSourceData> sourceData;
1127     if (ensureParsedDataReady())
1128         sourceData = ruleSourceDataFor(&rule->style());
1129     RefPtr<JSON::ArrayOf<Inspector::Protocol::CSS::CSSSelector>> selectors;
1130
1131     // This intentionally does not rely on the source data to avoid catching the trailing comments (before the declaration starting '{').
1132     String selectorText = rule->selectorText();
1133
1134     if (sourceData)
1135         selectors = selectorsFromSource(sourceData.get(), m_parsedStyleSheet->text(), rule->styleRule().selectorList(), element);
1136     else {
1137         selectors = JSON::ArrayOf<Inspector::Protocol::CSS::CSSSelector>::create();
1138         const CSSSelectorList& selectorList = rule->styleRule().selectorList();
1139         for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(selector))
1140             selectors->addItem(buildObjectForSelector(selector, element));
1141     }
1142     auto result = Inspector::Protocol::CSS::SelectorList::create()
1143         .setSelectors(WTFMove(selectors))
1144         .setText(selectorText)
1145         .release();
1146     if (sourceData)
1147         result->setRange(buildSourceRangeObject(sourceData->ruleHeaderRange, lineEndings(), &endingLine));
1148     return result;
1149 }
1150
1151 RefPtr<Inspector::Protocol::CSS::CSSRule> InspectorStyleSheet::buildObjectForRule(CSSStyleRule* rule, Element* element)
1152 {
1153     CSSStyleSheet* styleSheet = pageStyleSheet();
1154     if (!styleSheet)
1155         return nullptr;
1156
1157     int endingLine = 0;
1158     auto result = Inspector::Protocol::CSS::CSSRule::create()
1159         .setSelectorList(buildObjectForSelectorList(rule, element, endingLine))
1160         .setSourceLine(endingLine)
1161         .setOrigin(m_origin)
1162         .setStyle(buildObjectForStyle(&rule->style()))
1163         .release();
1164
1165     // "sourceURL" is present only for regular rules, otherwise "origin" should be used in the frontend.
1166     if (m_origin == Inspector::Protocol::CSS::StyleSheetOrigin::Regular)
1167         result->setSourceURL(finalURL());
1168
1169     if (canBind()) {
1170         InspectorCSSId id(ruleId(rule));
1171         if (!id.isEmpty())
1172             result->setRuleId(id.asProtocolValue<Inspector::Protocol::CSS::CSSRuleId>());
1173     }
1174
1175     auto mediaArray = ArrayOf<Inspector::Protocol::CSS::CSSMedia>::create();
1176
1177     fillMediaListChain(rule, mediaArray.get());
1178     if (mediaArray->length())
1179         result->setMedia(WTFMove(mediaArray));
1180
1181     return WTFMove(result);
1182 }
1183
1184 RefPtr<Inspector::Protocol::CSS::CSSStyle> InspectorStyleSheet::buildObjectForStyle(CSSStyleDeclaration* style)
1185 {
1186     RefPtr<CSSRuleSourceData> sourceData;
1187     if (ensureParsedDataReady())
1188         sourceData = ruleSourceDataFor(style);
1189
1190     InspectorCSSId id = ruleOrStyleId(style);
1191     if (id.isEmpty()) {
1192         return Inspector::Protocol::CSS::CSSStyle::create()
1193             .setCssProperties(ArrayOf<Inspector::Protocol::CSS::CSSProperty>::create())
1194             .setShorthandEntries(ArrayOf<Inspector::Protocol::CSS::ShorthandEntry>::create())
1195             .release();
1196     }
1197     RefPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id);
1198     RefPtr<Inspector::Protocol::CSS::CSSStyle> result = inspectorStyle->buildObjectForStyle();
1199
1200     // Style text cannot be retrieved without stylesheet, so set cssText here.
1201     if (sourceData) {
1202         auto sheetText = text();
1203         if (!sheetText.hasException()) {
1204             auto& bodyRange = sourceData->ruleBodyRange;
1205             result->setCssText(sheetText.releaseReturnValue().substring(bodyRange.start, bodyRange.end - bodyRange.start));
1206         }
1207     }
1208
1209     return result;
1210 }
1211
1212 ExceptionOr<void> InspectorStyleSheet::setStyleText(const InspectorCSSId& id, const String& text, String* oldText)
1213 {
1214     auto inspectorStyle = inspectorStyleForId(id);
1215     if (!inspectorStyle)
1216         return Exception { NotFoundError };
1217
1218     if (oldText) {
1219         auto result = inspectorStyle->text();
1220         if (result.hasException())
1221             return result.releaseException();
1222         *oldText = result.releaseReturnValue();
1223     }
1224
1225     auto result = inspectorStyle->setText(text);
1226     if (!result.hasException())
1227         fireStyleSheetChanged();
1228     return result;
1229 }
1230
1231 ExceptionOr<String> InspectorStyleSheet::text() const
1232 {
1233     if (!ensureText())
1234         return Exception { NotFoundError };
1235     return String { m_parsedStyleSheet->text() };
1236 }
1237
1238 CSSStyleDeclaration* InspectorStyleSheet::styleForId(const InspectorCSSId& id) const
1239 {
1240     CSSStyleRule* rule = ruleForId(id);
1241     if (!rule)
1242         return nullptr;
1243
1244     return &rule->style();
1245 }
1246
1247 void InspectorStyleSheet::fireStyleSheetChanged()
1248 {
1249     if (m_listener)
1250         m_listener->styleSheetChanged(this);
1251 }
1252
1253 RefPtr<InspectorStyle> InspectorStyleSheet::inspectorStyleForId(const InspectorCSSId& id)
1254 {
1255     CSSStyleDeclaration* style = styleForId(id);
1256     if (!style)
1257         return nullptr;
1258
1259     return InspectorStyle::create(id, *style, this);
1260 }
1261
1262 InspectorCSSId InspectorStyleSheet::ruleOrStyleId(CSSStyleDeclaration* style) const
1263 {
1264     unsigned index = ruleIndexByStyle(style);
1265     if (index != UINT_MAX)
1266         return InspectorCSSId(id(), index);
1267     return InspectorCSSId();
1268 }
1269
1270 Document* InspectorStyleSheet::ownerDocument() const
1271 {
1272     return m_pageStyleSheet->ownerDocument();
1273 }
1274
1275 RefPtr<CSSRuleSourceData> InspectorStyleSheet::ruleSourceDataFor(CSSStyleDeclaration* style) const
1276 {
1277     return m_parsedStyleSheet->ruleSourceDataAt(ruleIndexByStyle(style));
1278 }
1279
1280 Vector<size_t> InspectorStyleSheet::lineEndings() const
1281 {
1282     if (!m_parsedStyleSheet->hasText())
1283         return { };
1284     return ContentSearchUtilities::lineEndings(m_parsedStyleSheet->text());
1285 }
1286
1287 unsigned InspectorStyleSheet::ruleIndexByStyle(CSSStyleDeclaration* pageStyle) const
1288 {
1289     ensureFlatRules();
1290     unsigned index = 0;
1291     for (auto& rule : m_flatRules) {
1292         if (&rule->style() == pageStyle)
1293             return index;
1294
1295         ++index;
1296     }
1297     return UINT_MAX;
1298 }
1299
1300 bool InspectorStyleSheet::styleSheetMutated() const
1301 {
1302     return m_pageStyleSheet && m_pageStyleSheet->hadRulesMutation();
1303 }
1304
1305 bool InspectorStyleSheet::ensureParsedDataReady()
1306 {
1307     bool allowParsedData = m_origin == Inspector::Protocol::CSS::StyleSheetOrigin::Inspector || !styleSheetMutated();
1308     return allowParsedData && ensureText() && ensureSourceData();
1309 }
1310
1311 bool InspectorStyleSheet::ensureText() const
1312 {
1313     if (!m_parsedStyleSheet)
1314         return false;
1315     if (m_parsedStyleSheet->hasText())
1316         return true;
1317
1318     String text;
1319     bool success = originalStyleSheetText(&text);
1320     if (success)
1321         m_parsedStyleSheet->setText(text);
1322     // No need to clear m_flatRules here - it's empty.
1323
1324     return success;
1325 }
1326
1327 bool InspectorStyleSheet::ensureSourceData()
1328 {
1329     if (m_parsedStyleSheet->hasSourceData())
1330         return true;
1331
1332     if (!m_parsedStyleSheet->hasText())
1333         return false;
1334
1335     RefPtr<StyleSheetContents> newStyleSheet = StyleSheetContents::create();
1336     auto ruleSourceDataResult = std::make_unique<RuleSourceDataList>();
1337     
1338     CSSParserContext context(parserContextForDocument(m_pageStyleSheet->ownerDocument()));
1339     StyleSheetHandler handler(m_parsedStyleSheet->text(), m_pageStyleSheet->ownerDocument(), ruleSourceDataResult.get());
1340     CSSParser::parseSheetForInspector(context, newStyleSheet.get(), m_parsedStyleSheet->text(), handler);
1341     m_parsedStyleSheet->setSourceData(WTFMove(ruleSourceDataResult));
1342     return m_parsedStyleSheet->hasSourceData();
1343 }
1344
1345 void InspectorStyleSheet::ensureFlatRules() const
1346 {
1347     // We are fine with redoing this for empty stylesheets as this will run fast.
1348     if (m_flatRules.isEmpty())
1349         collectFlatRules(asCSSRuleList(pageStyleSheet()), &m_flatRules);
1350 }
1351
1352 ExceptionOr<void> InspectorStyleSheet::setStyleText(CSSStyleDeclaration* style, const String& text)
1353 {
1354     if (!m_pageStyleSheet)
1355         return Exception { NotFoundError };
1356     if (!ensureParsedDataReady())
1357         return Exception { NotFoundError };
1358
1359     String patchedStyleSheetText;
1360     bool success = styleSheetTextWithChangedStyle(style, text, &patchedStyleSheetText);
1361     if (!success)
1362         return Exception { NotFoundError };
1363
1364     InspectorCSSId id = ruleOrStyleId(style);
1365     if (id.isEmpty())
1366         return Exception { NotFoundError };
1367
1368     auto setCssTextResult = style->setCssText(text);
1369     if (setCssTextResult.hasException())
1370         return setCssTextResult.releaseException();
1371
1372     m_parsedStyleSheet->setText(patchedStyleSheetText);
1373     return { };
1374 }
1375
1376 bool InspectorStyleSheet::styleSheetTextWithChangedStyle(CSSStyleDeclaration* style, const String& newStyleText, String* result)
1377 {
1378     if (!style)
1379         return false;
1380
1381     if (!ensureParsedDataReady())
1382         return false;
1383
1384     RefPtr<CSSRuleSourceData> sourceData = ruleSourceDataFor(style);
1385     unsigned bodyStart = sourceData->ruleBodyRange.start;
1386     unsigned bodyEnd = sourceData->ruleBodyRange.end;
1387     ASSERT(bodyStart <= bodyEnd);
1388
1389     String text = m_parsedStyleSheet->text();
1390     ASSERT_WITH_SECURITY_IMPLICATION(bodyEnd <= text.length()); // bodyEnd is exclusive
1391
1392     text.replace(bodyStart, bodyEnd - bodyStart, newStyleText);
1393     *result = text;
1394     return true;
1395 }
1396
1397 InspectorCSSId InspectorStyleSheet::ruleId(CSSStyleRule* rule) const
1398 {
1399     return ruleOrStyleId(&rule->style());
1400 }
1401
1402 bool InspectorStyleSheet::originalStyleSheetText(String* result) const
1403 {
1404     bool success = inlineStyleSheetText(result);
1405     if (!success)
1406         success = resourceStyleSheetText(result);
1407     return success;
1408 }
1409
1410 bool InspectorStyleSheet::resourceStyleSheetText(String* result) const
1411 {
1412     if (m_origin == Inspector::Protocol::CSS::StyleSheetOrigin::User || m_origin == Inspector::Protocol::CSS::StyleSheetOrigin::UserAgent)
1413         return false;
1414
1415     if (!m_pageStyleSheet || !ownerDocument() || !ownerDocument()->frame())
1416         return false;
1417
1418     String error;
1419     bool base64Encoded;
1420     InspectorPageAgent::resourceContent(error, ownerDocument()->frame(), URL({ }, m_pageStyleSheet->href()), result, &base64Encoded);
1421     return error.isEmpty() && !base64Encoded;
1422 }
1423
1424 bool InspectorStyleSheet::inlineStyleSheetText(String* result) const
1425 {
1426     if (!m_pageStyleSheet)
1427         return false;
1428
1429     Node* ownerNode = m_pageStyleSheet->ownerNode();
1430     if (!is<Element>(ownerNode))
1431         return false;
1432     Element& ownerElement = downcast<Element>(*ownerNode);
1433
1434     if (!is<HTMLStyleElement>(ownerElement) && !is<SVGStyleElement>(ownerElement))
1435         return false;
1436     *result = ownerElement.textContent();
1437     return true;
1438 }
1439
1440 Ref<JSON::ArrayOf<Inspector::Protocol::CSS::CSSRule>> InspectorStyleSheet::buildArrayForRuleList(CSSRuleList* ruleList)
1441 {
1442     auto result = JSON::ArrayOf<Inspector::Protocol::CSS::CSSRule>::create();
1443     if (!ruleList)
1444         return result;
1445
1446     RefPtr<CSSRuleList> refRuleList = ruleList;
1447     CSSStyleRuleVector rules;
1448     collectFlatRules(WTFMove(refRuleList), &rules);
1449
1450     for (auto& rule : rules)
1451         result->addItem(buildObjectForRule(rule.get(), nullptr));
1452
1453     return result;
1454 }
1455
1456 void InspectorStyleSheet::collectFlatRules(RefPtr<CSSRuleList>&& ruleList, CSSStyleRuleVector* result)
1457 {
1458     if (!ruleList)
1459         return;
1460
1461     for (unsigned i = 0, size = ruleList->length(); i < size; ++i) {
1462         CSSRule* rule = ruleList->item(i);
1463         CSSStyleRule* styleRule = InspectorCSSAgent::asCSSStyleRule(*rule);
1464         if (styleRule)
1465             result->append(styleRule);
1466         else {
1467             RefPtr<CSSRuleList> childRuleList = asCSSRuleList(rule);
1468             if (childRuleList)
1469                 collectFlatRules(WTFMove(childRuleList), result);
1470         }
1471     }
1472 }
1473
1474 Ref<InspectorStyleSheetForInlineStyle> InspectorStyleSheetForInlineStyle::create(InspectorPageAgent* pageAgent, const String& id, Ref<StyledElement>&& element, Inspector::Protocol::CSS::StyleSheetOrigin origin, Listener* listener)
1475 {
1476     return adoptRef(*new InspectorStyleSheetForInlineStyle(pageAgent, id, WTFMove(element), origin, listener));
1477 }
1478
1479 InspectorStyleSheetForInlineStyle::InspectorStyleSheetForInlineStyle(InspectorPageAgent* pageAgent, const String& id, Ref<StyledElement>&& element, Inspector::Protocol::CSS::StyleSheetOrigin origin, Listener* listener)
1480     : InspectorStyleSheet(pageAgent, id, nullptr, origin, String(), listener)
1481     , m_element(WTFMove(element))
1482     , m_ruleSourceData(nullptr)
1483     , m_isStyleTextValid(false)
1484 {
1485     m_inspectorStyle = InspectorStyle::create(InspectorCSSId(id, 0), inlineStyle(), this);
1486     m_styleText = m_element->getAttribute("style").string();
1487 }
1488
1489 void InspectorStyleSheetForInlineStyle::didModifyElementAttribute()
1490 {
1491     m_isStyleTextValid = false;
1492     if (&m_element->cssomStyle() != &m_inspectorStyle->cssStyle())
1493         m_inspectorStyle = InspectorStyle::create(InspectorCSSId(id(), 0), inlineStyle(), this);
1494     m_ruleSourceData = nullptr;
1495 }
1496
1497 ExceptionOr<String> InspectorStyleSheetForInlineStyle::text() const
1498 {
1499     if (!m_isStyleTextValid) {
1500         m_styleText = elementStyleText();
1501         m_isStyleTextValid = true;
1502     }
1503     return String { m_styleText };
1504 }
1505
1506 ExceptionOr<void> InspectorStyleSheetForInlineStyle::setStyleText(CSSStyleDeclaration* style, const String& text)
1507 {
1508     ASSERT_UNUSED(style, style == &inlineStyle());
1509
1510     {
1511         InspectorCSSAgent::InlineStyleOverrideScope overrideScope(m_element->document());
1512         m_element->setAttribute(HTMLNames::styleAttr, text);
1513     }
1514
1515     m_styleText = text;
1516     m_isStyleTextValid = true;
1517     m_ruleSourceData = nullptr;
1518
1519     return { };
1520 }
1521
1522 Vector<size_t> InspectorStyleSheetForInlineStyle::lineEndings() const
1523 {
1524     return ContentSearchUtilities::lineEndings(elementStyleText());
1525 }
1526
1527 Document* InspectorStyleSheetForInlineStyle::ownerDocument() const
1528 {
1529     return &m_element->document();
1530 }
1531
1532 bool InspectorStyleSheetForInlineStyle::ensureParsedDataReady()
1533 {
1534     // The "style" property value can get changed indirectly, e.g. via element.style.borderWidth = "2px".
1535     const String& currentStyleText = elementStyleText();
1536     if (m_styleText != currentStyleText) {
1537         m_ruleSourceData = nullptr;
1538         m_styleText = currentStyleText;
1539         m_isStyleTextValid = true;
1540     }
1541
1542     if (m_ruleSourceData)
1543         return true;
1544
1545     m_ruleSourceData = ruleSourceData();
1546     return true;
1547 }
1548
1549 RefPtr<InspectorStyle> InspectorStyleSheetForInlineStyle::inspectorStyleForId(const InspectorCSSId& id)
1550 {
1551     ASSERT_UNUSED(id, !id.ordinal());
1552     return m_inspectorStyle.copyRef();
1553 }
1554
1555 CSSStyleDeclaration& InspectorStyleSheetForInlineStyle::inlineStyle() const
1556 {
1557     return m_element->cssomStyle();
1558 }
1559
1560 const String& InspectorStyleSheetForInlineStyle::elementStyleText() const
1561 {
1562     return m_element->getAttribute("style").string();
1563 }
1564
1565 Ref<CSSRuleSourceData> InspectorStyleSheetForInlineStyle::ruleSourceData() const
1566 {
1567     if (m_styleText.isEmpty()) {
1568         auto result = CSSRuleSourceData::create(StyleRule::Style);
1569         result->ruleBodyRange.start = 0;
1570         result->ruleBodyRange.end = 0;
1571         return result;
1572     }
1573
1574     CSSParserContext context(parserContextForDocument(&m_element->document()));
1575     RuleSourceDataList ruleSourceDataResult;
1576     StyleSheetHandler handler(m_styleText, &m_element->document(), &ruleSourceDataResult);
1577     CSSParser::parseDeclarationForInspector(context, m_styleText, handler);
1578     return WTFMove(ruleSourceDataResult.first());
1579 }
1580
1581 } // namespace WebCore