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