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