diff options
Diffstat (limited to 'Source/WebCore/inspector/InspectorStyleSheet.cpp')
-rw-r--r-- | Source/WebCore/inspector/InspectorStyleSheet.cpp | 1374 |
1 files changed, 701 insertions, 673 deletions
diff --git a/Source/WebCore/inspector/InspectorStyleSheet.cpp b/Source/WebCore/inspector/InspectorStyleSheet.cpp index 85cb2c461..b0c725ae2 100644 --- a/Source/WebCore/inspector/InspectorStyleSheet.cpp +++ b/Source/WebCore/inspector/InspectorStyleSheet.cpp @@ -23,16 +23,15 @@ */ #include "config.h" - -#if ENABLE(INSPECTOR) - #include "InspectorStyleSheet.h" -#include "CSSHostRule.h" #include "CSSImportRule.h" +#include "CSSKeyframesRule.h" #include "CSSMediaRule.h" #include "CSSParser.h" +#include "CSSParserObserver.h" #include "CSSPropertyNames.h" +#include "CSSPropertyParser.h" #include "CSSPropertySourceData.h" #include "CSSRule.h" #include "CSSRuleList.h" @@ -42,30 +41,28 @@ #include "ContentSecurityPolicy.h" #include "Document.h" #include "Element.h" +#include "ExceptionCode.h" #include "HTMLHeadElement.h" #include "HTMLNames.h" #include "HTMLParserIdioms.h" #include "HTMLStyleElement.h" #include "InspectorCSSAgent.h" #include "InspectorPageAgent.h" +#include "MediaList.h" #include "Node.h" -#include "SVGNames.h" +#include "SVGElement.h" +#include "SVGStyleElement.h" #include "StyleProperties.h" #include "StyleResolver.h" #include "StyleRule.h" #include "StyleRuleImport.h" #include "StyleSheetContents.h" #include "StyleSheetList.h" -#include "WebKitCSSKeyframesRule.h" #include <inspector/ContentSearchUtilities.h> -#include <inspector/InspectorValues.h> -#include <wtf/OwnPtr.h> -#include <wtf/PassOwnPtr.h> -#include <wtf/Vector.h> #include <wtf/text/StringBuilder.h> #include <yarr/RegularExpression.h> -using Inspector::TypeBuilder::Array; +using Inspector::Protocol::Array; using WebCore::RuleSourceDataList; using WebCore::CSSRuleSourceData; @@ -78,15 +75,15 @@ public: void setText(const String& text); bool hasText() const { return m_hasText; } RuleSourceDataList* sourceData() const { return m_sourceData.get(); } - void setSourceData(PassOwnPtr<RuleSourceDataList>); - bool hasSourceData() const { return m_sourceData; } - PassRefPtr<WebCore::CSSRuleSourceData> ruleSourceDataAt(unsigned) const; + void setSourceData(std::unique_ptr<RuleSourceDataList>); + bool hasSourceData() const { return m_sourceData != nullptr; } + WebCore::CSSRuleSourceData* ruleSourceDataAt(unsigned) const; private: String m_text; bool m_hasText; - OwnPtr<RuleSourceDataList> m_sourceData; + std::unique_ptr<RuleSourceDataList> m_sourceData; }; ParsedStyleSheet::ParsedStyleSheet() @@ -101,52 +98,286 @@ void ParsedStyleSheet::setText(const String& text) setSourceData(nullptr); } -static void flattenSourceData(RuleSourceDataList* dataList, RuleSourceDataList* target) +static void flattenSourceData(RuleSourceDataList& dataList, RuleSourceDataList& target) { - for (size_t i = 0; i < dataList->size(); ++i) { - RefPtr<CSSRuleSourceData>& data = dataList->at(i); - if (data->type == CSSRuleSourceData::STYLE_RULE) - target->append(data); - else if (data->type == CSSRuleSourceData::MEDIA_RULE) - flattenSourceData(&data->childRules, target); -#if ENABLE(SHADOW_DOM) - else if (data->type == CSSRuleSourceData::HOST_RULE) - flattenSourceData(&data->childRules, target); -#endif -#if ENABLE(CSS3_CONDITIONAL_RULES) - else if (data->type == CSSRuleSourceData::SUPPORTS_RULE) - flattenSourceData(&data->childRules, target); -#endif + for (auto& data : dataList) { + if (data->type == WebCore::StyleRule::Style) + target.append(data.copyRef()); + else if (data->type == WebCore::StyleRule::Media) + flattenSourceData(data->childRules, target); + else if (data->type == WebCore::StyleRule::Supports) + flattenSourceData(data->childRules, target); } } -void ParsedStyleSheet::setSourceData(PassOwnPtr<RuleSourceDataList> sourceData) +void ParsedStyleSheet::setSourceData(std::unique_ptr<RuleSourceDataList> sourceData) { if (!sourceData) { - m_sourceData.clear(); + m_sourceData.reset(); return; } - m_sourceData = adoptPtr(new RuleSourceDataList()); + m_sourceData = std::make_unique<RuleSourceDataList>(); // FIXME: This is a temporary solution to retain the original flat sourceData structure // containing only style rules, even though CSSParser now provides the full rule source data tree. // Normally, we should just assign m_sourceData = sourceData; - flattenSourceData(sourceData.get(), m_sourceData.get()); + flattenSourceData(*sourceData, *m_sourceData); } -PassRefPtr<WebCore::CSSRuleSourceData> ParsedStyleSheet::ruleSourceDataAt(unsigned index) const +WebCore::CSSRuleSourceData* ParsedStyleSheet::ruleSourceDataAt(unsigned index) const { if (!hasSourceData() || index >= m_sourceData->size()) return nullptr; - return m_sourceData->at(index); + return m_sourceData->at(index).ptr(); } using namespace Inspector; namespace WebCore { +static CSSParserContext parserContextForDocument(Document* document) +{ + return document ? CSSParserContext(*document) : strictCSSParserContext(); +} + +class StyleSheetHandler : public CSSParserObserver { +public: + StyleSheetHandler(const String& parsedText, Document* document, RuleSourceDataList* result) + : m_parsedText(parsedText) + , m_document(document) + , m_ruleSourceDataResult(result) + { + ASSERT(m_ruleSourceDataResult); + } + +private: + void startRuleHeader(StyleRule::Type, unsigned) override; + void endRuleHeader(unsigned) override; + void observeSelector(unsigned startOffset, unsigned endOffset) override; + void startRuleBody(unsigned) override; + void endRuleBody(unsigned) override; + void observeProperty(unsigned startOffset, unsigned endOffset, bool isImportant, bool isParsed) override; + void observeComment(unsigned startOffset, unsigned endOffset) override; + + Ref<CSSRuleSourceData> popRuleData(); + template <typename CharacterType> inline void setRuleHeaderEnd(const CharacterType*, unsigned); + void fixUnparsedPropertyRanges(CSSRuleSourceData*); + + const String& m_parsedText; + Document* m_document; + + RuleSourceDataList m_currentRuleDataStack; + RefPtr<CSSRuleSourceData> m_currentRuleData; + RuleSourceDataList* m_ruleSourceDataResult { nullptr }; +}; + +void StyleSheetHandler::startRuleHeader(StyleRule::Type type, unsigned offset) +{ + // Pop off data for a previous invalid rule. + if (m_currentRuleData) + m_currentRuleDataStack.removeLast(); + + auto data = CSSRuleSourceData::create(type); + data->ruleHeaderRange.start = offset; + m_currentRuleData = data.copyRef(); + m_currentRuleDataStack.append(WTFMove(data)); +} + +template <typename CharacterType> inline void StyleSheetHandler::setRuleHeaderEnd(const CharacterType* dataStart, unsigned listEndOffset) +{ + while (listEndOffset > 1) { + if (isHTMLSpace<CharacterType>(*(dataStart + listEndOffset - 1))) + --listEndOffset; + else + break; + } + + m_currentRuleDataStack.last()->ruleHeaderRange.end = listEndOffset; + if (!m_currentRuleDataStack.last()->selectorRanges.isEmpty()) + m_currentRuleDataStack.last()->selectorRanges.last().end = listEndOffset; +} + +void StyleSheetHandler::endRuleHeader(unsigned offset) +{ + ASSERT(!m_currentRuleDataStack.isEmpty()); + + if (m_parsedText.is8Bit()) + setRuleHeaderEnd<LChar>(m_parsedText.characters8(), offset); + else + setRuleHeaderEnd<UChar>(m_parsedText.characters16(), offset); +} + +void StyleSheetHandler::observeSelector(unsigned startOffset, unsigned endOffset) +{ + ASSERT(m_currentRuleDataStack.size()); + m_currentRuleDataStack.last()->selectorRanges.append(SourceRange(startOffset, endOffset)); +} + +void StyleSheetHandler::startRuleBody(unsigned offset) +{ + m_currentRuleData = nullptr; + ASSERT(!m_currentRuleDataStack.isEmpty()); + + // Skip the rule body opening brace. + if (m_parsedText[offset] == '{') + ++offset; + + m_currentRuleDataStack.last()->ruleBodyRange.start = offset; +} + +void StyleSheetHandler::endRuleBody(unsigned offset) +{ + ASSERT(!m_currentRuleDataStack.isEmpty()); + m_currentRuleDataStack.last()->ruleBodyRange.end = offset; + auto rule = popRuleData(); + fixUnparsedPropertyRanges(rule.ptr()); + if (m_currentRuleDataStack.isEmpty()) + m_ruleSourceDataResult->append(WTFMove(rule)); + else + m_currentRuleDataStack.last()->childRules.append(WTFMove(rule)); +} + +Ref<CSSRuleSourceData> StyleSheetHandler::popRuleData() +{ + ASSERT(!m_currentRuleDataStack.isEmpty()); + m_currentRuleData = nullptr; + auto data = WTFMove(m_currentRuleDataStack.last()); + m_currentRuleDataStack.removeLast(); + return data; +} + +template <typename CharacterType> +static inline void fixUnparsedProperties(const CharacterType* characters, CSSRuleSourceData* ruleData) +{ + Vector<CSSPropertySourceData>& propertyData = ruleData->styleSourceData->propertyData; + unsigned size = propertyData.size(); + if (!size) + return; + + unsigned styleStart = ruleData->ruleBodyRange.start; + + CSSPropertySourceData* nextData = &(propertyData.at(0)); + for (unsigned i = 0; i < size; ++i) { + CSSPropertySourceData* currentData = nextData; + nextData = i < size - 1 ? &(propertyData.at(i + 1)) : nullptr; + + if (currentData->parsedOk) + continue; + if (currentData->range.end > 0 && characters[styleStart + currentData->range.end - 1] == ';') + continue; + + unsigned propertyEnd; + if (!nextData) + propertyEnd = ruleData->ruleBodyRange.end - 1; + else + propertyEnd = styleStart + nextData->range.start - 1; + + while (isHTMLSpace<CharacterType>(characters[propertyEnd])) + --propertyEnd; + + // propertyEnd points at the last property text character. + unsigned newPropertyEnd = propertyEnd + styleStart + 1; + if (currentData->range.end != newPropertyEnd) { + currentData->range.end = newPropertyEnd; + unsigned valueStart = styleStart + currentData->range.start + currentData->name.length(); + while (valueStart < propertyEnd && characters[valueStart] != ':') + ++valueStart; + + // Shift past the ':'. + if (valueStart < propertyEnd) + ++valueStart; + + while (valueStart < propertyEnd && isHTMLSpace<CharacterType>(characters[valueStart])) + ++valueStart; + + // Need to exclude the trailing ';' from the property value. + currentData->value = String(characters + valueStart, propertyEnd - valueStart + (characters[propertyEnd] == ';' ? 0 : 1)); + } + } +} + +void StyleSheetHandler::fixUnparsedPropertyRanges(CSSRuleSourceData* ruleData) +{ + if (!ruleData->styleSourceData) + return; + + if (m_parsedText.is8Bit()) { + fixUnparsedProperties<LChar>(m_parsedText.characters8(), ruleData); + return; + } + + fixUnparsedProperties<UChar>(m_parsedText.characters16(), ruleData); +} + +void StyleSheetHandler::observeProperty(unsigned startOffset, unsigned endOffset, bool isImportant, bool isParsed) +{ + if (m_currentRuleDataStack.isEmpty() || !m_currentRuleDataStack.last()->styleSourceData) + return; + + ASSERT(endOffset <= m_parsedText.length()); + + // Include semicolon in the property text. + if (endOffset < m_parsedText.length() && m_parsedText[endOffset] == ';') + ++endOffset; + + ASSERT(startOffset < endOffset); + String propertyString = m_parsedText.substring(startOffset, endOffset - startOffset).stripWhiteSpace(); + if (propertyString.endsWith(';')) + propertyString = propertyString.left(propertyString.length() - 1); + size_t colonIndex = propertyString.find(':'); + ASSERT(colonIndex != notFound); + + String name = propertyString.left(colonIndex).stripWhiteSpace(); + String value = propertyString.substring(colonIndex + 1, propertyString.length()).stripWhiteSpace(); + + // FIXME-NEWPARSER: The property range is relative to the declaration start offset, but no + // good reason for it, and it complicates fixUnparsedProperties. + SourceRange& topRuleBodyRange = m_currentRuleDataStack.last()->ruleBodyRange; + m_currentRuleDataStack.last()->styleSourceData->propertyData.append(CSSPropertySourceData(name, value, isImportant, false, isParsed, SourceRange(startOffset - topRuleBodyRange.start, endOffset - topRuleBodyRange.start))); +} + +void StyleSheetHandler::observeComment(unsigned startOffset, unsigned endOffset) +{ + ASSERT(endOffset <= m_parsedText.length()); + + if (m_currentRuleDataStack.isEmpty() || !m_currentRuleDataStack.last()->ruleHeaderRange.end || !m_currentRuleDataStack.last()->styleSourceData) + return; + + // The lexer is not inside a property AND it is scanning a declaration-aware + // rule body. + String commentText = m_parsedText.substring(startOffset, endOffset - startOffset); + + ASSERT(commentText.startsWith("/*")); + commentText = commentText.substring(2); + + // Require well-formed comments. + if (!commentText.endsWith("*/")) + return; + commentText = commentText.substring(0, commentText.length() - 2).stripWhiteSpace(); + if (commentText.isEmpty()) + return; + + // FIXME: Use the actual rule type rather than STYLE_RULE? + RuleSourceDataList sourceData; + + StyleSheetHandler handler(commentText, m_document, &sourceData); + CSSParser::parseDeclarationForInspector(parserContextForDocument(m_document), commentText, handler); + Vector<CSSPropertySourceData>& commentPropertyData = sourceData.first()->styleSourceData->propertyData; + if (commentPropertyData.size() != 1) + return; + CSSPropertySourceData& propertyData = commentPropertyData.at(0); + bool parsedOk = propertyData.parsedOk || propertyData.name.startsWith("-moz-") || propertyData.name.startsWith("-o-") || propertyData.name.startsWith("-webkit-") || propertyData.name.startsWith("-ms-"); + if (!parsedOk || propertyData.range.length() != commentText.length()) + return; + + // FIXME-NEWPARSER: The property range is relative to the declaration start offset, but no + // good reason for it, and it complicates fixUnparsedProperties. + SourceRange& topRuleBodyRange = m_currentRuleDataStack.last()->ruleBodyRange; + m_currentRuleDataStack.last()->styleSourceData->propertyData.append(CSSPropertySourceData(propertyData.name, propertyData.value, false, true, true, SourceRange(startOffset - topRuleBodyRange.start, endOffset - topRuleBodyRange.start))); +} + enum MediaListSource { MediaListSourceLinkedSheet, MediaListSourceInlineSheet, @@ -154,92 +385,85 @@ enum MediaListSource { MediaListSourceImportRule }; -static PassRefPtr<Inspector::TypeBuilder::CSS::SourceRange> buildSourceRangeObject(const SourceRange& range, Vector<size_t>* lineEndings) +static RefPtr<Inspector::Protocol::CSS::SourceRange> buildSourceRangeObject(const SourceRange& range, Vector<size_t>* lineEndings, int* endingLine = nullptr) { if (!lineEndings) return nullptr; TextPosition start = ContentSearchUtilities::textPositionFromOffset(range.start, *lineEndings); TextPosition end = ContentSearchUtilities::textPositionFromOffset(range.end, *lineEndings); - RefPtr<Inspector::TypeBuilder::CSS::SourceRange> result = Inspector::TypeBuilder::CSS::SourceRange::create() + if (endingLine) + *endingLine = end.m_line.zeroBasedInt(); + + return Inspector::Protocol::CSS::SourceRange::create() .setStartLine(start.m_line.zeroBasedInt()) .setStartColumn(start.m_column.zeroBasedInt()) .setEndLine(end.m_line.zeroBasedInt()) - .setEndColumn(end.m_column.zeroBasedInt()); - return result.release(); + .setEndColumn(end.m_column.zeroBasedInt()) + .release(); } -static PassRefPtr<Inspector::TypeBuilder::CSS::CSSMedia> buildMediaObject(const MediaList* media, MediaListSource mediaListSource, const String& sourceURL) +static Ref<Inspector::Protocol::CSS::CSSMedia> buildMediaObject(const MediaList* media, MediaListSource mediaListSource, const String& sourceURL) { // Make certain compilers happy by initializing |source| up-front. - Inspector::TypeBuilder::CSS::CSSMedia::Source::Enum source = Inspector::TypeBuilder::CSS::CSSMedia::Source::InlineSheet; + Inspector::Protocol::CSS::CSSMedia::Source source = Inspector::Protocol::CSS::CSSMedia::Source::InlineSheet; switch (mediaListSource) { case MediaListSourceMediaRule: - source = Inspector::TypeBuilder::CSS::CSSMedia::Source::MediaRule; + source = Inspector::Protocol::CSS::CSSMedia::Source::MediaRule; break; case MediaListSourceImportRule: - source = Inspector::TypeBuilder::CSS::CSSMedia::Source::ImportRule; + source = Inspector::Protocol::CSS::CSSMedia::Source::ImportRule; break; case MediaListSourceLinkedSheet: - source = Inspector::TypeBuilder::CSS::CSSMedia::Source::LinkedSheet; + source = Inspector::Protocol::CSS::CSSMedia::Source::LinkedSheet; break; case MediaListSourceInlineSheet: - source = Inspector::TypeBuilder::CSS::CSSMedia::Source::InlineSheet; + source = Inspector::Protocol::CSS::CSSMedia::Source::InlineSheet; break; } - RefPtr<Inspector::TypeBuilder::CSS::CSSMedia> mediaObject = Inspector::TypeBuilder::CSS::CSSMedia::create() + auto mediaObject = Inspector::Protocol::CSS::CSSMedia::create() .setText(media->mediaText()) - .setSource(source); + .setSource(source) + .release(); if (!sourceURL.isEmpty()) { mediaObject->setSourceURL(sourceURL); mediaObject->setSourceLine(media->queries()->lastLine()); } - return mediaObject.release(); + return mediaObject; } -static PassRefPtr<CSSRuleList> asCSSRuleList(CSSStyleSheet* styleSheet) +static RefPtr<CSSRuleList> asCSSRuleList(CSSStyleSheet* styleSheet) { if (!styleSheet) return nullptr; RefPtr<StaticCSSRuleList> list = StaticCSSRuleList::create(); Vector<RefPtr<CSSRule>>& listRules = list->rules(); - for (unsigned i = 0, size = styleSheet->length(); i < size; ++i) { - CSSRule* item = styleSheet->item(i); - if (item->type() == CSSRule::CHARSET_RULE) - continue; - listRules.append(item); - } - return list.release(); + for (unsigned i = 0, size = styleSheet->length(); i < size; ++i) + listRules.append(styleSheet->item(i)); + return WTFMove(list); } -static PassRefPtr<CSSRuleList> asCSSRuleList(CSSRule* rule) +static RefPtr<CSSRuleList> asCSSRuleList(CSSRule* rule) { if (!rule) return nullptr; - if (rule->type() == CSSRule::MEDIA_RULE) - return static_cast<CSSMediaRule*>(rule)->cssRules(); + if (is<CSSMediaRule>(*rule)) + return &downcast<CSSMediaRule>(*rule).cssRules(); - if (rule->type() == CSSRule::WEBKIT_KEYFRAMES_RULE) - return static_cast<WebKitCSSKeyframesRule*>(rule)->cssRules(); + if (is<CSSKeyframesRule>(*rule)) + return &downcast<CSSKeyframesRule>(*rule).cssRules(); -#if ENABLE(SHADOW_DOM) - if (rule->type() == CSSRule::HOST_RULE) - return static_cast<CSSHostRule*>(rule)->cssRules(); -#endif - -#if ENABLE(CSS3_CONDITIONAL_RULES) - if (rule->type() == CSSRule::SUPPORTS_RULE) - return static_cast<CSSSupportsRule*>(rule)->cssRules(); -#endif + if (is<CSSSupportsRule>(*rule)) + return &downcast<CSSSupportsRule>(*rule).cssRules(); return nullptr; } -static void fillMediaListChain(CSSRule* rule, Array<Inspector::TypeBuilder::CSS::CSSMedia>* mediaArray) +static void fillMediaListChain(CSSRule* rule, Array<Inspector::Protocol::CSS::CSSMedia>& mediaArray) { MediaList* mediaList; CSSRule* parentRule = rule; @@ -247,14 +471,14 @@ static void fillMediaListChain(CSSRule* rule, Array<Inspector::TypeBuilder::CSS: while (parentRule) { CSSStyleSheet* parentStyleSheet = nullptr; bool isMediaRule = true; - if (parentRule->type() == CSSRule::MEDIA_RULE) { - CSSMediaRule* mediaRule = static_cast<CSSMediaRule*>(parentRule); - mediaList = mediaRule->media(); - parentStyleSheet = mediaRule->parentStyleSheet(); - } else if (parentRule->type() == CSSRule::IMPORT_RULE) { - CSSImportRule* importRule = static_cast<CSSImportRule*>(parentRule); - mediaList = importRule->media(); - parentStyleSheet = importRule->parentStyleSheet(); + if (is<CSSMediaRule>(*parentRule)) { + CSSMediaRule& mediaRule = downcast<CSSMediaRule>(*parentRule); + mediaList = mediaRule.media(); + parentStyleSheet = mediaRule.parentStyleSheet(); + } else if (is<CSSImportRule>(*parentRule)) { + CSSImportRule& importRule = downcast<CSSImportRule>(*parentRule); + mediaList = &importRule.media(); + parentStyleSheet = importRule.parentStyleSheet(); isMediaRule = false; } else mediaList = nullptr; @@ -264,10 +488,10 @@ static void fillMediaListChain(CSSRule* rule, Array<Inspector::TypeBuilder::CSS: if (sourceURL.isEmpty()) sourceURL = InspectorDOMAgent::documentURLString(parentStyleSheet->ownerDocument()); } else - sourceURL = ""; + sourceURL = emptyString(); if (mediaList && mediaList->length()) - mediaArray->addItem(buildMediaObject(mediaList, isMediaRule ? MediaListSourceMediaRule : MediaListSourceImportRule, sourceURL)); + mediaArray.addItem(buildMediaObject(mediaList, isMediaRule ? MediaListSourceMediaRule : MediaListSourceImportRule, sourceURL)); if (parentRule->parentRule()) parentRule = parentRule->parentRule(); @@ -282,8 +506,8 @@ static void fillMediaListChain(CSSRule* rule, Array<Inspector::TypeBuilder::CSS: else if (!styleSheet->contents().baseURL().isEmpty()) sourceURL = styleSheet->contents().baseURL(); else - sourceURL = ""; - mediaArray->addItem(buildMediaObject(mediaList, styleSheet->ownerNode() ? MediaListSourceLinkedSheet : MediaListSourceInlineSheet, sourceURL)); + sourceURL = emptyString(); + mediaArray.addItem(buildMediaObject(mediaList, styleSheet->ownerNode() ? MediaListSourceLinkedSheet : MediaListSourceInlineSheet, sourceURL)); } parentRule = styleSheet->ownerRule(); if (parentRule) @@ -294,21 +518,15 @@ static void fillMediaListChain(CSSRule* rule, Array<Inspector::TypeBuilder::CSS: } } -static PassOwnPtr<CSSParser> createCSSParser(Document* document) +Ref<InspectorStyle> InspectorStyle::create(const InspectorCSSId& styleId, RefPtr<CSSStyleDeclaration>&& style, InspectorStyleSheet* parentStyleSheet) { - return adoptPtr(new CSSParser(document ? CSSParserContext(*document) : strictCSSParserContext())); + return adoptRef(*new InspectorStyle(styleId, WTFMove(style), parentStyleSheet)); } -PassRefPtr<InspectorStyle> InspectorStyle::create(const InspectorCSSId& styleId, PassRefPtr<CSSStyleDeclaration> style, InspectorStyleSheet* parentStyleSheet) -{ - return adoptRef(new InspectorStyle(styleId, style, parentStyleSheet)); -} - -InspectorStyle::InspectorStyle(const InspectorCSSId& styleId, PassRefPtr<CSSStyleDeclaration> style, InspectorStyleSheet* parentStyleSheet) +InspectorStyle::InspectorStyle(const InspectorCSSId& styleId, RefPtr<CSSStyleDeclaration>&& style, InspectorStyleSheet* parentStyleSheet) : m_styleId(styleId) , m_style(style) , m_parentStyleSheet(parentStyleSheet) - , m_formatAcquired(false) { ASSERT(m_style); } @@ -317,11 +535,11 @@ InspectorStyle::~InspectorStyle() { } -PassRefPtr<Inspector::TypeBuilder::CSS::CSSStyle> InspectorStyle::buildObjectForStyle() const +RefPtr<Inspector::Protocol::CSS::CSSStyle> InspectorStyle::buildObjectForStyle() const { - RefPtr<Inspector::TypeBuilder::CSS::CSSStyle> result = styleWithProperties(); + Ref<Inspector::Protocol::CSS::CSSStyle> result = styleWithProperties(); if (!m_styleId.isEmpty()) - result->setStyleId(m_styleId.asProtocolValue<Inspector::TypeBuilder::CSS::CSSStyleId>()); + result->setStyleId(m_styleId.asProtocolValue<Inspector::Protocol::CSS::CSSStyleId>()); result->setWidth(m_style->getPropertyValue("width")); result->setHeight(m_style->getPropertyValue("height")); @@ -330,225 +548,116 @@ PassRefPtr<Inspector::TypeBuilder::CSS::CSSStyle> InspectorStyle::buildObjectFor if (sourceData) result->setRange(buildSourceRangeObject(sourceData->ruleBodyRange, m_parentStyleSheet->lineEndings().get())); - return result.release(); + return WTFMove(result); } -PassRefPtr<Inspector::TypeBuilder::Array<Inspector::TypeBuilder::CSS::CSSComputedStyleProperty>> InspectorStyle::buildArrayForComputedStyle() const +Ref<Inspector::Protocol::Array<Inspector::Protocol::CSS::CSSComputedStyleProperty>> InspectorStyle::buildArrayForComputedStyle() const { - RefPtr<Inspector::TypeBuilder::Array<Inspector::TypeBuilder::CSS::CSSComputedStyleProperty>> result = Inspector::TypeBuilder::Array<Inspector::TypeBuilder::CSS::CSSComputedStyleProperty>::create(); + auto result = Inspector::Protocol::Array<Inspector::Protocol::CSS::CSSComputedStyleProperty>::create(); Vector<InspectorStyleProperty> properties; populateAllProperties(&properties); - for (Vector<InspectorStyleProperty>::iterator it = properties.begin(), itEnd = properties.end(); it != itEnd; ++it) { - const CSSPropertySourceData& propertyEntry = it->sourceData; - RefPtr<Inspector::TypeBuilder::CSS::CSSComputedStyleProperty> entry = Inspector::TypeBuilder::CSS::CSSComputedStyleProperty::create() + for (auto& property : properties) { + const CSSPropertySourceData& propertyEntry = property.sourceData; + auto entry = Inspector::Protocol::CSS::CSSComputedStyleProperty::create() .setName(propertyEntry.name) - .setValue(propertyEntry.value); - result->addItem(entry); - } - - return result.release(); -} - -// This method does the following preprocessing of |propertyText| with |overwrite| == false and |index| past the last active property: -// - If the last property (if present) has no closing ";", the ";" is prepended to the current |propertyText| value. -// - A heuristic formatting is attempted to retain the style structure. -// -// The propertyText (if not empty) is checked to be a valid style declaration (containing at least one property). If not, -// the method returns false (denoting an error). -bool InspectorStyle::setPropertyText(unsigned index, const String& propertyText, bool overwrite, String* oldText, ExceptionCode& ec) -{ - ASSERT(m_parentStyleSheet); - DEFINE_STATIC_LOCAL(String, bogusPropertyName, (ASCIILiteral("-webkit-boguz-propertee"))); - - if (!m_parentStyleSheet->ensureParsedDataReady()) { - ec = NOT_FOUND_ERR; - return false; - } - - if (propertyText.stripWhiteSpace().length()) { - RefPtr<MutableStyleProperties> tempMutableStyle = MutableStyleProperties::create(); - RefPtr<CSSRuleSourceData> sourceData = CSSRuleSourceData::create(CSSRuleSourceData::STYLE_RULE); - Document* ownerDocument = m_parentStyleSheet->pageStyleSheet() ? m_parentStyleSheet->pageStyleSheet()->ownerDocument() : nullptr; - createCSSParser(ownerDocument)->parseDeclaration(tempMutableStyle.get(), propertyText + " " + bogusPropertyName + ": none", sourceData, &m_style->parentStyleSheet()->contents()); - Vector<CSSPropertySourceData>& propertyData = sourceData->styleSourceData->propertyData; - unsigned propertyCount = propertyData.size(); - - // At least one property + the bogus property added just above should be present. - if (propertyCount < 2) { - ec = SYNTAX_ERR; - return false; - } - - // Check for a proper propertyText termination (the parser could at least restore to the PROPERTY_NAME state). - if (propertyData.at(propertyCount - 1).name != bogusPropertyName) { - ec = SYNTAX_ERR; - return false; - } - } - - RefPtr<CSSRuleSourceData> sourceData = extractSourceData(); - if (!sourceData) { - ec = NOT_FOUND_ERR; - return false; - } - - String text; - bool success = styleText(&text); - if (!success) { - ec = NOT_FOUND_ERR; - return false; + .setValue(propertyEntry.value) + .release(); + result->addItem(WTFMove(entry)); } - Vector<InspectorStyleProperty> allProperties; - populateAllProperties(&allProperties); - - InspectorStyleTextEditor editor(&allProperties, &m_disabledProperties, text, newLineAndWhitespaceDelimiters()); - if (overwrite) { - if (index >= allProperties.size()) { - ec = INDEX_SIZE_ERR; - return false; - } - *oldText = allProperties.at(index).rawText; - editor.replaceProperty(index, propertyText); - } else - editor.insertProperty(index, propertyText, sourceData->ruleBodyRange.length()); - - return applyStyleText(editor.styleText()); + return result; } -bool InspectorStyle::toggleProperty(unsigned index, bool disable, ExceptionCode& ec) +ExceptionOr<String> InspectorStyle::text() const { - ASSERT(m_parentStyleSheet); - if (!m_parentStyleSheet->ensureParsedDataReady()) { - ec = NO_MODIFICATION_ALLOWED_ERR; - return false; - } - - RefPtr<CSSRuleSourceData> sourceData = extractSourceData(); - if (!sourceData) { - ec = NOT_FOUND_ERR; - return false; - } - - String text; - bool success = styleText(&text); - if (!success) { - ec = NOT_FOUND_ERR; - return false; - } - - Vector<InspectorStyleProperty> allProperties; - populateAllProperties(&allProperties); - if (index >= allProperties.size()) { - ec = INDEX_SIZE_ERR; - return false; - } + // Precondition: m_parentStyleSheet->ensureParsedDataReady() has been called successfully. + auto sourceData = extractSourceData(); + if (!sourceData) + return Exception { NOT_FOUND_ERR }; - InspectorStyleProperty& property = allProperties.at(index); - if (property.disabled == disable) - return true; // Idempotent operation. + auto result = m_parentStyleSheet->text(); + if (result.hasException()) + return result.releaseException(); - InspectorStyleTextEditor editor(&allProperties, &m_disabledProperties, text, newLineAndWhitespaceDelimiters()); - if (disable) - editor.disableProperty(index); - else - editor.enableProperty(index); - - return applyStyleText(editor.styleText()); + auto& bodyRange = sourceData->ruleBodyRange; + return result.releaseReturnValue().substring(bodyRange.start, bodyRange.end - bodyRange.start); } -bool InspectorStyle::getText(String* result) const +static String lowercasePropertyName(const String& name) { - // Precondition: m_parentStyleSheet->ensureParsedDataReady() has been called successfully. - RefPtr<CSSRuleSourceData> sourceData = extractSourceData(); - if (!sourceData) - return false; - - String styleSheetText; - bool success = m_parentStyleSheet->getText(&styleSheetText); - if (!success) - return false; - - SourceRange& bodyRange = sourceData->ruleBodyRange; - *result = styleSheetText.substring(bodyRange.start, bodyRange.end - bodyRange.start); - return true; + // Custom properties are case-sensitive. + if (name.startsWith("--")) + return name; + return name.convertToASCIILowercase(); } -bool InspectorStyle::populateAllProperties(Vector<InspectorStyleProperty>* result) const +void InspectorStyle::populateAllProperties(Vector<InspectorStyleProperty>* result) const { HashSet<String> sourcePropertyNames; - unsigned disabledIndex = 0; - unsigned disabledLength = m_disabledProperties.size(); - InspectorStyleProperty disabledProperty; - if (disabledIndex < disabledLength) - disabledProperty = m_disabledProperties.at(disabledIndex); - RefPtr<CSSRuleSourceData> sourceData = extractSourceData(); - Vector<CSSPropertySourceData>* sourcePropertyData = sourceData ? &(sourceData->styleSourceData->propertyData) : nullptr; + auto sourceData = extractSourceData(); + auto* sourcePropertyData = sourceData ? &sourceData->styleSourceData->propertyData : nullptr; if (sourcePropertyData) { - String styleDeclaration; - bool isStyleTextKnown = styleText(&styleDeclaration); - ASSERT_UNUSED(isStyleTextKnown, isStyleTextKnown); - for (Vector<CSSPropertySourceData>::const_iterator it = sourcePropertyData->begin(); it != sourcePropertyData->end(); ++it) { - while (disabledIndex < disabledLength && disabledProperty.sourceData.range.start <= it->range.start) { - result->append(disabledProperty); - if (++disabledIndex < disabledLength) - disabledProperty = m_disabledProperties.at(disabledIndex); - } - InspectorStyleProperty p(*it, true, false); + auto styleDeclarationOrException = text(); + ASSERT(!styleDeclarationOrException.hasException()); + String styleDeclaration = styleDeclarationOrException.hasException() ? emptyString() : styleDeclarationOrException.releaseReturnValue(); + for (auto& sourceData : *sourcePropertyData) { + // FIXME: <https://webkit.org/b/166787> Web Inspector: Frontend should be made to expect and handle disabled properties + if (sourceData.disabled) + continue; + InspectorStyleProperty p(sourceData, true, sourceData.disabled); p.setRawTextFromStyleDeclaration(styleDeclaration); result->append(p); - sourcePropertyNames.add(it->name.lower()); + sourcePropertyNames.add(lowercasePropertyName(sourceData.name)); } } - while (disabledIndex < disabledLength) { - disabledProperty = m_disabledProperties.at(disabledIndex++); - result->append(disabledProperty); - } - for (int i = 0, size = m_style->length(); i < size; ++i) { String name = m_style->item(i); - if (sourcePropertyNames.contains(name.lower())) - continue; - - sourcePropertyNames.add(name.lower()); - result->append(InspectorStyleProperty(CSSPropertySourceData(name, m_style->getPropertyValue(name), !m_style->getPropertyPriority(name).isEmpty(), true, SourceRange()), false, false)); + if (sourcePropertyNames.add(lowercasePropertyName(name))) + result->append(InspectorStyleProperty(CSSPropertySourceData(name, m_style->getPropertyValue(name), !m_style->getPropertyPriority(name).isEmpty(), false, true, SourceRange()), false, false)); } - - return true; } -PassRefPtr<Inspector::TypeBuilder::CSS::CSSStyle> InspectorStyle::styleWithProperties() const +Ref<Inspector::Protocol::CSS::CSSStyle> InspectorStyle::styleWithProperties() const { Vector<InspectorStyleProperty> properties; populateAllProperties(&properties); - RefPtr<Array<Inspector::TypeBuilder::CSS::CSSProperty>> propertiesObject = Array<Inspector::TypeBuilder::CSS::CSSProperty>::create(); - RefPtr<Array<Inspector::TypeBuilder::CSS::ShorthandEntry>> shorthandEntries = Array<Inspector::TypeBuilder::CSS::ShorthandEntry>::create(); - HashMap<String, RefPtr<Inspector::TypeBuilder::CSS::CSSProperty>> propertyNameToPreviousActiveProperty; + auto propertiesObject = Array<Inspector::Protocol::CSS::CSSProperty>::create(); + auto shorthandEntries = Array<Inspector::Protocol::CSS::ShorthandEntry>::create(); + HashMap<String, RefPtr<Inspector::Protocol::CSS::CSSProperty>> propertyNameToPreviousActiveProperty; HashSet<String> foundShorthands; String previousPriority; String previousStatus; - OwnPtr<Vector<size_t>> lineEndings(m_parentStyleSheet ? m_parentStyleSheet->lineEndings() : PassOwnPtr<Vector<size_t>>()); - RefPtr<CSSRuleSourceData> sourceData = extractSourceData(); + std::unique_ptr<Vector<size_t>> lineEndings(m_parentStyleSheet ? m_parentStyleSheet->lineEndings() : nullptr); + auto sourceData = extractSourceData(); unsigned ruleBodyRangeStart = sourceData ? sourceData->ruleBodyRange.start : 0; for (Vector<InspectorStyleProperty>::iterator it = properties.begin(), itEnd = properties.end(); it != itEnd; ++it) { const CSSPropertySourceData& propertyEntry = it->sourceData; const String& name = propertyEntry.name; - Inspector::TypeBuilder::CSS::CSSProperty::Status::Enum status = it->disabled ? Inspector::TypeBuilder::CSS::CSSProperty::Status::Disabled : Inspector::TypeBuilder::CSS::CSSProperty::Status::Active; + // Visual Studio disagrees with other compilers as to whether 'class' is needed here. +#if COMPILER(MSVC) + enum class Protocol::CSS::CSSPropertyStatus status; +#else + enum Inspector::Protocol::CSS::CSSPropertyStatus status; +#endif + status = it->disabled ? Inspector::Protocol::CSS::CSSPropertyStatus::Disabled : Inspector::Protocol::CSS::CSSPropertyStatus::Active; + + RefPtr<Inspector::Protocol::CSS::CSSProperty> property = Inspector::Protocol::CSS::CSSProperty::create() + .setName(name.convertToASCIILowercase()) + .setValue(propertyEntry.value) + .release(); - RefPtr<Inspector::TypeBuilder::CSS::CSSProperty> property = Inspector::TypeBuilder::CSS::CSSProperty::create() - .setName(name) - .setValue(propertyEntry.value); + propertiesObject->addItem(property.copyRef()); - propertiesObject->addItem(property); + CSSPropertyID propertyId = cssPropertyID(name); // Default "parsedOk" == true. - if (!propertyEntry.parsedOk) + if (!propertyEntry.parsedOk || isInternalCSSProperty(propertyId)) property->setParsedOk(false); if (it->hasRawText()) property->setText(it->rawText); @@ -571,25 +680,25 @@ PassRefPtr<Inspector::TypeBuilder::CSS::CSSStyle> InspectorStyle::styleWithPrope // Parsed property overrides any property with the same name. Non-parsed property overrides // previous non-parsed property with the same name (if any). bool shouldInactivate = false; - CSSPropertyID propertyId = cssPropertyID(name); + // Canonicalize property names to treat non-prefixed and vendor-prefixed property names the same (opacity vs. -webkit-opacity). String canonicalPropertyName = propertyId ? getPropertyNameString(propertyId) : name; - HashMap<String, RefPtr<Inspector::TypeBuilder::CSS::CSSProperty>>::iterator activeIt = propertyNameToPreviousActiveProperty.find(canonicalPropertyName); + HashMap<String, RefPtr<Inspector::Protocol::CSS::CSSProperty>>::iterator activeIt = propertyNameToPreviousActiveProperty.find(canonicalPropertyName); if (activeIt != propertyNameToPreviousActiveProperty.end()) { if (propertyEntry.parsedOk) { - bool successPriority = activeIt->value->getString(Inspector::TypeBuilder::CSS::CSSProperty::Priority, &previousPriority); - bool successStatus = activeIt->value->getString(Inspector::TypeBuilder::CSS::CSSProperty::Status, &previousStatus); + bool successPriority = activeIt->value->getString(Inspector::Protocol::CSS::CSSProperty::Priority, previousPriority); + bool successStatus = activeIt->value->getString(Inspector::Protocol::CSS::CSSProperty::Status, previousStatus); if (successStatus && previousStatus != "inactive") { if (propertyEntry.important || !successPriority) // Priority not set == "not important". shouldInactivate = true; - else if (status == Inspector::TypeBuilder::CSS::CSSProperty::Status::Active) { + else if (status == Inspector::Protocol::CSS::CSSPropertyStatus::Active) { // Inactivate a non-important property following the same-named important property. - status = Inspector::TypeBuilder::CSS::CSSProperty::Status::Inactive; + status = Inspector::Protocol::CSS::CSSPropertyStatus::Inactive; } } } else { bool previousParsedOk; - bool success = activeIt->value->getBoolean(Inspector::TypeBuilder::CSS::CSSProperty::ParsedOk, &previousParsedOk); + bool success = activeIt->value->getBoolean(Inspector::Protocol::CSS::CSSProperty::ParsedOk, previousParsedOk); if (success && !previousParsedOk) shouldInactivate = true; } @@ -597,7 +706,7 @@ PassRefPtr<Inspector::TypeBuilder::CSS::CSSStyle> InspectorStyle::styleWithPrope propertyNameToPreviousActiveProperty.set(canonicalPropertyName, property); if (shouldInactivate) { - activeIt->value->setStatus(Inspector::TypeBuilder::CSS::CSSProperty::Status::Inactive); + activeIt->value->setStatus(Inspector::Protocol::CSS::CSSPropertyStatus::Inactive); propertyNameToPreviousActiveProperty.set(canonicalPropertyName, property); } } else { @@ -605,63 +714,65 @@ PassRefPtr<Inspector::TypeBuilder::CSS::CSSStyle> InspectorStyle::styleWithPrope // Default "implicit" == false. if (implicit) property->setImplicit(true); - status = Inspector::TypeBuilder::CSS::CSSProperty::Status::Style; + status = Inspector::Protocol::CSS::CSSPropertyStatus::Style; String shorthand = m_style->getPropertyShorthand(name); if (!shorthand.isEmpty()) { if (!foundShorthands.contains(shorthand)) { foundShorthands.add(shorthand); - RefPtr<Inspector::TypeBuilder::CSS::ShorthandEntry> entry = Inspector::TypeBuilder::CSS::ShorthandEntry::create() + auto entry = Inspector::Protocol::CSS::ShorthandEntry::create() .setName(shorthand) - .setValue(shorthandValue(shorthand)); - shorthandEntries->addItem(entry); + .setValue(shorthandValue(shorthand)) + .release(); + shorthandEntries->addItem(WTFMove(entry)); } } } } // Default "status" == "style". - if (status != Inspector::TypeBuilder::CSS::CSSProperty::Status::Style) + if (status != Inspector::Protocol::CSS::CSSPropertyStatus::Style) property->setStatus(status); } - RefPtr<Inspector::TypeBuilder::CSS::CSSStyle> result = Inspector::TypeBuilder::CSS::CSSStyle::create() - .setCssProperties(propertiesObject) - .setShorthandEntries(shorthandEntries); - return result.release(); + return Inspector::Protocol::CSS::CSSStyle::create() + .setCssProperties(WTFMove(propertiesObject)) + .setShorthandEntries(WTFMove(shorthandEntries)) + .release(); } -PassRefPtr<CSSRuleSourceData> InspectorStyle::extractSourceData() const +RefPtr<CSSRuleSourceData> InspectorStyle::extractSourceData() const { if (!m_parentStyleSheet || !m_parentStyleSheet->ensureParsedDataReady()) return nullptr; return m_parentStyleSheet->ruleSourceDataFor(m_style.get()); } -bool InspectorStyle::setText(const String& text, ExceptionCode& ec) +ExceptionOr<void> InspectorStyle::setText(const String& text) { - return m_parentStyleSheet->setStyleText(m_style.get(), text, ec); + return m_parentStyleSheet->setStyleText(m_style.get(), text); } String InspectorStyle::shorthandValue(const String& shorthandProperty) const { String value = m_style->getPropertyValue(shorthandProperty); - if (value.isEmpty()) { - for (unsigned i = 0; i < m_style->length(); ++i) { - String individualProperty = m_style->item(i); - if (m_style->getPropertyShorthand(individualProperty) != shorthandProperty) - continue; - if (m_style->isPropertyImplicit(individualProperty)) - continue; - String individualValue = m_style->getPropertyValue(individualProperty); - if (individualValue == "initial") - continue; - if (value.length()) - value.append(" "); - value.append(individualValue); - } + if (!value.isEmpty()) + return value; + StringBuilder builder; + for (unsigned i = 0; i < m_style->length(); ++i) { + String individualProperty = m_style->item(i); + if (m_style->getPropertyShorthand(individualProperty) != shorthandProperty) + continue; + if (m_style->isPropertyImplicit(individualProperty)) + continue; + String individualValue = m_style->getPropertyValue(individualProperty); + if (individualValue == "initial") + continue; + if (!builder.isEmpty()) + builder.append(' '); + builder.append(individualValue); } - return value; + return builder.toString(); } String InspectorStyle::shorthandPriority(const String& shorthandProperty) const @@ -694,78 +805,11 @@ Vector<String> InspectorStyle::longhandProperties(const String& shorthandPropert return properties; } -NewLineAndWhitespace& InspectorStyle::newLineAndWhitespaceDelimiters() const +Ref<InspectorStyleSheet> InspectorStyleSheet::create(InspectorPageAgent* pageAgent, const String& id, RefPtr<CSSStyleSheet>&& pageStyleSheet, Inspector::Protocol::CSS::StyleSheetOrigin origin, const String& documentURL, Listener* listener) { - DEFINE_STATIC_LOCAL(String, defaultPrefix, (ASCIILiteral(" "))); - - if (m_formatAcquired) - return m_format; - - RefPtr<CSSRuleSourceData> sourceData = extractSourceData(); - Vector<CSSPropertySourceData>* sourcePropertyData = sourceData ? &(sourceData->styleSourceData->propertyData) : nullptr; - int propertyCount; - if (!sourcePropertyData || !(propertyCount = sourcePropertyData->size())) { - m_format.first = "\n"; - m_format.second = defaultPrefix; - return m_format; // Do not remember the default formatting and attempt to acquire it later. - } - - String text; - bool success = styleText(&text); - ASSERT_UNUSED(success, success); - - m_formatAcquired = true; - - String candidatePrefix = defaultPrefix; - StringBuilder formatLineFeed; - StringBuilder prefix; - int scanStart = 0; - int propertyIndex = 0; - bool isFullPrefixScanned = false; - bool lineFeedTerminated = false; - const UChar* characters = text.deprecatedCharacters(); - while (propertyIndex < propertyCount) { - const WebCore::CSSPropertySourceData& currentProperty = sourcePropertyData->at(propertyIndex++); - - bool processNextProperty = false; - int scanEnd = currentProperty.range.start; - for (int i = scanStart; i < scanEnd; ++i) { - UChar ch = characters[i]; - bool isLineFeed = isHTMLLineBreak(ch); - if (isLineFeed) { - if (!lineFeedTerminated) - formatLineFeed.append(ch); - prefix.clear(); - } else if (isHTMLSpace(ch)) - prefix.append(ch); - else { - candidatePrefix = prefix.toString(); - prefix.clear(); - scanStart = currentProperty.range.end; - ++propertyIndex; - processNextProperty = true; - break; - } - if (!isLineFeed && formatLineFeed.length()) - lineFeedTerminated = true; - } - if (!processNextProperty) { - isFullPrefixScanned = true; - break; - } - } - - m_format.first = formatLineFeed.toString(); - m_format.second = isFullPrefixScanned ? prefix.toString() : candidatePrefix; - return m_format; -} - -PassRefPtr<InspectorStyleSheet> InspectorStyleSheet::create(InspectorPageAgent* pageAgent, const String& id, PassRefPtr<CSSStyleSheet> pageStyleSheet, Inspector::TypeBuilder::CSS::StyleSheetOrigin::Enum origin, const String& documentURL, Listener* listener) -{ - return adoptRef(new InspectorStyleSheet(pageAgent, id, pageStyleSheet, origin, documentURL, listener)); + return adoptRef(*new InspectorStyleSheet(pageAgent, id, WTFMove(pageStyleSheet), origin, documentURL, listener)); } -// static String InspectorStyleSheet::styleSheetURL(CSSStyleSheet* pageStyleSheet) { if (pageStyleSheet && !pageStyleSheet->contents().baseURL().isEmpty()) @@ -773,13 +817,12 @@ String InspectorStyleSheet::styleSheetURL(CSSStyleSheet* pageStyleSheet) return emptyString(); } -InspectorStyleSheet::InspectorStyleSheet(InspectorPageAgent* pageAgent, const String& id, PassRefPtr<CSSStyleSheet> pageStyleSheet, Inspector::TypeBuilder::CSS::StyleSheetOrigin::Enum origin, const String& documentURL, Listener* listener) +InspectorStyleSheet::InspectorStyleSheet(InspectorPageAgent* pageAgent, const String& id, RefPtr<CSSStyleSheet>&& pageStyleSheet, Inspector::Protocol::CSS::StyleSheetOrigin origin, const String& documentURL, Listener* listener) : m_pageAgent(pageAgent) , m_id(id) - , m_pageStyleSheet(pageStyleSheet) + , m_pageStyleSheet(WTFMove(pageStyleSheet)) , m_origin(origin) , m_documentURL(documentURL) - , m_isRevalidating(false) , m_listener(listener) { m_parsedStyleSheet = new ParsedStyleSheet(); @@ -807,103 +850,103 @@ void InspectorStyleSheet::reparseStyleSheet(const String& text) CSSStyleSheet::RuleMutationScope mutationScope(m_pageStyleSheet.get()); m_pageStyleSheet->contents().parseString(text); m_pageStyleSheet->clearChildRuleCSSOMWrappers(); - m_inspectorStyles.clear(); fireStyleSheetChanged(); } + + // We just wiped the entire contents of the stylesheet. Clear the mutation flag. + m_pageStyleSheet->clearHadRulesMutation(); } -bool InspectorStyleSheet::setText(const String& text, ExceptionCode& ec) +ExceptionOr<void> InspectorStyleSheet::setText(const String& text) { - if (!checkPageStyleSheet(ec)) - return false; - if (!m_parsedStyleSheet) - return false; + if (!m_pageStyleSheet) + return Exception { NOT_SUPPORTED_ERR }; m_parsedStyleSheet->setText(text); m_flatRules.clear(); - return true; + return { }; } -String InspectorStyleSheet::ruleSelector(const InspectorCSSId& id, ExceptionCode& ec) +ExceptionOr<String> InspectorStyleSheet::ruleSelector(const InspectorCSSId& id) { CSSStyleRule* rule = ruleForId(id); - if (!rule) { - ec = NOT_FOUND_ERR; - return ""; - } + if (!rule) + return Exception { NOT_FOUND_ERR }; return rule->selectorText(); } -bool InspectorStyleSheet::setRuleSelector(const InspectorCSSId& id, const String& selector, ExceptionCode& ec) +static bool isValidSelectorListString(const String& selector, Document* document) { - if (!checkPageStyleSheet(ec)) - return false; + CSSSelectorList selectorList; + CSSParser parser(parserContextForDocument(document)); + parser.parseSelector(selector, selectorList); + return selectorList.isValid(); +} + +ExceptionOr<void> InspectorStyleSheet::setRuleSelector(const InspectorCSSId& id, const String& selector) +{ + if (!m_pageStyleSheet) + return Exception { NOT_SUPPORTED_ERR }; + + // If the selector is invalid, do not proceed any further. + if (!isValidSelectorListString(selector, m_pageStyleSheet->ownerDocument())) + return Exception { SYNTAX_ERR }; + CSSStyleRule* rule = ruleForId(id); - if (!rule) { - ec = NOT_FOUND_ERR; - return false; - } + if (!rule) + return Exception { NOT_FOUND_ERR }; + CSSStyleSheet* styleSheet = rule->parentStyleSheet(); - if (!styleSheet || !ensureParsedDataReady()) { - ec = NOT_FOUND_ERR; - return false; - } + if (!styleSheet || !ensureParsedDataReady()) + return Exception { NOT_FOUND_ERR }; + + // If the stylesheet is already mutated at this point, that must mean that our data has been modified + // elsewhere. This should never happen as ensureParsedDataReady would return false in that case. + ASSERT(!styleSheetMutated()); rule->setSelectorText(selector); - RefPtr<CSSRuleSourceData> sourceData = ruleSourceDataFor(rule->style()); - if (!sourceData) { - ec = NOT_FOUND_ERR; - return false; - } + auto sourceData = ruleSourceDataFor(&rule->style()); + if (!sourceData) + return Exception { NOT_FOUND_ERR }; String sheetText = m_parsedStyleSheet->text(); sheetText.replace(sourceData->ruleHeaderRange.start, sourceData->ruleHeaderRange.length(), selector); m_parsedStyleSheet->setText(sheetText); + m_pageStyleSheet->clearHadRulesMutation(); fireStyleSheetChanged(); - return true; + return { }; } -static bool checkStyleRuleSelector(Document* document, const String& selector) +ExceptionOr<CSSStyleRule*> InspectorStyleSheet::addRule(const String& selector) { - CSSSelectorList selectorList; - createCSSParser(document)->parseSelector(selector, selectorList); - return selectorList.isValid(); -} + if (!m_pageStyleSheet) + return Exception { NOT_SUPPORTED_ERR }; -CSSStyleRule* InspectorStyleSheet::addRule(const String& selector, ExceptionCode& ec) -{ - if (!checkPageStyleSheet(ec)) - return nullptr; - if (!checkStyleRuleSelector(m_pageStyleSheet->ownerDocument(), selector)) { - ec = SYNTAX_ERR; - return nullptr; - } + if (!isValidSelectorListString(selector, m_pageStyleSheet->ownerDocument())) + return Exception { SYNTAX_ERR }; + + auto text = this->text(); + if (text.hasException()) + return text.releaseException(); - String text; - bool success = getText(&text); - if (!success) { - ec = NOT_FOUND_ERR; - return nullptr; - } StringBuilder styleSheetText; - styleSheetText.append(text); + styleSheetText.append(text.releaseReturnValue()); - m_pageStyleSheet->addRule(selector, "", ec); - if (ec) - return nullptr; + auto addRuleResult = m_pageStyleSheet->addRule(selector, emptyString(), std::nullopt); + if (addRuleResult.hasException()) + return addRuleResult.releaseException(); ASSERT(m_pageStyleSheet->length()); unsigned lastRuleIndex = m_pageStyleSheet->length() - 1; CSSRule* rule = m_pageStyleSheet->item(lastRuleIndex); ASSERT(rule); - CSSStyleRule* styleRule = InspectorCSSAgent::asCSSStyleRule(rule); + CSSStyleRule* styleRule = InspectorCSSAgent::asCSSStyleRule(*rule); if (!styleRule) { // What we just added has to be a CSSStyleRule - we cannot handle other types of rules yet. // If it is not a style rule, pretend we never touched the stylesheet. - m_pageStyleSheet->deleteRule(lastRuleIndex, ASSERT_NO_EXCEPTION); - ec = SYNTAX_ERR; - return nullptr; + m_pageStyleSheet->deleteRule(lastRuleIndex); + return Exception { SYNTAX_ERR }; } if (!styleSheetText.isEmpty()) @@ -911,46 +954,41 @@ CSSStyleRule* InspectorStyleSheet::addRule(const String& selector, ExceptionCode styleSheetText.append(selector); styleSheetText.appendLiteral(" {}"); - // Using setText() as this operation changes the style sheet rule set. - setText(styleSheetText.toString(), ASSERT_NO_EXCEPTION); + // Using setText() as this operation changes the stylesheet rule set. + setText(styleSheetText.toString()); fireStyleSheetChanged(); return styleRule; } -bool InspectorStyleSheet::deleteRule(const InspectorCSSId& id, ExceptionCode& ec) +ExceptionOr<void> InspectorStyleSheet::deleteRule(const InspectorCSSId& id) { - if (!checkPageStyleSheet(ec)) - return false; + if (!m_pageStyleSheet) + return Exception { NOT_SUPPORTED_ERR }; + RefPtr<CSSStyleRule> rule = ruleForId(id); - if (!rule) { - ec = NOT_FOUND_ERR; - return false; - } + if (!rule) + return Exception { NOT_FOUND_ERR }; CSSStyleSheet* styleSheet = rule->parentStyleSheet(); - if (!styleSheet || !ensureParsedDataReady()) { - ec = NOT_FOUND_ERR; - return false; - } + if (!styleSheet || !ensureParsedDataReady()) + return Exception { NOT_FOUND_ERR }; - RefPtr<CSSRuleSourceData> sourceData = ruleSourceDataFor(rule->style()); - if (!sourceData) { - ec = NOT_FOUND_ERR; - return false; - } + auto sourceData = ruleSourceDataFor(&rule->style()); + if (!sourceData) + return Exception { NOT_FOUND_ERR }; - styleSheet->deleteRule(id.ordinal(), ec); - // |rule| MAY NOT be addressed after this line! + auto deleteRuleResult = styleSheet->deleteRule(id.ordinal()); + if (deleteRuleResult.hasException()) + return deleteRuleResult.releaseException(); - if (ec) - return false; + // |rule| MAY NOT be addressed after this! String sheetText = m_parsedStyleSheet->text(); sheetText.remove(sourceData->ruleHeaderRange.start, sourceData->ruleBodyRange.end - sourceData->ruleHeaderRange.start + 1); - setText(sheetText, ASSERT_NO_EXCEPTION); + setText(sheetText); fireStyleSheetChanged(); - return true; + return { }; } CSSStyleRule* InspectorStyleSheet::ruleForId(const InspectorCSSId& id) const @@ -961,10 +999,9 @@ CSSStyleRule* InspectorStyleSheet::ruleForId(const InspectorCSSId& id) const ASSERT(!id.isEmpty()); ensureFlatRules(); return id.ordinal() >= m_flatRules.size() ? nullptr : m_flatRules.at(id.ordinal()).get(); - } -PassRefPtr<Inspector::TypeBuilder::CSS::CSSStyleSheetBody> InspectorStyleSheet::buildObjectForStyleSheet() +RefPtr<Inspector::Protocol::CSS::CSSStyleSheetBody> InspectorStyleSheet::buildObjectForStyleSheet() { CSSStyleSheet* styleSheet = pageStyleSheet(); if (!styleSheet) @@ -972,19 +1009,19 @@ PassRefPtr<Inspector::TypeBuilder::CSS::CSSStyleSheetBody> InspectorStyleSheet:: RefPtr<CSSRuleList> cssRuleList = asCSSRuleList(styleSheet); - RefPtr<Inspector::TypeBuilder::CSS::CSSStyleSheetBody> result = Inspector::TypeBuilder::CSS::CSSStyleSheetBody::create() + auto result = Inspector::Protocol::CSS::CSSStyleSheetBody::create() .setStyleSheetId(id()) - .setRules(buildArrayForRuleList(cssRuleList.get())); + .setRules(buildArrayForRuleList(cssRuleList.get())) + .release(); - String styleSheetText; - bool success = getText(&styleSheetText); - if (success) - result->setText(styleSheetText); + auto styleSheetText = text(); + if (!styleSheetText.hasException()) + result->setText(styleSheetText.releaseReturnValue()); - return result.release(); + return WTFMove(result); } -PassRefPtr<Inspector::TypeBuilder::CSS::CSSStyleSheetHeader> InspectorStyleSheet::buildObjectForStyleSheetInfo() +RefPtr<Inspector::Protocol::CSS::CSSStyleSheetHeader> InspectorStyleSheet::buildObjectForStyleSheetInfo() { CSSStyleSheet* styleSheet = pageStyleSheet(); if (!styleSheet) @@ -992,92 +1029,160 @@ PassRefPtr<Inspector::TypeBuilder::CSS::CSSStyleSheetHeader> InspectorStyleSheet Document* document = styleSheet->ownerDocument(); Frame* frame = document ? document->frame() : nullptr; - RefPtr<Inspector::TypeBuilder::CSS::CSSStyleSheetHeader> result = Inspector::TypeBuilder::CSS::CSSStyleSheetHeader::create() + return Inspector::Protocol::CSS::CSSStyleSheetHeader::create() .setStyleSheetId(id()) .setOrigin(m_origin) .setDisabled(styleSheet->disabled()) .setSourceURL(finalURL()) .setTitle(styleSheet->title()) - .setFrameId(m_pageAgent->frameId(frame)); + .setFrameId(m_pageAgent->frameId(frame)) + .setIsInline(styleSheet->isInline() && styleSheet->startPosition() != TextPosition()) + .setStartLine(styleSheet->startPosition().m_line.zeroBasedInt()) + .setStartColumn(styleSheet->startPosition().m_column.zeroBasedInt()) + .release(); +} - return result.release(); +static bool hasDynamicSpecificity(const CSSSelector& simpleSelector) +{ + // It is possible that these can have a static specificity if each selector in the list has + // equal specificity, but lets always report that they can be dynamic. + for (const CSSSelector* selector = &simpleSelector; selector; selector = selector->tagHistory()) { + if (selector->match() == CSSSelector::PseudoClass) { + CSSSelector::PseudoClassType pseudoClassType = selector->pseudoClassType(); + if (pseudoClassType == CSSSelector::PseudoClassMatches) + return true; + if (pseudoClassType == CSSSelector::PseudoClassNthChild || pseudoClassType == CSSSelector::PseudoClassNthLastChild) { + if (selector->selectorList()) + return true; + return false; + } + } + } + + return false; } -static PassRefPtr<Inspector::TypeBuilder::Array<String>> selectorsFromSource(const CSSRuleSourceData* sourceData, const String& sheetText) +static Ref<Inspector::Protocol::CSS::CSSSelector> buildObjectForSelectorHelper(const String& selectorText, const CSSSelector& selector, Element* element) { - DEFINE_STATIC_LOCAL(JSC::Yarr::RegularExpression, comment, ("/\\*[^]*?\\*/", TextCaseSensitive, JSC::Yarr::MultilineEnabled)); - RefPtr<Inspector::TypeBuilder::Array<String>> result = Inspector::TypeBuilder::Array<String>::create(); - const SelectorRangeList& ranges = sourceData->selectorRanges; - for (size_t i = 0, size = ranges.size(); i < size; ++i) { - const SourceRange& range = ranges.at(i); - String selector = sheetText.substring(range.start, range.length()); + auto inspectorSelector = Inspector::Protocol::CSS::CSSSelector::create() + .setText(selectorText) + .release(); + + if (element) { + bool dynamic = hasDynamicSpecificity(selector); + if (dynamic) + inspectorSelector->setDynamic(true); + + SelectorChecker::CheckingContext context(SelectorChecker::Mode::CollectingRules); + SelectorChecker selectorChecker(element->document()); + + unsigned specificity; + bool okay = selectorChecker.match(selector, *element, context, specificity); + if (!okay) + specificity = selector.staticSpecificity(okay); + + if (okay) { + auto tuple = Inspector::Protocol::Array<int>::create(); + tuple->addItem(static_cast<int>((specificity & CSSSelector::idMask) >> 16)); + tuple->addItem(static_cast<int>((specificity & CSSSelector::classMask) >> 8)); + tuple->addItem(static_cast<int>(specificity & CSSSelector::elementMask)); + inspectorSelector->setSpecificity(WTFMove(tuple)); + } + } + + return inspectorSelector; +} + +static Ref<Inspector::Protocol::Array<Inspector::Protocol::CSS::CSSSelector>> selectorsFromSource(const CSSRuleSourceData* sourceData, const String& sheetText, const CSSSelectorList& selectorList, Element* element) +{ + static NeverDestroyed<JSC::Yarr::RegularExpression> comment("/\\*[^]*?\\*/", TextCaseSensitive, JSC::Yarr::MultilineEnabled); + + auto result = Inspector::Protocol::Array<Inspector::Protocol::CSS::CSSSelector>::create(); + const CSSSelector* selector = selectorList.first(); + for (auto& range : sourceData->selectorRanges) { + // If we don't have a selector, that means the SourceData for this CSSStyleSheet + // no longer matches up with the actual rules in the CSSStyleSheet. + ASSERT(selector); + if (!selector) + break; + + String selectorText = sheetText.substring(range.start, range.length()); // We don't want to see any comments in the selector components, only the meaningful parts. - replace(selector, comment, ""); - result->addItem(selector.stripWhiteSpace()); + replace(selectorText, comment, String()); + result->addItem(buildObjectForSelectorHelper(selectorText.stripWhiteSpace(), *selector, element)); + + selector = CSSSelectorList::next(selector); } - return result.release(); + return result; } -PassRefPtr<Inspector::TypeBuilder::CSS::SelectorList> InspectorStyleSheet::buildObjectForSelectorList(CSSStyleRule* rule) +Ref<Inspector::Protocol::CSS::CSSSelector> InspectorStyleSheet::buildObjectForSelector(const CSSSelector* selector, Element* element) +{ + return buildObjectForSelectorHelper(selector->selectorText(), *selector, element); +} + +Ref<Inspector::Protocol::CSS::SelectorList> InspectorStyleSheet::buildObjectForSelectorList(CSSStyleRule* rule, Element* element, int& endingLine) { RefPtr<CSSRuleSourceData> sourceData; if (ensureParsedDataReady()) - sourceData = ruleSourceDataFor(rule->style()); - RefPtr<Inspector::TypeBuilder::Array<String>> selectors; + sourceData = ruleSourceDataFor(&rule->style()); + RefPtr<Inspector::Protocol::Array<Inspector::Protocol::CSS::CSSSelector>> selectors; // This intentionally does not rely on the source data to avoid catching the trailing comments (before the declaration starting '{'). String selectorText = rule->selectorText(); if (sourceData) - selectors = selectorsFromSource(sourceData.get(), m_parsedStyleSheet->text()); + selectors = selectorsFromSource(sourceData.get(), m_parsedStyleSheet->text(), rule->styleRule().selectorList(), element); else { - selectors = Inspector::TypeBuilder::Array<String>::create(); - const CSSSelectorList& selectorList = rule->styleRule()->selectorList(); + selectors = Inspector::Protocol::Array<Inspector::Protocol::CSS::CSSSelector>::create(); + const CSSSelectorList& selectorList = rule->styleRule().selectorList(); for (const CSSSelector* selector = selectorList.first(); selector; selector = CSSSelectorList::next(selector)) - selectors->addItem(selector->selectorText()); + selectors->addItem(buildObjectForSelector(selector, element)); } - RefPtr<Inspector::TypeBuilder::CSS::SelectorList> result = Inspector::TypeBuilder::CSS::SelectorList::create() - .setSelectors(selectors) + auto result = Inspector::Protocol::CSS::SelectorList::create() + .setSelectors(WTFMove(selectors)) .setText(selectorText) .release(); if (sourceData) - result->setRange(buildSourceRangeObject(sourceData->ruleHeaderRange, lineEndings().get())); - return result.release(); + result->setRange(buildSourceRangeObject(sourceData->ruleHeaderRange, lineEndings().get(), &endingLine)); + return result; } -PassRefPtr<Inspector::TypeBuilder::CSS::CSSRule> InspectorStyleSheet::buildObjectForRule(CSSStyleRule* rule) +RefPtr<Inspector::Protocol::CSS::CSSRule> InspectorStyleSheet::buildObjectForRule(CSSStyleRule* rule, Element* element) { CSSStyleSheet* styleSheet = pageStyleSheet(); if (!styleSheet) return nullptr; - RefPtr<Inspector::TypeBuilder::CSS::CSSRule> result = Inspector::TypeBuilder::CSS::CSSRule::create() - .setSelectorList(buildObjectForSelectorList(rule)) - .setSourceLine(rule->styleRule()->sourceLine()) + int endingLine = 0; + auto result = Inspector::Protocol::CSS::CSSRule::create() + .setSelectorList(buildObjectForSelectorList(rule, element, endingLine)) + .setSourceLine(endingLine) .setOrigin(m_origin) - .setStyle(buildObjectForStyle(rule->style())); + .setStyle(buildObjectForStyle(&rule->style())) + .release(); // "sourceURL" is present only for regular rules, otherwise "origin" should be used in the frontend. - if (m_origin == Inspector::TypeBuilder::CSS::StyleSheetOrigin::Regular) + if (m_origin == Inspector::Protocol::CSS::StyleSheetOrigin::Regular) result->setSourceURL(finalURL()); if (canBind()) { InspectorCSSId id(ruleId(rule)); if (!id.isEmpty()) - result->setRuleId(id.asProtocolValue<Inspector::TypeBuilder::CSS::CSSRuleId>()); + result->setRuleId(id.asProtocolValue<Inspector::Protocol::CSS::CSSRuleId>()); } - RefPtr<Array<Inspector::TypeBuilder::CSS::CSSMedia>> mediaArray = Array<Inspector::TypeBuilder::CSS::CSSMedia>::create(); + auto mediaArray = Array<Inspector::Protocol::CSS::CSSMedia>::create(); fillMediaListChain(rule, mediaArray.get()); if (mediaArray->length()) - result->setMedia(mediaArray.release()); + result->setMedia(WTFMove(mediaArray)); - return result.release(); + return WTFMove(result); } -PassRefPtr<Inspector::TypeBuilder::CSS::CSSStyle> InspectorStyleSheet::buildObjectForStyle(CSSStyleDeclaration* style) +RefPtr<Inspector::Protocol::CSS::CSSStyle> InspectorStyleSheet::buildObjectForStyle(CSSStyleDeclaration* style) { RefPtr<CSSRuleSourceData> sourceData; if (ensureParsedDataReady()) @@ -1085,83 +1190,50 @@ PassRefPtr<Inspector::TypeBuilder::CSS::CSSStyle> InspectorStyleSheet::buildObje InspectorCSSId id = ruleOrStyleId(style); if (id.isEmpty()) { - RefPtr<Inspector::TypeBuilder::CSS::CSSStyle> bogusStyle = Inspector::TypeBuilder::CSS::CSSStyle::create() - .setCssProperties(Array<Inspector::TypeBuilder::CSS::CSSProperty>::create()) - .setShorthandEntries(Array<Inspector::TypeBuilder::CSS::ShorthandEntry>::create()); - return bogusStyle.release(); + return Inspector::Protocol::CSS::CSSStyle::create() + .setCssProperties(Array<Inspector::Protocol::CSS::CSSProperty>::create()) + .setShorthandEntries(Array<Inspector::Protocol::CSS::ShorthandEntry>::create()) + .release(); } RefPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id); - RefPtr<Inspector::TypeBuilder::CSS::CSSStyle> result = inspectorStyle->buildObjectForStyle(); + RefPtr<Inspector::Protocol::CSS::CSSStyle> result = inspectorStyle->buildObjectForStyle(); // Style text cannot be retrieved without stylesheet, so set cssText here. if (sourceData) { - String sheetText; - bool success = getText(&sheetText); - if (success) { - const SourceRange& bodyRange = sourceData->ruleBodyRange; - result->setCssText(sheetText.substring(bodyRange.start, bodyRange.end - bodyRange.start)); + auto sheetText = text(); + if (!sheetText.hasException()) { + auto& bodyRange = sourceData->ruleBodyRange; + result->setCssText(sheetText.releaseReturnValue().substring(bodyRange.start, bodyRange.end - bodyRange.start)); } } - return result.release(); + return result; } -bool InspectorStyleSheet::setStyleText(const InspectorCSSId& id, const String& text, String* oldText, ExceptionCode& ec) +ExceptionOr<void> InspectorStyleSheet::setStyleText(const InspectorCSSId& id, const String& text, String* oldText) { - RefPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id); - if (!inspectorStyle) { - ec = NOT_FOUND_ERR; - return false; - } - - if (oldText && !inspectorStyle->getText(oldText)) - return false; + auto inspectorStyle = inspectorStyleForId(id); + if (!inspectorStyle) + return Exception { NOT_FOUND_ERR }; - bool success = inspectorStyle->setText(text, ec); - if (success) - fireStyleSheetChanged(); - return success; -} - -bool InspectorStyleSheet::setPropertyText(const InspectorCSSId& id, unsigned propertyIndex, const String& text, bool overwrite, String* oldText, ExceptionCode& ec) -{ - RefPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id); - if (!inspectorStyle) { - ec = NOT_FOUND_ERR; - return false; + if (oldText) { + auto result = inspectorStyle->text(); + if (result.hasException()) + return result.releaseException(); + *oldText = result.releaseReturnValue(); } - bool success = inspectorStyle->setPropertyText(propertyIndex, text, overwrite, oldText, ec); - if (success) + auto result = inspectorStyle->setText(text); + if (!result.hasException()) fireStyleSheetChanged(); - return success; -} - -bool InspectorStyleSheet::toggleProperty(const InspectorCSSId& id, unsigned propertyIndex, bool disable, ExceptionCode& ec) -{ - RefPtr<InspectorStyle> inspectorStyle = inspectorStyleForId(id); - if (!inspectorStyle) { - ec = NOT_FOUND_ERR; - return false; - } - - bool success = inspectorStyle->toggleProperty(propertyIndex, disable, ec); - if (success) { - if (disable) - rememberInspectorStyle(inspectorStyle); - else if (!inspectorStyle->hasDisabledProperties()) - forgetInspectorStyle(inspectorStyle->cssStyle()); - fireStyleSheetChanged(); - } - return success; + return result; } -bool InspectorStyleSheet::getText(String* result) const +ExceptionOr<String> InspectorStyleSheet::text() const { if (!ensureText()) - return false; - *result = m_parsedStyleSheet->text(); - return true; + return Exception { NOT_FOUND_ERR }; + return String { m_parsedStyleSheet->text() }; } CSSStyleDeclaration* InspectorStyleSheet::styleForId(const InspectorCSSId& id) const @@ -1170,7 +1242,7 @@ CSSStyleDeclaration* InspectorStyleSheet::styleForId(const InspectorCSSId& id) c if (!rule) return nullptr; - return rule->style(); + return &rule->style(); } void InspectorStyleSheet::fireStyleSheetChanged() @@ -1179,28 +1251,13 @@ void InspectorStyleSheet::fireStyleSheetChanged() m_listener->styleSheetChanged(this); } -PassRefPtr<InspectorStyle> InspectorStyleSheet::inspectorStyleForId(const InspectorCSSId& id) +RefPtr<InspectorStyle> InspectorStyleSheet::inspectorStyleForId(const InspectorCSSId& id) { CSSStyleDeclaration* style = styleForId(id); if (!style) return nullptr; - InspectorStyleMap::iterator it = m_inspectorStyles.find(style); - if (it == m_inspectorStyles.end()) { - RefPtr<InspectorStyle> inspectorStyle = InspectorStyle::create(id, style, this); - return inspectorStyle.release(); - } - return it->value; -} - -void InspectorStyleSheet::rememberInspectorStyle(RefPtr<InspectorStyle> inspectorStyle) -{ - m_inspectorStyles.set(inspectorStyle->cssStyle(), inspectorStyle); -} - -void InspectorStyleSheet::forgetInspectorStyle(CSSStyleDeclaration* style) -{ - m_inspectorStyles.remove(style); + return InspectorStyle::create(id, style, this); } InspectorCSSId InspectorStyleSheet::ruleOrStyleId(CSSStyleDeclaration* style) const @@ -1221,10 +1278,10 @@ RefPtr<CSSRuleSourceData> InspectorStyleSheet::ruleSourceDataFor(CSSStyleDeclara return m_parsedStyleSheet->ruleSourceDataAt(ruleIndexByStyle(style)); } -PassOwnPtr<Vector<size_t>> InspectorStyleSheet::lineEndings() const +std::unique_ptr<Vector<size_t>> InspectorStyleSheet::lineEndings() const { if (!m_parsedStyleSheet->hasText()) - return PassOwnPtr<Vector<size_t>>(); + return nullptr; return ContentSearchUtilities::lineEndings(m_parsedStyleSheet->text()); } @@ -1232,8 +1289,8 @@ unsigned InspectorStyleSheet::ruleIndexByStyle(CSSStyleDeclaration* pageStyle) c { ensureFlatRules(); unsigned index = 0; - for (unsigned i = 0, size = m_flatRules.size(); i < size; ++i) { - if (m_flatRules.at(i)->style() == pageStyle) + for (auto& rule : m_flatRules) { + if (&rule->style() == pageStyle) return index; ++index; @@ -1241,18 +1298,15 @@ unsigned InspectorStyleSheet::ruleIndexByStyle(CSSStyleDeclaration* pageStyle) c return UINT_MAX; } -bool InspectorStyleSheet::checkPageStyleSheet(ExceptionCode& ec) const +bool InspectorStyleSheet::styleSheetMutated() const { - if (!m_pageStyleSheet) { - ec = NOT_SUPPORTED_ERR; - return false; - } - return true; + return m_pageStyleSheet && m_pageStyleSheet->hadRulesMutation(); } bool InspectorStyleSheet::ensureParsedDataReady() { - return ensureText() && ensureSourceData(); + bool allowParsedData = m_origin == Inspector::Protocol::CSS::StyleSheetOrigin::Inspector || !styleSheetMutated(); + return allowParsedData && ensureText() && ensureSourceData(); } bool InspectorStyleSheet::ensureText() const @@ -1280,9 +1334,12 @@ bool InspectorStyleSheet::ensureSourceData() return false; RefPtr<StyleSheetContents> newStyleSheet = StyleSheetContents::create(); - OwnPtr<RuleSourceDataList> ruleSourceDataResult = adoptPtr(new RuleSourceDataList()); - createCSSParser(m_pageStyleSheet->ownerDocument())->parseSheet(newStyleSheet.get(), m_parsedStyleSheet->text(), 0, ruleSourceDataResult.get()); - m_parsedStyleSheet->setSourceData(ruleSourceDataResult.release()); + auto ruleSourceDataResult = std::make_unique<RuleSourceDataList>(); + + CSSParserContext context(parserContextForDocument(m_pageStyleSheet->ownerDocument())); + StyleSheetHandler handler(m_parsedStyleSheet->text(), m_pageStyleSheet->ownerDocument(), ruleSourceDataResult.get()); + CSSParser::parseSheetForInspector(context, newStyleSheet.get(), m_parsedStyleSheet->text(), handler); + m_parsedStyleSheet->setSourceData(WTFMove(ruleSourceDataResult)); return m_parsedStyleSheet->hasSourceData(); } @@ -1293,27 +1350,28 @@ void InspectorStyleSheet::ensureFlatRules() const collectFlatRules(asCSSRuleList(pageStyleSheet()), &m_flatRules); } -bool InspectorStyleSheet::setStyleText(CSSStyleDeclaration* style, const String& text, ExceptionCode& ec) +ExceptionOr<void> InspectorStyleSheet::setStyleText(CSSStyleDeclaration* style, const String& text) { if (!m_pageStyleSheet) - return false; + return Exception { NOT_FOUND_ERR }; if (!ensureParsedDataReady()) - return false; + return Exception { NOT_FOUND_ERR }; String patchedStyleSheetText; bool success = styleSheetTextWithChangedStyle(style, text, &patchedStyleSheetText); if (!success) - return false; + return Exception { NOT_FOUND_ERR }; InspectorCSSId id = ruleOrStyleId(style); if (id.isEmpty()) - return false; + return Exception { NOT_FOUND_ERR }; - style->setCssText(text, ec); - if (!ec) - m_parsedStyleSheet->setText(patchedStyleSheetText); + auto setCssTextResult = style->setCssText(text); + if (setCssTextResult.hasException()) + return setCssTextResult.releaseException(); - return !ec; + m_parsedStyleSheet->setText(patchedStyleSheetText); + return { }; } bool InspectorStyleSheet::styleSheetTextWithChangedStyle(CSSStyleDeclaration* style, const String& newStyleText, String* result) @@ -1339,30 +1397,7 @@ bool InspectorStyleSheet::styleSheetTextWithChangedStyle(CSSStyleDeclaration* st InspectorCSSId InspectorStyleSheet::ruleId(CSSStyleRule* rule) const { - return ruleOrStyleId(rule->style()); -} - -void InspectorStyleSheet::revalidateStyle(CSSStyleDeclaration* pageStyle) -{ - if (m_isRevalidating) - return; - - m_isRevalidating = true; - ensureFlatRules(); - for (unsigned i = 0, size = m_flatRules.size(); i < size; ++i) { - CSSStyleRule* parsedRule = m_flatRules.at(i).get(); - if (parsedRule->style() == pageStyle) { - if (parsedRule->styleRule()->properties().asText() != pageStyle->cssText()) { - // Clear the disabled properties for the invalid style here. - m_inspectorStyles.remove(pageStyle); - - ExceptionCode ec = 0; - setStyleText(pageStyle, pageStyle->cssText(), ec); - } - break; - } - } - m_isRevalidating = false; + return ruleOrStyleId(&rule->style()); } bool InspectorStyleSheet::originalStyleSheetText(String* result) const @@ -1375,7 +1410,7 @@ bool InspectorStyleSheet::originalStyleSheetText(String* result) const bool InspectorStyleSheet::resourceStyleSheetText(String* result) const { - if (m_origin == Inspector::TypeBuilder::CSS::StyleSheetOrigin::User || m_origin == Inspector::TypeBuilder::CSS::StyleSheetOrigin::UserAgent) + if (m_origin == Inspector::Protocol::CSS::StyleSheetOrigin::User || m_origin == Inspector::Protocol::CSS::StyleSheetOrigin::UserAgent) return false; if (!m_pageStyleSheet || !ownerDocument() || !ownerDocument()->frame()) @@ -1383,7 +1418,7 @@ bool InspectorStyleSheet::resourceStyleSheetText(String* result) const String error; bool base64Encoded; - InspectorPageAgent::resourceContent(&error, ownerDocument()->frame(), URL(ParsedURLString, m_pageStyleSheet->href()), result, &base64Encoded); + InspectorPageAgent::resourceContent(error, ownerDocument()->frame(), URL(ParsedURLString, m_pageStyleSheet->href()), result, &base64Encoded); return error.isEmpty() && !base64Encoded; } @@ -1393,62 +1428,58 @@ bool InspectorStyleSheet::inlineStyleSheetText(String* result) const return false; Node* ownerNode = m_pageStyleSheet->ownerNode(); - if (!ownerNode || !ownerNode->isElementNode()) + if (!is<Element>(ownerNode)) return false; - Element* ownerElement = toElement(ownerNode); + Element& ownerElement = downcast<Element>(*ownerNode); - if (!isHTMLStyleElement(ownerElement) -#if ENABLE(SVG) - && !ownerElement->hasTagName(SVGNames::styleTag) -#endif - ) + if (!is<HTMLStyleElement>(ownerElement) && !is<SVGStyleElement>(ownerElement)) return false; - *result = ownerElement->textContent(); + *result = ownerElement.textContent(); return true; } -PassRefPtr<Inspector::TypeBuilder::Array<Inspector::TypeBuilder::CSS::CSSRule>> InspectorStyleSheet::buildArrayForRuleList(CSSRuleList* ruleList) +Ref<Inspector::Protocol::Array<Inspector::Protocol::CSS::CSSRule>> InspectorStyleSheet::buildArrayForRuleList(CSSRuleList* ruleList) { - RefPtr<Inspector::TypeBuilder::Array<Inspector::TypeBuilder::CSS::CSSRule>> result = Inspector::TypeBuilder::Array<Inspector::TypeBuilder::CSS::CSSRule>::create(); + auto result = Inspector::Protocol::Array<Inspector::Protocol::CSS::CSSRule>::create(); if (!ruleList) - return result.release(); + return result; RefPtr<CSSRuleList> refRuleList = ruleList; CSSStyleRuleVector rules; - collectFlatRules(refRuleList, &rules); + collectFlatRules(WTFMove(refRuleList), &rules); - for (unsigned i = 0, size = rules.size(); i < size; ++i) - result->addItem(buildObjectForRule(rules.at(i).get())); + for (auto& rule : rules) + result->addItem(buildObjectForRule(rule.get(), nullptr)); - return result.release(); + return result; } -void InspectorStyleSheet::collectFlatRules(PassRefPtr<CSSRuleList> ruleList, CSSStyleRuleVector* result) +void InspectorStyleSheet::collectFlatRules(RefPtr<CSSRuleList>&& ruleList, CSSStyleRuleVector* result) { if (!ruleList) return; for (unsigned i = 0, size = ruleList->length(); i < size; ++i) { CSSRule* rule = ruleList->item(i); - CSSStyleRule* styleRule = InspectorCSSAgent::asCSSStyleRule(rule); + CSSStyleRule* styleRule = InspectorCSSAgent::asCSSStyleRule(*rule); if (styleRule) result->append(styleRule); else { RefPtr<CSSRuleList> childRuleList = asCSSRuleList(rule); if (childRuleList) - collectFlatRules(childRuleList, result); + collectFlatRules(WTFMove(childRuleList), result); } } } -PassRefPtr<InspectorStyleSheetForInlineStyle> InspectorStyleSheetForInlineStyle::create(InspectorPageAgent* pageAgent, const String& id, PassRefPtr<Element> element, Inspector::TypeBuilder::CSS::StyleSheetOrigin::Enum origin, Listener* listener) +Ref<InspectorStyleSheetForInlineStyle> InspectorStyleSheetForInlineStyle::create(InspectorPageAgent* pageAgent, const String& id, RefPtr<Element>&& element, Inspector::Protocol::CSS::StyleSheetOrigin origin, Listener* listener) { - return adoptRef(new InspectorStyleSheetForInlineStyle(pageAgent, id, element, origin, listener)); + return adoptRef(*new InspectorStyleSheetForInlineStyle(pageAgent, id, WTFMove(element), origin, listener)); } -InspectorStyleSheetForInlineStyle::InspectorStyleSheetForInlineStyle(InspectorPageAgent* pageAgent, const String& id, PassRefPtr<Element> element, Inspector::TypeBuilder::CSS::StyleSheetOrigin::Enum origin, Listener* listener) - : InspectorStyleSheet(pageAgent, id, nullptr, origin, "", listener) - , m_element(element) +InspectorStyleSheetForInlineStyle::InspectorStyleSheetForInlineStyle(InspectorPageAgent* pageAgent, const String& id, RefPtr<Element>&& element, Inspector::Protocol::CSS::StyleSheetOrigin origin, Listener* listener) + : InspectorStyleSheet(pageAgent, id, nullptr, origin, String(), listener) + , m_element(WTFMove(element)) , m_ruleSourceData(nullptr) , m_isStyleTextValid(false) { @@ -1460,37 +1491,37 @@ InspectorStyleSheetForInlineStyle::InspectorStyleSheetForInlineStyle(InspectorPa void InspectorStyleSheetForInlineStyle::didModifyElementAttribute() { m_isStyleTextValid = false; - if (m_element->isStyledElement() && m_element->style() != m_inspectorStyle->cssStyle()) + if (m_element->isStyledElement() && m_element->cssomStyle() != m_inspectorStyle->cssStyle()) m_inspectorStyle = InspectorStyle::create(InspectorCSSId(id(), 0), inlineStyle(), this); - m_ruleSourceData.clear(); + m_ruleSourceData = nullptr; } -bool InspectorStyleSheetForInlineStyle::getText(String* result) const +ExceptionOr<String> InspectorStyleSheetForInlineStyle::text() const { if (!m_isStyleTextValid) { m_styleText = elementStyleText(); m_isStyleTextValid = true; } - *result = m_styleText; - return true; + return String { m_styleText }; } -bool InspectorStyleSheetForInlineStyle::setStyleText(CSSStyleDeclaration* style, const String& text, ExceptionCode& ec) +ExceptionOr<void> InspectorStyleSheetForInlineStyle::setStyleText(CSSStyleDeclaration* style, const String& text) { ASSERT_UNUSED(style, style == inlineStyle()); { - InspectorCSSAgent::InlineStyleOverrideScope overrideScope(&m_element->document()); - m_element->setAttribute("style", text, ec); + InspectorCSSAgent::InlineStyleOverrideScope overrideScope(m_element->document()); + m_element->setAttribute(HTMLNames::styleAttr, text); } m_styleText = text; m_isStyleTextValid = true; - m_ruleSourceData.clear(); - return !ec; + m_ruleSourceData = nullptr; + + return { }; } -PassOwnPtr<Vector<size_t>> InspectorStyleSheetForInlineStyle::lineEndings() const +std::unique_ptr<Vector<size_t>> InspectorStyleSheetForInlineStyle::lineEndings() const { return ContentSearchUtilities::lineEndings(elementStyleText()); } @@ -1505,7 +1536,7 @@ bool InspectorStyleSheetForInlineStyle::ensureParsedDataReady() // The "style" property value can get changed indirectly, e.g. via element.style.borderWidth = "2px". const String& currentStyleText = elementStyleText(); if (m_styleText != currentStyleText) { - m_ruleSourceData.clear(); + m_ruleSourceData = nullptr; m_styleText = currentStyleText; m_isStyleTextValid = true; } @@ -1513,23 +1544,22 @@ bool InspectorStyleSheetForInlineStyle::ensureParsedDataReady() if (m_ruleSourceData) return true; - m_ruleSourceData = CSSRuleSourceData::create(CSSRuleSourceData::STYLE_RULE); - bool success = getStyleAttributeRanges(m_ruleSourceData.get()); - if (!success) + if (!m_element->isStyledElement()) return false; + m_ruleSourceData = ruleSourceData(); return true; } -PassRefPtr<InspectorStyle> InspectorStyleSheetForInlineStyle::inspectorStyleForId(const InspectorCSSId& id) +RefPtr<InspectorStyle> InspectorStyleSheetForInlineStyle::inspectorStyleForId(const InspectorCSSId& id) { ASSERT_UNUSED(id, !id.ordinal()); - return m_inspectorStyle; + return m_inspectorStyle.copyRef(); } CSSStyleDeclaration* InspectorStyleSheetForInlineStyle::inlineStyle() const { - return m_element->style(); + return m_element->cssomStyle(); } const String& InspectorStyleSheetForInlineStyle::elementStyleText() const @@ -1537,22 +1567,20 @@ const String& InspectorStyleSheetForInlineStyle::elementStyleText() const return m_element->getAttribute("style").string(); } -bool InspectorStyleSheetForInlineStyle::getStyleAttributeRanges(CSSRuleSourceData* result) const +Ref<CSSRuleSourceData> InspectorStyleSheetForInlineStyle::ruleSourceData() const { - if (!m_element->isStyledElement()) - return false; - if (m_styleText.isEmpty()) { + auto result = CSSRuleSourceData::create(StyleRule::Style); result->ruleBodyRange.start = 0; result->ruleBodyRange.end = 0; - return true; + return result; } - RefPtr<MutableStyleProperties> tempDeclaration = MutableStyleProperties::create(); - createCSSParser(&m_element->document())->parseDeclaration(tempDeclaration.get(), m_styleText, result, &m_element->document().elementSheet().contents()); - return true; + CSSParserContext context(parserContextForDocument(&m_element->document())); + RuleSourceDataList ruleSourceDataResult; + StyleSheetHandler handler(m_styleText, &m_element->document(), &ruleSourceDataResult); + CSSParser::parseDeclarationForInspector(context, m_styleText, handler); + return WTFMove(ruleSourceDataResult.first()); } } // namespace WebCore - -#endif // ENABLE(INSPECTOR) |