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