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