• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010, Google Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1.  Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  * 2.  Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16  * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
17  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  */
24 
25 #include "config.h"
26 #include "InspectorStyleSheet.h"
27 
28 #if ENABLE(INSPECTOR)
29 
30 #include "CSSImportRule.h"
31 #include "CSSMediaRule.h"
32 #include "CSSParser.h"
33 #include "CSSPropertySourceData.h"
34 #include "CSSRule.h"
35 #include "CSSRuleList.h"
36 #include "CSSStyleRule.h"
37 #include "CSSStyleSelector.h"
38 #include "CSSStyleSheet.h"
39 #include "Document.h"
40 #include "Element.h"
41 #include "HTMLHeadElement.h"
42 #include "HTMLParserIdioms.h"
43 #include "InspectorCSSAgent.h"
44 #include "InspectorResourceAgent.h"
45 #include "InspectorValues.h"
46 #include "Node.h"
47 #include "StyleSheetList.h"
48 #include "WebKitCSSKeyframesRule.h"
49 
50 #include <wtf/OwnPtr.h>
51 #include <wtf/PassOwnPtr.h>
52 #include <wtf/Vector.h>
53 
54 class ParsedStyleSheet {
55 public:
56     typedef Vector<RefPtr<WebCore::CSSRuleSourceData> > SourceData;
57     ParsedStyleSheet();
58 
cssStyleSheet() const59     WebCore::CSSStyleSheet* cssStyleSheet() const { return m_parserOutput; }
text() const60     const String& text() const { return m_text; }
61     void setText(const String& text);
hasText() const62     bool hasText() const { return m_hasText; }
sourceData() const63     SourceData* sourceData() const { return m_sourceData.get(); }
64     void setSourceData(PassOwnPtr<SourceData> sourceData);
hasSourceData() const65     bool hasSourceData() const { return m_sourceData; }
66     RefPtr<WebCore::CSSRuleSourceData> ruleSourceDataAt(unsigned index) const;
67 
68 private:
69 
70     // StyleSheet constructed while parsing m_text.
71     WebCore::CSSStyleSheet* m_parserOutput;
72     String m_text;
73     bool m_hasText;
74     OwnPtr<SourceData> m_sourceData;
75 };
76 
ParsedStyleSheet()77 ParsedStyleSheet::ParsedStyleSheet()
78     : m_parserOutput(0)
79     , m_hasText(false)
80 {
81 }
82 
setText(const String & text)83 void ParsedStyleSheet::setText(const String& text)
84 {
85     m_hasText = true;
86     m_text = text;
87     setSourceData(0);
88 }
89 
setSourceData(PassOwnPtr<SourceData> sourceData)90 void ParsedStyleSheet::setSourceData(PassOwnPtr<SourceData> sourceData)
91 {
92     m_sourceData = sourceData;
93 }
94 
ruleSourceDataAt(unsigned index) const95 RefPtr<WebCore::CSSRuleSourceData> ParsedStyleSheet::ruleSourceDataAt(unsigned index) const
96 {
97     if (!hasSourceData() || index >= m_sourceData->size())
98         return 0;
99 
100     return m_sourceData->at(index);
101 }
102 
103 namespace WebCore {
104 
buildSourceRangeObject(const SourceRange & range)105 static PassRefPtr<InspectorObject> buildSourceRangeObject(const SourceRange& range)
106 {
107     RefPtr<InspectorObject> result = InspectorObject::create();
108     result->setNumber("start", range.start);
109     result->setNumber("end", range.end);
110     return result.release();
111 }
112 
asCSSRuleList(StyleBase * styleBase)113 static PassRefPtr<CSSRuleList> asCSSRuleList(StyleBase* styleBase)
114 {
115     if (!styleBase)
116         return 0;
117 
118     if (styleBase->isCSSStyleSheet())
119         return CSSRuleList::create(static_cast<CSSStyleSheet*>(styleBase), true);
120     if (styleBase->isRule()) {
121         unsigned ruleType = static_cast<CSSRule*>(styleBase)->type();
122         RefPtr<CSSRuleList> result = 0;
123 
124         switch (ruleType) {
125         case CSSRule::MEDIA_RULE:
126             result = static_cast<CSSMediaRule*>(styleBase)->cssRules();
127             break;
128         case CSSRule::WEBKIT_KEYFRAMES_RULE:
129             result = static_cast<WebKitCSSKeyframesRule*>(styleBase)->cssRules();
130             break;
131         case CSSRule::IMPORT_RULE:
132         case CSSRule::PAGE_RULE:
133         default:
134             return 0;
135         }
136 
137         return result.release();
138     }
139     return 0;
140 }
141 
create(const InspectorCSSId & styleId,PassRefPtr<CSSStyleDeclaration> style,InspectorStyleSheet * parentStyleSheet)142 PassRefPtr<InspectorStyle> InspectorStyle::create(const InspectorCSSId& styleId, PassRefPtr<CSSStyleDeclaration> style, InspectorStyleSheet* parentStyleSheet)
143 {
144     return adoptRef(new InspectorStyle(styleId, style, parentStyleSheet));
145 }
146 
InspectorStyle(const InspectorCSSId & styleId,PassRefPtr<CSSStyleDeclaration> style,InspectorStyleSheet * parentStyleSheet)147 InspectorStyle::InspectorStyle(const InspectorCSSId& styleId, PassRefPtr<CSSStyleDeclaration> style, InspectorStyleSheet* parentStyleSheet)
148     : m_styleId(styleId)
149     , m_style(style)
150     , m_parentStyleSheet(parentStyleSheet)
151 {
152     ASSERT(m_style);
153 }
154 
~InspectorStyle()155 InspectorStyle::~InspectorStyle()
156 {
157 }
158 
buildObjectForStyle() const159 PassRefPtr<InspectorObject> InspectorStyle::buildObjectForStyle() const
160 {
161     RefPtr<InspectorObject> result = InspectorObject::create();
162     if (!m_styleId.isEmpty())
163         result->setValue("styleId", m_styleId.asInspectorValue());
164 
165     result->setString("width", m_style->getPropertyValue("width"));
166     result->setString("height", m_style->getPropertyValue("height"));
167 
168     RefPtr<CSSRuleSourceData> sourceData = m_parentStyleSheet ? m_parentStyleSheet->ruleSourceDataFor(m_style.get()) : 0;
169     if (sourceData)
170         result->setObject("range", buildSourceRangeObject(sourceData->styleSourceData->styleBodyRange));
171 
172     populateObjectWithStyleProperties(result.get());
173 
174     return result.release();
175 }
176 
177 // This method does the following preprocessing of |propertyText| with |overwrite| == false and |index| past the last active property:
178 // - If the last property (if present) has no subsequent whitespace in the style declaration, a space is prepended to |propertyText|.
179 // - If the last property (if present) has no closing ";", the ";" is prepended to the current |propertyText| value.
180 //
181 // The propertyText (if not empty) is checked to be a valid style declaration (containing at least one property). If not,
182 // the method returns false (denoting an error).
setPropertyText(ErrorString * errorString,unsigned index,const String & propertyText,bool overwrite)183 bool InspectorStyle::setPropertyText(ErrorString* errorString, unsigned index, const String& propertyText, bool overwrite)
184 {
185     ASSERT(m_parentStyleSheet);
186     if (!m_parentStyleSheet->ensureParsedDataReady()) {
187         *errorString = "Internal error: no stylesheet parsed data available";
188         return false;
189     }
190 
191     Vector<InspectorStyleProperty> allProperties;
192     populateAllProperties(&allProperties);
193 
194     unsigned propertyStart = 0; // Need to initialize to make the compiler happy.
195     long propertyLengthDelta;
196 
197     if (propertyText.stripWhiteSpace().length()) {
198         RefPtr<CSSMutableStyleDeclaration> tempMutableStyle = CSSMutableStyleDeclaration::create();
199         CSSParser p;
200         RefPtr<CSSStyleSourceData> sourceData = CSSStyleSourceData::create();
201         p.parseDeclaration(tempMutableStyle.get(), propertyText + " -webkit-boguz-propertee: none", &sourceData);
202         Vector<CSSPropertySourceData>& propertyData = sourceData->propertyData;
203         unsigned propertyCount = propertyData.size();
204 
205         // At least one property + the bogus property added just above should be present.
206         if (propertyCount < 2) {
207             *errorString = "Invalid property value";
208             return false;
209         }
210 
211         // Check for a proper propertyText termination (the parser could at least restore to the PROPERTY_NAME state).
212         if (propertyData.at(propertyCount - 1).name != "-webkit-boguz-propertee") {
213             *errorString = "Invalid property value";
214             return false;
215         }
216     }
217 
218     if (overwrite) {
219         ASSERT(index < allProperties.size());
220         InspectorStyleProperty& property = allProperties.at(index);
221         propertyStart = property.sourceData.range.start;
222         unsigned propertyEnd = property.sourceData.range.end;
223         unsigned oldLength = propertyEnd - propertyStart;
224         unsigned newLength = propertyText.length();
225         propertyLengthDelta = newLength - oldLength;
226 
227         if (!property.disabled) {
228             bool success = replacePropertyInStyleText(property, propertyText);
229             if (!success) {
230                 *errorString = "Internal error: could not replace property value";
231                 return false;
232             }
233         } else {
234             unsigned textLength = propertyText.length();
235             unsigned disabledIndex = disabledIndexByOrdinal(index, false, allProperties);
236             if (!textLength) {
237                 // Delete disabled property.
238                 m_disabledProperties.remove(disabledIndex);
239             } else {
240                 // Patch disabled property text.
241                 m_disabledProperties.at(disabledIndex).rawText = propertyText;
242             }
243 
244             // We should not shift subsequent disabled properties when altering a disabled property.
245             return true;
246         }
247     } else {
248         // Insert at index.
249         RefPtr<CSSRuleSourceData> sourceData = m_parentStyleSheet->ruleSourceDataFor(m_style.get());
250         if (!sourceData) {
251             *errorString = "Internal error: no CSS rule source found";
252             return false;
253         }
254         String text;
255         bool success = styleText(&text);
256         if (!success) {
257             *errorString = "Internal error: could not fetch style text";
258             return false;
259         }
260         propertyLengthDelta = propertyText.length();
261 
262         bool insertLast = true;
263         if (index < allProperties.size()) {
264             InspectorStyleProperty& property = allProperties.at(index);
265             if (property.hasSource) {
266                 propertyStart = property.sourceData.range.start;
267                 // If inserting before a disabled property, it should be shifted, too.
268                 insertLast = false;
269             }
270         }
271 
272         String textToSet = propertyText;
273         if (insertLast) {
274             propertyStart = sourceData->styleSourceData->styleBodyRange.end - sourceData->styleSourceData->styleBodyRange.start;
275             if (propertyStart && propertyText.length()) {
276                 const UChar* characters = text.characters();
277 
278                 unsigned curPos = propertyStart - 1; // The last position of style declaration, since propertyStart points past one.
279                 while (curPos && isHTMLSpace(characters[curPos]))
280                     --curPos;
281                 if (curPos && characters[curPos] != ';') {
282                     // Prepend a ";" to the property text if appending to a style declaration where
283                     // the last property has no trailing ";".
284                     textToSet.insert("; ", 0);
285                 } else if (!isHTMLSpace(characters[propertyStart - 1])) {
286                     // Prepend a " " if the last declaration character is not an HTML space.
287                     textToSet.insert(" ", 0);
288                 }
289             }
290         }
291 
292         text.insert(textToSet, propertyStart);
293         m_parentStyleSheet->setStyleText(m_style.get(), text);
294     }
295 
296     // Recompute subsequent disabled property ranges if acting on a non-disabled property.
297     shiftDisabledProperties(disabledIndexByOrdinal(index, true, allProperties), propertyLengthDelta);
298 
299     return true;
300 }
301 
toggleProperty(ErrorString * errorString,unsigned index,bool disable)302 bool InspectorStyle::toggleProperty(ErrorString* errorString, unsigned index, bool disable)
303 {
304     ASSERT(m_parentStyleSheet);
305     if (!m_parentStyleSheet->ensureParsedDataReady()) {
306         *errorString = "Can toggle only source-based properties";
307         return false;
308     }
309     RefPtr<CSSRuleSourceData> sourceData = m_parentStyleSheet->ruleSourceDataFor(m_style.get());
310     if (!sourceData) {
311         *errorString = "Internal error: No source data for the style found";
312         return false;
313     }
314 
315     Vector<InspectorStyleProperty> allProperties;
316     populateAllProperties(&allProperties);
317     if (index >= allProperties.size()) {
318         *errorString = "Property index is outside of property range";
319         return false;
320     }
321 
322     InspectorStyleProperty& property = allProperties.at(index);
323     if (property.disabled == disable)
324         return true; // Idempotent operation.
325 
326     bool success;
327     if (!disable)
328         success = enableProperty(index, allProperties);
329     else
330         success = disableProperty(index, allProperties);
331 
332     return success;
333 }
334 
335 // static
disabledIndexByOrdinal(unsigned ordinal,bool canUseSubsequent,Vector<InspectorStyleProperty> & allProperties)336 unsigned InspectorStyle::disabledIndexByOrdinal(unsigned ordinal, bool canUseSubsequent, Vector<InspectorStyleProperty>& allProperties)
337 {
338     unsigned disabledIndex = 0;
339     for (unsigned i = 0, size = allProperties.size(); i < size; ++i) {
340         InspectorStyleProperty& property = allProperties.at(i);
341         if (property.disabled) {
342             if (i == ordinal || (canUseSubsequent && i > ordinal))
343                 return disabledIndex;
344             ++disabledIndex;
345         }
346     }
347 
348     return UINT_MAX;
349 }
350 
styleText(String * result) const351 bool InspectorStyle::styleText(String* result) const
352 {
353     // Precondition: m_parentStyleSheet->ensureParsedDataReady() has been called successfully.
354     RefPtr<CSSRuleSourceData> sourceData = m_parentStyleSheet->ruleSourceDataFor(m_style.get());
355     if (!sourceData)
356         return false;
357 
358     String styleSheetText;
359     bool success = m_parentStyleSheet->text(&styleSheetText);
360     if (!success)
361         return false;
362 
363     SourceRange& bodyRange = sourceData->styleSourceData->styleBodyRange;
364     *result = styleSheetText.substring(bodyRange.start, bodyRange.end - bodyRange.start);
365     return true;
366 }
367 
disableProperty(unsigned indexToDisable,Vector<InspectorStyleProperty> & allProperties)368 bool InspectorStyle::disableProperty(unsigned indexToDisable, Vector<InspectorStyleProperty>& allProperties)
369 {
370     // Precondition: |indexToEnable| points to an enabled property.
371     const InspectorStyleProperty& property = allProperties.at(indexToDisable);
372     unsigned propertyStart = property.sourceData.range.start;
373     InspectorStyleProperty disabledProperty(property);
374     String oldStyleText;
375     bool success = styleText(&oldStyleText);
376     if (!success)
377         return false;
378     disabledProperty.setRawTextFromStyleDeclaration(oldStyleText);
379     disabledProperty.disabled = true;
380     disabledProperty.sourceData.range.end = propertyStart;
381     // This may have to be negated below.
382     long propertyLength = property.sourceData.range.end - propertyStart;
383     success = replacePropertyInStyleText(property, "");
384     if (!success)
385         return false;
386 
387     // Add disabled property at correct position.
388     unsigned insertionIndex = disabledIndexByOrdinal(indexToDisable, true, allProperties);
389     if (insertionIndex == UINT_MAX)
390         m_disabledProperties.append(disabledProperty);
391     else {
392         m_disabledProperties.insert(insertionIndex, disabledProperty);
393         shiftDisabledProperties(insertionIndex + 1, -propertyLength); // Property removed from text - shift these back.
394     }
395     return true;
396 }
397 
enableProperty(unsigned indexToEnable,Vector<InspectorStyleProperty> & allProperties)398 bool InspectorStyle::enableProperty(unsigned indexToEnable, Vector<InspectorStyleProperty>& allProperties)
399 {
400     // Precondition: |indexToEnable| points to a disabled property.
401     unsigned disabledIndex = disabledIndexByOrdinal(indexToEnable, false, allProperties);
402     if (disabledIndex == UINT_MAX)
403         return false;
404 
405     InspectorStyleProperty disabledProperty = m_disabledProperties.at(disabledIndex);
406     m_disabledProperties.remove(disabledIndex);
407     bool success = replacePropertyInStyleText(disabledProperty, disabledProperty.rawText);
408     if (success)
409         shiftDisabledProperties(disabledIndex, disabledProperty.rawText.length());
410     return success;
411 }
412 
populateAllProperties(Vector<InspectorStyleProperty> * result) const413 bool InspectorStyle::populateAllProperties(Vector<InspectorStyleProperty>* result) const
414 {
415     HashSet<String> foundShorthands;
416     HashSet<String> sourcePropertyNames;
417     unsigned disabledIndex = 0;
418     unsigned disabledLength = m_disabledProperties.size();
419     InspectorStyleProperty disabledProperty;
420     if (disabledIndex < disabledLength)
421         disabledProperty = m_disabledProperties.at(disabledIndex);
422 
423     RefPtr<CSSRuleSourceData> sourceData = (m_parentStyleSheet && m_parentStyleSheet->ensureParsedDataReady()) ? m_parentStyleSheet->ruleSourceDataFor(m_style.get()) : 0;
424     Vector<CSSPropertySourceData>* sourcePropertyData = sourceData ? &(sourceData->styleSourceData->propertyData) : 0;
425     if (sourcePropertyData) {
426         String styleDeclaration;
427         bool isStyleTextKnown = styleText(&styleDeclaration);
428         ASSERT_UNUSED(isStyleTextKnown, isStyleTextKnown);
429         for (Vector<CSSPropertySourceData>::const_iterator it = sourcePropertyData->begin(); it != sourcePropertyData->end(); ++it) {
430             while (disabledIndex < disabledLength && disabledProperty.sourceData.range.start <= it->range.start) {
431                 result->append(disabledProperty);
432                 if (++disabledIndex < disabledLength)
433                     disabledProperty = m_disabledProperties.at(disabledIndex);
434             }
435             InspectorStyleProperty p(*it, true, false);
436             p.setRawTextFromStyleDeclaration(styleDeclaration);
437             result->append(p);
438             sourcePropertyNames.add(it->name.lower());
439         }
440     }
441 
442     while (disabledIndex < disabledLength) {
443         disabledProperty = m_disabledProperties.at(disabledIndex++);
444         result->append(disabledProperty);
445     }
446 
447     for (int i = 0, size = m_style->length(); i < size; ++i) {
448         String name = m_style->item(i);
449         if (sourcePropertyNames.contains(name.lower()))
450             continue;
451 
452         sourcePropertyNames.add(name.lower());
453         result->append(InspectorStyleProperty(CSSPropertySourceData(name, m_style->getPropertyValue(name), !m_style->getPropertyPriority(name).isEmpty(), true, SourceRange()), false, false));
454     }
455 
456     return true;
457 }
458 
populateObjectWithStyleProperties(InspectorObject * result) const459 void InspectorStyle::populateObjectWithStyleProperties(InspectorObject* result) const
460 {
461     Vector<InspectorStyleProperty> properties;
462     populateAllProperties(&properties);
463 
464     RefPtr<InspectorArray> propertiesObject = InspectorArray::create();
465     RefPtr<InspectorArray> shorthandEntries = InspectorArray::create();
466     HashMap<String, RefPtr<InspectorObject> > propertyNameToPreviousActiveProperty;
467     HashSet<String> foundShorthands;
468 
469     for (Vector<InspectorStyleProperty>::iterator it = properties.begin(), itEnd = properties.end(); it != itEnd; ++it) {
470         const CSSPropertySourceData& propertyEntry = it->sourceData;
471         const String& name = propertyEntry.name;
472 
473         RefPtr<InspectorObject> property = InspectorObject::create();
474         propertiesObject->pushObject(property);
475         String status = it->disabled ? "disabled" : "active";
476 
477         // Default "parsedOk" == true.
478         if (!propertyEntry.parsedOk)
479             property->setBoolean("parsedOk", false);
480         if (it->hasRawText())
481             property->setString("text", it->rawText);
482         property->setString("name", name);
483         property->setString("value", propertyEntry.value);
484 
485         // Default "priority" == "".
486         if (propertyEntry.important)
487             property->setString("priority", "important");
488         if (!it->disabled) {
489             if (it->hasSource) {
490                 property->setBoolean("implicit", false);
491                 property->setObject("range", buildSourceRangeObject(propertyEntry.range));
492 
493                 // Parsed property overrides any property with the same name. Non-parsed property overrides
494                 // previous non-parsed property with the same name (if any).
495                 bool shouldInactivate = false;
496                 HashMap<String, RefPtr<InspectorObject> >::iterator activeIt = propertyNameToPreviousActiveProperty.find(name);
497                 if (activeIt != propertyNameToPreviousActiveProperty.end()) {
498                     if (propertyEntry.parsedOk)
499                         shouldInactivate = true;
500                     else {
501                         bool previousParsedOk;
502                         bool success = activeIt->second->getBoolean("parsedOk", &previousParsedOk);
503                         if (success && !previousParsedOk)
504                             shouldInactivate = true;
505                     }
506                 } else
507                     propertyNameToPreviousActiveProperty.set(name, property);
508 
509                 if (shouldInactivate) {
510                     activeIt->second->setString("status", "inactive");
511                     activeIt->second->remove("shorthandName");
512                     propertyNameToPreviousActiveProperty.set(name, property);
513                 }
514             } else {
515                 bool implicit = m_style->isPropertyImplicit(name);
516                 // Default "implicit" == false.
517                 if (implicit)
518                     property->setBoolean("implicit", true);
519                 status = "";
520             }
521         }
522 
523         // Default "status" == "style".
524         if (!status.isEmpty())
525             property->setString("status", status);
526 
527         if (propertyEntry.parsedOk) {
528             // Both for style-originated and parsed source properties.
529             String shorthand = m_style->getPropertyShorthand(name);
530             if (!shorthand.isEmpty()) {
531                 // Default "shorthandName" == "".
532                 property->setString("shorthandName", shorthand);
533                 if (!foundShorthands.contains(shorthand)) {
534                     foundShorthands.add(shorthand);
535                     RefPtr<InspectorObject> shorthandEntry = InspectorObject::create();
536                     shorthandEntry->setString("name", shorthand);
537                     shorthandEntry->setString("value", shorthandValue(shorthand));
538                     shorthandEntries->pushObject(shorthandEntry.release());
539                 }
540             }
541         }
542         // else shorthandName is not set
543     }
544 
545     result->setArray("cssProperties", propertiesObject);
546     result->setArray("shorthandEntries", shorthandEntries);
547 }
548 
549 
shiftDisabledProperties(unsigned fromIndex,long delta)550 void InspectorStyle::shiftDisabledProperties(unsigned fromIndex, long delta)
551 {
552     for (unsigned i = fromIndex, size = m_disabledProperties.size(); i < size; ++i) {
553         SourceRange& range = m_disabledProperties.at(i).sourceData.range;
554         range.start += delta;
555         range.end += delta;
556     }
557 }
558 
replacePropertyInStyleText(const InspectorStyleProperty & property,const String & newText)559 bool InspectorStyle::replacePropertyInStyleText(const InspectorStyleProperty& property, const String& newText)
560 {
561     // Precondition: m_parentStyleSheet->ensureParsedDataReady() has been called successfully.
562     String text;
563     bool success = styleText(&text);
564     if (!success)
565         return false;
566     const SourceRange& range = property.sourceData.range;
567     text.replace(range.start, range.end - range.start, newText);
568     success = m_parentStyleSheet->setStyleText(m_style.get(), text);
569     return success;
570 }
571 
shorthandValue(const String & shorthandProperty) const572 String InspectorStyle::shorthandValue(const String& shorthandProperty) const
573 {
574     String value = m_style->getPropertyValue(shorthandProperty);
575     if (value.isEmpty()) {
576         for (unsigned i = 0; i < m_style->length(); ++i) {
577             String individualProperty = m_style->item(i);
578             if (m_style->getPropertyShorthand(individualProperty) != shorthandProperty)
579                 continue;
580             if (m_style->isPropertyImplicit(individualProperty))
581                 continue;
582             String individualValue = m_style->getPropertyValue(individualProperty);
583             if (individualValue == "initial")
584                 continue;
585             if (value.length())
586                 value.append(" ");
587             value.append(individualValue);
588         }
589     }
590     return value;
591 }
592 
shorthandPriority(const String & shorthandProperty) const593 String InspectorStyle::shorthandPriority(const String& shorthandProperty) const
594 {
595     String priority = m_style->getPropertyPriority(shorthandProperty);
596     if (priority.isEmpty()) {
597         for (unsigned i = 0; i < m_style->length(); ++i) {
598             String individualProperty = m_style->item(i);
599             if (m_style->getPropertyShorthand(individualProperty) != shorthandProperty)
600                 continue;
601             priority = m_style->getPropertyPriority(individualProperty);
602             break;
603         }
604     }
605     return priority;
606 }
607 
longhandProperties(const String & shorthandProperty) const608 Vector<String> InspectorStyle::longhandProperties(const String& shorthandProperty) const
609 {
610     Vector<String> properties;
611     HashSet<String> foundProperties;
612     for (unsigned i = 0; i < m_style->length(); ++i) {
613         String individualProperty = m_style->item(i);
614         if (foundProperties.contains(individualProperty) || m_style->getPropertyShorthand(individualProperty) != shorthandProperty)
615             continue;
616 
617         foundProperties.add(individualProperty);
618         properties.append(individualProperty);
619     }
620     return properties;
621 }
622 
create(const String & id,PassRefPtr<CSSStyleSheet> pageStyleSheet,const String & origin,const String & documentURL)623 PassRefPtr<InspectorStyleSheet> InspectorStyleSheet::create(const String& id, PassRefPtr<CSSStyleSheet> pageStyleSheet, const String& origin, const String& documentURL)
624 {
625     return adoptRef(new InspectorStyleSheet(id, pageStyleSheet, origin, documentURL));
626 }
627 
InspectorStyleSheet(const String & id,PassRefPtr<CSSStyleSheet> pageStyleSheet,const String & origin,const String & documentURL)628 InspectorStyleSheet::InspectorStyleSheet(const String& id, PassRefPtr<CSSStyleSheet> pageStyleSheet, const String& origin, const String& documentURL)
629     : m_id(id)
630     , m_pageStyleSheet(pageStyleSheet)
631     , m_origin(origin)
632     , m_documentURL(documentURL)
633     , m_isRevalidating(false)
634 {
635     m_parsedStyleSheet = new ParsedStyleSheet();
636 }
637 
~InspectorStyleSheet()638 InspectorStyleSheet::~InspectorStyleSheet()
639 {
640     delete m_parsedStyleSheet;
641 }
642 
finalURL() const643 String InspectorStyleSheet::finalURL() const
644 {
645     if (m_pageStyleSheet && !m_pageStyleSheet->finalURL().isEmpty())
646         return m_pageStyleSheet->finalURL().string();
647     return m_documentURL;
648 }
649 
reparseStyleSheet(const String & text)650 void InspectorStyleSheet::reparseStyleSheet(const String& text)
651 {
652     for (unsigned i = 0, size = m_pageStyleSheet->length(); i < size; ++i)
653         m_pageStyleSheet->remove(0);
654     m_pageStyleSheet->parseString(text, m_pageStyleSheet->useStrictParsing());
655     m_pageStyleSheet->styleSheetChanged();
656     m_inspectorStyles.clear();
657 }
658 
setText(const String & text)659 bool InspectorStyleSheet::setText(const String& text)
660 {
661     if (!m_parsedStyleSheet)
662         return false;
663 
664     m_parsedStyleSheet->setText(text);
665     m_flatRules.clear();
666 
667     return true;
668 }
669 
setRuleSelector(const InspectorCSSId & id,const String & selector)670 bool InspectorStyleSheet::setRuleSelector(const InspectorCSSId& id, const String& selector)
671 {
672     CSSStyleRule* rule = ruleForId(id);
673     if (!rule)
674         return false;
675     CSSStyleSheet* styleSheet = InspectorCSSAgent::parentStyleSheet(rule);
676     if (!styleSheet || !ensureParsedDataReady())
677         return false;
678 
679     rule->setSelectorText(selector);
680     RefPtr<CSSRuleSourceData> sourceData = ruleSourceDataFor(rule->style());
681     if (!sourceData)
682         return false;
683 
684     String sheetText = m_parsedStyleSheet->text();
685     sheetText.replace(sourceData->selectorListRange.start, sourceData->selectorListRange.end - sourceData->selectorListRange.start, selector);
686     m_parsedStyleSheet->setText(sheetText);
687     return true;
688 }
689 
addRule(const String & selector)690 CSSStyleRule* InspectorStyleSheet::addRule(const String& selector)
691 {
692     String styleSheetText;
693     bool success = text(&styleSheetText);
694     if (!success)
695         return 0;
696 
697     ExceptionCode ec = 0;
698     m_pageStyleSheet->addRule(selector, "", ec);
699     if (ec)
700         return 0;
701     RefPtr<CSSRuleList> rules = m_pageStyleSheet->cssRules();
702     ASSERT(rules->length());
703     CSSStyleRule* rule = InspectorCSSAgent::asCSSStyleRule(rules->item(rules->length() - 1));
704     ASSERT(rule);
705 
706     if (styleSheetText.length())
707         styleSheetText += "\n";
708 
709     styleSheetText += selector;
710     styleSheetText += " {}";
711     // Using setText() as this operation changes the style sheet rule set.
712     setText(styleSheetText);
713 
714     return rule;
715 }
716 
ruleForId(const InspectorCSSId & id) const717 CSSStyleRule* InspectorStyleSheet::ruleForId(const InspectorCSSId& id) const
718 {
719     if (!m_pageStyleSheet)
720         return 0;
721 
722     ASSERT(!id.isEmpty());
723     ensureFlatRules();
724     return id.ordinal() >= m_flatRules.size() ? 0 : m_flatRules.at(id.ordinal());
725 
726 }
727 
buildObjectForStyleSheet()728 PassRefPtr<InspectorObject> InspectorStyleSheet::buildObjectForStyleSheet()
729 {
730     CSSStyleSheet* styleSheet = pageStyleSheet();
731     if (!styleSheet)
732         return 0;
733 
734     RefPtr<InspectorObject> result = InspectorObject::create();
735     result->setString("styleSheetId", id());
736     RefPtr<CSSRuleList> cssRuleList = CSSRuleList::create(styleSheet, true);
737     RefPtr<InspectorArray> cssRules = buildArrayForRuleList(cssRuleList.get());
738     result->setArray("rules", cssRules.release());
739 
740     String styleSheetText;
741     bool success = text(&styleSheetText);
742     if (success)
743         result->setString("text", styleSheetText);
744 
745     return result.release();
746 }
747 
buildObjectForStyleSheetInfo()748 PassRefPtr<InspectorObject> InspectorStyleSheet::buildObjectForStyleSheetInfo()
749 {
750     CSSStyleSheet* styleSheet = pageStyleSheet();
751     if (!styleSheet)
752         return 0;
753 
754     RefPtr<InspectorObject> result = InspectorObject::create();
755     result->setString("styleSheetId", id());
756     result->setBoolean("disabled", styleSheet->disabled());
757     result->setString("sourceURL", finalURL());
758     result->setString("title", styleSheet->title());
759     return result.release();
760 }
761 
buildObjectForRule(CSSStyleRule * rule)762 PassRefPtr<InspectorObject> InspectorStyleSheet::buildObjectForRule(CSSStyleRule* rule)
763 {
764     CSSStyleSheet* styleSheet = pageStyleSheet();
765     if (!styleSheet)
766         return 0;
767 
768     RefPtr<InspectorObject> result = InspectorObject::create();
769     result->setString("selectorText", rule->selectorText());
770     // "sourceURL" is present only for regular rules, otherwise "origin" should be used in the frontend.
771     if (!m_origin.length())
772         result->setString("sourceURL", finalURL());
773     result->setNumber("sourceLine", rule->sourceLine());
774     result->setString("origin", m_origin);
775 
776     result->setObject("style", buildObjectForStyle(rule->style()));
777     if (canBind()) {
778         InspectorCSSId id(ruleId(rule));
779         if (!id.isEmpty())
780             result->setValue("ruleId", id.asInspectorValue());
781     }
782 
783     RefPtr<CSSRuleSourceData> sourceData;
784     if (ensureParsedDataReady())
785         sourceData = ruleSourceDataFor(rule->style());
786     if (sourceData) {
787         RefPtr<InspectorObject> selectorRange = InspectorObject::create();
788         selectorRange->setNumber("start", sourceData->selectorListRange.start);
789         selectorRange->setNumber("end", sourceData->selectorListRange.end);
790         result->setObject("selectorRange", selectorRange.release());
791     }
792 
793     return result.release();
794 }
795 
buildObjectForStyle(CSSStyleDeclaration * style)796 PassRefPtr<InspectorObject> InspectorStyleSheet::buildObjectForStyle(CSSStyleDeclaration* style)
797 {
798     RefPtr<CSSRuleSourceData> sourceData;
799     if (ensureParsedDataReady())
800         sourceData = ruleSourceDataFor(style);
801 
802     InspectorCSSId id = ruleOrStyleId(style);
803     if (id.isEmpty()) {
804         RefPtr<InspectorObject> bogusStyle = InspectorObject::create();
805         bogusStyle->setArray("cssProperties", InspectorArray::create());
806         bogusStyle->setObject("shorthandValues", InspectorObject::create());
807         bogusStyle->setObject("properties", InspectorObject::create());
808         return bogusStyle.release();
809     }
810     RefPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id);
811     RefPtr<InspectorObject> result = inspectorStyle->buildObjectForStyle();
812 
813     // Style text cannot be retrieved without stylesheet, so set cssText here.
814     if (sourceData) {
815         String sheetText;
816         bool success = text(&sheetText);
817         if (success) {
818             const SourceRange& bodyRange = sourceData->styleSourceData->styleBodyRange;
819             result->setString("cssText", sheetText.substring(bodyRange.start, bodyRange.end - bodyRange.start));
820         }
821     }
822 
823     return result.release();
824 }
825 
setPropertyText(ErrorString * errorString,const InspectorCSSId & id,unsigned propertyIndex,const String & text,bool overwrite)826 bool InspectorStyleSheet::setPropertyText(ErrorString* errorString, const InspectorCSSId& id, unsigned propertyIndex, const String& text, bool overwrite)
827 {
828     RefPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id);
829     if (!inspectorStyle) {
830         *errorString = "No style found for given id";
831         return false;
832     }
833 
834     return inspectorStyle->setPropertyText(errorString, propertyIndex, text, overwrite);
835 }
836 
toggleProperty(ErrorString * errorString,const InspectorCSSId & id,unsigned propertyIndex,bool disable)837 bool InspectorStyleSheet::toggleProperty(ErrorString* errorString, const InspectorCSSId& id, unsigned propertyIndex, bool disable)
838 {
839     RefPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id);
840     if (!inspectorStyle) {
841         *errorString = "No style found for given id";
842         return false;
843     }
844 
845     bool success = inspectorStyle->toggleProperty(errorString, propertyIndex, disable);
846     if (success) {
847         if (disable)
848             rememberInspectorStyle(inspectorStyle);
849         else if (!inspectorStyle->hasDisabledProperties())
850             forgetInspectorStyle(inspectorStyle->cssStyle());
851     }
852     return success;
853 }
854 
text(String * result) const855 bool InspectorStyleSheet::text(String* result) const
856 {
857     if (!ensureText())
858         return false;
859     *result = m_parsedStyleSheet->text();
860     return true;
861 }
862 
styleForId(const InspectorCSSId & id) const863 CSSStyleDeclaration* InspectorStyleSheet::styleForId(const InspectorCSSId& id) const
864 {
865     CSSStyleRule* rule = ruleForId(id);
866     if (!rule)
867         return 0;
868 
869     return rule->style();
870 }
871 
inspectorStyleForId(const InspectorCSSId & id)872 PassRefPtr<InspectorStyle> InspectorStyleSheet::inspectorStyleForId(const InspectorCSSId& id)
873 {
874     CSSStyleDeclaration* style = styleForId(id);
875     if (!style)
876         return 0;
877 
878     InspectorStyleMap::iterator it = m_inspectorStyles.find(style);
879     if (it == m_inspectorStyles.end()) {
880         RefPtr<InspectorStyle> inspectorStyle = InspectorStyle::create(id, style, this);
881         return inspectorStyle.release();
882     }
883     return it->second;
884 }
885 
rememberInspectorStyle(RefPtr<InspectorStyle> inspectorStyle)886 void InspectorStyleSheet::rememberInspectorStyle(RefPtr<InspectorStyle> inspectorStyle)
887 {
888     m_inspectorStyles.set(inspectorStyle->cssStyle(), inspectorStyle);
889 }
890 
forgetInspectorStyle(CSSStyleDeclaration * style)891 void InspectorStyleSheet::forgetInspectorStyle(CSSStyleDeclaration* style)
892 {
893     m_inspectorStyles.remove(style);
894 }
895 
ruleOrStyleId(CSSStyleDeclaration * style) const896 InspectorCSSId InspectorStyleSheet::ruleOrStyleId(CSSStyleDeclaration* style) const
897 {
898     unsigned index = ruleIndexByStyle(style);
899     if (index != UINT_MAX)
900         return InspectorCSSId(id(), index);
901     return InspectorCSSId();
902 }
903 
ownerDocument() const904 Document* InspectorStyleSheet::ownerDocument() const
905 {
906     return m_pageStyleSheet->document();
907 }
908 
ruleSourceDataFor(CSSStyleDeclaration * style) const909 RefPtr<CSSRuleSourceData> InspectorStyleSheet::ruleSourceDataFor(CSSStyleDeclaration* style) const
910 {
911     return m_parsedStyleSheet->ruleSourceDataAt(ruleIndexByStyle(style));
912 }
913 
ruleIndexByStyle(CSSStyleDeclaration * pageStyle) const914 unsigned InspectorStyleSheet::ruleIndexByStyle(CSSStyleDeclaration* pageStyle) const
915 {
916     ensureFlatRules();
917     unsigned index = 0;
918     for (unsigned i = 0, size = m_flatRules.size(); i < size; ++i) {
919         if (m_flatRules.at(i)->style() == pageStyle)
920             return index;
921 
922         ++index;
923     }
924     return UINT_MAX;
925 }
926 
ensureParsedDataReady()927 bool InspectorStyleSheet::ensureParsedDataReady()
928 {
929     return ensureText() && ensureSourceData();
930 }
931 
ensureText() const932 bool InspectorStyleSheet::ensureText() const
933 {
934     if (!m_parsedStyleSheet)
935         return false;
936     if (m_parsedStyleSheet->hasText())
937         return true;
938 
939     String text;
940     bool success = originalStyleSheetText(&text);
941     if (success)
942         m_parsedStyleSheet->setText(text);
943     // No need to clear m_flatRules here - it's empty.
944 
945     return success;
946 }
947 
ensureSourceData()948 bool InspectorStyleSheet::ensureSourceData()
949 {
950     if (m_parsedStyleSheet->hasSourceData())
951         return true;
952 
953     if (!m_parsedStyleSheet->hasText())
954         return false;
955 
956     RefPtr<CSSStyleSheet> newStyleSheet = CSSStyleSheet::create();
957     CSSParser p;
958     StyleRuleRangeMap ruleRangeMap;
959     p.parseSheet(newStyleSheet.get(), m_parsedStyleSheet->text(), 0, &ruleRangeMap);
960     OwnPtr<ParsedStyleSheet::SourceData> rangesVector(new ParsedStyleSheet::SourceData());
961 
962     Vector<CSSStyleRule*> rules;
963     RefPtr<CSSRuleList> ruleList = asCSSRuleList(newStyleSheet.get());
964     collectFlatRules(ruleList, &rules);
965     for (unsigned i = 0, size = rules.size(); i < size; ++i) {
966         StyleRuleRangeMap::iterator it = ruleRangeMap.find(rules.at(i));
967         if (it != ruleRangeMap.end()) {
968             fixUnparsedPropertyRanges(it->second.get(), m_parsedStyleSheet->text());
969             rangesVector->append(it->second);
970         }
971     }
972 
973     m_parsedStyleSheet->setSourceData(rangesVector.release());
974     return m_parsedStyleSheet->hasSourceData();
975 }
976 
ensureFlatRules() const977 void InspectorStyleSheet::ensureFlatRules() const
978 {
979     // We are fine with redoing this for empty stylesheets as this will run fast.
980     if (m_flatRules.isEmpty())
981         collectFlatRules(asCSSRuleList(pageStyleSheet()), &m_flatRules);
982 }
983 
setStyleText(CSSStyleDeclaration * style,const String & text)984 bool InspectorStyleSheet::setStyleText(CSSStyleDeclaration* style, const String& text)
985 {
986     if (!pageStyleSheet())
987         return false;
988     if (!ensureParsedDataReady())
989         return false;
990 
991     String patchedStyleSheetText;
992     bool success = styleSheetTextWithChangedStyle(style, text, &patchedStyleSheetText);
993     if (!success)
994         return false;
995 
996     InspectorCSSId id = ruleOrStyleId(style);
997     if (id.isEmpty())
998         return false;
999 
1000     ExceptionCode ec = 0;
1001     style->setCssText(text, ec);
1002     if (!ec)
1003         m_parsedStyleSheet->setText(patchedStyleSheetText);
1004 
1005     return !ec;
1006 }
1007 
styleSheetTextWithChangedStyle(CSSStyleDeclaration * style,const String & newStyleText,String * result)1008 bool InspectorStyleSheet::styleSheetTextWithChangedStyle(CSSStyleDeclaration* style, const String& newStyleText, String* result)
1009 {
1010     if (!style)
1011         return false;
1012 
1013     if (!ensureParsedDataReady())
1014         return false;
1015 
1016     RefPtr<CSSRuleSourceData> sourceData = ruleSourceDataFor(style);
1017     unsigned bodyStart = sourceData->styleSourceData->styleBodyRange.start;
1018     unsigned bodyEnd = sourceData->styleSourceData->styleBodyRange.end;
1019     ASSERT(bodyStart <= bodyEnd);
1020 
1021     String text = m_parsedStyleSheet->text();
1022     ASSERT(bodyEnd <= text.length()); // bodyEnd is exclusive
1023 
1024     text.replace(bodyStart, bodyEnd - bodyStart, newStyleText);
1025     *result = text;
1026     return true;
1027 }
1028 
ruleId(CSSStyleRule * rule) const1029 InspectorCSSId InspectorStyleSheet::ruleId(CSSStyleRule* rule) const
1030 {
1031     return ruleOrStyleId(rule->style());
1032 }
1033 
revalidateStyle(CSSStyleDeclaration * pageStyle)1034 void InspectorStyleSheet::revalidateStyle(CSSStyleDeclaration* pageStyle)
1035 {
1036     if (m_isRevalidating)
1037         return;
1038 
1039     m_isRevalidating = true;
1040     ensureFlatRules();
1041     for (unsigned i = 0, size = m_flatRules.size(); i < size; ++i) {
1042         CSSStyleRule* parsedRule = m_flatRules.at(i);
1043         if (parsedRule->style() == pageStyle) {
1044             if (parsedRule->style()->cssText() != pageStyle->cssText()) {
1045                 // Clear the disabled properties for the invalid style here.
1046                 m_inspectorStyles.remove(pageStyle);
1047                 setStyleText(pageStyle, pageStyle->cssText());
1048             }
1049             break;
1050         }
1051     }
1052     m_isRevalidating = false;
1053 }
1054 
originalStyleSheetText(String * result) const1055 bool InspectorStyleSheet::originalStyleSheetText(String* result) const
1056 {
1057     String text;
1058     bool success = inlineStyleSheetText(&text);
1059     if (!success)
1060         success = resourceStyleSheetText(&text);
1061     if (success)
1062         *result = text;
1063     return success;
1064 }
1065 
resourceStyleSheetText(String * result) const1066 bool InspectorStyleSheet::resourceStyleSheetText(String* result) const
1067 {
1068     if (m_origin == "user" || m_origin == "user-agent")
1069         return false;
1070 
1071     if (!m_pageStyleSheet || !ownerDocument())
1072         return false;
1073 
1074     String error;
1075     InspectorResourceAgent::resourceContent(&error, ownerDocument()->frame(), m_pageStyleSheet->finalURL(), result);
1076     return error.isEmpty();
1077 }
1078 
inlineStyleSheetText(String * result) const1079 bool InspectorStyleSheet::inlineStyleSheetText(String* result) const
1080 {
1081     if (!m_pageStyleSheet)
1082         return false;
1083 
1084     Node* ownerNode = m_pageStyleSheet->ownerNode();
1085     if (!ownerNode || ownerNode->nodeType() != Node::ELEMENT_NODE)
1086         return false;
1087     Element* ownerElement = static_cast<Element*>(ownerNode);
1088     if (ownerElement->tagName().lower() != "style")
1089         return false;
1090     *result = ownerElement->innerText();
1091     return true;
1092 }
1093 
buildArrayForRuleList(CSSRuleList * ruleList)1094 PassRefPtr<InspectorArray> InspectorStyleSheet::buildArrayForRuleList(CSSRuleList* ruleList)
1095 {
1096     RefPtr<InspectorArray> result = InspectorArray::create();
1097     if (!ruleList)
1098         return result.release();
1099 
1100     RefPtr<CSSRuleList> refRuleList = ruleList;
1101     Vector<CSSStyleRule*> rules;
1102     collectFlatRules(refRuleList, &rules);
1103 
1104     for (unsigned i = 0, size = rules.size(); i < size; ++i)
1105         result->pushObject(buildObjectForRule(rules.at(i)));
1106 
1107     return result.release();
1108 }
1109 
fixUnparsedPropertyRanges(CSSRuleSourceData * ruleData,const String & styleSheetText)1110 void InspectorStyleSheet::fixUnparsedPropertyRanges(CSSRuleSourceData* ruleData, const String& styleSheetText)
1111 {
1112     Vector<CSSPropertySourceData>& propertyData = ruleData->styleSourceData->propertyData;
1113     unsigned size = propertyData.size();
1114     if (!size)
1115         return;
1116 
1117     unsigned styleStart = ruleData->styleSourceData->styleBodyRange.start;
1118     const UChar* characters = styleSheetText.characters();
1119     CSSPropertySourceData* nextData = &(propertyData.at(0));
1120     for (unsigned i = 0; i < size; ++i) {
1121         CSSPropertySourceData* currentData = nextData;
1122         nextData = i < size - 1 ? &(propertyData.at(i + 1)) : 0;
1123 
1124         if (currentData->parsedOk)
1125             continue;
1126         if (currentData->range.end > 0 && characters[styleStart + currentData->range.end - 1] == ';')
1127             continue;
1128 
1129         unsigned propertyEndInStyleSheet;
1130         if (!nextData)
1131             propertyEndInStyleSheet = ruleData->styleSourceData->styleBodyRange.end - 1;
1132         else
1133             propertyEndInStyleSheet = styleStart + nextData->range.start - 1;
1134 
1135         while (isHTMLSpace(characters[propertyEndInStyleSheet]))
1136             --propertyEndInStyleSheet;
1137 
1138         // propertyEndInStyleSheet points at the last property text character.
1139         unsigned newPropertyEnd = propertyEndInStyleSheet - styleStart + 1; // Exclusive of the last property text character.
1140         if (currentData->range.end != newPropertyEnd) {
1141             currentData->range.end = newPropertyEnd;
1142             unsigned valueStartInStyleSheet = styleStart + currentData->range.start + currentData->name.length();
1143             while (valueStartInStyleSheet < propertyEndInStyleSheet && characters[valueStartInStyleSheet] != ':')
1144                 ++valueStartInStyleSheet;
1145             if (valueStartInStyleSheet < propertyEndInStyleSheet)
1146                 ++valueStartInStyleSheet; // Shift past the ':'.
1147             while (valueStartInStyleSheet < propertyEndInStyleSheet && isHTMLSpace(characters[valueStartInStyleSheet]))
1148                 ++valueStartInStyleSheet;
1149             // Need to exclude the trailing ';' from the property value.
1150             currentData->value = styleSheetText.substring(valueStartInStyleSheet, propertyEndInStyleSheet - valueStartInStyleSheet + (characters[propertyEndInStyleSheet] == ';' ? 0 : 1));
1151         }
1152     }
1153 }
1154 
collectFlatRules(PassRefPtr<CSSRuleList> ruleList,Vector<CSSStyleRule * > * result)1155 void InspectorStyleSheet::collectFlatRules(PassRefPtr<CSSRuleList> ruleList, Vector<CSSStyleRule*>* result)
1156 {
1157     if (!ruleList)
1158         return;
1159 
1160     for (unsigned i = 0, size = ruleList->length(); i < size; ++i) {
1161         CSSRule* rule = ruleList->item(i);
1162         CSSStyleRule* styleRule = InspectorCSSAgent::asCSSStyleRule(rule);
1163         if (styleRule)
1164             result->append(styleRule);
1165         else {
1166             RefPtr<CSSRuleList> childRuleList = asCSSRuleList(rule);
1167             if (childRuleList)
1168                 collectFlatRules(childRuleList, result);
1169         }
1170     }
1171 }
1172 
create(const String & id,PassRefPtr<Element> element,const String & origin)1173 PassRefPtr<InspectorStyleSheetForInlineStyle> InspectorStyleSheetForInlineStyle::create(const String& id, PassRefPtr<Element> element, const String& origin)
1174 {
1175     return adoptRef(new InspectorStyleSheetForInlineStyle(id, element, origin));
1176 }
1177 
InspectorStyleSheetForInlineStyle(const String & id,PassRefPtr<Element> element,const String & origin)1178 InspectorStyleSheetForInlineStyle::InspectorStyleSheetForInlineStyle(const String& id, PassRefPtr<Element> element, const String& origin)
1179     : InspectorStyleSheet(id, 0, origin, "")
1180     , m_element(element)
1181     , m_ruleSourceData(0)
1182 {
1183     ASSERT(m_element);
1184     m_inspectorStyle = InspectorStyle::create(InspectorCSSId(id, 0), inlineStyle(), this);
1185     m_styleText = m_element->isStyledElement() ? m_element->getAttribute("style").string() : String();
1186 }
1187 
didModifyElementAttribute()1188 void InspectorStyleSheetForInlineStyle::didModifyElementAttribute()
1189 {
1190     String newStyleText = elementStyleText();
1191     bool shouldDropSourceData = false;
1192     if (m_element->isStyledElement() && m_element->style() != m_inspectorStyle->cssStyle()) {
1193         m_inspectorStyle = InspectorStyle::create(InspectorCSSId(id(), 0), inlineStyle(), this);
1194         shouldDropSourceData = true;
1195     }
1196     if (newStyleText != m_styleText) {
1197         m_styleText = newStyleText;
1198         shouldDropSourceData = true;
1199     }
1200     if (shouldDropSourceData)
1201         m_ruleSourceData.clear();
1202 }
1203 
text(String * result) const1204 bool InspectorStyleSheetForInlineStyle::text(String* result) const
1205 {
1206     *result = m_styleText;
1207     return true;
1208 }
1209 
setStyleText(CSSStyleDeclaration * style,const String & text)1210 bool InspectorStyleSheetForInlineStyle::setStyleText(CSSStyleDeclaration* style, const String& text)
1211 {
1212     ASSERT_UNUSED(style, style == inlineStyle());
1213     ExceptionCode ec = 0;
1214     m_element->setAttribute("style", text, ec);
1215     m_styleText = text;
1216     m_ruleSourceData.clear();
1217     return !ec;
1218 }
1219 
ownerDocument() const1220 Document* InspectorStyleSheetForInlineStyle::ownerDocument() const
1221 {
1222     return m_element->document();
1223 }
1224 
ensureParsedDataReady()1225 bool InspectorStyleSheetForInlineStyle::ensureParsedDataReady()
1226 {
1227     // The "style" property value can get changed indirectly, e.g. via element.style.borderWidth = "2px".
1228     const String& currentStyleText = elementStyleText();
1229     if (m_styleText != currentStyleText) {
1230         m_ruleSourceData.clear();
1231         m_styleText = currentStyleText;
1232     }
1233 
1234     if (m_ruleSourceData)
1235         return true;
1236 
1237     m_ruleSourceData = CSSRuleSourceData::create();
1238     RefPtr<CSSStyleSourceData> sourceData = CSSStyleSourceData::create();
1239     bool success = getStyleAttributeRanges(&sourceData);
1240     if (!success)
1241         return false;
1242 
1243     m_ruleSourceData->styleSourceData = sourceData.release();
1244     return true;
1245 }
1246 
inspectorStyleForId(const InspectorCSSId & id)1247 PassRefPtr<InspectorStyle> InspectorStyleSheetForInlineStyle::inspectorStyleForId(const InspectorCSSId& id)
1248 {
1249     ASSERT_UNUSED(id, !id.ordinal());
1250     return m_inspectorStyle;
1251 }
1252 
inlineStyle() const1253 CSSStyleDeclaration* InspectorStyleSheetForInlineStyle::inlineStyle() const
1254 {
1255     return m_element->style();
1256 }
1257 
elementStyleText() const1258 const String& InspectorStyleSheetForInlineStyle::elementStyleText() const
1259 {
1260     return m_element->getAttribute("style").string();
1261 }
1262 
getStyleAttributeRanges(RefPtr<CSSStyleSourceData> * result)1263 bool InspectorStyleSheetForInlineStyle::getStyleAttributeRanges(RefPtr<CSSStyleSourceData>* result)
1264 {
1265     if (!m_element->isStyledElement())
1266         return false;
1267 
1268     if (m_styleText.isEmpty()) {
1269         (*result)->styleBodyRange.start = 0;
1270         (*result)->styleBodyRange.end = 0;
1271         return true;
1272     }
1273 
1274     RefPtr<CSSMutableStyleDeclaration> tempDeclaration = CSSMutableStyleDeclaration::create();
1275     CSSParser p;
1276     p.parseDeclaration(tempDeclaration.get(), m_styleText, result);
1277     return true;
1278 }
1279 
1280 } // namespace WebCore
1281 
1282 #endif // ENABLE(INSPECTOR)
1283