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 27#if ENABLE(INSPECTOR) 28 29#include "InspectorStyleSheet.h" 30 31#include "CSSImportRule.h" 32#include "CSSMediaRule.h" 33#include "CSSParser.h" 34#include "CSSPropertyNames.h" 35#include "CSSPropertySourceData.h" 36#include "CSSRule.h" 37#include "CSSRuleList.h" 38#include "CSSStyleRule.h" 39#include "CSSStyleSheet.h" 40#include "CSSSupportsRule.h" 41#include "ContentSecurityPolicy.h" 42#include "Document.h" 43#include "Element.h" 44#include "HTMLHeadElement.h" 45#include "HTMLNames.h" 46#include "HTMLParserIdioms.h" 47#include "HTMLStyleElement.h" 48#include "InspectorCSSAgent.h" 49#include "InspectorPageAgent.h" 50#include "Node.h" 51#include "SVGElement.h" 52#include "SVGNames.h" 53#include "StyleProperties.h" 54#include "StyleResolver.h" 55#include "StyleRule.h" 56#include "StyleRuleImport.h" 57#include "StyleSheetContents.h" 58#include "StyleSheetList.h" 59#include "WebKitCSSKeyframesRule.h" 60#include <inspector/ContentSearchUtilities.h> 61#include <inspector/InspectorValues.h> 62#include <wtf/Vector.h> 63#include <wtf/text/StringBuilder.h> 64#include <yarr/RegularExpression.h> 65 66using Inspector::TypeBuilder::Array; 67using WebCore::RuleSourceDataList; 68using WebCore::CSSRuleSourceData; 69 70class ParsedStyleSheet { 71 WTF_MAKE_FAST_ALLOCATED; 72public: 73 ParsedStyleSheet(); 74 75 const String& text() const { ASSERT(m_hasText); return m_text; } 76 void setText(const String& text); 77 bool hasText() const { return m_hasText; } 78 RuleSourceDataList* sourceData() const { return m_sourceData.get(); } 79 void setSourceData(std::unique_ptr<RuleSourceDataList>); 80 bool hasSourceData() const { return m_sourceData != nullptr; } 81 PassRefPtr<WebCore::CSSRuleSourceData> ruleSourceDataAt(unsigned) const; 82 83private: 84 85 String m_text; 86 bool m_hasText; 87 std::unique_ptr<RuleSourceDataList> m_sourceData; 88}; 89 90ParsedStyleSheet::ParsedStyleSheet() 91 : m_hasText(false) 92{ 93} 94 95void ParsedStyleSheet::setText(const String& text) 96{ 97 m_hasText = true; 98 m_text = text; 99 setSourceData(nullptr); 100} 101 102static void flattenSourceData(RuleSourceDataList* dataList, RuleSourceDataList* target) 103{ 104 for (size_t i = 0; i < dataList->size(); ++i) { 105 RefPtr<CSSRuleSourceData>& data = dataList->at(i); 106 if (data->type == CSSRuleSourceData::STYLE_RULE) 107 target->append(data); 108 else if (data->type == CSSRuleSourceData::MEDIA_RULE) 109 flattenSourceData(&data->childRules, target); 110#if ENABLE(CSS3_CONDITIONAL_RULES) 111 else if (data->type == CSSRuleSourceData::SUPPORTS_RULE) 112 flattenSourceData(&data->childRules, target); 113#endif 114 } 115} 116 117void ParsedStyleSheet::setSourceData(std::unique_ptr<RuleSourceDataList> sourceData) 118{ 119 if (!sourceData) { 120 m_sourceData.reset(); 121 return; 122 } 123 124 m_sourceData = std::make_unique<RuleSourceDataList>(); 125 126 // FIXME: This is a temporary solution to retain the original flat sourceData structure 127 // containing only style rules, even though CSSParser now provides the full rule source data tree. 128 // Normally, we should just assign m_sourceData = sourceData; 129 flattenSourceData(sourceData.get(), m_sourceData.get()); 130} 131 132PassRefPtr<WebCore::CSSRuleSourceData> ParsedStyleSheet::ruleSourceDataAt(unsigned index) const 133{ 134 if (!hasSourceData() || index >= m_sourceData->size()) 135 return nullptr; 136 137 return m_sourceData->at(index); 138} 139 140using namespace Inspector; 141 142namespace WebCore { 143 144enum MediaListSource { 145 MediaListSourceLinkedSheet, 146 MediaListSourceInlineSheet, 147 MediaListSourceMediaRule, 148 MediaListSourceImportRule 149}; 150 151static PassRefPtr<Inspector::TypeBuilder::CSS::SourceRange> buildSourceRangeObject(const SourceRange& range, Vector<size_t>* lineEndings) 152{ 153 if (!lineEndings) 154 return nullptr; 155 TextPosition start = ContentSearchUtilities::textPositionFromOffset(range.start, *lineEndings); 156 TextPosition end = ContentSearchUtilities::textPositionFromOffset(range.end, *lineEndings); 157 158 RefPtr<Inspector::TypeBuilder::CSS::SourceRange> result = Inspector::TypeBuilder::CSS::SourceRange::create() 159 .setStartLine(start.m_line.zeroBasedInt()) 160 .setStartColumn(start.m_column.zeroBasedInt()) 161 .setEndLine(end.m_line.zeroBasedInt()) 162 .setEndColumn(end.m_column.zeroBasedInt()); 163 return result.release(); 164} 165 166static PassRefPtr<Inspector::TypeBuilder::CSS::CSSMedia> buildMediaObject(const MediaList* media, MediaListSource mediaListSource, const String& sourceURL) 167{ 168 // Make certain compilers happy by initializing |source| up-front. 169 Inspector::TypeBuilder::CSS::CSSMedia::Source::Enum source = Inspector::TypeBuilder::CSS::CSSMedia::Source::InlineSheet; 170 switch (mediaListSource) { 171 case MediaListSourceMediaRule: 172 source = Inspector::TypeBuilder::CSS::CSSMedia::Source::MediaRule; 173 break; 174 case MediaListSourceImportRule: 175 source = Inspector::TypeBuilder::CSS::CSSMedia::Source::ImportRule; 176 break; 177 case MediaListSourceLinkedSheet: 178 source = Inspector::TypeBuilder::CSS::CSSMedia::Source::LinkedSheet; 179 break; 180 case MediaListSourceInlineSheet: 181 source = Inspector::TypeBuilder::CSS::CSSMedia::Source::InlineSheet; 182 break; 183 } 184 185 RefPtr<Inspector::TypeBuilder::CSS::CSSMedia> mediaObject = Inspector::TypeBuilder::CSS::CSSMedia::create() 186 .setText(media->mediaText()) 187 .setSource(source); 188 189 if (!sourceURL.isEmpty()) { 190 mediaObject->setSourceURL(sourceURL); 191 mediaObject->setSourceLine(media->queries()->lastLine()); 192 } 193 return mediaObject.release(); 194} 195 196static PassRefPtr<CSSRuleList> asCSSRuleList(CSSStyleSheet* styleSheet) 197{ 198 if (!styleSheet) 199 return nullptr; 200 201 RefPtr<StaticCSSRuleList> list = StaticCSSRuleList::create(); 202 Vector<RefPtr<CSSRule>>& listRules = list->rules(); 203 for (unsigned i = 0, size = styleSheet->length(); i < size; ++i) { 204 CSSRule* item = styleSheet->item(i); 205 if (item->type() == CSSRule::CHARSET_RULE) 206 continue; 207 listRules.append(item); 208 } 209 return list.release(); 210} 211 212static PassRefPtr<CSSRuleList> asCSSRuleList(CSSRule* rule) 213{ 214 if (!rule) 215 return nullptr; 216 217 if (rule->type() == CSSRule::MEDIA_RULE) 218 return static_cast<CSSMediaRule*>(rule)->cssRules(); 219 220 if (rule->type() == CSSRule::WEBKIT_KEYFRAMES_RULE) 221 return static_cast<WebKitCSSKeyframesRule*>(rule)->cssRules(); 222 223#if ENABLE(CSS3_CONDITIONAL_RULES) 224 if (rule->type() == CSSRule::SUPPORTS_RULE) 225 return static_cast<CSSSupportsRule*>(rule)->cssRules(); 226#endif 227 228 return nullptr; 229} 230 231static void fillMediaListChain(CSSRule* rule, Array<Inspector::TypeBuilder::CSS::CSSMedia>* mediaArray) 232{ 233 MediaList* mediaList; 234 CSSRule* parentRule = rule; 235 String sourceURL; 236 while (parentRule) { 237 CSSStyleSheet* parentStyleSheet = nullptr; 238 bool isMediaRule = true; 239 if (parentRule->type() == CSSRule::MEDIA_RULE) { 240 CSSMediaRule* mediaRule = static_cast<CSSMediaRule*>(parentRule); 241 mediaList = mediaRule->media(); 242 parentStyleSheet = mediaRule->parentStyleSheet(); 243 } else if (parentRule->type() == CSSRule::IMPORT_RULE) { 244 CSSImportRule* importRule = static_cast<CSSImportRule*>(parentRule); 245 mediaList = importRule->media(); 246 parentStyleSheet = importRule->parentStyleSheet(); 247 isMediaRule = false; 248 } else 249 mediaList = nullptr; 250 251 if (parentStyleSheet) { 252 sourceURL = parentStyleSheet->contents().baseURL(); 253 if (sourceURL.isEmpty()) 254 sourceURL = InspectorDOMAgent::documentURLString(parentStyleSheet->ownerDocument()); 255 } else 256 sourceURL = ""; 257 258 if (mediaList && mediaList->length()) 259 mediaArray->addItem(buildMediaObject(mediaList, isMediaRule ? MediaListSourceMediaRule : MediaListSourceImportRule, sourceURL)); 260 261 if (parentRule->parentRule()) 262 parentRule = parentRule->parentRule(); 263 else { 264 CSSStyleSheet* styleSheet = parentRule->parentStyleSheet(); 265 while (styleSheet) { 266 mediaList = styleSheet->media(); 267 if (mediaList && mediaList->length()) { 268 Document* doc = styleSheet->ownerDocument(); 269 if (doc) 270 sourceURL = doc->url(); 271 else if (!styleSheet->contents().baseURL().isEmpty()) 272 sourceURL = styleSheet->contents().baseURL(); 273 else 274 sourceURL = ""; 275 mediaArray->addItem(buildMediaObject(mediaList, styleSheet->ownerNode() ? MediaListSourceLinkedSheet : MediaListSourceInlineSheet, sourceURL)); 276 } 277 parentRule = styleSheet->ownerRule(); 278 if (parentRule) 279 break; 280 styleSheet = styleSheet->parentStyleSheet(); 281 } 282 } 283 } 284} 285 286static std::unique_ptr<CSSParser> createCSSParser(Document* document) 287{ 288 return std::make_unique<CSSParser>(document ? CSSParserContext(*document) : strictCSSParserContext()); 289} 290 291PassRefPtr<InspectorStyle> InspectorStyle::create(const InspectorCSSId& styleId, PassRefPtr<CSSStyleDeclaration> style, InspectorStyleSheet* parentStyleSheet) 292{ 293 return adoptRef(new InspectorStyle(styleId, style, parentStyleSheet)); 294} 295 296InspectorStyle::InspectorStyle(const InspectorCSSId& styleId, PassRefPtr<CSSStyleDeclaration> style, InspectorStyleSheet* parentStyleSheet) 297 : m_styleId(styleId) 298 , m_style(style) 299 , m_parentStyleSheet(parentStyleSheet) 300 , m_formatAcquired(false) 301{ 302 ASSERT(m_style); 303} 304 305InspectorStyle::~InspectorStyle() 306{ 307} 308 309PassRefPtr<Inspector::TypeBuilder::CSS::CSSStyle> InspectorStyle::buildObjectForStyle() const 310{ 311 RefPtr<Inspector::TypeBuilder::CSS::CSSStyle> result = styleWithProperties(); 312 if (!m_styleId.isEmpty()) 313 result->setStyleId(m_styleId.asProtocolValue<Inspector::TypeBuilder::CSS::CSSStyleId>()); 314 315 result->setWidth(m_style->getPropertyValue("width")); 316 result->setHeight(m_style->getPropertyValue("height")); 317 318 RefPtr<CSSRuleSourceData> sourceData = extractSourceData(); 319 if (sourceData) 320 result->setRange(buildSourceRangeObject(sourceData->ruleBodyRange, m_parentStyleSheet->lineEndings().get())); 321 322 return result.release(); 323} 324 325PassRefPtr<Inspector::TypeBuilder::Array<Inspector::TypeBuilder::CSS::CSSComputedStyleProperty>> InspectorStyle::buildArrayForComputedStyle() const 326{ 327 RefPtr<Inspector::TypeBuilder::Array<Inspector::TypeBuilder::CSS::CSSComputedStyleProperty>> result = Inspector::TypeBuilder::Array<Inspector::TypeBuilder::CSS::CSSComputedStyleProperty>::create(); 328 Vector<InspectorStyleProperty> properties; 329 populateAllProperties(&properties); 330 331 for (Vector<InspectorStyleProperty>::iterator it = properties.begin(), itEnd = properties.end(); it != itEnd; ++it) { 332 const CSSPropertySourceData& propertyEntry = it->sourceData; 333 RefPtr<Inspector::TypeBuilder::CSS::CSSComputedStyleProperty> entry = Inspector::TypeBuilder::CSS::CSSComputedStyleProperty::create() 334 .setName(propertyEntry.name) 335 .setValue(propertyEntry.value); 336 result->addItem(entry); 337 } 338 339 return result.release(); 340} 341 342// This method does the following preprocessing of |propertyText| with |overwrite| == false and |index| past the last active property: 343// - If the last property (if present) has no closing ";", the ";" is prepended to the current |propertyText| value. 344// - A heuristic formatting is attempted to retain the style structure. 345// 346// The propertyText (if not empty) is checked to be a valid style declaration (containing at least one property). If not, 347// the method returns false (denoting an error). 348bool InspectorStyle::setPropertyText(unsigned index, const String& propertyText, bool overwrite, String* oldText, ExceptionCode& ec) 349{ 350 ASSERT(m_parentStyleSheet); 351 DEPRECATED_DEFINE_STATIC_LOCAL(String, bogusPropertyName, (ASCIILiteral("-webkit-boguz-propertee"))); 352 353 if (!m_parentStyleSheet->ensureParsedDataReady()) { 354 ec = NOT_FOUND_ERR; 355 return false; 356 } 357 358 if (propertyText.stripWhiteSpace().length()) { 359 RefPtr<MutableStyleProperties> tempMutableStyle = MutableStyleProperties::create(); 360 RefPtr<CSSRuleSourceData> sourceData = CSSRuleSourceData::create(CSSRuleSourceData::STYLE_RULE); 361 Document* ownerDocument = m_parentStyleSheet->pageStyleSheet() ? m_parentStyleSheet->pageStyleSheet()->ownerDocument() : nullptr; 362 createCSSParser(ownerDocument)->parseDeclaration(tempMutableStyle.get(), propertyText + " " + bogusPropertyName + ": none", sourceData, &m_style->parentStyleSheet()->contents()); 363 Vector<CSSPropertySourceData>& propertyData = sourceData->styleSourceData->propertyData; 364 unsigned propertyCount = propertyData.size(); 365 366 // At least one property + the bogus property added just above should be present. 367 if (propertyCount < 2) { 368 ec = SYNTAX_ERR; 369 return false; 370 } 371 372 // Check for a proper propertyText termination (the parser could at least restore to the PROPERTY_NAME state). 373 if (propertyData.at(propertyCount - 1).name != bogusPropertyName) { 374 ec = SYNTAX_ERR; 375 return false; 376 } 377 } 378 379 RefPtr<CSSRuleSourceData> sourceData = extractSourceData(); 380 if (!sourceData) { 381 ec = NOT_FOUND_ERR; 382 return false; 383 } 384 385 String text; 386 bool success = styleText(&text); 387 if (!success) { 388 ec = NOT_FOUND_ERR; 389 return false; 390 } 391 392 Vector<InspectorStyleProperty> allProperties; 393 populateAllProperties(&allProperties); 394 395 InspectorStyleTextEditor editor(&allProperties, &m_disabledProperties, text, newLineAndWhitespaceDelimiters()); 396 if (overwrite) { 397 if (index >= allProperties.size()) { 398 ec = INDEX_SIZE_ERR; 399 return false; 400 } 401 *oldText = allProperties.at(index).rawText; 402 editor.replaceProperty(index, propertyText); 403 } else 404 editor.insertProperty(index, propertyText, sourceData->ruleBodyRange.length()); 405 406 return applyStyleText(editor.styleText()); 407} 408 409bool InspectorStyle::toggleProperty(unsigned index, bool disable, ExceptionCode& ec) 410{ 411 ASSERT(m_parentStyleSheet); 412 if (!m_parentStyleSheet->ensureParsedDataReady()) { 413 ec = NO_MODIFICATION_ALLOWED_ERR; 414 return false; 415 } 416 417 RefPtr<CSSRuleSourceData> sourceData = extractSourceData(); 418 if (!sourceData) { 419 ec = NOT_FOUND_ERR; 420 return false; 421 } 422 423 String text; 424 bool success = styleText(&text); 425 if (!success) { 426 ec = NOT_FOUND_ERR; 427 return false; 428 } 429 430 Vector<InspectorStyleProperty> allProperties; 431 populateAllProperties(&allProperties); 432 if (index >= allProperties.size()) { 433 ec = INDEX_SIZE_ERR; 434 return false; 435 } 436 437 InspectorStyleProperty& property = allProperties.at(index); 438 if (property.disabled == disable) 439 return true; // Idempotent operation. 440 441 InspectorStyleTextEditor editor(&allProperties, &m_disabledProperties, text, newLineAndWhitespaceDelimiters()); 442 if (disable) 443 editor.disableProperty(index); 444 else 445 editor.enableProperty(index); 446 447 return applyStyleText(editor.styleText()); 448} 449 450bool InspectorStyle::getText(String* result) const 451{ 452 // Precondition: m_parentStyleSheet->ensureParsedDataReady() has been called successfully. 453 RefPtr<CSSRuleSourceData> sourceData = extractSourceData(); 454 if (!sourceData) 455 return false; 456 457 String styleSheetText; 458 bool success = m_parentStyleSheet->getText(&styleSheetText); 459 if (!success) 460 return false; 461 462 SourceRange& bodyRange = sourceData->ruleBodyRange; 463 *result = styleSheetText.substring(bodyRange.start, bodyRange.end - bodyRange.start); 464 return true; 465} 466 467bool InspectorStyle::populateAllProperties(Vector<InspectorStyleProperty>* result) const 468{ 469 HashSet<String> sourcePropertyNames; 470 unsigned disabledIndex = 0; 471 unsigned disabledLength = m_disabledProperties.size(); 472 InspectorStyleProperty disabledProperty; 473 if (disabledIndex < disabledLength) 474 disabledProperty = m_disabledProperties.at(disabledIndex); 475 476 RefPtr<CSSRuleSourceData> sourceData = extractSourceData(); 477 Vector<CSSPropertySourceData>* sourcePropertyData = sourceData ? &(sourceData->styleSourceData->propertyData) : nullptr; 478 if (sourcePropertyData) { 479 String styleDeclaration; 480 bool isStyleTextKnown = styleText(&styleDeclaration); 481 ASSERT_UNUSED(isStyleTextKnown, isStyleTextKnown); 482 for (Vector<CSSPropertySourceData>::const_iterator it = sourcePropertyData->begin(); it != sourcePropertyData->end(); ++it) { 483 while (disabledIndex < disabledLength && disabledProperty.sourceData.range.start <= it->range.start) { 484 result->append(disabledProperty); 485 if (++disabledIndex < disabledLength) 486 disabledProperty = m_disabledProperties.at(disabledIndex); 487 } 488 InspectorStyleProperty p(*it, true, false); 489 p.setRawTextFromStyleDeclaration(styleDeclaration); 490 result->append(p); 491 sourcePropertyNames.add(it->name.lower()); 492 } 493 } 494 495 while (disabledIndex < disabledLength) { 496 disabledProperty = m_disabledProperties.at(disabledIndex++); 497 result->append(disabledProperty); 498 } 499 500 for (int i = 0, size = m_style->length(); i < size; ++i) { 501 String name = m_style->item(i); 502 if (sourcePropertyNames.contains(name.lower())) 503 continue; 504 505 sourcePropertyNames.add(name.lower()); 506 result->append(InspectorStyleProperty(CSSPropertySourceData(name, m_style->getPropertyValue(name), !m_style->getPropertyPriority(name).isEmpty(), true, SourceRange()), false, false)); 507 } 508 509 return true; 510} 511 512PassRefPtr<Inspector::TypeBuilder::CSS::CSSStyle> InspectorStyle::styleWithProperties() const 513{ 514 Vector<InspectorStyleProperty> properties; 515 populateAllProperties(&properties); 516 517 RefPtr<Array<Inspector::TypeBuilder::CSS::CSSProperty>> propertiesObject = Array<Inspector::TypeBuilder::CSS::CSSProperty>::create(); 518 RefPtr<Array<Inspector::TypeBuilder::CSS::ShorthandEntry>> shorthandEntries = Array<Inspector::TypeBuilder::CSS::ShorthandEntry>::create(); 519 HashMap<String, RefPtr<Inspector::TypeBuilder::CSS::CSSProperty>> propertyNameToPreviousActiveProperty; 520 HashSet<String> foundShorthands; 521 String previousPriority; 522 String previousStatus; 523 std::unique_ptr<Vector<size_t>> lineEndings(m_parentStyleSheet ? m_parentStyleSheet->lineEndings() : nullptr); 524 RefPtr<CSSRuleSourceData> sourceData = extractSourceData(); 525 unsigned ruleBodyRangeStart = sourceData ? sourceData->ruleBodyRange.start : 0; 526 527 for (Vector<InspectorStyleProperty>::iterator it = properties.begin(), itEnd = properties.end(); it != itEnd; ++it) { 528 const CSSPropertySourceData& propertyEntry = it->sourceData; 529 const String& name = propertyEntry.name; 530 531 Inspector::TypeBuilder::CSS::CSSProperty::Status::Enum status = it->disabled ? Inspector::TypeBuilder::CSS::CSSProperty::Status::Disabled : Inspector::TypeBuilder::CSS::CSSProperty::Status::Active; 532 533 RefPtr<Inspector::TypeBuilder::CSS::CSSProperty> property = Inspector::TypeBuilder::CSS::CSSProperty::create() 534 .setName(name) 535 .setValue(propertyEntry.value); 536 537 propertiesObject->addItem(property); 538 539 // Default "parsedOk" == true. 540 if (!propertyEntry.parsedOk) 541 property->setParsedOk(false); 542 if (it->hasRawText()) 543 property->setText(it->rawText); 544 545 // Default "priority" == "". 546 if (propertyEntry.important) 547 property->setPriority("important"); 548 if (!it->disabled) { 549 if (it->hasSource) { 550 ASSERT(sourceData); 551 property->setImplicit(false); 552 // The property range is relative to the style body start. 553 // Should be converted into an absolute range (relative to the stylesheet start) 554 // for the proper conversion into line:column. 555 SourceRange absolutePropertyRange = propertyEntry.range; 556 absolutePropertyRange.start += ruleBodyRangeStart; 557 absolutePropertyRange.end += ruleBodyRangeStart; 558 property->setRange(buildSourceRangeObject(absolutePropertyRange, lineEndings.get())); 559 560 // Parsed property overrides any property with the same name. Non-parsed property overrides 561 // previous non-parsed property with the same name (if any). 562 bool shouldInactivate = false; 563 CSSPropertyID propertyId = cssPropertyID(name); 564 // Canonicalize property names to treat non-prefixed and vendor-prefixed property names the same (opacity vs. -webkit-opacity). 565 String canonicalPropertyName = propertyId ? getPropertyNameString(propertyId) : name; 566 HashMap<String, RefPtr<Inspector::TypeBuilder::CSS::CSSProperty>>::iterator activeIt = propertyNameToPreviousActiveProperty.find(canonicalPropertyName); 567 if (activeIt != propertyNameToPreviousActiveProperty.end()) { 568 if (propertyEntry.parsedOk) { 569 bool successPriority = activeIt->value->getString(Inspector::TypeBuilder::CSS::CSSProperty::Priority, &previousPriority); 570 bool successStatus = activeIt->value->getString(Inspector::TypeBuilder::CSS::CSSProperty::Status, &previousStatus); 571 if (successStatus && previousStatus != "inactive") { 572 if (propertyEntry.important || !successPriority) // Priority not set == "not important". 573 shouldInactivate = true; 574 else if (status == Inspector::TypeBuilder::CSS::CSSProperty::Status::Active) { 575 // Inactivate a non-important property following the same-named important property. 576 status = Inspector::TypeBuilder::CSS::CSSProperty::Status::Inactive; 577 } 578 } 579 } else { 580 bool previousParsedOk; 581 bool success = activeIt->value->getBoolean(Inspector::TypeBuilder::CSS::CSSProperty::ParsedOk, &previousParsedOk); 582 if (success && !previousParsedOk) 583 shouldInactivate = true; 584 } 585 } else 586 propertyNameToPreviousActiveProperty.set(canonicalPropertyName, property); 587 588 if (shouldInactivate) { 589 activeIt->value->setStatus(Inspector::TypeBuilder::CSS::CSSProperty::Status::Inactive); 590 propertyNameToPreviousActiveProperty.set(canonicalPropertyName, property); 591 } 592 } else { 593 bool implicit = m_style->isPropertyImplicit(name); 594 // Default "implicit" == false. 595 if (implicit) 596 property->setImplicit(true); 597 status = Inspector::TypeBuilder::CSS::CSSProperty::Status::Style; 598 599 String shorthand = m_style->getPropertyShorthand(name); 600 if (!shorthand.isEmpty()) { 601 if (!foundShorthands.contains(shorthand)) { 602 foundShorthands.add(shorthand); 603 RefPtr<Inspector::TypeBuilder::CSS::ShorthandEntry> entry = Inspector::TypeBuilder::CSS::ShorthandEntry::create() 604 .setName(shorthand) 605 .setValue(shorthandValue(shorthand)); 606 shorthandEntries->addItem(entry); 607 } 608 } 609 } 610 } 611 612 // Default "status" == "style". 613 if (status != Inspector::TypeBuilder::CSS::CSSProperty::Status::Style) 614 property->setStatus(status); 615 } 616 617 RefPtr<Inspector::TypeBuilder::CSS::CSSStyle> result = Inspector::TypeBuilder::CSS::CSSStyle::create() 618 .setCssProperties(propertiesObject) 619 .setShorthandEntries(shorthandEntries); 620 return result.release(); 621} 622 623PassRefPtr<CSSRuleSourceData> InspectorStyle::extractSourceData() const 624{ 625 if (!m_parentStyleSheet || !m_parentStyleSheet->ensureParsedDataReady()) 626 return nullptr; 627 return m_parentStyleSheet->ruleSourceDataFor(m_style.get()); 628} 629 630bool InspectorStyle::setText(const String& text, ExceptionCode& ec) 631{ 632 return m_parentStyleSheet->setStyleText(m_style.get(), text, ec); 633} 634 635String InspectorStyle::shorthandValue(const String& shorthandProperty) const 636{ 637 String value = m_style->getPropertyValue(shorthandProperty); 638 if (!value.isEmpty()) 639 return value; 640 StringBuilder builder; 641 for (unsigned i = 0; i < m_style->length(); ++i) { 642 String individualProperty = m_style->item(i); 643 if (m_style->getPropertyShorthand(individualProperty) != shorthandProperty) 644 continue; 645 if (m_style->isPropertyImplicit(individualProperty)) 646 continue; 647 String individualValue = m_style->getPropertyValue(individualProperty); 648 if (individualValue == "initial") 649 continue; 650 if (!builder.isEmpty()) 651 builder.append(' '); 652 builder.append(individualValue); 653 } 654 return builder.toString(); 655} 656 657String InspectorStyle::shorthandPriority(const String& shorthandProperty) const 658{ 659 String priority = m_style->getPropertyPriority(shorthandProperty); 660 if (priority.isEmpty()) { 661 for (unsigned i = 0; i < m_style->length(); ++i) { 662 String individualProperty = m_style->item(i); 663 if (m_style->getPropertyShorthand(individualProperty) != shorthandProperty) 664 continue; 665 priority = m_style->getPropertyPriority(individualProperty); 666 break; 667 } 668 } 669 return priority; 670} 671 672Vector<String> InspectorStyle::longhandProperties(const String& shorthandProperty) const 673{ 674 Vector<String> properties; 675 HashSet<String> foundProperties; 676 for (unsigned i = 0; i < m_style->length(); ++i) { 677 String individualProperty = m_style->item(i); 678 if (foundProperties.contains(individualProperty) || m_style->getPropertyShorthand(individualProperty) != shorthandProperty) 679 continue; 680 681 foundProperties.add(individualProperty); 682 properties.append(individualProperty); 683 } 684 return properties; 685} 686 687NewLineAndWhitespace& InspectorStyle::newLineAndWhitespaceDelimiters() const 688{ 689 DEPRECATED_DEFINE_STATIC_LOCAL(String, defaultPrefix, (ASCIILiteral(" "))); 690 691 if (m_formatAcquired) 692 return m_format; 693 694 RefPtr<CSSRuleSourceData> sourceData = extractSourceData(); 695 Vector<CSSPropertySourceData>* sourcePropertyData = sourceData ? &(sourceData->styleSourceData->propertyData) : nullptr; 696 int propertyCount; 697 if (!sourcePropertyData || !(propertyCount = sourcePropertyData->size())) { 698 m_format.first = "\n"; 699 m_format.second = defaultPrefix; 700 return m_format; // Do not remember the default formatting and attempt to acquire it later. 701 } 702 703 String text; 704 bool success = styleText(&text); 705 ASSERT_UNUSED(success, success); 706 707 m_formatAcquired = true; 708 709 String candidatePrefix = defaultPrefix; 710 StringBuilder formatLineFeed; 711 StringBuilder prefix; 712 int scanStart = 0; 713 int propertyIndex = 0; 714 bool isFullPrefixScanned = false; 715 bool lineFeedTerminated = false; 716 while (propertyIndex < propertyCount) { 717 const WebCore::CSSPropertySourceData& currentProperty = sourcePropertyData->at(propertyIndex++); 718 719 bool processNextProperty = false; 720 int scanEnd = currentProperty.range.start; 721 for (int i = scanStart; i < scanEnd; ++i) { 722 UChar ch = text[i]; 723 bool isLineFeed = isHTMLLineBreak(ch); 724 if (isLineFeed) { 725 if (!lineFeedTerminated) 726 formatLineFeed.append(ch); 727 prefix.clear(); 728 } else if (isHTMLSpace(ch)) 729 prefix.append(ch); 730 else { 731 candidatePrefix = prefix.toString(); 732 prefix.clear(); 733 scanStart = currentProperty.range.end; 734 ++propertyIndex; 735 processNextProperty = true; 736 break; 737 } 738 if (!isLineFeed && formatLineFeed.length()) 739 lineFeedTerminated = true; 740 } 741 if (!processNextProperty) { 742 isFullPrefixScanned = true; 743 break; 744 } 745 } 746 747 m_format.first = formatLineFeed.toString(); 748 m_format.second = isFullPrefixScanned ? prefix.toString() : candidatePrefix; 749 return m_format; 750} 751 752PassRefPtr<InspectorStyleSheet> InspectorStyleSheet::create(InspectorPageAgent* pageAgent, const String& id, PassRefPtr<CSSStyleSheet> pageStyleSheet, Inspector::TypeBuilder::CSS::StyleSheetOrigin::Enum origin, const String& documentURL, Listener* listener) 753{ 754 return adoptRef(new InspectorStyleSheet(pageAgent, id, pageStyleSheet, origin, documentURL, listener)); 755} 756 757// static 758String InspectorStyleSheet::styleSheetURL(CSSStyleSheet* pageStyleSheet) 759{ 760 if (pageStyleSheet && !pageStyleSheet->contents().baseURL().isEmpty()) 761 return pageStyleSheet->contents().baseURL().string(); 762 return emptyString(); 763} 764 765InspectorStyleSheet::InspectorStyleSheet(InspectorPageAgent* pageAgent, const String& id, PassRefPtr<CSSStyleSheet> pageStyleSheet, Inspector::TypeBuilder::CSS::StyleSheetOrigin::Enum origin, const String& documentURL, Listener* listener) 766 : m_pageAgent(pageAgent) 767 , m_id(id) 768 , m_pageStyleSheet(pageStyleSheet) 769 , m_origin(origin) 770 , m_documentURL(documentURL) 771 , m_isRevalidating(false) 772 , m_listener(listener) 773{ 774 m_parsedStyleSheet = new ParsedStyleSheet(); 775} 776 777InspectorStyleSheet::~InspectorStyleSheet() 778{ 779 delete m_parsedStyleSheet; 780} 781 782String InspectorStyleSheet::finalURL() const 783{ 784 String url = styleSheetURL(m_pageStyleSheet.get()); 785 return url.isEmpty() ? m_documentURL : url; 786} 787 788void InspectorStyleSheet::reparseStyleSheet(const String& text) 789{ 790 { 791 // Have a separate scope for clearRules() (bug 95324). 792 CSSStyleSheet::RuleMutationScope mutationScope(m_pageStyleSheet.get()); 793 m_pageStyleSheet->contents().clearRules(); 794 } 795 { 796 CSSStyleSheet::RuleMutationScope mutationScope(m_pageStyleSheet.get()); 797 m_pageStyleSheet->contents().parseString(text); 798 m_pageStyleSheet->clearChildRuleCSSOMWrappers(); 799 m_inspectorStyles.clear(); 800 fireStyleSheetChanged(); 801 } 802} 803 804bool InspectorStyleSheet::setText(const String& text, ExceptionCode& ec) 805{ 806 if (!checkPageStyleSheet(ec)) 807 return false; 808 if (!m_parsedStyleSheet) 809 return false; 810 811 m_parsedStyleSheet->setText(text); 812 m_flatRules.clear(); 813 814 return true; 815} 816 817String InspectorStyleSheet::ruleSelector(const InspectorCSSId& id, ExceptionCode& ec) 818{ 819 CSSStyleRule* rule = ruleForId(id); 820 if (!rule) { 821 ec = NOT_FOUND_ERR; 822 return ""; 823 } 824 return rule->selectorText(); 825} 826 827bool InspectorStyleSheet::setRuleSelector(const InspectorCSSId& id, const String& selector, ExceptionCode& ec) 828{ 829 if (!checkPageStyleSheet(ec)) 830 return false; 831 CSSStyleRule* rule = ruleForId(id); 832 if (!rule) { 833 ec = NOT_FOUND_ERR; 834 return false; 835 } 836 CSSStyleSheet* styleSheet = rule->parentStyleSheet(); 837 if (!styleSheet || !ensureParsedDataReady()) { 838 ec = NOT_FOUND_ERR; 839 return false; 840 } 841 842 rule->setSelectorText(selector); 843 RefPtr<CSSRuleSourceData> sourceData = ruleSourceDataFor(rule->style()); 844 if (!sourceData) { 845 ec = NOT_FOUND_ERR; 846 return false; 847 } 848 849 String sheetText = m_parsedStyleSheet->text(); 850 sheetText.replace(sourceData->ruleHeaderRange.start, sourceData->ruleHeaderRange.length(), selector); 851 m_parsedStyleSheet->setText(sheetText); 852 fireStyleSheetChanged(); 853 return true; 854} 855 856static bool checkStyleRuleSelector(Document* document, const String& selector) 857{ 858 CSSSelectorList selectorList; 859 createCSSParser(document)->parseSelector(selector, selectorList); 860 return selectorList.isValid(); 861} 862 863CSSStyleRule* InspectorStyleSheet::addRule(const String& selector, ExceptionCode& ec) 864{ 865 if (!checkPageStyleSheet(ec)) 866 return nullptr; 867 if (!checkStyleRuleSelector(m_pageStyleSheet->ownerDocument(), selector)) { 868 ec = SYNTAX_ERR; 869 return nullptr; 870 } 871 872 String text; 873 bool success = getText(&text); 874 if (!success) { 875 ec = NOT_FOUND_ERR; 876 return nullptr; 877 } 878 StringBuilder styleSheetText; 879 styleSheetText.append(text); 880 881 m_pageStyleSheet->addRule(selector, "", ec); 882 if (ec) 883 return nullptr; 884 ASSERT(m_pageStyleSheet->length()); 885 unsigned lastRuleIndex = m_pageStyleSheet->length() - 1; 886 CSSRule* rule = m_pageStyleSheet->item(lastRuleIndex); 887 ASSERT(rule); 888 889 CSSStyleRule* styleRule = InspectorCSSAgent::asCSSStyleRule(rule); 890 if (!styleRule) { 891 // What we just added has to be a CSSStyleRule - we cannot handle other types of rules yet. 892 // If it is not a style rule, pretend we never touched the stylesheet. 893 m_pageStyleSheet->deleteRule(lastRuleIndex, ASSERT_NO_EXCEPTION); 894 ec = SYNTAX_ERR; 895 return nullptr; 896 } 897 898 if (!styleSheetText.isEmpty()) 899 styleSheetText.append('\n'); 900 901 styleSheetText.append(selector); 902 styleSheetText.appendLiteral(" {}"); 903 // Using setText() as this operation changes the style sheet rule set. 904 setText(styleSheetText.toString(), ASSERT_NO_EXCEPTION); 905 906 fireStyleSheetChanged(); 907 908 return styleRule; 909} 910 911bool InspectorStyleSheet::deleteRule(const InspectorCSSId& id, ExceptionCode& ec) 912{ 913 if (!checkPageStyleSheet(ec)) 914 return false; 915 RefPtr<CSSStyleRule> rule = ruleForId(id); 916 if (!rule) { 917 ec = NOT_FOUND_ERR; 918 return false; 919 } 920 CSSStyleSheet* styleSheet = rule->parentStyleSheet(); 921 if (!styleSheet || !ensureParsedDataReady()) { 922 ec = NOT_FOUND_ERR; 923 return false; 924 } 925 926 RefPtr<CSSRuleSourceData> sourceData = ruleSourceDataFor(rule->style()); 927 if (!sourceData) { 928 ec = NOT_FOUND_ERR; 929 return false; 930 } 931 932 styleSheet->deleteRule(id.ordinal(), ec); 933 // |rule| MAY NOT be addressed after this line! 934 935 if (ec) 936 return false; 937 938 String sheetText = m_parsedStyleSheet->text(); 939 sheetText.remove(sourceData->ruleHeaderRange.start, sourceData->ruleBodyRange.end - sourceData->ruleHeaderRange.start + 1); 940 setText(sheetText, ASSERT_NO_EXCEPTION); 941 fireStyleSheetChanged(); 942 return true; 943} 944 945CSSStyleRule* InspectorStyleSheet::ruleForId(const InspectorCSSId& id) const 946{ 947 if (!m_pageStyleSheet) 948 return nullptr; 949 950 ASSERT(!id.isEmpty()); 951 ensureFlatRules(); 952 return id.ordinal() >= m_flatRules.size() ? nullptr : m_flatRules.at(id.ordinal()).get(); 953 954} 955 956PassRefPtr<Inspector::TypeBuilder::CSS::CSSStyleSheetBody> InspectorStyleSheet::buildObjectForStyleSheet() 957{ 958 CSSStyleSheet* styleSheet = pageStyleSheet(); 959 if (!styleSheet) 960 return nullptr; 961 962 RefPtr<CSSRuleList> cssRuleList = asCSSRuleList(styleSheet); 963 964 RefPtr<Inspector::TypeBuilder::CSS::CSSStyleSheetBody> result = Inspector::TypeBuilder::CSS::CSSStyleSheetBody::create() 965 .setStyleSheetId(id()) 966 .setRules(buildArrayForRuleList(cssRuleList.get())); 967 968 String styleSheetText; 969 bool success = getText(&styleSheetText); 970 if (success) 971 result->setText(styleSheetText); 972 973 return result.release(); 974} 975 976PassRefPtr<Inspector::TypeBuilder::CSS::CSSStyleSheetHeader> InspectorStyleSheet::buildObjectForStyleSheetInfo() 977{ 978 CSSStyleSheet* styleSheet = pageStyleSheet(); 979 if (!styleSheet) 980 return nullptr; 981 982 Document* document = styleSheet->ownerDocument(); 983 Frame* frame = document ? document->frame() : nullptr; 984 RefPtr<Inspector::TypeBuilder::CSS::CSSStyleSheetHeader> result = Inspector::TypeBuilder::CSS::CSSStyleSheetHeader::create() 985 .setStyleSheetId(id()) 986 .setOrigin(m_origin) 987 .setDisabled(styleSheet->disabled()) 988 .setSourceURL(finalURL()) 989 .setTitle(styleSheet->title()) 990 .setFrameId(m_pageAgent->frameId(frame)); 991 992 return result.release(); 993} 994 995static PassRefPtr<Inspector::TypeBuilder::Array<String>> selectorsFromSource(const CSSRuleSourceData* sourceData, const String& sheetText) 996{ 997 DEPRECATED_DEFINE_STATIC_LOCAL(JSC::Yarr::RegularExpression, comment, ("/\\*[^]*?\\*/", TextCaseSensitive, JSC::Yarr::MultilineEnabled)); 998 RefPtr<Inspector::TypeBuilder::Array<String>> result = Inspector::TypeBuilder::Array<String>::create(); 999 const SelectorRangeList& ranges = sourceData->selectorRanges; 1000 for (size_t i = 0, size = ranges.size(); i < size; ++i) { 1001 const SourceRange& range = ranges.at(i); 1002 String selector = sheetText.substring(range.start, range.length()); 1003 1004 // We don't want to see any comments in the selector components, only the meaningful parts. 1005 replace(selector, comment, ""); 1006 result->addItem(selector.stripWhiteSpace()); 1007 } 1008 return result.release(); 1009} 1010 1011PassRefPtr<Inspector::TypeBuilder::CSS::SelectorList> InspectorStyleSheet::buildObjectForSelectorList(CSSStyleRule* rule) 1012{ 1013 RefPtr<CSSRuleSourceData> sourceData; 1014 if (ensureParsedDataReady()) 1015 sourceData = ruleSourceDataFor(rule->style()); 1016 RefPtr<Inspector::TypeBuilder::Array<String>> selectors; 1017 1018 // This intentionally does not rely on the source data to avoid catching the trailing comments (before the declaration starting '{'). 1019 String selectorText = rule->selectorText(); 1020 1021 if (sourceData) 1022 selectors = selectorsFromSource(sourceData.get(), m_parsedStyleSheet->text()); 1023 else { 1024 selectors = Inspector::TypeBuilder::Array<String>::create(); 1025 const CSSSelectorList& selectorList = rule->styleRule()->selectorList(); 1026 for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(selector)) 1027 selectors->addItem(selector->selectorText()); 1028 } 1029 RefPtr<Inspector::TypeBuilder::CSS::SelectorList> result = Inspector::TypeBuilder::CSS::SelectorList::create() 1030 .setSelectors(selectors) 1031 .setText(selectorText) 1032 .release(); 1033 if (sourceData) 1034 result->setRange(buildSourceRangeObject(sourceData->ruleHeaderRange, lineEndings().get())); 1035 return result.release(); 1036} 1037 1038PassRefPtr<Inspector::TypeBuilder::CSS::CSSRule> InspectorStyleSheet::buildObjectForRule(CSSStyleRule* rule) 1039{ 1040 CSSStyleSheet* styleSheet = pageStyleSheet(); 1041 if (!styleSheet) 1042 return nullptr; 1043 1044 RefPtr<Inspector::TypeBuilder::CSS::CSSRule> result = Inspector::TypeBuilder::CSS::CSSRule::create() 1045 .setSelectorList(buildObjectForSelectorList(rule)) 1046 .setSourceLine(rule->styleRule()->sourceLine()) 1047 .setOrigin(m_origin) 1048 .setStyle(buildObjectForStyle(rule->style())); 1049 1050 // "sourceURL" is present only for regular rules, otherwise "origin" should be used in the frontend. 1051 if (m_origin == Inspector::TypeBuilder::CSS::StyleSheetOrigin::Regular) 1052 result->setSourceURL(finalURL()); 1053 1054 if (canBind()) { 1055 InspectorCSSId id(ruleId(rule)); 1056 if (!id.isEmpty()) 1057 result->setRuleId(id.asProtocolValue<Inspector::TypeBuilder::CSS::CSSRuleId>()); 1058 } 1059 1060 RefPtr<Array<Inspector::TypeBuilder::CSS::CSSMedia>> mediaArray = Array<Inspector::TypeBuilder::CSS::CSSMedia>::create(); 1061 1062 fillMediaListChain(rule, mediaArray.get()); 1063 if (mediaArray->length()) 1064 result->setMedia(mediaArray.release()); 1065 1066 return result.release(); 1067} 1068 1069PassRefPtr<Inspector::TypeBuilder::CSS::CSSStyle> InspectorStyleSheet::buildObjectForStyle(CSSStyleDeclaration* style) 1070{ 1071 RefPtr<CSSRuleSourceData> sourceData; 1072 if (ensureParsedDataReady()) 1073 sourceData = ruleSourceDataFor(style); 1074 1075 InspectorCSSId id = ruleOrStyleId(style); 1076 if (id.isEmpty()) { 1077 RefPtr<Inspector::TypeBuilder::CSS::CSSStyle> bogusStyle = Inspector::TypeBuilder::CSS::CSSStyle::create() 1078 .setCssProperties(Array<Inspector::TypeBuilder::CSS::CSSProperty>::create()) 1079 .setShorthandEntries(Array<Inspector::TypeBuilder::CSS::ShorthandEntry>::create()); 1080 return bogusStyle.release(); 1081 } 1082 RefPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id); 1083 RefPtr<Inspector::TypeBuilder::CSS::CSSStyle> result = inspectorStyle->buildObjectForStyle(); 1084 1085 // Style text cannot be retrieved without stylesheet, so set cssText here. 1086 if (sourceData) { 1087 String sheetText; 1088 bool success = getText(&sheetText); 1089 if (success) { 1090 const SourceRange& bodyRange = sourceData->ruleBodyRange; 1091 result->setCssText(sheetText.substring(bodyRange.start, bodyRange.end - bodyRange.start)); 1092 } 1093 } 1094 1095 return result.release(); 1096} 1097 1098bool InspectorStyleSheet::setStyleText(const InspectorCSSId& id, const String& text, String* oldText, ExceptionCode& ec) 1099{ 1100 RefPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id); 1101 if (!inspectorStyle) { 1102 ec = NOT_FOUND_ERR; 1103 return false; 1104 } 1105 1106 if (oldText && !inspectorStyle->getText(oldText)) 1107 return false; 1108 1109 bool success = inspectorStyle->setText(text, ec); 1110 if (success) 1111 fireStyleSheetChanged(); 1112 return success; 1113} 1114 1115bool InspectorStyleSheet::setPropertyText(const InspectorCSSId& id, unsigned propertyIndex, const String& text, bool overwrite, String* oldText, ExceptionCode& ec) 1116{ 1117 RefPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id); 1118 if (!inspectorStyle) { 1119 ec = NOT_FOUND_ERR; 1120 return false; 1121 } 1122 1123 bool success = inspectorStyle->setPropertyText(propertyIndex, text, overwrite, oldText, ec); 1124 if (success) 1125 fireStyleSheetChanged(); 1126 return success; 1127} 1128 1129bool InspectorStyleSheet::toggleProperty(const InspectorCSSId& id, unsigned propertyIndex, bool disable, ExceptionCode& ec) 1130{ 1131 RefPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id); 1132 if (!inspectorStyle) { 1133 ec = NOT_FOUND_ERR; 1134 return false; 1135 } 1136 1137 bool success = inspectorStyle->toggleProperty(propertyIndex, disable, ec); 1138 if (success) { 1139 if (disable) 1140 rememberInspectorStyle(inspectorStyle); 1141 else if (!inspectorStyle->hasDisabledProperties()) 1142 forgetInspectorStyle(inspectorStyle->cssStyle()); 1143 fireStyleSheetChanged(); 1144 } 1145 return success; 1146} 1147 1148bool InspectorStyleSheet::getText(String* result) const 1149{ 1150 if (!ensureText()) 1151 return false; 1152 *result = m_parsedStyleSheet->text(); 1153 return true; 1154} 1155 1156CSSStyleDeclaration* InspectorStyleSheet::styleForId(const InspectorCSSId& id) const 1157{ 1158 CSSStyleRule* rule = ruleForId(id); 1159 if (!rule) 1160 return nullptr; 1161 1162 return rule->style(); 1163} 1164 1165void InspectorStyleSheet::fireStyleSheetChanged() 1166{ 1167 if (m_listener) 1168 m_listener->styleSheetChanged(this); 1169} 1170 1171PassRefPtr<InspectorStyle> InspectorStyleSheet::inspectorStyleForId(const InspectorCSSId& id) 1172{ 1173 CSSStyleDeclaration* style = styleForId(id); 1174 if (!style) 1175 return nullptr; 1176 1177 InspectorStyleMap::iterator it = m_inspectorStyles.find(style); 1178 if (it == m_inspectorStyles.end()) { 1179 RefPtr<InspectorStyle> inspectorStyle = InspectorStyle::create(id, style, this); 1180 return inspectorStyle.release(); 1181 } 1182 return it->value; 1183} 1184 1185void InspectorStyleSheet::rememberInspectorStyle(RefPtr<InspectorStyle> inspectorStyle) 1186{ 1187 m_inspectorStyles.set(inspectorStyle->cssStyle(), inspectorStyle); 1188} 1189 1190void InspectorStyleSheet::forgetInspectorStyle(CSSStyleDeclaration* style) 1191{ 1192 m_inspectorStyles.remove(style); 1193} 1194 1195InspectorCSSId InspectorStyleSheet::ruleOrStyleId(CSSStyleDeclaration* style) const 1196{ 1197 unsigned index = ruleIndexByStyle(style); 1198 if (index != UINT_MAX) 1199 return InspectorCSSId(id(), index); 1200 return InspectorCSSId(); 1201} 1202 1203Document* InspectorStyleSheet::ownerDocument() const 1204{ 1205 return m_pageStyleSheet->ownerDocument(); 1206} 1207 1208RefPtr<CSSRuleSourceData> InspectorStyleSheet::ruleSourceDataFor(CSSStyleDeclaration* style) const 1209{ 1210 return m_parsedStyleSheet->ruleSourceDataAt(ruleIndexByStyle(style)); 1211} 1212 1213std::unique_ptr<Vector<size_t>> InspectorStyleSheet::lineEndings() const 1214{ 1215 if (!m_parsedStyleSheet->hasText()) 1216 return nullptr; 1217 return ContentSearchUtilities::lineEndings(m_parsedStyleSheet->text()); 1218} 1219 1220unsigned InspectorStyleSheet::ruleIndexByStyle(CSSStyleDeclaration* pageStyle) const 1221{ 1222 ensureFlatRules(); 1223 unsigned index = 0; 1224 for (unsigned i = 0, size = m_flatRules.size(); i < size; ++i) { 1225 if (m_flatRules.at(i)->style() == pageStyle) 1226 return index; 1227 1228 ++index; 1229 } 1230 return UINT_MAX; 1231} 1232 1233bool InspectorStyleSheet::checkPageStyleSheet(ExceptionCode& ec) const 1234{ 1235 if (!m_pageStyleSheet) { 1236 ec = NOT_SUPPORTED_ERR; 1237 return false; 1238 } 1239 return true; 1240} 1241 1242bool InspectorStyleSheet::ensureParsedDataReady() 1243{ 1244 return ensureText() && ensureSourceData(); 1245} 1246 1247bool InspectorStyleSheet::ensureText() const 1248{ 1249 if (!m_parsedStyleSheet) 1250 return false; 1251 if (m_parsedStyleSheet->hasText()) 1252 return true; 1253 1254 String text; 1255 bool success = originalStyleSheetText(&text); 1256 if (success) 1257 m_parsedStyleSheet->setText(text); 1258 // No need to clear m_flatRules here - it's empty. 1259 1260 return success; 1261} 1262 1263bool InspectorStyleSheet::ensureSourceData() 1264{ 1265 if (m_parsedStyleSheet->hasSourceData()) 1266 return true; 1267 1268 if (!m_parsedStyleSheet->hasText()) 1269 return false; 1270 1271 RefPtr<StyleSheetContents> newStyleSheet = StyleSheetContents::create(); 1272 auto ruleSourceDataResult = std::make_unique<RuleSourceDataList>(); 1273 createCSSParser(m_pageStyleSheet->ownerDocument())->parseSheet(newStyleSheet.get(), m_parsedStyleSheet->text(), 0, ruleSourceDataResult.get()); 1274 m_parsedStyleSheet->setSourceData(WTF::move(ruleSourceDataResult)); 1275 return m_parsedStyleSheet->hasSourceData(); 1276} 1277 1278void InspectorStyleSheet::ensureFlatRules() const 1279{ 1280 // We are fine with redoing this for empty stylesheets as this will run fast. 1281 if (m_flatRules.isEmpty()) 1282 collectFlatRules(asCSSRuleList(pageStyleSheet()), &m_flatRules); 1283} 1284 1285bool InspectorStyleSheet::setStyleText(CSSStyleDeclaration* style, const String& text, ExceptionCode& ec) 1286{ 1287 if (!m_pageStyleSheet) 1288 return false; 1289 if (!ensureParsedDataReady()) 1290 return false; 1291 1292 String patchedStyleSheetText; 1293 bool success = styleSheetTextWithChangedStyle(style, text, &patchedStyleSheetText); 1294 if (!success) 1295 return false; 1296 1297 InspectorCSSId id = ruleOrStyleId(style); 1298 if (id.isEmpty()) 1299 return false; 1300 1301 style->setCssText(text, ec); 1302 if (!ec) 1303 m_parsedStyleSheet->setText(patchedStyleSheetText); 1304 1305 return !ec; 1306} 1307 1308bool InspectorStyleSheet::styleSheetTextWithChangedStyle(CSSStyleDeclaration* style, const String& newStyleText, String* result) 1309{ 1310 if (!style) 1311 return false; 1312 1313 if (!ensureParsedDataReady()) 1314 return false; 1315 1316 RefPtr<CSSRuleSourceData> sourceData = ruleSourceDataFor(style); 1317 unsigned bodyStart = sourceData->ruleBodyRange.start; 1318 unsigned bodyEnd = sourceData->ruleBodyRange.end; 1319 ASSERT(bodyStart <= bodyEnd); 1320 1321 String text = m_parsedStyleSheet->text(); 1322 ASSERT_WITH_SECURITY_IMPLICATION(bodyEnd <= text.length()); // bodyEnd is exclusive 1323 1324 text.replace(bodyStart, bodyEnd - bodyStart, newStyleText); 1325 *result = text; 1326 return true; 1327} 1328 1329InspectorCSSId InspectorStyleSheet::ruleId(CSSStyleRule* rule) const 1330{ 1331 return ruleOrStyleId(rule->style()); 1332} 1333 1334void InspectorStyleSheet::revalidateStyle(CSSStyleDeclaration* pageStyle) 1335{ 1336 if (m_isRevalidating) 1337 return; 1338 1339 m_isRevalidating = true; 1340 ensureFlatRules(); 1341 for (unsigned i = 0, size = m_flatRules.size(); i < size; ++i) { 1342 CSSStyleRule* parsedRule = m_flatRules.at(i).get(); 1343 if (parsedRule->style() == pageStyle) { 1344 if (parsedRule->styleRule()->properties().asText() != pageStyle->cssText()) { 1345 // Clear the disabled properties for the invalid style here. 1346 m_inspectorStyles.remove(pageStyle); 1347 1348 ExceptionCode ec = 0; 1349 setStyleText(pageStyle, pageStyle->cssText(), ec); 1350 } 1351 break; 1352 } 1353 } 1354 m_isRevalidating = false; 1355} 1356 1357bool InspectorStyleSheet::originalStyleSheetText(String* result) const 1358{ 1359 bool success = inlineStyleSheetText(result); 1360 if (!success) 1361 success = resourceStyleSheetText(result); 1362 return success; 1363} 1364 1365bool InspectorStyleSheet::resourceStyleSheetText(String* result) const 1366{ 1367 if (m_origin == Inspector::TypeBuilder::CSS::StyleSheetOrigin::User || m_origin == Inspector::TypeBuilder::CSS::StyleSheetOrigin::UserAgent) 1368 return false; 1369 1370 if (!m_pageStyleSheet || !ownerDocument() || !ownerDocument()->frame()) 1371 return false; 1372 1373 String error; 1374 bool base64Encoded; 1375 InspectorPageAgent::resourceContent(&error, ownerDocument()->frame(), URL(ParsedURLString, m_pageStyleSheet->href()), result, &base64Encoded); 1376 return error.isEmpty() && !base64Encoded; 1377} 1378 1379bool InspectorStyleSheet::inlineStyleSheetText(String* result) const 1380{ 1381 if (!m_pageStyleSheet) 1382 return false; 1383 1384 Node* ownerNode = m_pageStyleSheet->ownerNode(); 1385 if (!ownerNode || !ownerNode->isElementNode()) 1386 return false; 1387 Element* ownerElement = toElement(ownerNode); 1388 1389 if (!isHTMLStyleElement(ownerElement) && !ownerElement->hasTagName(SVGNames::styleTag)) 1390 return false; 1391 *result = ownerElement->textContent(); 1392 return true; 1393} 1394 1395PassRefPtr<Inspector::TypeBuilder::Array<Inspector::TypeBuilder::CSS::CSSRule>> InspectorStyleSheet::buildArrayForRuleList(CSSRuleList* ruleList) 1396{ 1397 RefPtr<Inspector::TypeBuilder::Array<Inspector::TypeBuilder::CSS::CSSRule>> result = Inspector::TypeBuilder::Array<Inspector::TypeBuilder::CSS::CSSRule>::create(); 1398 if (!ruleList) 1399 return result.release(); 1400 1401 RefPtr<CSSRuleList> refRuleList = ruleList; 1402 CSSStyleRuleVector rules; 1403 collectFlatRules(refRuleList, &rules); 1404 1405 for (unsigned i = 0, size = rules.size(); i < size; ++i) 1406 result->addItem(buildObjectForRule(rules.at(i).get())); 1407 1408 return result.release(); 1409} 1410 1411void InspectorStyleSheet::collectFlatRules(PassRefPtr<CSSRuleList> ruleList, CSSStyleRuleVector* result) 1412{ 1413 if (!ruleList) 1414 return; 1415 1416 for (unsigned i = 0, size = ruleList->length(); i < size; ++i) { 1417 CSSRule* rule = ruleList->item(i); 1418 CSSStyleRule* styleRule = InspectorCSSAgent::asCSSStyleRule(rule); 1419 if (styleRule) 1420 result->append(styleRule); 1421 else { 1422 RefPtr<CSSRuleList> childRuleList = asCSSRuleList(rule); 1423 if (childRuleList) 1424 collectFlatRules(childRuleList, result); 1425 } 1426 } 1427} 1428 1429PassRefPtr<InspectorStyleSheetForInlineStyle> InspectorStyleSheetForInlineStyle::create(InspectorPageAgent* pageAgent, const String& id, PassRefPtr<Element> element, Inspector::TypeBuilder::CSS::StyleSheetOrigin::Enum origin, Listener* listener) 1430{ 1431 return adoptRef(new InspectorStyleSheetForInlineStyle(pageAgent, id, element, origin, listener)); 1432} 1433 1434InspectorStyleSheetForInlineStyle::InspectorStyleSheetForInlineStyle(InspectorPageAgent* pageAgent, const String& id, PassRefPtr<Element> element, Inspector::TypeBuilder::CSS::StyleSheetOrigin::Enum origin, Listener* listener) 1435 : InspectorStyleSheet(pageAgent, id, nullptr, origin, "", listener) 1436 , m_element(element) 1437 , m_ruleSourceData(nullptr) 1438 , m_isStyleTextValid(false) 1439{ 1440 ASSERT(m_element); 1441 m_inspectorStyle = InspectorStyle::create(InspectorCSSId(id, 0), inlineStyle(), this); 1442 m_styleText = m_element->isStyledElement() ? m_element->getAttribute("style").string() : String(); 1443} 1444 1445void InspectorStyleSheetForInlineStyle::didModifyElementAttribute() 1446{ 1447 m_isStyleTextValid = false; 1448 if (m_element->isStyledElement() && m_element->style() != m_inspectorStyle->cssStyle()) 1449 m_inspectorStyle = InspectorStyle::create(InspectorCSSId(id(), 0), inlineStyle(), this); 1450 m_ruleSourceData.clear(); 1451} 1452 1453bool InspectorStyleSheetForInlineStyle::getText(String* result) const 1454{ 1455 if (!m_isStyleTextValid) { 1456 m_styleText = elementStyleText(); 1457 m_isStyleTextValid = true; 1458 } 1459 *result = m_styleText; 1460 return true; 1461} 1462 1463bool InspectorStyleSheetForInlineStyle::setStyleText(CSSStyleDeclaration* style, const String& text, ExceptionCode& ec) 1464{ 1465 ASSERT_UNUSED(style, style == inlineStyle()); 1466 1467 { 1468 InspectorCSSAgent::InlineStyleOverrideScope overrideScope(&m_element->document()); 1469 m_element->setAttribute("style", text, ec); 1470 } 1471 1472 m_styleText = text; 1473 m_isStyleTextValid = true; 1474 m_ruleSourceData.clear(); 1475 return !ec; 1476} 1477 1478std::unique_ptr<Vector<size_t>> InspectorStyleSheetForInlineStyle::lineEndings() const 1479{ 1480 return ContentSearchUtilities::lineEndings(elementStyleText()); 1481} 1482 1483Document* InspectorStyleSheetForInlineStyle::ownerDocument() const 1484{ 1485 return &m_element->document(); 1486} 1487 1488bool InspectorStyleSheetForInlineStyle::ensureParsedDataReady() 1489{ 1490 // The "style" property value can get changed indirectly, e.g. via element.style.borderWidth = "2px". 1491 const String& currentStyleText = elementStyleText(); 1492 if (m_styleText != currentStyleText) { 1493 m_ruleSourceData.clear(); 1494 m_styleText = currentStyleText; 1495 m_isStyleTextValid = true; 1496 } 1497 1498 if (m_ruleSourceData) 1499 return true; 1500 1501 m_ruleSourceData = CSSRuleSourceData::create(CSSRuleSourceData::STYLE_RULE); 1502 bool success = getStyleAttributeRanges(m_ruleSourceData.get()); 1503 if (!success) 1504 return false; 1505 1506 return true; 1507} 1508 1509PassRefPtr<InspectorStyle> InspectorStyleSheetForInlineStyle::inspectorStyleForId(const InspectorCSSId& id) 1510{ 1511 ASSERT_UNUSED(id, !id.ordinal()); 1512 return m_inspectorStyle; 1513} 1514 1515CSSStyleDeclaration* InspectorStyleSheetForInlineStyle::inlineStyle() const 1516{ 1517 return m_element->style(); 1518} 1519 1520const String& InspectorStyleSheetForInlineStyle::elementStyleText() const 1521{ 1522 return m_element->getAttribute("style").string(); 1523} 1524 1525bool InspectorStyleSheetForInlineStyle::getStyleAttributeRanges(CSSRuleSourceData* result) const 1526{ 1527 if (!m_element->isStyledElement()) 1528 return false; 1529 1530 if (m_styleText.isEmpty()) { 1531 result->ruleBodyRange.start = 0; 1532 result->ruleBodyRange.end = 0; 1533 return true; 1534 } 1535 1536 RefPtr<MutableStyleProperties> tempDeclaration = MutableStyleProperties::create(); 1537 createCSSParser(&m_element->document())->parseDeclaration(tempDeclaration.get(), m_styleText, result, &m_element->document().elementSheet().contents()); 1538 return true; 1539} 1540 1541} // namespace WebCore 1542 1543#endif // ENABLE(INSPECTOR) 1544