Web Inspector: replace HTMLCanvasElement with CanvasRenderingContext for instrumentat...
[WebKit-https.git] / Source / WebCore / inspector / InspectorStyleSheet.cpp
1 /*
2  * Copyright (C) 2010, Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1.  Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  * 2.  Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16  * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
17  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  */
24
25 #include "config.h"
26 #include "InspectorStyleSheet.h"
27
28 #include "CSSImportRule.h"
29 #include "CSSKeyframesRule.h"
30 #include "CSSMediaRule.h"
31 #include "CSSParser.h"
32 #include "CSSParserObserver.h"
33 #include "CSSPropertyNames.h"
34 #include "CSSPropertyParser.h"
35 #include "CSSPropertySourceData.h"
36 #include "CSSRule.h"
37 #include "CSSRuleList.h"
38 #include "CSSStyleRule.h"
39 #include "CSSStyleSheet.h"
40 #include "CSSSupportsRule.h"
41 #include "ContentSecurityPolicy.h"
42 #include "Document.h"
43 #include "Element.h"
44 #include "HTMLHeadElement.h"
45 #include "HTMLNames.h"
46 #include "HTMLParserIdioms.h"
47 #include "HTMLStyleElement.h"
48 #include "InspectorCSSAgent.h"
49 #include "InspectorPageAgent.h"
50 #include "MediaList.h"
51 #include "Node.h"
52 #include "SVGElement.h"
53 #include "SVGStyleElement.h"
54 #include "StyleProperties.h"
55 #include "StyleResolver.h"
56 #include "StyleRule.h"
57 #include "StyleRuleImport.h"
58 #include "StyleSheetContents.h"
59 #include "StyleSheetList.h"
60 #include <inspector/ContentSearchUtilities.h>
61 #include <wtf/text/StringBuilder.h>
62 #include <yarr/RegularExpression.h>
63
64 using 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, Vector<size_t>* lineEndings, int* endingLine = nullptr)
389 {
390     if (!lineEndings)
391         return nullptr;
392     TextPosition start = ContentSearchUtilities::textPositionFromOffset(range.start, *lineEndings);
393     TextPosition end = ContentSearchUtilities::textPositionFromOffset(range.end, *lineEndings);
394
395     if (endingLine)
396         *endingLine = end.m_line.zeroBasedInt();
397
398     return Inspector::Protocol::CSS::SourceRange::create()
399         .setStartLine(start.m_line.zeroBasedInt())
400         .setStartColumn(start.m_column.zeroBasedInt())
401         .setEndLine(end.m_line.zeroBasedInt())
402         .setEndColumn(end.m_column.zeroBasedInt())
403         .release();
404 }
405
406 static Ref<Inspector::Protocol::CSS::CSSMedia> buildMediaObject(const MediaList* media, MediaListSource mediaListSource, const String& sourceURL)
407 {
408     // Make certain compilers happy by initializing |source| up-front.
409     Inspector::Protocol::CSS::CSSMedia::Source source = Inspector::Protocol::CSS::CSSMedia::Source::InlineSheet;
410     switch (mediaListSource) {
411     case MediaListSourceMediaRule:
412         source = Inspector::Protocol::CSS::CSSMedia::Source::MediaRule;
413         break;
414     case MediaListSourceImportRule:
415         source = Inspector::Protocol::CSS::CSSMedia::Source::ImportRule;
416         break;
417     case MediaListSourceLinkedSheet:
418         source = Inspector::Protocol::CSS::CSSMedia::Source::LinkedSheet;
419         break;
420     case MediaListSourceInlineSheet:
421         source = Inspector::Protocol::CSS::CSSMedia::Source::InlineSheet;
422         break;
423     }
424
425     auto mediaObject = Inspector::Protocol::CSS::CSSMedia::create()
426         .setText(media->mediaText())
427         .setSource(source)
428         .release();
429
430     if (!sourceURL.isEmpty()) {
431         mediaObject->setSourceURL(sourceURL);
432         mediaObject->setSourceLine(media->queries()->lastLine());
433     }
434     return mediaObject;
435 }
436
437 static RefPtr<CSSRuleList> asCSSRuleList(CSSStyleSheet* styleSheet)
438 {
439     if (!styleSheet)
440         return nullptr;
441
442     RefPtr<StaticCSSRuleList> list = StaticCSSRuleList::create();
443     Vector<RefPtr<CSSRule>>& listRules = list->rules();
444     for (unsigned i = 0, size = styleSheet->length(); i < size; ++i)
445         listRules.append(styleSheet->item(i));
446     return WTFMove(list);
447 }
448
449 static RefPtr<CSSRuleList> asCSSRuleList(CSSRule* rule)
450 {
451     if (!rule)
452         return nullptr;
453
454     if (is<CSSMediaRule>(*rule))
455         return &downcast<CSSMediaRule>(*rule).cssRules();
456
457     if (is<CSSKeyframesRule>(*rule))
458         return &downcast<CSSKeyframesRule>(*rule).cssRules();
459
460     if (is<CSSSupportsRule>(*rule))
461         return &downcast<CSSSupportsRule>(*rule).cssRules();
462
463     return nullptr;
464 }
465
466 static void fillMediaListChain(CSSRule* rule, JSON::ArrayOf<Inspector::Protocol::CSS::CSSMedia>& mediaArray)
467 {
468     MediaList* mediaList;
469     CSSRule* parentRule = rule;
470     String sourceURL;
471     while (parentRule) {
472         CSSStyleSheet* parentStyleSheet = nullptr;
473         bool isMediaRule = true;
474         if (is<CSSMediaRule>(*parentRule)) {
475             CSSMediaRule& mediaRule = downcast<CSSMediaRule>(*parentRule);
476             mediaList = mediaRule.media();
477             parentStyleSheet = mediaRule.parentStyleSheet();
478         } else if (is<CSSImportRule>(*parentRule)) {
479             CSSImportRule& importRule = downcast<CSSImportRule>(*parentRule);
480             mediaList = &importRule.media();
481             parentStyleSheet = importRule.parentStyleSheet();
482             isMediaRule = false;
483         } else
484             mediaList = nullptr;
485
486         if (parentStyleSheet) {
487             sourceURL = parentStyleSheet->contents().baseURL();
488             if (sourceURL.isEmpty())
489                 sourceURL = InspectorDOMAgent::documentURLString(parentStyleSheet->ownerDocument());
490         } else
491             sourceURL = emptyString();
492
493         if (mediaList && mediaList->length())
494             mediaArray.addItem(buildMediaObject(mediaList, isMediaRule ? MediaListSourceMediaRule : MediaListSourceImportRule, sourceURL));
495
496         if (parentRule->parentRule())
497             parentRule = parentRule->parentRule();
498         else {
499             CSSStyleSheet* styleSheet = parentRule->parentStyleSheet();
500             while (styleSheet) {
501                 mediaList = styleSheet->media();
502                 if (mediaList && mediaList->length()) {
503                     Document* doc = styleSheet->ownerDocument();
504                     if (doc)
505                         sourceURL = doc->url();
506                     else if (!styleSheet->contents().baseURL().isEmpty())
507                         sourceURL = styleSheet->contents().baseURL();
508                     else
509                         sourceURL = emptyString();
510                     mediaArray.addItem(buildMediaObject(mediaList, styleSheet->ownerNode() ? MediaListSourceLinkedSheet : MediaListSourceInlineSheet, sourceURL));
511                 }
512                 parentRule = styleSheet->ownerRule();
513                 if (parentRule)
514                     break;
515                 styleSheet = styleSheet->parentStyleSheet();
516             }
517         }
518     }
519 }
520
521 Ref<InspectorStyle> InspectorStyle::create(const InspectorCSSId& styleId, Ref<CSSStyleDeclaration>&& style, InspectorStyleSheet* parentStyleSheet)
522 {
523     return adoptRef(*new InspectorStyle(styleId, WTFMove(style), parentStyleSheet));
524 }
525
526 InspectorStyle::InspectorStyle(const InspectorCSSId& styleId, Ref<CSSStyleDeclaration>&& style, InspectorStyleSheet* parentStyleSheet)
527     : m_styleId(styleId)
528     , m_style(WTFMove(style))
529     , m_parentStyleSheet(parentStyleSheet)
530 {
531 }
532
533 InspectorStyle::~InspectorStyle() = default;
534
535 RefPtr<Inspector::Protocol::CSS::CSSStyle> InspectorStyle::buildObjectForStyle() const
536 {
537     Ref<Inspector::Protocol::CSS::CSSStyle> result = styleWithProperties();
538     if (!m_styleId.isEmpty())
539         result->setStyleId(m_styleId.asProtocolValue<Inspector::Protocol::CSS::CSSStyleId>());
540
541     result->setWidth(m_style->getPropertyValue("width"));
542     result->setHeight(m_style->getPropertyValue("height"));
543
544     RefPtr<CSSRuleSourceData> sourceData = extractSourceData();
545     if (sourceData)
546         result->setRange(buildSourceRangeObject(sourceData->ruleBodyRange, m_parentStyleSheet->lineEndings().get()));
547
548     return WTFMove(result);
549 }
550
551 Ref<JSON::ArrayOf<Inspector::Protocol::CSS::CSSComputedStyleProperty>> InspectorStyle::buildArrayForComputedStyle() const
552 {
553     auto result = JSON::ArrayOf<Inspector::Protocol::CSS::CSSComputedStyleProperty>::create();
554     Vector<InspectorStyleProperty> properties;
555     populateAllProperties(&properties);
556
557     for (auto& property : properties) {
558         const CSSPropertySourceData& propertyEntry = property.sourceData;
559         auto entry = Inspector::Protocol::CSS::CSSComputedStyleProperty::create()
560             .setName(propertyEntry.name)
561             .setValue(propertyEntry.value)
562             .release();
563         result->addItem(WTFMove(entry));
564     }
565
566     return result;
567 }
568
569 ExceptionOr<String> InspectorStyle::text() const
570 {
571     // Precondition: m_parentStyleSheet->ensureParsedDataReady() has been called successfully.
572     auto sourceData = extractSourceData();
573     if (!sourceData)
574         return Exception { NotFoundError };
575
576     auto result = m_parentStyleSheet->text();
577     if (result.hasException())
578         return result.releaseException();
579
580     auto& bodyRange = sourceData->ruleBodyRange;
581     return result.releaseReturnValue().substring(bodyRange.start, bodyRange.end - bodyRange.start);
582 }
583
584 static String lowercasePropertyName(const String& name)
585 {
586     // Custom properties are case-sensitive.
587     if (name.startsWith("--"))
588         return name;
589     return name.convertToASCIILowercase();
590 }
591
592 void InspectorStyle::populateAllProperties(Vector<InspectorStyleProperty>* result) const
593 {
594     HashSet<String> sourcePropertyNames;
595
596     auto sourceData = extractSourceData();
597     auto* sourcePropertyData = sourceData ? &sourceData->styleSourceData->propertyData : nullptr;
598     if (sourcePropertyData) {
599         auto styleDeclarationOrException = text();
600         ASSERT(!styleDeclarationOrException.hasException());
601         String styleDeclaration = styleDeclarationOrException.hasException() ? emptyString() : styleDeclarationOrException.releaseReturnValue();
602         for (auto& sourceData : *sourcePropertyData) {
603             InspectorStyleProperty p(sourceData, true, sourceData.disabled);
604             p.setRawTextFromStyleDeclaration(styleDeclaration);
605             result->append(p);
606             sourcePropertyNames.add(lowercasePropertyName(sourceData.name));
607         }
608     }
609
610     for (int i = 0, size = m_style->length(); i < size; ++i) {
611         String name = m_style->item(i);
612         if (sourcePropertyNames.add(lowercasePropertyName(name)))
613             result->append(InspectorStyleProperty(CSSPropertySourceData(name, m_style->getPropertyValue(name), !m_style->getPropertyPriority(name).isEmpty(), false, true, SourceRange()), false, false));
614     }
615 }
616
617 Ref<Inspector::Protocol::CSS::CSSStyle> InspectorStyle::styleWithProperties() const
618 {
619     Vector<InspectorStyleProperty> properties;
620     populateAllProperties(&properties);
621
622     auto propertiesObject = JSON::ArrayOf<Inspector::Protocol::CSS::CSSProperty>::create();
623     auto shorthandEntries = ArrayOf<Inspector::Protocol::CSS::ShorthandEntry>::create();
624     HashMap<String, RefPtr<Inspector::Protocol::CSS::CSSProperty>> propertyNameToPreviousActiveProperty;
625     HashSet<String> foundShorthands;
626     String previousPriority;
627     String previousStatus;
628     std::unique_ptr<Vector<size_t>> lineEndings(m_parentStyleSheet ? m_parentStyleSheet->lineEndings() : nullptr);
629     auto sourceData = extractSourceData();
630     unsigned ruleBodyRangeStart = sourceData ? sourceData->ruleBodyRange.start : 0;
631
632     for (Vector<InspectorStyleProperty>::iterator it = properties.begin(), itEnd = properties.end(); it != itEnd; ++it) {
633         const CSSPropertySourceData& propertyEntry = it->sourceData;
634         const String& name = propertyEntry.name;
635
636         // Visual Studio disagrees with other compilers as to whether 'class' is needed here.
637 #if COMPILER(MSVC)
638         enum class Protocol::CSS::CSSPropertyStatus status;
639 #else
640         enum Inspector::Protocol::CSS::CSSPropertyStatus status;
641 #endif
642         status = it->disabled ? Inspector::Protocol::CSS::CSSPropertyStatus::Disabled : Inspector::Protocol::CSS::CSSPropertyStatus::Active;
643
644         RefPtr<Inspector::Protocol::CSS::CSSProperty> property = Inspector::Protocol::CSS::CSSProperty::create()
645             .setName(name.convertToASCIILowercase())
646             .setValue(propertyEntry.value)
647             .release();
648
649         propertiesObject->addItem(property.copyRef());
650
651         CSSPropertyID propertyId = cssPropertyID(name);
652
653         // Default "parsedOk" == true.
654         if (!propertyEntry.parsedOk || isInternalCSSProperty(propertyId))
655             property->setParsedOk(false);
656         if (it->hasRawText())
657             property->setText(it->rawText);
658
659         // Default "priority" == "".
660         if (propertyEntry.important)
661             property->setPriority("important");
662
663         if (it->hasSource) {
664             // The property range is relative to the style body start.
665             // Should be converted into an absolute range (relative to the stylesheet start)
666             // for the proper conversion into line:column.
667             SourceRange absolutePropertyRange = propertyEntry.range;
668             absolutePropertyRange.start += ruleBodyRangeStart;
669             absolutePropertyRange.end += ruleBodyRangeStart;
670             property->setRange(buildSourceRangeObject(absolutePropertyRange, lineEndings.get()));
671         }
672
673         if (!it->disabled) {
674             if (it->hasSource) {
675                 ASSERT(sourceData);
676                 property->setImplicit(false);
677
678                 // Parsed property overrides any property with the same name. Non-parsed property overrides
679                 // previous non-parsed property with the same name (if any).
680                 bool shouldInactivate = false;
681
682                 // Canonicalize property names to treat non-prefixed and vendor-prefixed property names the same (opacity vs. -webkit-opacity).
683                 String canonicalPropertyName = propertyId ? getPropertyNameString(propertyId) : name;
684                 HashMap<String, RefPtr<Inspector::Protocol::CSS::CSSProperty>>::iterator activeIt = propertyNameToPreviousActiveProperty.find(canonicalPropertyName);
685                 if (activeIt != propertyNameToPreviousActiveProperty.end()) {
686                     if (propertyEntry.parsedOk) {
687                         bool successPriority = activeIt->value->getString(Inspector::Protocol::CSS::CSSProperty::Priority, previousPriority);
688                         bool successStatus = activeIt->value->getString(Inspector::Protocol::CSS::CSSProperty::Status, previousStatus);
689                         if (successStatus && previousStatus != "inactive") {
690                             if (propertyEntry.important || !successPriority) // Priority not set == "not important".
691                                 shouldInactivate = true;
692                             else if (status == Inspector::Protocol::CSS::CSSPropertyStatus::Active) {
693                                 // Inactivate a non-important property following the same-named important property.
694                                 status = Inspector::Protocol::CSS::CSSPropertyStatus::Inactive;
695                             }
696                         }
697                     } else {
698                         bool previousParsedOk;
699                         bool success = activeIt->value->getBoolean(Inspector::Protocol::CSS::CSSProperty::ParsedOk, previousParsedOk);
700                         if (success && !previousParsedOk)
701                             shouldInactivate = true;
702                     }
703                 } else
704                     propertyNameToPreviousActiveProperty.set(canonicalPropertyName, property);
705
706                 if (shouldInactivate) {
707                     activeIt->value->setStatus(Inspector::Protocol::CSS::CSSPropertyStatus::Inactive);
708                     propertyNameToPreviousActiveProperty.set(canonicalPropertyName, property);
709                 }
710             } else {
711                 bool implicit = m_style->isPropertyImplicit(name);
712                 // Default "implicit" == false.
713                 if (implicit)
714                     property->setImplicit(true);
715                 status = Inspector::Protocol::CSS::CSSPropertyStatus::Style;
716
717                 String shorthand = m_style->getPropertyShorthand(name);
718                 if (!shorthand.isEmpty()) {
719                     if (!foundShorthands.contains(shorthand)) {
720                         foundShorthands.add(shorthand);
721                         auto entry = Inspector::Protocol::CSS::ShorthandEntry::create()
722                             .setName(shorthand)
723                             .setValue(shorthandValue(shorthand))
724                             .release();
725                         shorthandEntries->addItem(WTFMove(entry));
726                     }
727                 }
728             }
729         }
730
731         // Default "status" == "style".
732         if (status != Inspector::Protocol::CSS::CSSPropertyStatus::Style)
733             property->setStatus(status);
734     }
735
736     return Inspector::Protocol::CSS::CSSStyle::create()
737         .setCssProperties(WTFMove(propertiesObject))
738         .setShorthandEntries(WTFMove(shorthandEntries))
739         .release();
740 }
741
742 RefPtr<CSSRuleSourceData> InspectorStyle::extractSourceData() const
743 {
744     if (!m_parentStyleSheet || !m_parentStyleSheet->ensureParsedDataReady())
745         return nullptr;
746     return m_parentStyleSheet->ruleSourceDataFor(m_style.ptr());
747 }
748
749 ExceptionOr<void> InspectorStyle::setText(const String& text)
750 {
751     return m_parentStyleSheet->setStyleText(m_style.ptr(), text);
752 }
753
754 String InspectorStyle::shorthandValue(const String& shorthandProperty) const
755 {
756     String value = m_style->getPropertyValue(shorthandProperty);
757     if (!value.isEmpty())
758         return value;
759     StringBuilder builder;
760     for (unsigned i = 0; i < m_style->length(); ++i) {
761         String individualProperty = m_style->item(i);
762         if (m_style->getPropertyShorthand(individualProperty) != shorthandProperty)
763             continue;
764         if (m_style->isPropertyImplicit(individualProperty))
765             continue;
766         String individualValue = m_style->getPropertyValue(individualProperty);
767         if (individualValue == "initial")
768             continue;
769         if (!builder.isEmpty())
770             builder.append(' ');
771         builder.append(individualValue);
772     }
773     return builder.toString();
774 }
775
776 String InspectorStyle::shorthandPriority(const String& shorthandProperty) const
777 {
778     String priority = m_style->getPropertyPriority(shorthandProperty);
779     if (priority.isEmpty()) {
780         for (unsigned i = 0; i < m_style->length(); ++i) {
781             String individualProperty = m_style->item(i);
782             if (m_style->getPropertyShorthand(individualProperty) != shorthandProperty)
783                 continue;
784             priority = m_style->getPropertyPriority(individualProperty);
785             break;
786         }
787     }
788     return priority;
789 }
790
791 Vector<String> InspectorStyle::longhandProperties(const String& shorthandProperty) const
792 {
793     Vector<String> properties;
794     HashSet<String> foundProperties;
795     for (unsigned i = 0; i < m_style->length(); ++i) {
796         String individualProperty = m_style->item(i);
797         if (foundProperties.contains(individualProperty) || m_style->getPropertyShorthand(individualProperty) != shorthandProperty)
798             continue;
799
800         foundProperties.add(individualProperty);
801         properties.append(individualProperty);
802     }
803     return properties;
804 }
805
806 Ref<InspectorStyleSheet> InspectorStyleSheet::create(InspectorPageAgent* pageAgent, const String& id, RefPtr<CSSStyleSheet>&& pageStyleSheet, Inspector::Protocol::CSS::StyleSheetOrigin origin, const String& documentURL, Listener* listener)
807 {
808     return adoptRef(*new InspectorStyleSheet(pageAgent, id, WTFMove(pageStyleSheet), origin, documentURL, listener));
809 }
810
811 String InspectorStyleSheet::styleSheetURL(CSSStyleSheet* pageStyleSheet)
812 {
813     if (pageStyleSheet && !pageStyleSheet->contents().baseURL().isEmpty())
814         return pageStyleSheet->contents().baseURL().string();
815     return emptyString();
816 }
817
818 InspectorStyleSheet::InspectorStyleSheet(InspectorPageAgent* pageAgent, const String& id, RefPtr<CSSStyleSheet>&& pageStyleSheet, Inspector::Protocol::CSS::StyleSheetOrigin origin, const String& documentURL, Listener* listener)
819     : m_pageAgent(pageAgent)
820     , m_id(id)
821     , m_pageStyleSheet(WTFMove(pageStyleSheet))
822     , m_origin(origin)
823     , m_documentURL(documentURL)
824     , m_listener(listener)
825 {
826     m_parsedStyleSheet = new ParsedStyleSheet();
827 }
828
829 InspectorStyleSheet::~InspectorStyleSheet()
830 {
831     delete m_parsedStyleSheet;
832 }
833
834 String InspectorStyleSheet::finalURL() const
835 {
836     String url = styleSheetURL(m_pageStyleSheet.get());
837     return url.isEmpty() ? m_documentURL : url;
838 }
839
840 void InspectorStyleSheet::reparseStyleSheet(const String& text)
841 {
842     {
843         // Have a separate scope for clearRules() (bug 95324).
844         CSSStyleSheet::RuleMutationScope mutationScope(m_pageStyleSheet.get());
845         m_pageStyleSheet->contents().clearRules();
846     }
847     {
848         CSSStyleSheet::RuleMutationScope mutationScope(m_pageStyleSheet.get());
849         m_pageStyleSheet->contents().parseString(text);
850         m_pageStyleSheet->clearChildRuleCSSOMWrappers();
851         fireStyleSheetChanged();
852     }
853
854     // We just wiped the entire contents of the stylesheet. Clear the mutation flag.
855     m_pageStyleSheet->clearHadRulesMutation();
856 }
857
858 ExceptionOr<void> InspectorStyleSheet::setText(const String& text)
859 {
860     if (!m_pageStyleSheet)
861         return Exception { NotSupportedError };
862
863     m_parsedStyleSheet->setText(text);
864     m_flatRules.clear();
865
866     return { };
867 }
868
869 ExceptionOr<String> InspectorStyleSheet::ruleSelector(const InspectorCSSId& id)
870 {
871     CSSStyleRule* rule = ruleForId(id);
872     if (!rule)
873         return Exception { NotFoundError };
874     return rule->selectorText();
875 }
876
877 static bool isValidSelectorListString(const String& selector, Document* document)
878 {
879     CSSSelectorList selectorList;
880     CSSParser parser(parserContextForDocument(document));
881     parser.parseSelector(selector, selectorList);
882     return selectorList.isValid();
883 }
884
885 ExceptionOr<void> InspectorStyleSheet::setRuleSelector(const InspectorCSSId& id, const String& selector)
886 {
887     if (!m_pageStyleSheet)
888         return Exception { NotSupportedError };
889
890     // If the selector is invalid, do not proceed any further.
891     if (!isValidSelectorListString(selector, m_pageStyleSheet->ownerDocument()))
892         return Exception { SyntaxError };
893
894     CSSStyleRule* rule = ruleForId(id);
895     if (!rule)
896         return Exception { NotFoundError };
897
898     CSSStyleSheet* styleSheet = rule->parentStyleSheet();
899     if (!styleSheet || !ensureParsedDataReady())
900         return Exception { NotFoundError };
901
902     // If the stylesheet is already mutated at this point, that must mean that our data has been modified
903     // elsewhere. This should never happen as ensureParsedDataReady would return false in that case.
904     ASSERT(!styleSheetMutated());
905
906     rule->setSelectorText(selector);
907     auto sourceData = ruleSourceDataFor(&rule->style());
908     if (!sourceData)
909         return Exception { NotFoundError };
910
911     String sheetText = m_parsedStyleSheet->text();
912     sheetText.replace(sourceData->ruleHeaderRange.start, sourceData->ruleHeaderRange.length(), selector);
913     m_parsedStyleSheet->setText(sheetText);
914     m_pageStyleSheet->clearHadRulesMutation();
915     fireStyleSheetChanged();
916     return { };
917 }
918
919 ExceptionOr<CSSStyleRule*> InspectorStyleSheet::addRule(const String& selector)
920 {
921     if (!m_pageStyleSheet)
922         return Exception { NotSupportedError };
923
924     if (!isValidSelectorListString(selector, m_pageStyleSheet->ownerDocument()))
925         return Exception { SyntaxError };
926
927     auto text = this->text();
928     if (text.hasException())
929         return text.releaseException();
930
931     auto addRuleResult = m_pageStyleSheet->addRule(selector, emptyString(), std::nullopt);
932     if (addRuleResult.hasException())
933         return addRuleResult.releaseException();
934
935     StringBuilder styleSheetText;
936     styleSheetText.append(text.releaseReturnValue());
937
938     if (!styleSheetText.isEmpty())
939         styleSheetText.append('\n');
940
941     styleSheetText.append(selector);
942     styleSheetText.appendLiteral(" {}");
943
944     // Using setText() as this operation changes the stylesheet rule set.
945     setText(styleSheetText.toString());
946
947     // Inspector Style Sheets are always treated as though their parsed data is ready.
948     if (m_origin == Inspector::Protocol::CSS::StyleSheetOrigin::Inspector)
949         fireStyleSheetChanged();
950     else
951         reparseStyleSheet(styleSheetText.toString());
952
953     ASSERT(m_pageStyleSheet->length());
954     unsigned lastRuleIndex = m_pageStyleSheet->length() - 1;
955     CSSRule* rule = m_pageStyleSheet->item(lastRuleIndex);
956     ASSERT(rule);
957
958     CSSStyleRule* styleRule = InspectorCSSAgent::asCSSStyleRule(*rule);
959     if (!styleRule) {
960         // What we just added has to be a CSSStyleRule - we cannot handle other types of rules yet.
961         // If it is not a style rule, pretend we never touched the stylesheet.
962         m_pageStyleSheet->deleteRule(lastRuleIndex);
963         return Exception { SyntaxError };
964     }
965
966     return styleRule;
967 }
968
969 ExceptionOr<void> InspectorStyleSheet::deleteRule(const InspectorCSSId& id)
970 {
971     if (!m_pageStyleSheet)
972         return Exception { NotSupportedError };
973
974     RefPtr<CSSStyleRule> rule = ruleForId(id);
975     if (!rule)
976         return Exception { NotFoundError };
977     CSSStyleSheet* styleSheet = rule->parentStyleSheet();
978     if (!styleSheet || !ensureParsedDataReady())
979         return Exception { NotFoundError };
980
981     auto sourceData = ruleSourceDataFor(&rule->style());
982     if (!sourceData)
983         return Exception { NotFoundError };
984
985     auto deleteRuleResult = styleSheet->deleteRule(id.ordinal());
986     if (deleteRuleResult.hasException())
987         return deleteRuleResult.releaseException();
988
989     // |rule| MAY NOT be addressed after this!
990
991     String sheetText = m_parsedStyleSheet->text();
992     sheetText.remove(sourceData->ruleHeaderRange.start, sourceData->ruleBodyRange.end - sourceData->ruleHeaderRange.start + 1);
993     setText(sheetText);
994     fireStyleSheetChanged();
995     return { };
996 }
997
998 CSSStyleRule* InspectorStyleSheet::ruleForId(const InspectorCSSId& id) const
999 {
1000     if (!m_pageStyleSheet)
1001         return nullptr;
1002
1003     ASSERT(!id.isEmpty());
1004     ensureFlatRules();
1005     return id.ordinal() >= m_flatRules.size() ? nullptr : m_flatRules.at(id.ordinal()).get();
1006 }
1007
1008 RefPtr<Inspector::Protocol::CSS::CSSStyleSheetBody> InspectorStyleSheet::buildObjectForStyleSheet()
1009 {
1010     CSSStyleSheet* styleSheet = pageStyleSheet();
1011     if (!styleSheet)
1012         return nullptr;
1013
1014     RefPtr<CSSRuleList> cssRuleList = asCSSRuleList(styleSheet);
1015
1016     auto result = Inspector::Protocol::CSS::CSSStyleSheetBody::create()
1017         .setStyleSheetId(id())
1018         .setRules(buildArrayForRuleList(cssRuleList.get()))
1019         .release();
1020
1021     auto styleSheetText = text();
1022     if (!styleSheetText.hasException())
1023         result->setText(styleSheetText.releaseReturnValue());
1024
1025     return WTFMove(result);
1026 }
1027
1028 RefPtr<Inspector::Protocol::CSS::CSSStyleSheetHeader> InspectorStyleSheet::buildObjectForStyleSheetInfo()
1029 {
1030     CSSStyleSheet* styleSheet = pageStyleSheet();
1031     if (!styleSheet)
1032         return nullptr;
1033
1034     Document* document = styleSheet->ownerDocument();
1035     Frame* frame = document ? document->frame() : nullptr;
1036     return Inspector::Protocol::CSS::CSSStyleSheetHeader::create()
1037         .setStyleSheetId(id())
1038         .setOrigin(m_origin)
1039         .setDisabled(styleSheet->disabled())
1040         .setSourceURL(finalURL())
1041         .setTitle(styleSheet->title())
1042         .setFrameId(m_pageAgent->frameId(frame))
1043         .setIsInline(styleSheet->isInline() && styleSheet->startPosition() != TextPosition())
1044         .setStartLine(styleSheet->startPosition().m_line.zeroBasedInt())
1045         .setStartColumn(styleSheet->startPosition().m_column.zeroBasedInt())
1046         .release();
1047 }
1048
1049 static bool hasDynamicSpecificity(const CSSSelector& simpleSelector)
1050 {
1051     // It is possible that these can have a static specificity if each selector in the list has
1052     // equal specificity, but lets always report that they can be dynamic.
1053     for (const CSSSelector* selector = &simpleSelector; selector; selector = selector->tagHistory()) {
1054         if (selector->match() == CSSSelector::PseudoClass) {
1055             CSSSelector::PseudoClassType pseudoClassType = selector->pseudoClassType();
1056             if (pseudoClassType == CSSSelector::PseudoClassMatches)
1057                 return true;
1058             if (pseudoClassType == CSSSelector::PseudoClassNthChild || pseudoClassType == CSSSelector::PseudoClassNthLastChild) {
1059                 if (selector->selectorList())
1060                     return true;
1061                 return false;
1062             }
1063         }
1064     }
1065
1066     return false;
1067 }
1068
1069 static Ref<Inspector::Protocol::CSS::CSSSelector> buildObjectForSelectorHelper(const String& selectorText, const CSSSelector& selector, Element* element)
1070 {
1071     auto inspectorSelector = Inspector::Protocol::CSS::CSSSelector::create()
1072         .setText(selectorText)
1073         .release();
1074
1075     if (element) {
1076         bool dynamic = hasDynamicSpecificity(selector);
1077         if (dynamic)
1078             inspectorSelector->setDynamic(true);
1079
1080         SelectorChecker::CheckingContext context(SelectorChecker::Mode::CollectingRules);
1081         SelectorChecker selectorChecker(element->document());
1082
1083         unsigned specificity;
1084         bool okay = selectorChecker.match(selector, *element, context, specificity);
1085         if (!okay)
1086             specificity = selector.staticSpecificity(okay);
1087
1088         if (okay) {
1089             auto tuple = JSON::ArrayOf<int>::create();
1090             tuple->addItem(static_cast<int>((specificity & CSSSelector::idMask) >> 16));
1091             tuple->addItem(static_cast<int>((specificity & CSSSelector::classMask) >> 8));
1092             tuple->addItem(static_cast<int>(specificity & CSSSelector::elementMask));
1093             inspectorSelector->setSpecificity(WTFMove(tuple));
1094         }
1095     }
1096
1097     return inspectorSelector;
1098 }
1099
1100 static Ref<JSON::ArrayOf<Inspector::Protocol::CSS::CSSSelector>> selectorsFromSource(const CSSRuleSourceData* sourceData, const String& sheetText, const CSSSelectorList& selectorList, Element* element)
1101 {
1102     static NeverDestroyed<JSC::Yarr::RegularExpression> comment("/\\*[^]*?\\*/", JSC::Yarr::TextCaseSensitive, JSC::Yarr::MultilineEnabled);
1103
1104     auto result = JSON::ArrayOf<Inspector::Protocol::CSS::CSSSelector>::create();
1105     const CSSSelector* selector = selectorList.first();
1106     for (auto& range : sourceData->selectorRanges) {
1107         // If we don't have a selector, that means the SourceData for this CSSStyleSheet
1108         // no longer matches up with the actual rules in the CSSStyleSheet.
1109         ASSERT(selector);
1110         if (!selector)
1111             break;
1112
1113         String selectorText = sheetText.substring(range.start, range.length());
1114
1115         // We don't want to see any comments in the selector components, only the meaningful parts.
1116         replace(selectorText, comment, String());
1117         result->addItem(buildObjectForSelectorHelper(selectorText.stripWhiteSpace(), *selector, element));
1118
1119         selector = CSSSelectorList::next(selector);
1120     }
1121     return result;
1122 }
1123
1124 Ref<Inspector::Protocol::CSS::CSSSelector> InspectorStyleSheet::buildObjectForSelector(const CSSSelector* selector, Element* element)
1125 {
1126     return buildObjectForSelectorHelper(selector->selectorText(), *selector, element);
1127 }
1128
1129 Ref<Inspector::Protocol::CSS::SelectorList> InspectorStyleSheet::buildObjectForSelectorList(CSSStyleRule* rule, Element* element, int& endingLine)
1130 {
1131     RefPtr<CSSRuleSourceData> sourceData;
1132     if (ensureParsedDataReady())
1133         sourceData = ruleSourceDataFor(&rule->style());
1134     RefPtr<JSON::ArrayOf<Inspector::Protocol::CSS::CSSSelector>> selectors;
1135
1136     // This intentionally does not rely on the source data to avoid catching the trailing comments (before the declaration starting '{').
1137     String selectorText = rule->selectorText();
1138
1139     if (sourceData)
1140         selectors = selectorsFromSource(sourceData.get(), m_parsedStyleSheet->text(), rule->styleRule().selectorList(), element);
1141     else {
1142         selectors = JSON::ArrayOf<Inspector::Protocol::CSS::CSSSelector>::create();
1143         const CSSSelectorList& selectorList = rule->styleRule().selectorList();
1144         for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(selector))
1145             selectors->addItem(buildObjectForSelector(selector, element));
1146     }
1147     auto result = Inspector::Protocol::CSS::SelectorList::create()
1148         .setSelectors(WTFMove(selectors))
1149         .setText(selectorText)
1150         .release();
1151     if (sourceData)
1152         result->setRange(buildSourceRangeObject(sourceData->ruleHeaderRange, lineEndings().get(), &endingLine));
1153     return result;
1154 }
1155
1156 RefPtr<Inspector::Protocol::CSS::CSSRule> InspectorStyleSheet::buildObjectForRule(CSSStyleRule* rule, Element* element)
1157 {
1158     CSSStyleSheet* styleSheet = pageStyleSheet();
1159     if (!styleSheet)
1160         return nullptr;
1161
1162     int endingLine = 0;
1163     auto result = Inspector::Protocol::CSS::CSSRule::create()
1164         .setSelectorList(buildObjectForSelectorList(rule, element, endingLine))
1165         .setSourceLine(endingLine)
1166         .setOrigin(m_origin)
1167         .setStyle(buildObjectForStyle(&rule->style()))
1168         .release();
1169
1170     // "sourceURL" is present only for regular rules, otherwise "origin" should be used in the frontend.
1171     if (m_origin == Inspector::Protocol::CSS::StyleSheetOrigin::Regular)
1172         result->setSourceURL(finalURL());
1173
1174     if (canBind()) {
1175         InspectorCSSId id(ruleId(rule));
1176         if (!id.isEmpty())
1177             result->setRuleId(id.asProtocolValue<Inspector::Protocol::CSS::CSSRuleId>());
1178     }
1179
1180     auto mediaArray = ArrayOf<Inspector::Protocol::CSS::CSSMedia>::create();
1181
1182     fillMediaListChain(rule, mediaArray.get());
1183     if (mediaArray->length())
1184         result->setMedia(WTFMove(mediaArray));
1185
1186     return WTFMove(result);
1187 }
1188
1189 RefPtr<Inspector::Protocol::CSS::CSSStyle> InspectorStyleSheet::buildObjectForStyle(CSSStyleDeclaration* style)
1190 {
1191     RefPtr<CSSRuleSourceData> sourceData;
1192     if (ensureParsedDataReady())
1193         sourceData = ruleSourceDataFor(style);
1194
1195     InspectorCSSId id = ruleOrStyleId(style);
1196     if (id.isEmpty()) {
1197         return Inspector::Protocol::CSS::CSSStyle::create()
1198             .setCssProperties(ArrayOf<Inspector::Protocol::CSS::CSSProperty>::create())
1199             .setShorthandEntries(ArrayOf<Inspector::Protocol::CSS::ShorthandEntry>::create())
1200             .release();
1201     }
1202     RefPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id);
1203     RefPtr<Inspector::Protocol::CSS::CSSStyle> result = inspectorStyle->buildObjectForStyle();
1204
1205     // Style text cannot be retrieved without stylesheet, so set cssText here.
1206     if (sourceData) {
1207         auto sheetText = text();
1208         if (!sheetText.hasException()) {
1209             auto& bodyRange = sourceData->ruleBodyRange;
1210             result->setCssText(sheetText.releaseReturnValue().substring(bodyRange.start, bodyRange.end - bodyRange.start));
1211         }
1212     }
1213
1214     return result;
1215 }
1216
1217 ExceptionOr<void> InspectorStyleSheet::setStyleText(const InspectorCSSId& id, const String& text, String* oldText)
1218 {
1219     auto inspectorStyle = inspectorStyleForId(id);
1220     if (!inspectorStyle)
1221         return Exception { NotFoundError };
1222
1223     if (oldText) {
1224         auto result = inspectorStyle->text();
1225         if (result.hasException())
1226             return result.releaseException();
1227         *oldText = result.releaseReturnValue();
1228     }
1229
1230     auto result = inspectorStyle->setText(text);
1231     if (!result.hasException())
1232         fireStyleSheetChanged();
1233     return result;
1234 }
1235
1236 ExceptionOr<String> InspectorStyleSheet::text() const
1237 {
1238     if (!ensureText())
1239         return Exception { NotFoundError };
1240     return String { m_parsedStyleSheet->text() };
1241 }
1242
1243 CSSStyleDeclaration* InspectorStyleSheet::styleForId(const InspectorCSSId& id) const
1244 {
1245     CSSStyleRule* rule = ruleForId(id);
1246     if (!rule)
1247         return nullptr;
1248
1249     return &rule->style();
1250 }
1251
1252 void InspectorStyleSheet::fireStyleSheetChanged()
1253 {
1254     if (m_listener)
1255         m_listener->styleSheetChanged(this);
1256 }
1257
1258 RefPtr<InspectorStyle> InspectorStyleSheet::inspectorStyleForId(const InspectorCSSId& id)
1259 {
1260     CSSStyleDeclaration* style = styleForId(id);
1261     if (!style)
1262         return nullptr;
1263
1264     return InspectorStyle::create(id, *style, this);
1265 }
1266
1267 InspectorCSSId InspectorStyleSheet::ruleOrStyleId(CSSStyleDeclaration* style) const
1268 {
1269     unsigned index = ruleIndexByStyle(style);
1270     if (index != UINT_MAX)
1271         return InspectorCSSId(id(), index);
1272     return InspectorCSSId();
1273 }
1274
1275 Document* InspectorStyleSheet::ownerDocument() const
1276 {
1277     return m_pageStyleSheet->ownerDocument();
1278 }
1279
1280 RefPtr<CSSRuleSourceData> InspectorStyleSheet::ruleSourceDataFor(CSSStyleDeclaration* style) const
1281 {
1282     return m_parsedStyleSheet->ruleSourceDataAt(ruleIndexByStyle(style));
1283 }
1284
1285 std::unique_ptr<Vector<size_t>> InspectorStyleSheet::lineEndings() const
1286 {
1287     if (!m_parsedStyleSheet->hasText())
1288         return nullptr;
1289     return ContentSearchUtilities::lineEndings(m_parsedStyleSheet->text());
1290 }
1291
1292 unsigned InspectorStyleSheet::ruleIndexByStyle(CSSStyleDeclaration* pageStyle) const
1293 {
1294     ensureFlatRules();
1295     unsigned index = 0;
1296     for (auto& rule : m_flatRules) {
1297         if (&rule->style() == pageStyle)
1298             return index;
1299
1300         ++index;
1301     }
1302     return UINT_MAX;
1303 }
1304
1305 bool InspectorStyleSheet::styleSheetMutated() const
1306 {
1307     return m_pageStyleSheet && m_pageStyleSheet->hadRulesMutation();
1308 }
1309
1310 bool InspectorStyleSheet::ensureParsedDataReady()
1311 {
1312     bool allowParsedData = m_origin == Inspector::Protocol::CSS::StyleSheetOrigin::Inspector || !styleSheetMutated();
1313     return allowParsedData && ensureText() && ensureSourceData();
1314 }
1315
1316 bool InspectorStyleSheet::ensureText() const
1317 {
1318     if (!m_parsedStyleSheet)
1319         return false;
1320     if (m_parsedStyleSheet->hasText())
1321         return true;
1322
1323     String text;
1324     bool success = originalStyleSheetText(&text);
1325     if (success)
1326         m_parsedStyleSheet->setText(text);
1327     // No need to clear m_flatRules here - it's empty.
1328
1329     return success;
1330 }
1331
1332 bool InspectorStyleSheet::ensureSourceData()
1333 {
1334     if (m_parsedStyleSheet->hasSourceData())
1335         return true;
1336
1337     if (!m_parsedStyleSheet->hasText())
1338         return false;
1339
1340     RefPtr<StyleSheetContents> newStyleSheet = StyleSheetContents::create();
1341     auto ruleSourceDataResult = std::make_unique<RuleSourceDataList>();
1342     
1343     CSSParserContext context(parserContextForDocument(m_pageStyleSheet->ownerDocument()));
1344     StyleSheetHandler handler(m_parsedStyleSheet->text(), m_pageStyleSheet->ownerDocument(), ruleSourceDataResult.get());
1345     CSSParser::parseSheetForInspector(context, newStyleSheet.get(), m_parsedStyleSheet->text(), handler);
1346     m_parsedStyleSheet->setSourceData(WTFMove(ruleSourceDataResult));
1347     return m_parsedStyleSheet->hasSourceData();
1348 }
1349
1350 void InspectorStyleSheet::ensureFlatRules() const
1351 {
1352     // We are fine with redoing this for empty stylesheets as this will run fast.
1353     if (m_flatRules.isEmpty())
1354         collectFlatRules(asCSSRuleList(pageStyleSheet()), &m_flatRules);
1355 }
1356
1357 ExceptionOr<void> InspectorStyleSheet::setStyleText(CSSStyleDeclaration* style, const String& text)
1358 {
1359     if (!m_pageStyleSheet)
1360         return Exception { NotFoundError };
1361     if (!ensureParsedDataReady())
1362         return Exception { NotFoundError };
1363
1364     String patchedStyleSheetText;
1365     bool success = styleSheetTextWithChangedStyle(style, text, &patchedStyleSheetText);
1366     if (!success)
1367         return Exception { NotFoundError };
1368
1369     InspectorCSSId id = ruleOrStyleId(style);
1370     if (id.isEmpty())
1371         return Exception { NotFoundError };
1372
1373     auto setCssTextResult = style->setCssText(text);
1374     if (setCssTextResult.hasException())
1375         return setCssTextResult.releaseException();
1376
1377     m_parsedStyleSheet->setText(patchedStyleSheetText);
1378     return { };
1379 }
1380
1381 bool InspectorStyleSheet::styleSheetTextWithChangedStyle(CSSStyleDeclaration* style, const String& newStyleText, String* result)
1382 {
1383     if (!style)
1384         return false;
1385
1386     if (!ensureParsedDataReady())
1387         return false;
1388
1389     RefPtr<CSSRuleSourceData> sourceData = ruleSourceDataFor(style);
1390     unsigned bodyStart = sourceData->ruleBodyRange.start;
1391     unsigned bodyEnd = sourceData->ruleBodyRange.end;
1392     ASSERT(bodyStart <= bodyEnd);
1393
1394     String text = m_parsedStyleSheet->text();
1395     ASSERT_WITH_SECURITY_IMPLICATION(bodyEnd <= text.length()); // bodyEnd is exclusive
1396
1397     text.replace(bodyStart, bodyEnd - bodyStart, newStyleText);
1398     *result = text;
1399     return true;
1400 }
1401
1402 InspectorCSSId InspectorStyleSheet::ruleId(CSSStyleRule* rule) const
1403 {
1404     return ruleOrStyleId(&rule->style());
1405 }
1406
1407 bool InspectorStyleSheet::originalStyleSheetText(String* result) const
1408 {
1409     bool success = inlineStyleSheetText(result);
1410     if (!success)
1411         success = resourceStyleSheetText(result);
1412     return success;
1413 }
1414
1415 bool InspectorStyleSheet::resourceStyleSheetText(String* result) const
1416 {
1417     if (m_origin == Inspector::Protocol::CSS::StyleSheetOrigin::User || m_origin == Inspector::Protocol::CSS::StyleSheetOrigin::UserAgent)
1418         return false;
1419
1420     if (!m_pageStyleSheet || !ownerDocument() || !ownerDocument()->frame())
1421         return false;
1422
1423     String error;
1424     bool base64Encoded;
1425     InspectorPageAgent::resourceContent(error, ownerDocument()->frame(), URL(ParsedURLString, m_pageStyleSheet->href()), result, &base64Encoded);
1426     return error.isEmpty() && !base64Encoded;
1427 }
1428
1429 bool InspectorStyleSheet::inlineStyleSheetText(String* result) const
1430 {
1431     if (!m_pageStyleSheet)
1432         return false;
1433
1434     Node* ownerNode = m_pageStyleSheet->ownerNode();
1435     if (!is<Element>(ownerNode))
1436         return false;
1437     Element& ownerElement = downcast<Element>(*ownerNode);
1438
1439     if (!is<HTMLStyleElement>(ownerElement) && !is<SVGStyleElement>(ownerElement))
1440         return false;
1441     *result = ownerElement.textContent();
1442     return true;
1443 }
1444
1445 Ref<JSON::ArrayOf<Inspector::Protocol::CSS::CSSRule>> InspectorStyleSheet::buildArrayForRuleList(CSSRuleList* ruleList)
1446 {
1447     auto result = JSON::ArrayOf<Inspector::Protocol::CSS::CSSRule>::create();
1448     if (!ruleList)
1449         return result;
1450
1451     RefPtr<CSSRuleList> refRuleList = ruleList;
1452     CSSStyleRuleVector rules;
1453     collectFlatRules(WTFMove(refRuleList), &rules);
1454
1455     for (auto& rule : rules)
1456         result->addItem(buildObjectForRule(rule.get(), nullptr));
1457
1458     return result;
1459 }
1460
1461 void InspectorStyleSheet::collectFlatRules(RefPtr<CSSRuleList>&& ruleList, CSSStyleRuleVector* result)
1462 {
1463     if (!ruleList)
1464         return;
1465
1466     for (unsigned i = 0, size = ruleList->length(); i < size; ++i) {
1467         CSSRule* rule = ruleList->item(i);
1468         CSSStyleRule* styleRule = InspectorCSSAgent::asCSSStyleRule(*rule);
1469         if (styleRule)
1470             result->append(styleRule);
1471         else {
1472             RefPtr<CSSRuleList> childRuleList = asCSSRuleList(rule);
1473             if (childRuleList)
1474                 collectFlatRules(WTFMove(childRuleList), result);
1475         }
1476     }
1477 }
1478
1479 Ref<InspectorStyleSheetForInlineStyle> InspectorStyleSheetForInlineStyle::create(InspectorPageAgent* pageAgent, const String& id, Ref<StyledElement>&& element, Inspector::Protocol::CSS::StyleSheetOrigin origin, Listener* listener)
1480 {
1481     return adoptRef(*new InspectorStyleSheetForInlineStyle(pageAgent, id, WTFMove(element), origin, listener));
1482 }
1483
1484 InspectorStyleSheetForInlineStyle::InspectorStyleSheetForInlineStyle(InspectorPageAgent* pageAgent, const String& id, Ref<StyledElement>&& element, Inspector::Protocol::CSS::StyleSheetOrigin origin, Listener* listener)
1485     : InspectorStyleSheet(pageAgent, id, nullptr, origin, String(), listener)
1486     , m_element(WTFMove(element))
1487     , m_ruleSourceData(nullptr)
1488     , m_isStyleTextValid(false)
1489 {
1490     m_inspectorStyle = InspectorStyle::create(InspectorCSSId(id, 0), inlineStyle(), this);
1491     m_styleText = m_element->getAttribute("style").string();
1492 }
1493
1494 void InspectorStyleSheetForInlineStyle::didModifyElementAttribute()
1495 {
1496     m_isStyleTextValid = false;
1497     if (&m_element->cssomStyle() != &m_inspectorStyle->cssStyle())
1498         m_inspectorStyle = InspectorStyle::create(InspectorCSSId(id(), 0), inlineStyle(), this);
1499     m_ruleSourceData = nullptr;
1500 }
1501
1502 ExceptionOr<String> InspectorStyleSheetForInlineStyle::text() const
1503 {
1504     if (!m_isStyleTextValid) {
1505         m_styleText = elementStyleText();
1506         m_isStyleTextValid = true;
1507     }
1508     return String { m_styleText };
1509 }
1510
1511 ExceptionOr<void> InspectorStyleSheetForInlineStyle::setStyleText(CSSStyleDeclaration* style, const String& text)
1512 {
1513     ASSERT_UNUSED(style, style == &inlineStyle());
1514
1515     {
1516         InspectorCSSAgent::InlineStyleOverrideScope overrideScope(m_element->document());
1517         m_element->setAttribute(HTMLNames::styleAttr, text);
1518     }
1519
1520     m_styleText = text;
1521     m_isStyleTextValid = true;
1522     m_ruleSourceData = nullptr;
1523
1524     return { };
1525 }
1526
1527 std::unique_ptr<Vector<size_t>> InspectorStyleSheetForInlineStyle::lineEndings() const
1528 {
1529     return ContentSearchUtilities::lineEndings(elementStyleText());
1530 }
1531
1532 Document* InspectorStyleSheetForInlineStyle::ownerDocument() const
1533 {
1534     return &m_element->document();
1535 }
1536
1537 bool InspectorStyleSheetForInlineStyle::ensureParsedDataReady()
1538 {
1539     // The "style" property value can get changed indirectly, e.g. via element.style.borderWidth = "2px".
1540     const String& currentStyleText = elementStyleText();
1541     if (m_styleText != currentStyleText) {
1542         m_ruleSourceData = nullptr;
1543         m_styleText = currentStyleText;
1544         m_isStyleTextValid = true;
1545     }
1546
1547     if (m_ruleSourceData)
1548         return true;
1549
1550     m_ruleSourceData = ruleSourceData();
1551     return true;
1552 }
1553
1554 RefPtr<InspectorStyle> InspectorStyleSheetForInlineStyle::inspectorStyleForId(const InspectorCSSId& id)
1555 {
1556     ASSERT_UNUSED(id, !id.ordinal());
1557     return m_inspectorStyle.copyRef();
1558 }
1559
1560 CSSStyleDeclaration& InspectorStyleSheetForInlineStyle::inlineStyle() const
1561 {
1562     return m_element->cssomStyle();
1563 }
1564
1565 const String& InspectorStyleSheetForInlineStyle::elementStyleText() const
1566 {
1567     return m_element->getAttribute("style").string();
1568 }
1569
1570 Ref<CSSRuleSourceData> InspectorStyleSheetForInlineStyle::ruleSourceData() const
1571 {
1572     if (m_styleText.isEmpty()) {
1573         auto result = CSSRuleSourceData::create(StyleRule::Style);
1574         result->ruleBodyRange.start = 0;
1575         result->ruleBodyRange.end = 0;
1576         return result;
1577     }
1578
1579     CSSParserContext context(parserContextForDocument(&m_element->document()));
1580     RuleSourceDataList ruleSourceDataResult;
1581     StyleSheetHandler handler(m_styleText, &m_element->document(), &ruleSourceDataResult);
1582     CSSParser::parseDeclarationForInspector(context, m_styleText, handler);
1583     return WTFMove(ruleSourceDataResult.first());
1584 }
1585
1586 } // namespace WebCore