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
66using Inspector::TypeBuilder::Array;
67using WebCore::RuleSourceDataList;
68using WebCore::CSSRuleSourceData;
69
70class ParsedStyleSheet {
71    WTF_MAKE_FAST_ALLOCATED;
72public:
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
83private:
84
85    String m_text;
86    bool m_hasText;
87    std::unique_ptr<RuleSourceDataList> m_sourceData;
88};
89
90ParsedStyleSheet::ParsedStyleSheet()
91    : m_hasText(false)
92{
93}
94
95void ParsedStyleSheet::setText(const String& text)
96{
97    m_hasText = true;
98    m_text = text;
99    setSourceData(nullptr);
100}
101
102static 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
117void 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
132PassRefPtr<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
140using namespace Inspector;
141
142namespace WebCore {
143
144enum MediaListSource {
145    MediaListSourceLinkedSheet,
146    MediaListSourceInlineSheet,
147    MediaListSourceMediaRule,
148    MediaListSourceImportRule
149};
150
151static 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
166static 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
196static 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
212static 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
231static 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
286static std::unique_ptr<CSSParser> createCSSParser(Document* document)
287{
288    return std::make_unique<CSSParser>(document ? CSSParserContext(*document) : strictCSSParserContext());
289}
290
291PassRefPtr<InspectorStyle> InspectorStyle::create(const InspectorCSSId& styleId, PassRefPtr<CSSStyleDeclaration> style, InspectorStyleSheet* parentStyleSheet)
292{
293    return adoptRef(new InspectorStyle(styleId, style, parentStyleSheet));
294}
295
296InspectorStyle::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
305InspectorStyle::~InspectorStyle()
306{
307}
308
309PassRefPtr<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
325PassRefPtr<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).
348bool 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
409bool 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
450bool 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
467bool 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
512PassRefPtr<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
623PassRefPtr<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
630bool InspectorStyle::setText(const String& text, ExceptionCode& ec)
631{
632    return m_parentStyleSheet->setStyleText(m_style.get(), text, ec);
633}
634
635String InspectorStyle::shorthandValue(const String& shorthandProperty) const
636{
637    String value = m_style->getPropertyValue(shorthandProperty);
638    if (!value.isEmpty())
639        return value;
640    StringBuilder builder;
641    for (unsigned i = 0; i < m_style->length(); ++i) {
642        String individualProperty = m_style->item(i);
643        if (m_style->getPropertyShorthand(individualProperty) != shorthandProperty)
644            continue;
645        if (m_style->isPropertyImplicit(individualProperty))
646            continue;
647        String individualValue = m_style->getPropertyValue(individualProperty);
648        if (individualValue == "initial")
649            continue;
650        if (!builder.isEmpty())
651            builder.append(' ');
652        builder.append(individualValue);
653    }
654    return builder.toString();
655}
656
657String InspectorStyle::shorthandPriority(const String& shorthandProperty) const
658{
659    String priority = m_style->getPropertyPriority(shorthandProperty);
660    if (priority.isEmpty()) {
661        for (unsigned i = 0; i < m_style->length(); ++i) {
662            String individualProperty = m_style->item(i);
663            if (m_style->getPropertyShorthand(individualProperty) != shorthandProperty)
664                continue;
665            priority = m_style->getPropertyPriority(individualProperty);
666            break;
667        }
668    }
669    return priority;
670}
671
672Vector<String> InspectorStyle::longhandProperties(const String& shorthandProperty) const
673{
674    Vector<String> properties;
675    HashSet<String> foundProperties;
676    for (unsigned i = 0; i < m_style->length(); ++i) {
677        String individualProperty = m_style->item(i);
678        if (foundProperties.contains(individualProperty) || m_style->getPropertyShorthand(individualProperty) != shorthandProperty)
679            continue;
680
681        foundProperties.add(individualProperty);
682        properties.append(individualProperty);
683    }
684    return properties;
685}
686
687NewLineAndWhitespace& InspectorStyle::newLineAndWhitespaceDelimiters() const
688{
689    DEPRECATED_DEFINE_STATIC_LOCAL(String, defaultPrefix, (ASCIILiteral("    ")));
690
691    if (m_formatAcquired)
692        return m_format;
693
694    RefPtr<CSSRuleSourceData> sourceData = extractSourceData();
695    Vector<CSSPropertySourceData>* sourcePropertyData = sourceData ? &(sourceData->styleSourceData->propertyData) : nullptr;
696    int propertyCount;
697    if (!sourcePropertyData || !(propertyCount = sourcePropertyData->size())) {
698        m_format.first = "\n";
699        m_format.second = defaultPrefix;
700        return m_format; // Do not remember the default formatting and attempt to acquire it later.
701    }
702
703    String text;
704    bool success = styleText(&text);
705    ASSERT_UNUSED(success, success);
706
707    m_formatAcquired = true;
708
709    String candidatePrefix = defaultPrefix;
710    StringBuilder formatLineFeed;
711    StringBuilder prefix;
712    int scanStart = 0;
713    int propertyIndex = 0;
714    bool isFullPrefixScanned = false;
715    bool lineFeedTerminated = false;
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 = text[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
752PassRefPtr<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
758String InspectorStyleSheet::styleSheetURL(CSSStyleSheet* pageStyleSheet)
759{
760    if (pageStyleSheet && !pageStyleSheet->contents().baseURL().isEmpty())
761        return pageStyleSheet->contents().baseURL().string();
762    return emptyString();
763}
764
765InspectorStyleSheet::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
777InspectorStyleSheet::~InspectorStyleSheet()
778{
779    delete m_parsedStyleSheet;
780}
781
782String InspectorStyleSheet::finalURL() const
783{
784    String url = styleSheetURL(m_pageStyleSheet.get());
785    return url.isEmpty() ? m_documentURL : url;
786}
787
788void 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
804bool 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
817String 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
827bool 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
856static bool checkStyleRuleSelector(Document* document, const String& selector)
857{
858    CSSSelectorList selectorList;
859    createCSSParser(document)->parseSelector(selector, selectorList);
860    return selectorList.isValid();
861}
862
863CSSStyleRule* 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
911bool 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
945CSSStyleRule* 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
956PassRefPtr<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
976PassRefPtr<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
995static 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
1011PassRefPtr<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
1038PassRefPtr<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
1069PassRefPtr<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
1098bool 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
1115bool 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
1129bool 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
1148bool InspectorStyleSheet::getText(String* result) const
1149{
1150    if (!ensureText())
1151        return false;
1152    *result = m_parsedStyleSheet->text();
1153    return true;
1154}
1155
1156CSSStyleDeclaration* 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
1165void InspectorStyleSheet::fireStyleSheetChanged()
1166{
1167    if (m_listener)
1168        m_listener->styleSheetChanged(this);
1169}
1170
1171PassRefPtr<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
1185void InspectorStyleSheet::rememberInspectorStyle(RefPtr<InspectorStyle> inspectorStyle)
1186{
1187    m_inspectorStyles.set(inspectorStyle->cssStyle(), inspectorStyle);
1188}
1189
1190void InspectorStyleSheet::forgetInspectorStyle(CSSStyleDeclaration* style)
1191{
1192    m_inspectorStyles.remove(style);
1193}
1194
1195InspectorCSSId 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
1203Document* InspectorStyleSheet::ownerDocument() const
1204{
1205    return m_pageStyleSheet->ownerDocument();
1206}
1207
1208RefPtr<CSSRuleSourceData> InspectorStyleSheet::ruleSourceDataFor(CSSStyleDeclaration* style) const
1209{
1210    return m_parsedStyleSheet->ruleSourceDataAt(ruleIndexByStyle(style));
1211}
1212
1213std::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
1220unsigned 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
1233bool InspectorStyleSheet::checkPageStyleSheet(ExceptionCode& ec) const
1234{
1235    if (!m_pageStyleSheet) {
1236        ec = NOT_SUPPORTED_ERR;
1237        return false;
1238    }
1239    return true;
1240}
1241
1242bool InspectorStyleSheet::ensureParsedDataReady()
1243{
1244    return ensureText() && ensureSourceData();
1245}
1246
1247bool 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
1263bool 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(WTF::move(ruleSourceDataResult));
1275    return m_parsedStyleSheet->hasSourceData();
1276}
1277
1278void 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
1285bool 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
1308bool 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
1329InspectorCSSId InspectorStyleSheet::ruleId(CSSStyleRule* rule) const
1330{
1331    return ruleOrStyleId(rule->style());
1332}
1333
1334void 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
1357bool InspectorStyleSheet::originalStyleSheetText(String* result) const
1358{
1359    bool success = inlineStyleSheetText(result);
1360    if (!success)
1361        success = resourceStyleSheetText(result);
1362    return success;
1363}
1364
1365bool 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
1379bool 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
1395PassRefPtr<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
1411void 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
1429PassRefPtr<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
1434InspectorStyleSheetForInlineStyle::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
1445void 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
1453bool 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
1463bool 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
1478std::unique_ptr<Vector<size_t>> InspectorStyleSheetForInlineStyle::lineEndings() const
1479{
1480    return ContentSearchUtilities::lineEndings(elementStyleText());
1481}
1482
1483Document* InspectorStyleSheetForInlineStyle::ownerDocument() const
1484{
1485    return &m_element->document();
1486}
1487
1488bool 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
1509PassRefPtr<InspectorStyle> InspectorStyleSheetForInlineStyle::inspectorStyleForId(const InspectorCSSId& id)
1510{
1511    ASSERT_UNUSED(id, !id.ordinal());
1512    return m_inspectorStyle;
1513}
1514
1515CSSStyleDeclaration* InspectorStyleSheetForInlineStyle::inlineStyle() const
1516{
1517    return m_element->style();
1518}
1519
1520const String& InspectorStyleSheetForInlineStyle::elementStyleText() const
1521{
1522    return m_element->getAttribute("style").string();
1523}
1524
1525bool 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)
1544