Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / core / inspector / InspectorStyleSheet.cpp
1 /*
2  * Copyright (C) 2010, Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1.  Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  * 2.  Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16  * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
17  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  */
24
25 #include "config.h"
26 #include "core/inspector/InspectorStyleSheet.h"
27
28 #include "bindings/core/v8/ExceptionState.h"
29 #include "bindings/core/v8/ExceptionStatePlaceholder.h"
30 #include "bindings/core/v8/ScriptRegexp.h"
31 #include "core/CSSPropertyNames.h"
32 #include "core/css/CSSKeyframesRule.h"
33 #include "core/css/CSSMediaRule.h"
34 #include "core/css/parser/BisonCSSParser.h"
35 #include "core/css/CSSRuleList.h"
36 #include "core/css/CSSStyleRule.h"
37 #include "core/css/CSSStyleSheet.h"
38 #include "core/css/CSSSupportsRule.h"
39 #include "core/css/StylePropertySet.h"
40 #include "core/css/StyleRule.h"
41 #include "core/css/StyleSheetContents.h"
42 #include "core/dom/Document.h"
43 #include "core/dom/Element.h"
44 #include "core/html/HTMLStyleElement.h"
45 #include "core/html/parser/HTMLParserIdioms.h"
46 #include "core/inspector/ContentSearchUtils.h"
47 #include "core/inspector/InspectorCSSAgent.h"
48 #include "core/inspector/InspectorPageAgent.h"
49 #include "core/inspector/InspectorResourceAgent.h"
50 #include "core/svg/SVGStyleElement.h"
51 #include "wtf/OwnPtr.h"
52 #include "wtf/PassOwnPtr.h"
53 #include "wtf/text/StringBuilder.h"
54 #include "wtf/text/TextPosition.h"
55
56 using blink::TypeBuilder::Array;
57 using blink::RuleSourceDataList;
58 using blink::CSSRuleSourceData;
59 using blink::CSSStyleSheet;
60
61 namespace {
62
63 using namespace blink;
64
65 static CSSParserContext parserContextForDocument(Document *document)
66 {
67     return document ? CSSParserContext(*document, 0) : strictCSSParserContext();
68 }
69
70 class StyleSheetHandler FINAL : public CSSParserObserver {
71 public:
72     StyleSheetHandler(const String& parsedText, Document* document, StyleSheetContents* styleSheetContents, RuleSourceDataList* result)
73         : m_parsedText(parsedText)
74         , m_document(document)
75         , m_styleSheetContents(styleSheetContents)
76         , m_result(result)
77         , m_commentParser(parserContextForDocument(document))
78         , m_propertyRangeStart(UINT_MAX)
79         , m_selectorRangeStart(UINT_MAX)
80         , m_commentRangeStart(UINT_MAX)
81     {
82         ASSERT(m_result);
83     }
84
85 private:
86     virtual void startRuleHeader(CSSRuleSourceData::Type, unsigned) OVERRIDE;
87     virtual void endRuleHeader(unsigned) OVERRIDE;
88     virtual void startSelector(unsigned) OVERRIDE;
89     virtual void endSelector(unsigned) OVERRIDE;
90     virtual void startRuleBody(unsigned) OVERRIDE;
91     virtual void endRuleBody(unsigned, bool) OVERRIDE;
92     virtual void startEndUnknownRule() OVERRIDE { addNewRuleToSourceTree(CSSRuleSourceData::createUnknown()); }
93     virtual void startProperty(unsigned) OVERRIDE;
94     virtual void endProperty(bool, bool, unsigned, CSSParserError) OVERRIDE;
95     virtual void startComment(unsigned) OVERRIDE;
96     virtual void endComment(unsigned) OVERRIDE;
97
98     void addNewRuleToSourceTree(PassRefPtrWillBeRawPtr<CSSRuleSourceData>);
99     PassRefPtrWillBeRawPtr<CSSRuleSourceData> popRuleData();
100     template <typename CharacterType> inline void setRuleHeaderEnd(const CharacterType*, unsigned);
101     void fixUnparsedPropertyRanges(CSSRuleSourceData*);
102
103     const String& m_parsedText;
104     Document* m_document;
105     StyleSheetContents* m_styleSheetContents;
106     RawPtrWillBeMember<RuleSourceDataList> m_result;
107     RuleSourceDataList m_currentRuleDataStack;
108     RefPtrWillBeMember<CSSRuleSourceData> m_currentRuleData;
109     BisonCSSParser m_commentParser;
110     unsigned m_propertyRangeStart;
111     unsigned m_selectorRangeStart;
112     unsigned m_commentRangeStart;
113 };
114
115 void StyleSheetHandler::startRuleHeader(CSSRuleSourceData::Type type, unsigned offset)
116 {
117     // Pop off data for a previous invalid rule.
118     if (m_currentRuleData)
119         m_currentRuleDataStack.removeLast();
120
121     RefPtrWillBeRawPtr<CSSRuleSourceData> data = CSSRuleSourceData::create(type);
122     data->ruleHeaderRange.start = offset;
123     m_currentRuleData = data;
124     m_currentRuleDataStack.append(data.release());
125 }
126
127 template <typename CharacterType>
128 inline void StyleSheetHandler::setRuleHeaderEnd(const CharacterType* dataStart, unsigned listEndOffset)
129 {
130     while (listEndOffset > 1) {
131         if (isHTMLSpace<CharacterType>(*(dataStart + listEndOffset - 1)))
132             --listEndOffset;
133         else
134             break;
135     }
136
137     m_currentRuleDataStack.last()->ruleHeaderRange.end = listEndOffset;
138     if (!m_currentRuleDataStack.last()->selectorRanges.isEmpty())
139         m_currentRuleDataStack.last()->selectorRanges.last().end = listEndOffset;
140 }
141
142 void StyleSheetHandler::endRuleHeader(unsigned offset)
143 {
144     ASSERT(!m_currentRuleDataStack.isEmpty());
145
146     if (m_parsedText.is8Bit())
147         setRuleHeaderEnd<LChar>(m_parsedText.characters8(), offset);
148     else
149         setRuleHeaderEnd<UChar>(m_parsedText.characters16(), offset);
150 }
151
152 void StyleSheetHandler::startSelector(unsigned offset)
153 {
154     m_selectorRangeStart = offset;
155 }
156
157 void StyleSheetHandler::endSelector(unsigned offset)
158 {
159     ASSERT(m_currentRuleDataStack.size());
160     m_currentRuleDataStack.last()->selectorRanges.append(SourceRange(m_selectorRangeStart, offset));
161     m_selectorRangeStart = UINT_MAX;
162 }
163
164 void StyleSheetHandler::startRuleBody(unsigned offset)
165 {
166     m_currentRuleData.clear();
167     ASSERT(!m_currentRuleDataStack.isEmpty());
168     if (m_parsedText[offset] == '{')
169         ++offset; // Skip the rule body opening brace.
170     m_currentRuleDataStack.last()->ruleBodyRange.start = offset;
171 }
172
173 void StyleSheetHandler::endRuleBody(unsigned offset, bool error)
174 {
175     ASSERT(!m_currentRuleDataStack.isEmpty());
176     m_currentRuleDataStack.last()->ruleBodyRange.end = offset;
177     m_propertyRangeStart = UINT_MAX;
178     RefPtrWillBeRawPtr<CSSRuleSourceData> rule = popRuleData();
179     if (error)
180         return;
181
182     fixUnparsedPropertyRanges(rule.get());
183     addNewRuleToSourceTree(rule.release());
184 }
185
186 void StyleSheetHandler::addNewRuleToSourceTree(PassRefPtrWillBeRawPtr<CSSRuleSourceData> rule)
187 {
188     if (m_currentRuleDataStack.isEmpty())
189         m_result->append(rule);
190     else
191         m_currentRuleDataStack.last()->childRules.append(rule);
192 }
193
194 PassRefPtrWillBeRawPtr<CSSRuleSourceData> StyleSheetHandler::popRuleData()
195 {
196     ASSERT(!m_currentRuleDataStack.isEmpty());
197     m_currentRuleData.clear();
198     RefPtrWillBeRawPtr<CSSRuleSourceData> data = m_currentRuleDataStack.last();
199     m_currentRuleDataStack.removeLast();
200     return data.release();
201 }
202
203 template <typename CharacterType>
204 static inline void fixUnparsedProperties(const CharacterType* characters, CSSRuleSourceData* ruleData)
205 {
206     WillBeHeapVector<CSSPropertySourceData>& propertyData = ruleData->styleSourceData->propertyData;
207     unsigned size = propertyData.size();
208     if (!size)
209         return;
210
211     unsigned styleStart = ruleData->ruleBodyRange.start;
212     CSSPropertySourceData* nextData = &(propertyData.at(0));
213     for (unsigned i = 0; i < size; ++i) {
214         CSSPropertySourceData* currentData = nextData;
215         nextData = i < size - 1 ? &(propertyData.at(i + 1)) : 0;
216
217         if (currentData->parsedOk)
218             continue;
219         if (currentData->range.end > 0 && characters[styleStart + currentData->range.end - 1] == ';')
220             continue;
221
222         unsigned propertyEndInStyleSheet;
223         if (!nextData)
224             propertyEndInStyleSheet = ruleData->ruleBodyRange.end - 1;
225         else
226             propertyEndInStyleSheet = styleStart + nextData->range.start - 1;
227
228         while (isHTMLSpace<CharacterType>(characters[propertyEndInStyleSheet]))
229             --propertyEndInStyleSheet;
230
231         // propertyEndInStyleSheet points at the last property text character.
232         unsigned newPropertyEnd = propertyEndInStyleSheet - styleStart + 1; // Exclusive of the last property text character.
233         if (currentData->range.end != newPropertyEnd) {
234             currentData->range.end = newPropertyEnd;
235             unsigned valueStartInStyleSheet = styleStart + currentData->range.start + currentData->name.length();
236             while (valueStartInStyleSheet < propertyEndInStyleSheet && characters[valueStartInStyleSheet] != ':')
237                 ++valueStartInStyleSheet;
238             if (valueStartInStyleSheet < propertyEndInStyleSheet)
239                 ++valueStartInStyleSheet; // Shift past the ':'.
240             while (valueStartInStyleSheet < propertyEndInStyleSheet && isHTMLSpace<CharacterType>(characters[valueStartInStyleSheet]))
241                 ++valueStartInStyleSheet;
242             // Need to exclude the trailing ';' from the property value.
243             currentData->value = String(characters + valueStartInStyleSheet, propertyEndInStyleSheet - valueStartInStyleSheet + (characters[propertyEndInStyleSheet] == ';' ? 0 : 1));
244         }
245     }
246 }
247
248 void StyleSheetHandler::fixUnparsedPropertyRanges(CSSRuleSourceData* ruleData)
249 {
250     if (!ruleData->styleSourceData)
251         return;
252
253     if (m_parsedText.is8Bit()) {
254         fixUnparsedProperties<LChar>(m_parsedText.characters8(), ruleData);
255         return;
256     }
257
258     fixUnparsedProperties<UChar>(m_parsedText.characters16(), ruleData);
259 }
260
261 void StyleSheetHandler::startProperty(unsigned offset)
262 {
263     if (m_currentRuleDataStack.isEmpty() || !m_currentRuleDataStack.last()->styleSourceData)
264         return;
265     m_propertyRangeStart = offset;
266 }
267
268 void StyleSheetHandler::endProperty(bool isImportant, bool isParsed, unsigned offset, CSSParserError errorType)
269 {
270     // FIXME: This is the only place CSSParserError is every read!?
271     if (errorType != NoCSSError)
272         m_propertyRangeStart = UINT_MAX;
273
274     if (m_propertyRangeStart == UINT_MAX || m_currentRuleDataStack.isEmpty() || !m_currentRuleDataStack.last()->styleSourceData)
275         return;
276
277     ASSERT(offset <= m_parsedText.length());
278     if (offset < m_parsedText.length() && m_parsedText[offset] == ';') // Include semicolon into the property text.
279         ++offset;
280
281     const unsigned start = m_propertyRangeStart;
282     const unsigned end = offset;
283     ASSERT(start < end);
284     String propertyString = m_parsedText.substring(start, end - start).stripWhiteSpace();
285     if (propertyString.endsWith(';'))
286         propertyString = propertyString.left(propertyString.length() - 1);
287     size_t colonIndex = propertyString.find(':');
288     ASSERT(colonIndex != kNotFound);
289
290     String name = propertyString.left(colonIndex).stripWhiteSpace();
291     String value = propertyString.substring(colonIndex + 1, propertyString.length()).stripWhiteSpace();
292     // The property range is relative to the declaration start offset.
293     unsigned topRuleBodyRangeStart = m_currentRuleDataStack.last()->ruleBodyRange.start;
294     m_currentRuleDataStack.last()->styleSourceData->propertyData.append(
295         CSSPropertySourceData(name, value, isImportant, false, isParsed, SourceRange(start - topRuleBodyRangeStart, end - topRuleBodyRangeStart)));
296     m_propertyRangeStart = UINT_MAX;
297 }
298
299 void StyleSheetHandler::startComment(unsigned offset)
300 {
301     ASSERT(m_commentRangeStart == UINT_MAX);
302     m_commentRangeStart = offset;
303 }
304
305 void StyleSheetHandler::endComment(unsigned offset)
306 {
307     ASSERT(offset <= m_parsedText.length());
308
309     unsigned startOffset = m_commentRangeStart;
310     m_commentRangeStart = UINT_MAX;
311     if (m_propertyRangeStart != UINT_MAX) {
312         ASSERT(startOffset >= m_propertyRangeStart);
313         // startProperty() is called automatically at the start of a style declaration.
314         // Check if no text has been scanned yet, otherwise the comment is inside a property.
315         if (!m_parsedText.substring(m_propertyRangeStart, startOffset).stripWhiteSpace().isEmpty())
316             return;
317         m_propertyRangeStart = UINT_MAX;
318     }
319     if (m_currentRuleDataStack.isEmpty() || !m_currentRuleDataStack.last()->ruleHeaderRange.end || !m_currentRuleDataStack.last()->styleSourceData)
320         return;
321
322     // The lexer is not inside a property AND it is scanning a declaration-aware rule body.
323     String commentText = m_parsedText.substring(startOffset, offset - startOffset);
324
325     ASSERT(commentText.startsWith("/*"));
326     commentText = commentText.substring(2);
327
328     // Require well-formed comments.
329     if (!commentText.endsWith("*/"))
330         return;
331     commentText = commentText.substring(0, commentText.length() - 2).stripWhiteSpace();
332     if (commentText.isEmpty())
333         return;
334
335     // FIXME: Use the actual rule type rather than STYLE_RULE?
336     RuleSourceDataList sourceData;
337
338     // FIXME: Use another subclass of BisonCSSParser::SourceDataHandler and assert that
339     // no comments are encountered (will not need m_document and m_styleSheetContents).
340     StyleSheetHandler handler(commentText, m_document, m_styleSheetContents, &sourceData);
341     RefPtrWillBeRawPtr<MutableStylePropertySet> tempMutableStyle = MutableStylePropertySet::create();
342     m_commentParser.parseDeclaration(tempMutableStyle.get(), commentText, &handler, m_styleSheetContents);
343     WillBeHeapVector<CSSPropertySourceData>& commentPropertyData = sourceData.first()->styleSourceData->propertyData;
344     if (commentPropertyData.size() != 1)
345         return;
346     CSSPropertySourceData& propertyData = commentPropertyData.at(0);
347     if (propertyData.range.length() != commentText.length())
348         return;
349
350     unsigned topRuleBodyRangeStart = m_currentRuleDataStack.last()->ruleBodyRange.start;
351     m_currentRuleDataStack.last()->styleSourceData->propertyData.append(
352         CSSPropertySourceData(propertyData.name, propertyData.value, false, true, true, SourceRange(startOffset - topRuleBodyRangeStart, offset - topRuleBodyRangeStart)));
353 }
354
355 } // namespace
356
357 class ParsedStyleSheet {
358     WTF_MAKE_FAST_ALLOCATED;
359 public:
360     ParsedStyleSheet(CSSStyleSheet* pageStyleSheet);
361
362     const String& text() const { ASSERT(m_hasText); return m_text; }
363     void setText(const String&);
364     bool hasText() const { return m_hasText; }
365     bool ensureSourceData();
366     bool hasSourceData() const { return m_sourceData; }
367     PassRefPtrWillBeRawPtr<blink::CSSRuleSourceData> ruleSourceDataAt(unsigned) const;
368     unsigned ruleCount() { return m_sourceData->size(); }
369
370 private:
371     void flattenSourceData(RuleSourceDataList*);
372     void setSourceData(PassOwnPtrWillBeRawPtr<RuleSourceDataList>);
373
374     String m_text;
375     bool m_hasText;
376     OwnPtrWillBePersistent<RuleSourceDataList> m_sourceData;
377     RefPtrWillBePersistent<CSSStyleSheet> m_pageStyleSheet;
378 };
379
380 ParsedStyleSheet::ParsedStyleSheet(CSSStyleSheet* pageStyleSheet)
381     : m_hasText(false)
382     , m_pageStyleSheet(pageStyleSheet)
383 {
384 }
385
386 void ParsedStyleSheet::setText(const String& text)
387 {
388     m_hasText = true;
389     m_text = text;
390     setSourceData(nullptr);
391 }
392
393 void ParsedStyleSheet::flattenSourceData(RuleSourceDataList* dataList)
394 {
395     for (size_t i = 0; i < dataList->size(); ++i) {
396         RefPtrWillBeMember<CSSRuleSourceData>& data = dataList->at(i);
397         if (data->type == CSSRuleSourceData::STYLE_RULE) {
398             m_sourceData->append(data);
399         } else if (data->type == CSSRuleSourceData::IMPORT_RULE) {
400             m_sourceData->append(data);
401         } else if (data->type == CSSRuleSourceData::MEDIA_RULE) {
402             m_sourceData->append(data);
403             flattenSourceData(&data->childRules);
404         } else if (data->type == CSSRuleSourceData::SUPPORTS_RULE) {
405             flattenSourceData(&data->childRules);
406         }
407     }
408 }
409
410 bool ParsedStyleSheet::ensureSourceData()
411 {
412     if (hasSourceData())
413         return true;
414
415     if (!hasText())
416         return false;
417
418     RefPtrWillBeRawPtr<StyleSheetContents> newStyleSheet = StyleSheetContents::create(strictCSSParserContext());
419     OwnPtrWillBeRawPtr<RuleSourceDataList> result = adoptPtrWillBeNoop(new RuleSourceDataList());
420     StyleSheetHandler handler(text(), m_pageStyleSheet->ownerDocument(), newStyleSheet.get(), result.get());
421     BisonCSSParser(parserContextForDocument(m_pageStyleSheet->ownerDocument())).parseSheet(newStyleSheet.get(), text(), TextPosition::minimumPosition(), &handler);
422     setSourceData(result.release());
423     return hasSourceData();
424 }
425
426 void ParsedStyleSheet::setSourceData(PassOwnPtrWillBeRawPtr<RuleSourceDataList> sourceData)
427 {
428     if (!sourceData) {
429         m_sourceData.clear();
430         return;
431     }
432     m_sourceData = adoptPtrWillBeNoop(new RuleSourceDataList());
433
434     // FIXME: This is a temporary solution to retain the original flat sourceData structure
435     // containing only style rules, even though BisonCSSParser now provides the full rule source data tree.
436     // Normally, we should just assign m_sourceData = sourceData;
437     flattenSourceData(sourceData.get());
438 }
439
440 PassRefPtrWillBeRawPtr<blink::CSSRuleSourceData> ParsedStyleSheet::ruleSourceDataAt(unsigned index) const
441 {
442     if (!hasSourceData() || index >= m_sourceData->size())
443         return nullptr;
444
445     return m_sourceData->at(index);
446 }
447
448 namespace blink {
449
450 enum MediaListSource {
451     MediaListSourceLinkedSheet,
452     MediaListSourceInlineSheet,
453     MediaListSourceMediaRule,
454     MediaListSourceImportRule
455 };
456
457 static PassRefPtr<TypeBuilder::CSS::SourceRange> buildSourceRangeObject(const SourceRange& range, Vector<unsigned>* lineEndings)
458 {
459     if (!lineEndings)
460         return nullptr;
461     TextPosition start = TextPosition::fromOffsetAndLineEndings(range.start, *lineEndings);
462     TextPosition end = TextPosition::fromOffsetAndLineEndings(range.end, *lineEndings);
463
464     RefPtr<TypeBuilder::CSS::SourceRange> result = TypeBuilder::CSS::SourceRange::create()
465         .setStartLine(start.m_line.zeroBasedInt())
466         .setStartColumn(start.m_column.zeroBasedInt())
467         .setEndLine(end.m_line.zeroBasedInt())
468         .setEndColumn(end.m_column.zeroBasedInt());
469     return result.release();
470 }
471
472 static PassRefPtrWillBeRawPtr<CSSRuleList> asCSSRuleList(CSSStyleSheet* styleSheet)
473 {
474     if (!styleSheet)
475         return nullptr;
476
477     RefPtrWillBeRawPtr<StaticCSSRuleList> list = StaticCSSRuleList::create();
478     WillBeHeapVector<RefPtrWillBeMember<CSSRule> >& listRules = list->rules();
479     for (unsigned i = 0, size = styleSheet->length(); i < size; ++i) {
480         CSSRule* item = styleSheet->item(i);
481         if (item->type() == CSSRule::CHARSET_RULE)
482             continue;
483         listRules.append(item);
484     }
485     return list.release();
486 }
487
488 static PassRefPtrWillBeRawPtr<CSSRuleList> asCSSRuleList(CSSRule* rule)
489 {
490     if (!rule)
491         return nullptr;
492
493     if (rule->type() == CSSRule::MEDIA_RULE)
494         return toCSSMediaRule(rule)->cssRules();
495
496     if (rule->type() == CSSRule::KEYFRAMES_RULE)
497         return toCSSKeyframesRule(rule)->cssRules();
498
499     if (rule->type() == CSSRule::SUPPORTS_RULE)
500         return toCSSSupportsRule(rule)->cssRules();
501
502     return nullptr;
503 }
504
505 PassRefPtrWillBeRawPtr<InspectorStyle> InspectorStyle::create(const InspectorCSSId& styleId, PassRefPtrWillBeRawPtr<CSSStyleDeclaration> style, InspectorStyleSheetBase* parentStyleSheet)
506 {
507     return adoptRefWillBeNoop(new InspectorStyle(styleId, style, parentStyleSheet));
508 }
509
510 InspectorStyle::InspectorStyle(const InspectorCSSId& styleId, PassRefPtrWillBeRawPtr<CSSStyleDeclaration> style, InspectorStyleSheetBase* parentStyleSheet)
511     : m_styleId(styleId)
512     , m_style(style)
513     , m_parentStyleSheet(parentStyleSheet)
514     , m_formatAcquired(false)
515 {
516     ASSERT(m_style);
517 }
518
519 PassRefPtr<TypeBuilder::CSS::CSSStyle> InspectorStyle::buildObjectForStyle() const
520 {
521     RefPtr<TypeBuilder::CSS::CSSStyle> result = styleWithProperties();
522     if (!m_styleId.isEmpty())
523         result->setStyleSheetId(m_styleId.styleSheetId());
524
525     RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = extractSourceData();
526     if (sourceData)
527         result->setRange(buildSourceRangeObject(sourceData->ruleBodyRange, m_parentStyleSheet->lineEndings().get()));
528
529     return result.release();
530 }
531
532 PassRefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSComputedStyleProperty> > InspectorStyle::buildArrayForComputedStyle() const
533 {
534     RefPtr<TypeBuilder::Array<TypeBuilder::CSS::CSSComputedStyleProperty> > result = TypeBuilder::Array<TypeBuilder::CSS::CSSComputedStyleProperty>::create();
535     WillBeHeapVector<InspectorStyleProperty> properties;
536     populateAllProperties(properties);
537
538     for (WillBeHeapVector<InspectorStyleProperty>::iterator it = properties.begin(), itEnd = properties.end(); it != itEnd; ++it) {
539         const CSSPropertySourceData& propertyEntry = it->sourceData;
540         RefPtr<TypeBuilder::CSS::CSSComputedStyleProperty> entry = TypeBuilder::CSS::CSSComputedStyleProperty::create()
541             .setName(propertyEntry.name)
542             .setValue(propertyEntry.value);
543         result->addItem(entry);
544     }
545
546     return result.release();
547 }
548
549 bool InspectorStyle::verifyPropertyText(const String& propertyText, bool canOmitSemicolon)
550 {
551     DEFINE_STATIC_LOCAL(String, bogusPropertyName, ("-webkit-boguz-propertee"));
552     RefPtrWillBeRawPtr<MutableStylePropertySet> tempMutableStyle = MutableStylePropertySet::create();
553     RuleSourceDataList sourceData;
554     RefPtrWillBeRawPtr<StyleSheetContents> styleSheetContents = StyleSheetContents::create(strictCSSParserContext());
555     String declarationText = propertyText + (canOmitSemicolon ? ";" : " ") + bogusPropertyName + ": none";
556     StyleSheetHandler handler(declarationText, ownerDocument(), styleSheetContents.get(), &sourceData);
557     BisonCSSParser(parserContextForDocument(ownerDocument())).parseDeclaration(tempMutableStyle.get(), declarationText, &handler, styleSheetContents.get());
558     WillBeHeapVector<CSSPropertySourceData>& propertyData = sourceData.first()->styleSourceData->propertyData;
559     unsigned propertyCount = propertyData.size();
560
561     // At least one property + the bogus property added just above should be present.
562     if (propertyCount < 2)
563         return false;
564
565     // Check for the proper propertyText termination (the parser could at least restore to the PROPERTY_NAME state).
566     if (propertyData.at(propertyCount - 1).name != bogusPropertyName)
567         return false;
568
569     return true;
570 }
571
572 bool InspectorStyle::setPropertyText(unsigned index, const String& propertyText, bool overwrite, ExceptionState& exceptionState)
573 {
574     ASSERT(m_parentStyleSheet);
575
576     if (!m_parentStyleSheet->ensureParsedDataReady()) {
577         exceptionState.throwDOMException(NotFoundError, "The parent style sheet's data hasn't been processed.");
578         return false;
579     }
580
581     if (!propertyText.stripWhiteSpace().isEmpty()) {
582         if (!verifyPropertyText(propertyText, false) && !verifyPropertyText(propertyText, true)) {
583             exceptionState.throwDOMException(SyntaxError, "The property '" + propertyText + "' could not be set.");
584             return false;
585         }
586     }
587
588     RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = extractSourceData();
589     if (!sourceData) {
590         exceptionState.throwDOMException(NotFoundError, "The property '" + propertyText + "' could not be set.");
591         return false;
592     }
593
594     String text;
595     bool success = styleText(&text);
596     if (!success) {
597         exceptionState.throwDOMException(NotFoundError, "The property '" + propertyText + "' could not be set.");
598         return false;
599     }
600
601     WillBeHeapVector<InspectorStyleProperty> allProperties;
602     populateAllProperties(allProperties);
603
604     InspectorStyleTextEditor editor(&allProperties, text, newLineAndWhitespaceDelimiters());
605     if (overwrite) {
606         if (index >= allProperties.size()) {
607             exceptionState.throwDOMException(IndexSizeError, "The index provided (" + String::number(index) + ") is greater than or equal to the maximum bound (" + String::number(allProperties.size()) + ").");
608             return false;
609         }
610         editor.replaceProperty(index, propertyText);
611     } else {
612         editor.insertProperty(index, propertyText, sourceData->ruleBodyRange.length());
613     }
614
615     return m_parentStyleSheet->setStyleText(m_styleId, editor.styleText());
616 }
617
618 bool InspectorStyle::styleText(String* result) const
619 {
620     RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = extractSourceData();
621     if (!sourceData)
622         return false;
623
624     String styleSheetText;
625     bool success = m_parentStyleSheet->getText(&styleSheetText);
626     if (!success)
627         return false;
628
629     SourceRange& bodyRange = sourceData->ruleBodyRange;
630     *result = styleSheetText.substring(bodyRange.start, bodyRange.end - bodyRange.start);
631     return true;
632 }
633
634 void InspectorStyle::populateAllProperties(WillBeHeapVector<InspectorStyleProperty>& result) const
635 {
636     HashSet<String> sourcePropertyNames;
637
638     RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = extractSourceData();
639     if (sourceData && sourceData->styleSourceData) {
640         String styleDeclaration;
641         bool isStyleTextKnown = styleText(&styleDeclaration);
642         ASSERT_UNUSED(isStyleTextKnown, isStyleTextKnown);
643         WillBeHeapVector<CSSPropertySourceData>& sourcePropertyData = sourceData->styleSourceData->propertyData;
644         for (WillBeHeapVector<CSSPropertySourceData>::const_iterator it = sourcePropertyData.begin(); it != sourcePropertyData.end(); ++it) {
645             InspectorStyleProperty p(*it, true);
646             p.setRawTextFromStyleDeclaration(styleDeclaration);
647             result.append(p);
648             sourcePropertyNames.add(it->name.lower());
649         }
650     }
651
652     for (int i = 0, size = m_style->length(); i < size; ++i) {
653         String name = m_style->item(i);
654         if (!sourcePropertyNames.add(name.lower()).isNewEntry)
655             continue;
656
657         result.append(InspectorStyleProperty(CSSPropertySourceData(name, m_style->getPropertyValue(name), !m_style->getPropertyPriority(name).isEmpty(), false, true, SourceRange()), false));
658     }
659 }
660
661 PassRefPtr<TypeBuilder::CSS::CSSStyle> InspectorStyle::styleWithProperties() const
662 {
663     RefPtr<Array<TypeBuilder::CSS::CSSProperty> > propertiesObject = Array<TypeBuilder::CSS::CSSProperty>::create();
664     RefPtr<Array<TypeBuilder::CSS::ShorthandEntry> > shorthandEntries = Array<TypeBuilder::CSS::ShorthandEntry>::create();
665     HashSet<String> foundShorthands;
666     OwnPtr<Vector<unsigned> > lineEndings(m_parentStyleSheet ? m_parentStyleSheet->lineEndings() : PassOwnPtr<Vector<unsigned> >());
667     RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = extractSourceData();
668     unsigned ruleBodyRangeStart = sourceData ? sourceData->ruleBodyRange.start : 0;
669
670     WillBeHeapVector<InspectorStyleProperty> properties;
671     populateAllProperties(properties);
672
673     for (WillBeHeapVector<InspectorStyleProperty>::iterator it = properties.begin(), itEnd = properties.end(); it != itEnd; ++it) {
674         const CSSPropertySourceData& propertyEntry = it->sourceData;
675         const String& name = propertyEntry.name;
676
677         RefPtr<TypeBuilder::CSS::CSSProperty> property = TypeBuilder::CSS::CSSProperty::create()
678             .setName(name)
679             .setValue(propertyEntry.value);
680         propertiesObject->addItem(property);
681
682         // Default "parsedOk" == true.
683         if (!propertyEntry.parsedOk)
684             property->setParsedOk(false);
685         if (it->hasRawText())
686             property->setText(it->rawText);
687
688         if (propertyEntry.important)
689             property->setImportant(true);
690         if (it->hasSource) {
691             // The property range is relative to the style body start.
692             // Should be converted into an absolute range (relative to the stylesheet start)
693             // for the proper conversion into line:column.
694             SourceRange absolutePropertyRange = propertyEntry.range;
695             absolutePropertyRange.start += ruleBodyRangeStart;
696             absolutePropertyRange.end += ruleBodyRangeStart;
697             property->setRange(buildSourceRangeObject(absolutePropertyRange, lineEndings.get()));
698             if (!propertyEntry.disabled) {
699                 ASSERT(sourceData);
700                 property->setImplicit(false);
701             }
702             property->setDisabled(propertyEntry.disabled);
703         } else if (!propertyEntry.disabled) {
704             bool implicit = m_style->isPropertyImplicit(name);
705             // Default "implicit" == false.
706             if (implicit)
707                 property->setImplicit(true);
708
709             String shorthand = m_style->getPropertyShorthand(name);
710             if (!shorthand.isEmpty()) {
711                 if (foundShorthands.add(shorthand).isNewEntry) {
712                     RefPtr<TypeBuilder::CSS::ShorthandEntry> entry = TypeBuilder::CSS::ShorthandEntry::create()
713                         .setName(shorthand)
714                         .setValue(shorthandValue(shorthand));
715                     shorthandEntries->addItem(entry);
716                 }
717             }
718         }
719     }
720
721     RefPtr<TypeBuilder::CSS::CSSStyle> result = TypeBuilder::CSS::CSSStyle::create()
722         .setCssProperties(propertiesObject)
723         .setShorthandEntries(shorthandEntries);
724     return result.release();
725 }
726
727 PassRefPtrWillBeRawPtr<CSSRuleSourceData> InspectorStyle::extractSourceData() const
728 {
729     if (!m_parentStyleSheet || !m_parentStyleSheet->ensureParsedDataReady())
730         return nullptr;
731     return m_parentStyleSheet->ruleSourceDataAt(m_styleId.ordinal());
732 }
733
734 String InspectorStyle::shorthandValue(const String& shorthandProperty) const
735 {
736     String value = m_style->getPropertyValue(shorthandProperty);
737     if (value.isEmpty()) {
738         StringBuilder builder;
739
740         for (unsigned i = 0; i < m_style->length(); ++i) {
741             String individualProperty = m_style->item(i);
742             if (m_style->getPropertyShorthand(individualProperty) != shorthandProperty)
743                 continue;
744             if (m_style->isPropertyImplicit(individualProperty))
745                 continue;
746             String individualValue = m_style->getPropertyValue(individualProperty);
747             if (individualValue == "initial")
748                 continue;
749             if (!builder.isEmpty())
750                 builder.append(" ");
751             builder.append(individualValue);
752         }
753
754         return builder.toString();
755     }
756     return value;
757 }
758
759 NewLineAndWhitespace& InspectorStyle::newLineAndWhitespaceDelimiters() const
760 {
761     DEFINE_STATIC_LOCAL(String, defaultPrefix, ("    "));
762
763     if (m_formatAcquired)
764         return m_format;
765
766     RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = extractSourceData();
767     WillBeHeapVector<CSSPropertySourceData>* sourcePropertyData = sourceData ? &(sourceData->styleSourceData->propertyData) : 0;
768     int propertyCount = sourcePropertyData ? sourcePropertyData->size() : 0;
769     if (!propertyCount) {
770         m_format.first = "\n";
771         m_format.second = defaultPrefix;
772         return m_format; // Do not remember the default formatting and attempt to acquire it later.
773     }
774
775     String text;
776     bool success = styleText(&text);
777     ASSERT_UNUSED(success, success);
778
779     m_formatAcquired = true;
780
781     String candidatePrefix = defaultPrefix;
782     StringBuilder formatLineFeed;
783     StringBuilder prefix;
784     int scanStart = 0;
785     int propertyIndex = 0;
786     bool isFullPrefixScanned = false;
787     bool lineFeedTerminated = false;
788     while (propertyIndex < propertyCount) {
789         const blink::CSSPropertySourceData& currentProperty = sourcePropertyData->at(propertyIndex++);
790
791         bool processNextProperty = false;
792         int scanEnd = currentProperty.range.start;
793         for (int i = scanStart; i < scanEnd; ++i) {
794             UChar ch = text[i];
795             bool isLineFeed = isHTMLLineBreak(ch);
796             if (isLineFeed) {
797                 if (!lineFeedTerminated)
798                     formatLineFeed.append(ch);
799                 prefix.clear();
800             } else if (isHTMLSpace<UChar>(ch))
801                 prefix.append(ch);
802             else {
803                 candidatePrefix = prefix.toString();
804                 prefix.clear();
805                 scanStart = currentProperty.range.end;
806                 ++propertyIndex;
807                 processNextProperty = true;
808                 break;
809             }
810             if (!isLineFeed && formatLineFeed.length())
811                 lineFeedTerminated = true;
812         }
813         if (!processNextProperty) {
814             isFullPrefixScanned = true;
815             break;
816         }
817     }
818
819     m_format.first = formatLineFeed.toString();
820     m_format.second = isFullPrefixScanned ? prefix.toString() : candidatePrefix;
821     return m_format;
822 }
823
824 Document* InspectorStyle::ownerDocument() const
825 {
826     return m_parentStyleSheet->ownerDocument();
827 }
828
829 void InspectorStyle::trace(Visitor* visitor)
830 {
831     visitor->trace(m_style);
832     visitor->trace(m_parentStyleSheet);
833 }
834
835 InspectorStyleSheetBase::InspectorStyleSheetBase(const String& id, Listener* listener)
836     : m_id(id)
837     , m_listener(listener)
838 {
839 }
840
841 bool InspectorStyleSheetBase::setPropertyText(const InspectorCSSId& id, unsigned propertyIndex, const String& text, bool overwrite, ExceptionState& exceptionState)
842 {
843     RefPtrWillBeRawPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id);
844     if (!inspectorStyle) {
845         exceptionState.throwDOMException(NotFoundError, "No property could be found for the given ID.");
846         return false;
847     }
848     return inspectorStyle->setPropertyText(propertyIndex, text, overwrite, exceptionState);
849 }
850
851 bool InspectorStyleSheetBase::getStyleText(const InspectorCSSId& id, String* text)
852 {
853     RefPtrWillBeRawPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id);
854     if (!inspectorStyle)
855         return false;
856     return inspectorStyle->styleText(text);
857 }
858
859 void InspectorStyleSheetBase::fireStyleSheetChanged()
860 {
861     if (listener())
862         listener()->styleSheetChanged(this);
863 }
864
865 PassRefPtr<TypeBuilder::CSS::CSSStyle> InspectorStyleSheetBase::buildObjectForStyle(CSSStyleDeclaration* style)
866 {
867     RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = nullptr;
868     if (ensureParsedDataReady())
869         sourceData = ruleSourceDataAt(styleId(style).ordinal());
870
871     InspectorCSSId id = styleId(style);
872     if (id.isEmpty()) {
873         // Any rule coming from User Agent and not from DefaultStyleSheet will not have id.
874         // See InspectorCSSAgent::buildObjectForRule for details.
875         RefPtrWillBeRawPtr<InspectorStyle> inspectorStyle = InspectorStyle::create(id, style, this);
876         return inspectorStyle->buildObjectForStyle();
877     }
878     RefPtrWillBeRawPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id);
879     RefPtr<TypeBuilder::CSS::CSSStyle> result = inspectorStyle->buildObjectForStyle();
880
881     // Style text cannot be retrieved without stylesheet, so set cssText here.
882     if (sourceData) {
883         String sheetText;
884         bool success = getText(&sheetText);
885         if (success) {
886             const SourceRange& bodyRange = sourceData->ruleBodyRange;
887             result->setCssText(sheetText.substring(bodyRange.start, bodyRange.end - bodyRange.start));
888         }
889     }
890
891     return result.release();
892 }
893
894 PassOwnPtr<Vector<unsigned> > InspectorStyleSheetBase::lineEndings()
895 {
896     String text;
897     if (!getText(&text))
898         return PassOwnPtr<Vector<unsigned> >();
899     return WTF::lineEndings(text);
900 }
901
902 bool InspectorStyleSheetBase::lineNumberAndColumnToOffset(unsigned lineNumber, unsigned columnNumber, unsigned* offset)
903 {
904     OwnPtr<Vector<unsigned> > endings = lineEndings();
905     if (lineNumber >= endings->size())
906         return false;
907     unsigned charactersInLine = lineNumber > 0 ? endings->at(lineNumber) - endings->at(lineNumber - 1) - 1 : endings->at(0);
908     if (columnNumber > charactersInLine)
909         return false;
910     TextPosition position(OrdinalNumber::fromZeroBasedInt(lineNumber), OrdinalNumber::fromZeroBasedInt(columnNumber));
911     *offset = position.toOffset(*endings).zeroBasedInt();
912     return true;
913 }
914
915 bool InspectorStyleSheetBase::findPropertyByRange(const SourceRange& sourceRange, InspectorCSSId* ruleId, unsigned* propertyIndex, bool* overwrite)
916 {
917     if (!ensureParsedDataReady())
918         return false;
919     for (size_t i = 0; i < ruleCount(); ++i) {
920         RefPtrWillBeRawPtr<CSSRuleSourceData> ruleSourceData = ruleSourceDataAt(i);
921         RefPtrWillBeRawPtr<CSSStyleSourceData> styleSourceData = ruleSourceData->styleSourceData;
922         if (!styleSourceData)
923             continue;
924         if (ruleSourceData->ruleBodyRange.end < sourceRange.start || sourceRange.end < ruleSourceData->ruleBodyRange.start)
925             continue;
926         WillBeHeapVector<CSSPropertySourceData>& propertyData = styleSourceData->propertyData;
927         for (size_t j = 0; j < propertyData.size(); ++j) {
928             CSSPropertySourceData& property = propertyData.at(j);
929             unsigned styleStart = ruleSourceData->ruleBodyRange.start;
930             if (sourceRange.length() && property.range.start + styleStart == sourceRange.start && property.range.end + styleStart == sourceRange.end) {
931                 *ruleId = InspectorCSSId(id(), i);
932                 *propertyIndex = j;
933                 *overwrite = true;
934                 return true;
935             }
936             if (!sourceRange.length() && styleStart <= sourceRange.start && sourceRange.start <= property.range.start + styleStart) {
937                 *ruleId = InspectorCSSId(id(), i);
938                 *propertyIndex = j;
939                 *overwrite = false;
940                 return true;
941             }
942         }
943         if (!sourceRange.length() && ruleSourceData->ruleBodyRange.start <= sourceRange.start && sourceRange.start <= ruleSourceData->ruleBodyRange.end) {
944             *ruleId = InspectorCSSId(id(), i);
945             *propertyIndex = propertyData.size();
946             *overwrite = false;
947             return true;
948         }
949     }
950     return false;
951 }
952
953 PassRefPtrWillBeRawPtr<InspectorStyleSheet> InspectorStyleSheet::create(InspectorPageAgent* pageAgent, InspectorResourceAgent* resourceAgent, const String& id, PassRefPtrWillBeRawPtr<CSSStyleSheet> pageStyleSheet, TypeBuilder::CSS::StyleSheetOrigin::Enum origin, const String& documentURL, Listener* listener)
954 {
955     return adoptRefWillBeNoop(new InspectorStyleSheet(pageAgent, resourceAgent, id, pageStyleSheet, origin, documentURL, listener));
956 }
957
958 InspectorStyleSheet::InspectorStyleSheet(InspectorPageAgent* pageAgent, InspectorResourceAgent* resourceAgent, const String& id, PassRefPtrWillBeRawPtr<CSSStyleSheet> pageStyleSheet, TypeBuilder::CSS::StyleSheetOrigin::Enum origin, const String& documentURL, Listener* listener)
959     : InspectorStyleSheetBase(id, listener)
960     , m_pageAgent(pageAgent)
961     , m_resourceAgent(resourceAgent)
962     , m_pageStyleSheet(pageStyleSheet)
963     , m_origin(origin)
964     , m_documentURL(documentURL)
965 {
966     m_parsedStyleSheet = adoptPtr(new ParsedStyleSheet(m_pageStyleSheet.get()));
967 }
968
969 InspectorStyleSheet::~InspectorStyleSheet()
970 {
971 }
972
973 void InspectorStyleSheet::trace(Visitor* visitor)
974 {
975     visitor->trace(m_pageAgent);
976     visitor->trace(m_resourceAgent);
977     visitor->trace(m_pageStyleSheet);
978     visitor->trace(m_flatRules);
979     InspectorStyleSheetBase::trace(visitor);
980 }
981
982 static String styleSheetURL(CSSStyleSheet* pageStyleSheet)
983 {
984     if (pageStyleSheet && !pageStyleSheet->contents()->baseURL().isEmpty())
985         return pageStyleSheet->contents()->baseURL().string();
986     return emptyString();
987 }
988
989 String InspectorStyleSheet::finalURL() const
990 {
991     String url = styleSheetURL(m_pageStyleSheet.get());
992     return url.isEmpty() ? m_documentURL : url;
993 }
994
995 bool InspectorStyleSheet::setText(const String& text, ExceptionState& exceptionState)
996 {
997     updateText(text);
998     m_flatRules.clear();
999
1000     if (listener())
1001         listener()->willReparseStyleSheet();
1002
1003     {
1004         // Have a separate scope for clearRules() (bug 95324).
1005         CSSStyleSheet::RuleMutationScope mutationScope(m_pageStyleSheet.get());
1006         m_pageStyleSheet->contents()->clearRules();
1007         m_pageStyleSheet->clearChildRuleCSSOMWrappers();
1008     }
1009     {
1010         CSSStyleSheet::RuleMutationScope mutationScope(m_pageStyleSheet.get());
1011         m_pageStyleSheet->contents()->parseString(text);
1012     }
1013
1014     if (listener())
1015         listener()->didReparseStyleSheet();
1016     fireStyleSheetChanged();
1017     m_pageStyleSheet->ownerDocument()->styleResolverChanged(FullStyleUpdate);
1018     return true;
1019 }
1020
1021 String InspectorStyleSheet::ruleSelector(const InspectorCSSId& id, ExceptionState& exceptionState)
1022 {
1023     CSSStyleRule* rule = ruleForId(id);
1024     if (!rule) {
1025         exceptionState.throwDOMException(NotFoundError, "No rule was found for the given ID.");
1026         return "";
1027     }
1028     return rule->selectorText();
1029 }
1030
1031 bool InspectorStyleSheet::setRuleSelector(const InspectorCSSId& id, const String& selector, ExceptionState& exceptionState)
1032 {
1033     CSSStyleRule* rule = ruleForId(id);
1034     if (!rule) {
1035         exceptionState.throwDOMException(NotFoundError, "No rule was found for the given ID.");
1036         return false;
1037     }
1038     CSSStyleSheet* styleSheet = rule->parentStyleSheet();
1039     if (!styleSheet || !ensureParsedDataReady()) {
1040         exceptionState.throwDOMException(NotFoundError, "No stylesheet could be found in which to set the selector.");
1041         return false;
1042     }
1043
1044     rule->setSelectorText(selector);
1045     RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = ruleSourceDataAt(id.ordinal());
1046     if (!sourceData) {
1047         exceptionState.throwDOMException(NotFoundError, "The selector '" + selector + "' could not be set.");
1048         return false;
1049     }
1050
1051     String sheetText = m_parsedStyleSheet->text();
1052     sheetText.replace(sourceData->ruleHeaderRange.start, sourceData->ruleHeaderRange.length(), selector);
1053     updateText(sheetText);
1054     fireStyleSheetChanged();
1055     return true;
1056 }
1057
1058 unsigned InspectorStyleSheet::ruleIndexBySourceRange(const CSSMediaRule* parentMediaRule, const SourceRange& sourceRange)
1059 {
1060     unsigned index = 0;
1061     for (size_t i = 0; i < m_flatRules.size(); ++i) {
1062         RefPtrWillBeRawPtr<CSSRule> rule = m_flatRules.at(i);
1063         if (rule->parentRule() != parentMediaRule)
1064             continue;
1065         RefPtrWillBeRawPtr<CSSRuleSourceData> ruleSourceData = m_parsedStyleSheet->ruleSourceDataAt(i);
1066         if (ruleSourceData->ruleBodyRange.end < sourceRange.start)
1067             ++index;
1068     }
1069     return index;
1070 }
1071
1072 CSSStyleRule* InspectorStyleSheet::insertCSSOMRuleInStyleSheet(const SourceRange& sourceRange, const String& ruleText, ExceptionState& exceptionState)
1073 {
1074     unsigned index = ruleIndexBySourceRange(nullptr, sourceRange);
1075     m_pageStyleSheet->insertRule(ruleText, index, exceptionState);
1076     CSSRule* rule = m_pageStyleSheet->item(index);
1077     CSSStyleRule* styleRule = InspectorCSSAgent::asCSSStyleRule(rule);
1078     if (!styleRule) {
1079         m_pageStyleSheet->deleteRule(index, ASSERT_NO_EXCEPTION);
1080         exceptionState.throwDOMException(SyntaxError, "The rule '" + ruleText + "' could not be added in style sheet.");
1081         return 0;
1082     }
1083     return styleRule;
1084 }
1085
1086 CSSStyleRule* InspectorStyleSheet::insertCSSOMRuleInMediaRule(CSSMediaRule* mediaRule, const SourceRange& sourceRange, const String& ruleText, ExceptionState& exceptionState)
1087 {
1088     unsigned index = ruleIndexBySourceRange(mediaRule, sourceRange);
1089     mediaRule->insertRule(ruleText, index, exceptionState);
1090     CSSRule* rule = mediaRule->item(index);
1091     CSSStyleRule* styleRule = InspectorCSSAgent::asCSSStyleRule(rule);
1092     if (!styleRule) {
1093         mediaRule->deleteRule(index, ASSERT_NO_EXCEPTION);
1094         exceptionState.throwDOMException(SyntaxError, "The rule '" + ruleText + "' could not be added in media rule.");
1095         return 0;
1096     }
1097     return styleRule;
1098 }
1099
1100 CSSStyleRule* InspectorStyleSheet::insertCSSOMRuleBySourceRange(const SourceRange& sourceRange, const String& ruleText, ExceptionState& exceptionState)
1101 {
1102     int containingRuleIndex = -1;
1103     unsigned containingRuleLength = 0;
1104     for (size_t i = 0; i < m_parsedStyleSheet->ruleCount(); ++i) {
1105         RefPtrWillBeRawPtr<CSSRuleSourceData> ruleSourceData = m_parsedStyleSheet->ruleSourceDataAt(i);
1106         if (ruleSourceData->ruleHeaderRange.start < sourceRange.start && sourceRange.start < ruleSourceData->ruleBodyRange.start) {
1107             exceptionState.throwDOMException(NotFoundError, "Cannot insert rule inside rule selector.");
1108             return 0;
1109         }
1110         if (sourceRange.start < ruleSourceData->ruleBodyRange.start || ruleSourceData->ruleBodyRange.end < sourceRange.start)
1111             continue;
1112         if (containingRuleIndex == -1 || containingRuleLength > ruleSourceData->ruleBodyRange.length()) {
1113             containingRuleIndex = i;
1114             containingRuleLength = ruleSourceData->ruleBodyRange.length();
1115         }
1116     }
1117     if (containingRuleIndex == -1)
1118         return insertCSSOMRuleInStyleSheet(sourceRange, ruleText, exceptionState);
1119     RefPtrWillBeRawPtr<CSSRule> rule = m_flatRules.at(containingRuleIndex);
1120     if (rule->type() != CSSRule::MEDIA_RULE) {
1121         exceptionState.throwDOMException(NotFoundError, "Cannot insert rule in non-media rule.");
1122         return 0;
1123     }
1124     return insertCSSOMRuleInMediaRule(toCSSMediaRule(rule.get()), sourceRange, ruleText, exceptionState);
1125 }
1126
1127 bool InspectorStyleSheet::verifyRuleText(const String& ruleText)
1128 {
1129     DEFINE_STATIC_LOCAL(String, bogusPropertyName, ("-webkit-boguz-propertee"));
1130     RuleSourceDataList sourceData;
1131     RefPtrWillBeRawPtr<StyleSheetContents> styleSheetContents = StyleSheetContents::create(strictCSSParserContext());
1132     String text = ruleText + " div { " + bogusPropertyName + ": none; }";
1133     StyleSheetHandler handler(text, ownerDocument(), styleSheetContents.get(), &sourceData);
1134     BisonCSSParser(parserContextForDocument(ownerDocument())).parseSheet(styleSheetContents.get(), text, TextPosition::minimumPosition(), &handler);
1135     unsigned ruleCount = sourceData.size();
1136
1137     // Exactly two rules should be parsed.
1138     if (ruleCount != 2)
1139         return false;
1140
1141     // Added rule must be style rule.
1142     if (!sourceData.at(0)->styleSourceData)
1143         return false;
1144
1145     WillBeHeapVector<CSSPropertySourceData>& propertyData = sourceData.at(1)->styleSourceData->propertyData;
1146     unsigned propertyCount = propertyData.size();
1147
1148     // Exactly one property should be in rule.
1149     if (propertyCount != 1)
1150         return false;
1151
1152     // Check for the property name.
1153     if (propertyData.at(0).name != bogusPropertyName)
1154         return false;
1155
1156     return true;
1157 }
1158
1159 CSSStyleRule* InspectorStyleSheet::addRule(const String& ruleText, const SourceRange& location, ExceptionState& exceptionState)
1160 {
1161     if (!ensureParsedDataReady()) {
1162         exceptionState.throwDOMException(NotFoundError, "Cannot parse style sheet.");
1163         return 0;
1164     }
1165
1166     if (location.start != location.end) {
1167         exceptionState.throwDOMException(NotFoundError, "Source range must be collapsed.");
1168         return 0;
1169     }
1170
1171     if (!verifyRuleText(ruleText)) {
1172         exceptionState.throwDOMException(SyntaxError, "Rule text is not valid.");
1173         return 0;
1174     }
1175
1176     String text;
1177     bool success = getText(&text);
1178     if (!success) {
1179         exceptionState.throwDOMException(NotFoundError, "The rule '" + ruleText + "' could not be added.");
1180         return 0;
1181     }
1182
1183     ensureFlatRules();
1184     CSSStyleRule* styleRule = insertCSSOMRuleBySourceRange(location, ruleText, exceptionState);
1185     if (exceptionState.hadException())
1186         return 0;
1187
1188     text.insert(ruleText, location.start);
1189
1190     m_parsedStyleSheet->setText(text);
1191     m_flatRules.clear();
1192
1193     fireStyleSheetChanged();
1194     return styleRule;
1195 }
1196
1197 bool InspectorStyleSheet::deleteRule(const InspectorCSSId& id, const String& oldText, ExceptionState& exceptionState)
1198 {
1199     RefPtrWillBeRawPtr<CSSStyleRule> rule = ruleForId(id);
1200     if (!rule) {
1201         exceptionState.throwDOMException(NotFoundError, "No style rule could be found for the provided ID.");
1202         return false;
1203     }
1204     CSSStyleSheet* styleSheet = rule->parentStyleSheet();
1205     if (!styleSheet || !ensureParsedDataReady()) {
1206         exceptionState.throwDOMException(NotFoundError, "No parent stylesheet could be found.");
1207         return false;
1208     }
1209
1210     RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = ruleSourceDataAt(id.ordinal());
1211     if (!sourceData) {
1212         exceptionState.throwDOMException(NotFoundError, "No style rule could be found for the provided ID.");
1213         return false;
1214     }
1215
1216     CSSRule* parentRule = rule->parentRule();
1217     if (parentRule) {
1218         if (parentRule->type() != CSSRule::MEDIA_RULE) {
1219             exceptionState.throwDOMException(NotFoundError, "Cannot remove rule from non-media rule.");
1220             return false;
1221         }
1222         CSSMediaRule* parentMediaRule = toCSSMediaRule(parentRule);
1223         size_t index = 0;
1224         while (index < parentMediaRule->length() && parentMediaRule->item(index) != rule)
1225             ++index;
1226         ASSERT(index < parentMediaRule->length());
1227         parentMediaRule->deleteRule(index, exceptionState);
1228     } else {
1229         size_t index = 0;
1230         while (index < styleSheet->length() && styleSheet->item(index) != rule)
1231             ++index;
1232         ASSERT(index < styleSheet->length());
1233         styleSheet->deleteRule(index, exceptionState);
1234     }
1235     // |rule| MAY NOT be addressed after this line!
1236
1237     if (exceptionState.hadException())
1238         return false;
1239
1240     m_parsedStyleSheet->setText(oldText);
1241     m_flatRules.clear();
1242     fireStyleSheetChanged();
1243     return true;
1244 }
1245
1246 void InspectorStyleSheet::updateText(const String& newText)
1247 {
1248     Element* element = ownerStyleElement();
1249     if (!element)
1250         m_pageAgent->addEditedResourceContent(finalURL(), newText);
1251     m_parsedStyleSheet->setText(newText);
1252 }
1253
1254
1255 CSSStyleRule* InspectorStyleSheet::ruleForId(const InspectorCSSId& id) const
1256 {
1257     ASSERT(!id.isEmpty());
1258     ensureFlatRules();
1259     return InspectorCSSAgent::asCSSStyleRule(id.ordinal() >= m_flatRules.size() ? 0 : m_flatRules.at(id.ordinal()).get());
1260 }
1261
1262 PassRefPtr<TypeBuilder::CSS::CSSStyleSheetHeader> InspectorStyleSheet::buildObjectForStyleSheetInfo() const
1263 {
1264     CSSStyleSheet* styleSheet = pageStyleSheet();
1265     if (!styleSheet)
1266         return nullptr;
1267
1268     Document* document = styleSheet->ownerDocument();
1269     LocalFrame* frame = document ? document->frame() : 0;
1270
1271     RefPtr<TypeBuilder::CSS::CSSStyleSheetHeader> result = TypeBuilder::CSS::CSSStyleSheetHeader::create()
1272         .setStyleSheetId(id())
1273         .setOrigin(m_origin)
1274         .setDisabled(styleSheet->disabled())
1275         .setSourceURL(url())
1276         .setTitle(styleSheet->title())
1277         .setFrameId(m_pageAgent->frameId(frame))
1278         .setIsInline(styleSheet->isInline() && !startsAtZero())
1279         .setStartLine(styleSheet->startPositionInSource().m_line.zeroBasedInt())
1280         .setStartColumn(styleSheet->startPositionInSource().m_column.zeroBasedInt());
1281
1282     if (hasSourceURL())
1283         result->setHasSourceURL(true);
1284
1285     String sourceMapURLValue = sourceMapURL();
1286     if (!sourceMapURLValue.isEmpty())
1287         result->setSourceMapURL(sourceMapURLValue);
1288     return result.release();
1289 }
1290
1291 PassRefPtr<TypeBuilder::Array<TypeBuilder::CSS::Selector> > InspectorStyleSheet::selectorsFromSource(const CSSRuleSourceData* sourceData, const String& sheetText)
1292 {
1293     ScriptRegexp comment("/\\*[^]*?\\*/", TextCaseSensitive, MultilineEnabled);
1294     RefPtr<TypeBuilder::Array<TypeBuilder::CSS::Selector> > result = TypeBuilder::Array<TypeBuilder::CSS::Selector>::create();
1295     const SelectorRangeList& ranges = sourceData->selectorRanges;
1296     for (size_t i = 0, size = ranges.size(); i < size; ++i) {
1297         const SourceRange& range = ranges.at(i);
1298         String selector = sheetText.substring(range.start, range.length());
1299
1300         // We don't want to see any comments in the selector components, only the meaningful parts.
1301         int matchLength;
1302         int offset = 0;
1303         while ((offset = comment.match(selector, offset, &matchLength)) >= 0)
1304             selector.replace(offset, matchLength, "");
1305
1306         RefPtr<TypeBuilder::CSS::Selector> simpleSelector = TypeBuilder::CSS::Selector::create()
1307             .setValue(selector.stripWhiteSpace());
1308         simpleSelector->setRange(buildSourceRangeObject(range, lineEndings().get()));
1309         result->addItem(simpleSelector.release());
1310     }
1311     return result.release();
1312 }
1313
1314 PassRefPtr<TypeBuilder::CSS::SelectorList> InspectorStyleSheet::buildObjectForSelectorList(CSSStyleRule* rule)
1315 {
1316     RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = nullptr;
1317     if (ensureParsedDataReady())
1318         sourceData = ruleSourceDataAt(styleId(rule->style()).ordinal());
1319     RefPtr<TypeBuilder::Array<TypeBuilder::CSS::Selector> > selectors;
1320
1321     // This intentionally does not rely on the source data to avoid catching the trailing comments (before the declaration starting '{').
1322     String selectorText = rule->selectorText();
1323
1324     if (sourceData)
1325         selectors = selectorsFromSource(sourceData.get(), m_parsedStyleSheet->text());
1326     else {
1327         selectors = TypeBuilder::Array<TypeBuilder::CSS::Selector>::create();
1328         const CSSSelectorList& selectorList = rule->styleRule()->selectorList();
1329         for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(*selector))
1330             selectors->addItem(TypeBuilder::CSS::Selector::create().setValue(selector->selectorText()).release());
1331     }
1332     RefPtr<TypeBuilder::CSS::SelectorList> result = TypeBuilder::CSS::SelectorList::create()
1333         .setSelectors(selectors)
1334         .setText(selectorText)
1335         .release();
1336     return result.release();
1337 }
1338
1339 static bool canBind(TypeBuilder::CSS::StyleSheetOrigin::Enum origin)
1340 {
1341     return origin != TypeBuilder::CSS::StyleSheetOrigin::User_agent && origin != TypeBuilder::CSS::StyleSheetOrigin::User;
1342 }
1343
1344 PassRefPtr<TypeBuilder::CSS::CSSRule> InspectorStyleSheet::buildObjectForRule(CSSStyleRule* rule, PassRefPtr<Array<TypeBuilder::CSS::CSSMedia> > mediaStack)
1345 {
1346     CSSStyleSheet* styleSheet = pageStyleSheet();
1347     if (!styleSheet)
1348         return nullptr;
1349
1350     RefPtr<TypeBuilder::CSS::CSSRule> result = TypeBuilder::CSS::CSSRule::create()
1351         .setSelectorList(buildObjectForSelectorList(rule))
1352         .setOrigin(m_origin)
1353         .setStyle(buildObjectForStyle(rule->style()));
1354
1355     if (canBind(m_origin)) {
1356         InspectorCSSId id(ruleId(rule));
1357         if (!id.isEmpty())
1358             result->setStyleSheetId(id.styleSheetId());
1359     }
1360
1361     if (mediaStack)
1362         result->setMedia(mediaStack);
1363
1364     return result.release();
1365 }
1366
1367 bool InspectorStyleSheet::getText(String* result) const
1368 {
1369     if (!ensureText())
1370         return false;
1371     *result = m_parsedStyleSheet->text();
1372     return true;
1373 }
1374
1375 CSSStyleDeclaration* InspectorStyleSheet::styleForId(const InspectorCSSId& id) const
1376 {
1377     CSSStyleRule* rule = ruleForId(id);
1378     if (!rule)
1379         return 0;
1380
1381     return rule->style();
1382 }
1383
1384 PassRefPtr<TypeBuilder::CSS::SourceRange> InspectorStyleSheet::ruleHeaderSourceRange(const CSSRule* rule)
1385 {
1386     if (!ensureParsedDataReady())
1387         return nullptr;
1388
1389     ensureFlatRules();
1390     size_t index = m_flatRules.find(rule);
1391     // FIXME(lusnikov): m_flatRules are not always aligned with the m_parsedStyleSheet rule source
1392     // datas due to the CSSOM operations that add/remove rules without changing source.
1393     // This is a design issue. See crbug.com/178410
1394     if (index == kNotFound || index >= m_parsedStyleSheet->ruleCount())
1395         return nullptr;
1396     RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = m_parsedStyleSheet->ruleSourceDataAt(static_cast<unsigned>(index));
1397     return buildSourceRangeObject(sourceData->ruleHeaderRange, lineEndings().get());
1398 }
1399
1400 PassRefPtrWillBeRawPtr<InspectorStyle> InspectorStyleSheet::inspectorStyleForId(const InspectorCSSId& id)
1401 {
1402     CSSStyleDeclaration* style = styleForId(id);
1403     if (!style)
1404         return nullptr;
1405
1406     return InspectorStyle::create(id, style, this);
1407 }
1408
1409 unsigned InspectorStyleSheet::ruleCount()
1410 {
1411     return m_parsedStyleSheet->ruleCount();
1412 }
1413
1414 String InspectorStyleSheet::sourceURL() const
1415 {
1416     if (!m_sourceURL.isNull())
1417         return m_sourceURL;
1418     if (m_origin != TypeBuilder::CSS::StyleSheetOrigin::Regular) {
1419         m_sourceURL = "";
1420         return m_sourceURL;
1421     }
1422
1423     String styleSheetText;
1424     bool success = getText(&styleSheetText);
1425     if (success) {
1426         bool deprecated;
1427         String commentValue = ContentSearchUtils::findSourceURL(styleSheetText, ContentSearchUtils::CSSMagicComment, &deprecated);
1428         if (!commentValue.isEmpty()) {
1429             // FIXME: add deprecated console message here.
1430             m_sourceURL = commentValue;
1431             return commentValue;
1432         }
1433     }
1434     m_sourceURL = "";
1435     return m_sourceURL;
1436 }
1437
1438 String InspectorStyleSheet::url() const
1439 {
1440     // "sourceURL" is present only for regular rules, otherwise "origin" should be used in the frontend.
1441     if (m_origin != TypeBuilder::CSS::StyleSheetOrigin::Regular)
1442         return String();
1443
1444     CSSStyleSheet* styleSheet = pageStyleSheet();
1445     if (!styleSheet)
1446         return String();
1447
1448     if (hasSourceURL())
1449         return sourceURL();
1450
1451     if (styleSheet->isInline() && startsAtZero())
1452         return String();
1453
1454     return finalURL();
1455 }
1456
1457 bool InspectorStyleSheet::hasSourceURL() const
1458 {
1459     return !sourceURL().isEmpty();
1460 }
1461
1462 bool InspectorStyleSheet::startsAtZero() const
1463 {
1464     CSSStyleSheet* styleSheet = pageStyleSheet();
1465     if (!styleSheet)
1466         return true;
1467
1468     return styleSheet->startPositionInSource() == TextPosition::minimumPosition();
1469 }
1470
1471 String InspectorStyleSheet::sourceMapURL() const
1472 {
1473     if (m_origin != TypeBuilder::CSS::StyleSheetOrigin::Regular)
1474         return String();
1475
1476     String styleSheetText;
1477     bool success = getText(&styleSheetText);
1478     if (success) {
1479         bool deprecated;
1480         String commentValue = ContentSearchUtils::findSourceMapURL(styleSheetText, ContentSearchUtils::CSSMagicComment, &deprecated);
1481         if (!commentValue.isEmpty()) {
1482             // FIXME: add deprecated console message here.
1483             return commentValue;
1484         }
1485     }
1486     return m_pageAgent->resourceSourceMapURL(finalURL());
1487 }
1488
1489 InspectorCSSId InspectorStyleSheet::styleId(CSSStyleDeclaration* style) const
1490 {
1491     unsigned index = ruleIndexByStyle(style);
1492     if (index != UINT_MAX)
1493         return InspectorCSSId(id(), index);
1494     return InspectorCSSId();
1495 }
1496
1497 bool InspectorStyleSheet::findRuleBySelectorRange(const SourceRange& sourceRange, InspectorCSSId* ruleId)
1498 {
1499     if (!ensureParsedDataReady())
1500         return false;
1501     for (size_t i = 0; i < ruleCount(); ++i) {
1502         RefPtrWillBeRawPtr<CSSRuleSourceData> ruleSourceData = ruleSourceDataAt(i);
1503         if (!ruleSourceData->styleSourceData)
1504             continue;
1505         if (ruleSourceData->ruleHeaderRange.start == sourceRange.start && ruleSourceData->ruleHeaderRange.end == sourceRange.end) {
1506             *ruleId = InspectorCSSId(id(), i);
1507             return true;
1508         }
1509     }
1510     return false;
1511 }
1512
1513 const CSSRuleVector& InspectorStyleSheet::flatRules()
1514 {
1515     ensureFlatRules();
1516     return m_flatRules;
1517 }
1518
1519 Document* InspectorStyleSheet::ownerDocument() const
1520 {
1521     return m_pageStyleSheet->ownerDocument();
1522 }
1523
1524 PassRefPtrWillBeRawPtr<CSSRuleSourceData> InspectorStyleSheet::ruleSourceDataAt(unsigned ruleIndex) const
1525 {
1526     return m_parsedStyleSheet->ruleSourceDataAt(ruleIndex);
1527 }
1528
1529 unsigned InspectorStyleSheet::ruleIndexByStyle(CSSStyleDeclaration* pageStyle) const
1530 {
1531     ensureFlatRules();
1532     for (unsigned i = 0, size = m_flatRules.size(); i < size; ++i) {
1533         CSSStyleRule* styleRule = InspectorCSSAgent::asCSSStyleRule(m_flatRules.at(i).get());
1534         if (styleRule && styleRule->style() == pageStyle)
1535             return i;
1536     }
1537     return UINT_MAX;
1538 }
1539
1540 bool InspectorStyleSheet::ensureParsedDataReady()
1541 {
1542     return ensureText() && m_parsedStyleSheet->ensureSourceData();
1543 }
1544
1545 bool InspectorStyleSheet::ensureText() const
1546 {
1547     if (m_parsedStyleSheet->hasText())
1548         return true;
1549
1550     String text;
1551     bool success = originalStyleSheetText(&text);
1552     if (success)
1553         m_parsedStyleSheet->setText(text);
1554     // No need to clear m_flatRules here - it's empty.
1555
1556     return success;
1557 }
1558
1559 static void collectFlatRules(PassRefPtrWillBeRawPtr<CSSRuleList> ruleList, CSSRuleVector* result)
1560 {
1561     if (!ruleList)
1562         return;
1563
1564     for (unsigned i = 0, size = ruleList->length(); i < size; ++i) {
1565         CSSRule* rule = ruleList->item(i);
1566
1567         // The result->append()'ed types should be exactly the same as in ParsedStyleSheet::flattenSourceData().
1568         switch (rule->type()) {
1569         case CSSRule::STYLE_RULE:
1570             result->append(rule);
1571             continue;
1572         case CSSRule::IMPORT_RULE:
1573         case CSSRule::MEDIA_RULE:
1574             result->append(rule);
1575             break;
1576         default:
1577             break;
1578         }
1579         RefPtrWillBeRawPtr<CSSRuleList> childRuleList = asCSSRuleList(rule);
1580         if (childRuleList)
1581             collectFlatRules(childRuleList, result);
1582     }
1583 }
1584
1585 void InspectorStyleSheet::ensureFlatRules() const
1586 {
1587     // We are fine with redoing this for empty stylesheets as this will run fast.
1588     if (m_flatRules.isEmpty())
1589         collectFlatRules(asCSSRuleList(pageStyleSheet()), &m_flatRules);
1590 }
1591
1592 bool InspectorStyleSheet::setStyleText(const InspectorCSSId& id, const String& text)
1593 {
1594     CSSStyleDeclaration* style = styleForId(id);
1595     if (!style)
1596         return false;
1597
1598     if (!ensureParsedDataReady())
1599         return false;
1600
1601     String patchedStyleSheetText;
1602     bool success = styleSheetTextWithChangedStyle(style, text, &patchedStyleSheetText);
1603     if (!success)
1604         return false;
1605
1606     TrackExceptionState exceptionState;
1607     style->setCSSText(text, exceptionState);
1608     if (!exceptionState.hadException()) {
1609         updateText(patchedStyleSheetText);
1610         fireStyleSheetChanged();
1611     }
1612
1613     return !exceptionState.hadException();
1614 }
1615
1616 bool InspectorStyleSheet::styleSheetTextWithChangedStyle(CSSStyleDeclaration* style, const String& newStyleText, String* result)
1617 {
1618     if (!style)
1619         return false;
1620     if (!ensureParsedDataReady())
1621         return false;
1622
1623     RefPtrWillBeRawPtr<CSSRuleSourceData> sourceData = ruleSourceDataAt(styleId(style).ordinal());
1624     unsigned bodyStart = sourceData->ruleBodyRange.start;
1625     unsigned bodyEnd = sourceData->ruleBodyRange.end;
1626     ASSERT(bodyStart <= bodyEnd);
1627
1628     String text = m_parsedStyleSheet->text();
1629     ASSERT_WITH_SECURITY_IMPLICATION(bodyEnd <= text.length()); // bodyEnd is exclusive
1630
1631     text.replace(bodyStart, bodyEnd - bodyStart, newStyleText);
1632     *result = text;
1633     return true;
1634 }
1635
1636 InspectorCSSId InspectorStyleSheet::ruleId(CSSStyleRule* rule) const
1637 {
1638     return styleId(rule->style());
1639 }
1640
1641 bool InspectorStyleSheet::originalStyleSheetText(String* result) const
1642 {
1643     bool success = inlineStyleSheetText(result);
1644     if (!success)
1645         success = resourceStyleSheetText(result);
1646     return success;
1647 }
1648
1649 bool InspectorStyleSheet::resourceStyleSheetText(String* result) const
1650 {
1651     if (m_origin == TypeBuilder::CSS::StyleSheetOrigin::User || m_origin == TypeBuilder::CSS::StyleSheetOrigin::User_agent)
1652         return false;
1653
1654     if (!ownerDocument())
1655         return false;
1656
1657     KURL url(ParsedURLString, m_pageStyleSheet->href());
1658     if (m_pageAgent->getEditedResourceContent(url, result))
1659         return true;
1660
1661     bool base64Encoded;
1662     bool success = m_resourceAgent->fetchResourceContent(ownerDocument(), url, result, &base64Encoded);
1663     return success && !base64Encoded;
1664 }
1665
1666 Element* InspectorStyleSheet::ownerStyleElement() const
1667 {
1668     Node* ownerNode = m_pageStyleSheet->ownerNode();
1669     if (!ownerNode || !ownerNode->isElementNode())
1670         return 0;
1671     Element* ownerElement = toElement(ownerNode);
1672
1673     if (!isHTMLStyleElement(ownerElement) && !isSVGStyleElement(ownerElement))
1674         return 0;
1675     return ownerElement;
1676 }
1677
1678 bool InspectorStyleSheet::inlineStyleSheetText(String* result) const
1679 {
1680     Element* ownerElement = ownerStyleElement();
1681     if (!ownerElement)
1682         return false;
1683     *result = ownerElement->textContent();
1684     return true;
1685 }
1686
1687 PassRefPtrWillBeRawPtr<InspectorStyleSheetForInlineStyle> InspectorStyleSheetForInlineStyle::create(const String& id, PassRefPtrWillBeRawPtr<Element> element, Listener* listener)
1688 {
1689     return adoptRefWillBeNoop(new InspectorStyleSheetForInlineStyle(id, element, listener));
1690 }
1691
1692 InspectorStyleSheetForInlineStyle::InspectorStyleSheetForInlineStyle(const String& id, PassRefPtrWillBeRawPtr<Element> element, Listener* listener)
1693     : InspectorStyleSheetBase(id, listener)
1694     , m_element(element)
1695     , m_ruleSourceData(nullptr)
1696     , m_isStyleTextValid(false)
1697 {
1698     ASSERT(m_element);
1699     m_inspectorStyle = InspectorStyle::create(InspectorCSSId(id, 0), inlineStyle(), this);
1700     m_styleText = m_element->isStyledElement() ? m_element->getAttribute("style").string() : String();
1701 }
1702
1703 void InspectorStyleSheetForInlineStyle::didModifyElementAttribute()
1704 {
1705     m_isStyleTextValid = false;
1706     if (m_element->isStyledElement() && m_element->style() != m_inspectorStyle->cssStyle())
1707         m_inspectorStyle = InspectorStyle::create(InspectorCSSId(id(), 0), inlineStyle(), this);
1708     m_ruleSourceData.clear();
1709 }
1710
1711 bool InspectorStyleSheetForInlineStyle::setText(const String& text, ExceptionState& exceptionState)
1712 {
1713     bool success = setStyleText(InspectorCSSId(id(), 0), text);
1714     if (!success)
1715         exceptionState.throwDOMException(SyntaxError, "Style sheet text is invalid.");
1716     else
1717         fireStyleSheetChanged();
1718     return success;
1719 }
1720
1721 bool InspectorStyleSheetForInlineStyle::getText(String* result) const
1722 {
1723     if (!m_isStyleTextValid) {
1724         m_styleText = elementStyleText();
1725         m_isStyleTextValid = true;
1726     }
1727     *result = m_styleText;
1728     return true;
1729 }
1730
1731 bool InspectorStyleSheetForInlineStyle::setStyleText(const InspectorCSSId& id, const String& text)
1732 {
1733     CSSStyleDeclaration* style = styleForId(id);
1734     if (!style)
1735         return false;
1736     ASSERT_UNUSED(style, style == inlineStyle());
1737     TrackExceptionState exceptionState;
1738
1739     {
1740         InspectorCSSAgent::InlineStyleOverrideScope overrideScope(m_element->ownerDocument());
1741         m_element->setAttribute("style", AtomicString(text), exceptionState);
1742     }
1743     if (!exceptionState.hadException()) {
1744         m_styleText = text;
1745         m_isStyleTextValid = true;
1746         m_ruleSourceData.clear();
1747         fireStyleSheetChanged();
1748     }
1749     return !exceptionState.hadException();
1750 }
1751
1752 Document* InspectorStyleSheetForInlineStyle::ownerDocument() const
1753 {
1754     return &m_element->document();
1755 }
1756
1757 bool InspectorStyleSheetForInlineStyle::ensureParsedDataReady()
1758 {
1759     // The "style" property value can get changed indirectly, e.g. via element.style.borderWidth = "2px".
1760     const String& currentStyleText = elementStyleText();
1761     if (m_styleText != currentStyleText) {
1762         m_ruleSourceData.clear();
1763         m_styleText = currentStyleText;
1764         m_isStyleTextValid = true;
1765     }
1766
1767     if (m_ruleSourceData)
1768         return true;
1769
1770     m_ruleSourceData = getStyleAttributeData();
1771
1772     bool success = !!m_ruleSourceData;
1773     if (!success) {
1774         m_ruleSourceData = CSSRuleSourceData::create(CSSRuleSourceData::STYLE_RULE);
1775         return false;
1776     }
1777
1778     return true;
1779 }
1780
1781 PassRefPtrWillBeRawPtr<InspectorStyle> InspectorStyleSheetForInlineStyle::inspectorStyleForId(const InspectorCSSId& id)
1782 {
1783     ASSERT_UNUSED(id, !id.ordinal());
1784     return m_inspectorStyle;
1785 }
1786
1787 CSSStyleDeclaration* InspectorStyleSheetForInlineStyle::inlineStyle() const
1788 {
1789     return m_element->style();
1790 }
1791
1792 const String& InspectorStyleSheetForInlineStyle::elementStyleText() const
1793 {
1794     return m_element->getAttribute("style").string();
1795 }
1796
1797 PassRefPtrWillBeRawPtr<CSSRuleSourceData> InspectorStyleSheetForInlineStyle::getStyleAttributeData() const
1798 {
1799     if (!m_element->isStyledElement())
1800         return nullptr;
1801
1802     if (m_styleText.isEmpty()) {
1803         RefPtrWillBeRawPtr<CSSRuleSourceData> result = CSSRuleSourceData::create(CSSRuleSourceData::STYLE_RULE);
1804         result->ruleBodyRange.start = 0;
1805         result->ruleBodyRange.end = 0;
1806         return result.release();
1807     }
1808
1809     RefPtrWillBeRawPtr<MutableStylePropertySet> tempDeclaration = MutableStylePropertySet::create();
1810     RuleSourceDataList ruleSourceDataResult;
1811     StyleSheetHandler handler(m_styleText, &m_element->document(), m_element->document().elementSheet().contents(), &ruleSourceDataResult);
1812     BisonCSSParser(parserContextForDocument(&m_element->document())).parseDeclaration(tempDeclaration.get(), m_styleText, &handler, m_element->document().elementSheet().contents());
1813     return ruleSourceDataResult.first().release();
1814 }
1815
1816 void InspectorStyleSheetForInlineStyle::trace(Visitor* visitor)
1817 {
1818     visitor->trace(m_element);
1819     visitor->trace(m_ruleSourceData);
1820     visitor->trace(m_inspectorStyle);
1821     InspectorStyleSheetBase::trace(visitor);
1822 }
1823
1824 } // namespace blink
1825