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 59 WebCore::CSSStyleSheet* cssStyleSheet() const { return m_parserOutput; } 60 const String& text() const { return m_text; } 61 void setText(const String& text); 62 bool hasText() const { return m_hasText; } 63 SourceData* sourceData() const { return m_sourceData.get(); } 64 void setSourceData(PassOwnPtr<SourceData> sourceData); 65 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 77 ParsedStyleSheet::ParsedStyleSheet() 78 : m_parserOutput(0) 79 , m_hasText(false) 80 { 81 } 82 83 void ParsedStyleSheet::setText(const String& text) 84 { 85 m_hasText = true; 86 m_text = text; 87 setSourceData(0); 88 } 89 90 void ParsedStyleSheet::setSourceData(PassOwnPtr<SourceData> sourceData) 91 { 92 m_sourceData = sourceData; 93 } 94 95 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 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 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 142 PassRefPtr<InspectorStyle> InspectorStyle::create(const InspectorCSSId& styleId, PassRefPtr<CSSStyleDeclaration> style, InspectorStyleSheet* parentStyleSheet) 143 { 144 return adoptRef(new InspectorStyle(styleId, style, parentStyleSheet)); 145 } 146 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 155 InspectorStyle::~InspectorStyle() 156 { 157 } 158 159 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). 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 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 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 351 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 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 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 413 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 459 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 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 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 572 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 593 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 608 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 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 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 638 InspectorStyleSheet::~InspectorStyleSheet() 639 { 640 delete m_parsedStyleSheet; 641 } 642 643 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 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 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 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 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 717 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 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 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 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 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 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 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 855 bool InspectorStyleSheet::text(String* result) const 856 { 857 if (!ensureText()) 858 return false; 859 *result = m_parsedStyleSheet->text(); 860 return true; 861 } 862 863 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 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 886 void InspectorStyleSheet::rememberInspectorStyle(RefPtr<InspectorStyle> inspectorStyle) 887 { 888 m_inspectorStyles.set(inspectorStyle->cssStyle(), inspectorStyle); 889 } 890 891 void InspectorStyleSheet::forgetInspectorStyle(CSSStyleDeclaration* style) 892 { 893 m_inspectorStyles.remove(style); 894 } 895 896 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 904 Document* InspectorStyleSheet::ownerDocument() const 905 { 906 return m_pageStyleSheet->document(); 907 } 908 909 RefPtr<CSSRuleSourceData> InspectorStyleSheet::ruleSourceDataFor(CSSStyleDeclaration* style) const 910 { 911 return m_parsedStyleSheet->ruleSourceDataAt(ruleIndexByStyle(style)); 912 } 913 914 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 927 bool InspectorStyleSheet::ensureParsedDataReady() 928 { 929 return ensureText() && ensureSourceData(); 930 } 931 932 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 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 977 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 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 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 1029 InspectorCSSId InspectorStyleSheet::ruleId(CSSStyleRule* rule) const 1030 { 1031 return ruleOrStyleId(rule->style()); 1032 } 1033 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 1055 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 1066 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 1079 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 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 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 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 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 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 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 1204 bool InspectorStyleSheetForInlineStyle::text(String* result) const 1205 { 1206 *result = m_styleText; 1207 return true; 1208 } 1209 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 1220 Document* InspectorStyleSheetForInlineStyle::ownerDocument() const 1221 { 1222 return m_element->document(); 1223 } 1224 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 1247 PassRefPtr<InspectorStyle> InspectorStyleSheetForInlineStyle::inspectorStyleForId(const InspectorCSSId& id) 1248 { 1249 ASSERT_UNUSED(id, !id.ordinal()); 1250 return m_inspectorStyle; 1251 } 1252 1253 CSSStyleDeclaration* InspectorStyleSheetForInlineStyle::inlineStyle() const 1254 { 1255 return m_element->style(); 1256 } 1257 1258 const String& InspectorStyleSheetForInlineStyle::elementStyleText() const 1259 { 1260 return m_element->getAttribute("style").string(); 1261 } 1262 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