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