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