summaryrefslogtreecommitdiff
path: root/Source/WebCore/css/parser/CSSParserImpl.cpp
diff options
context:
space:
mode:
authorLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
committerLorry Tar Creator <lorry-tar-importer@lorry>2017-06-27 06:07:23 +0000
commit1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch)
tree46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebCore/css/parser/CSSParserImpl.cpp
parent32761a6cee1d0dee366b885b7b9c777e67885688 (diff)
downloadWebKitGtk-tarball-master.tar.gz
Diffstat (limited to 'Source/WebCore/css/parser/CSSParserImpl.cpp')
-rw-r--r--Source/WebCore/css/parser/CSSParserImpl.cpp910
1 files changed, 910 insertions, 0 deletions
diff --git a/Source/WebCore/css/parser/CSSParserImpl.cpp b/Source/WebCore/css/parser/CSSParserImpl.cpp
new file mode 100644
index 000000000..2da1144d9
--- /dev/null
+++ b/Source/WebCore/css/parser/CSSParserImpl.cpp
@@ -0,0 +1,910 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Copyright (C) 2016 Apple Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+#include "config.h"
+#include "CSSParserImpl.h"
+
+#include "CSSAtRuleID.h"
+#include "CSSCustomPropertyValue.h"
+#include "CSSDeferredParser.h"
+#include "CSSKeyframeRule.h"
+#include "CSSKeyframesRule.h"
+#include "CSSParserObserver.h"
+#include "CSSParserObserverWrapper.h"
+#include "CSSParserSelector.h"
+#include "CSSPropertyParser.h"
+#include "CSSSelectorParser.h"
+#include "CSSStyleSheet.h"
+#include "CSSSupportsParser.h"
+#include "CSSTokenizer.h"
+#include "CSSVariableParser.h"
+#include "Document.h"
+#include "Element.h"
+#include "MediaQueryParser.h"
+#include "StyleProperties.h"
+#include "StyleRuleImport.h"
+#include "StyleSheetContents.h"
+
+#include <bitset>
+#include <memory>
+
+namespace WebCore {
+
+CSSParserImpl::CSSParserImpl(const CSSParserContext& context, StyleSheetContents* styleSheet)
+ : m_context(context)
+ , m_styleSheet(styleSheet)
+{
+
+}
+
+CSSParserImpl::CSSParserImpl(CSSDeferredParser& deferredParser)
+ : m_context(deferredParser.context())
+ , m_styleSheet(deferredParser.styleSheet())
+ , m_deferredParser(&deferredParser)
+{
+}
+
+CSSParserImpl::CSSParserImpl(const CSSParserContext& context, const String& string, StyleSheetContents* styleSheet, CSSParserObserverWrapper* wrapper, CSSParser::RuleParsing ruleParsing)
+ : m_context(context)
+ , m_styleSheet(styleSheet)
+ , m_observerWrapper(wrapper)
+{
+ m_tokenizer = wrapper ? std::make_unique<CSSTokenizer>(string, *wrapper) : std::make_unique<CSSTokenizer>(string);
+ if (context.deferredCSSParserEnabled && !wrapper && styleSheet && ruleParsing == CSSParser::RuleParsing::Deferred)
+ m_deferredParser = CSSDeferredParser::create(context, string, *styleSheet);
+}
+
+CSSParser::ParseResult CSSParserImpl::parseValue(MutableStyleProperties* declaration, CSSPropertyID propertyID, const String& string, bool important, const CSSParserContext& context)
+{
+ CSSParserImpl parser(context, string);
+ StyleRule::Type ruleType = StyleRule::Style;
+#if ENABLE(CSS_DEVICE_ADAPTATION)
+ if (declaration->cssParserMode() == CSSViewportRuleMode)
+ ruleType = StyleRule::Viewport;
+#endif
+ parser.consumeDeclarationValue(parser.tokenizer()->tokenRange(), propertyID, important, ruleType);
+ if (parser.m_parsedProperties.isEmpty())
+ return CSSParser::ParseResult::Error;
+ return declaration->addParsedProperties(parser.m_parsedProperties) ? CSSParser::ParseResult::Changed : CSSParser::ParseResult::Unchanged;
+}
+
+CSSParser::ParseResult CSSParserImpl::parseCustomPropertyValue(MutableStyleProperties* declaration, const AtomicString& propertyName, const String& string, bool important, const CSSParserContext& context)
+{
+ CSSParserImpl parser(context, string);
+ parser.consumeCustomPropertyValue(parser.tokenizer()->tokenRange(), propertyName, important);
+ if (parser.m_parsedProperties.isEmpty())
+ return CSSParser::ParseResult::Error;
+ return declaration->addParsedProperties(parser.m_parsedProperties) ? CSSParser::ParseResult::Changed : CSSParser::ParseResult::Unchanged;
+}
+
+static inline void filterProperties(bool important, const ParsedPropertyVector& input, ParsedPropertyVector& output, size_t& unusedEntries, std::bitset<numCSSProperties>& seenProperties, HashSet<AtomicString>& seenCustomProperties)
+{
+ // Add properties in reverse order so that highest priority definitions are reached first. Duplicate definitions can then be ignored when found.
+ for (size_t i = input.size(); i--; ) {
+ const CSSProperty& property = input[i];
+ if (property.isImportant() != important)
+ continue;
+ const unsigned propertyIDIndex = property.id() - firstCSSProperty;
+
+ if (property.id() == CSSPropertyCustom) {
+ auto& name = downcast<CSSCustomPropertyValue>(*property.value()).name();
+ if (!seenCustomProperties.add(name).isNewEntry)
+ continue;
+ output[--unusedEntries] = property;
+ continue;
+ }
+
+ // FIXME-NEWPARSER: We won't support @apply yet.
+ /*else if (property.id() == CSSPropertyApplyAtRule) {
+ // FIXME: Do we need to do anything here?
+ } */
+
+ if (seenProperties.test(propertyIDIndex))
+ continue;
+ seenProperties.set(propertyIDIndex);
+
+ output[--unusedEntries] = property;
+ }
+}
+
+Ref<DeferredStyleProperties> CSSParserImpl::createDeferredStyleProperties(const CSSParserTokenRange& propertyRange)
+{
+ ASSERT(m_deferredParser);
+ return DeferredStyleProperties::create(propertyRange, *m_deferredParser);
+}
+
+static Ref<ImmutableStyleProperties> createStyleProperties(ParsedPropertyVector& parsedProperties, CSSParserMode mode)
+{
+ std::bitset<numCSSProperties> seenProperties;
+ size_t unusedEntries = parsedProperties.size();
+ ParsedPropertyVector results(unusedEntries);
+ HashSet<AtomicString> seenCustomProperties;
+
+ filterProperties(true, parsedProperties, results, unusedEntries, seenProperties, seenCustomProperties);
+ filterProperties(false, parsedProperties, results, unusedEntries, seenProperties, seenCustomProperties);
+
+ Ref<ImmutableStyleProperties> result = ImmutableStyleProperties::create(results.data() + unusedEntries, results.size() - unusedEntries, mode);
+ parsedProperties.clear();
+ return result;
+}
+
+Ref<ImmutableStyleProperties> CSSParserImpl::parseInlineStyleDeclaration(const String& string, Element* element)
+{
+ CSSParserContext context(element->document());
+ context.mode = strictToCSSParserMode(element->isHTMLElement() && !element->document().inQuirksMode());
+
+ CSSParserImpl parser(context, string);
+ parser.consumeDeclarationList(parser.tokenizer()->tokenRange(), StyleRule::Style);
+ return createStyleProperties(parser.m_parsedProperties, context.mode);
+}
+
+Ref<ImmutableStyleProperties> CSSParserImpl::parseDeferredDeclaration(CSSParserTokenRange tokenRange, const CSSParserContext& context, StyleSheetContents* styleSheet)
+{
+ if (!styleSheet) {
+ ParsedPropertyVector properties;
+ return createStyleProperties(properties, context.mode);
+ }
+ CSSParserImpl parser(context, styleSheet);
+ parser.consumeDeclarationList(tokenRange, StyleRule::Style);
+ return createStyleProperties(parser.m_parsedProperties, context.mode);
+}
+
+void CSSParserImpl::parseDeferredRuleList(CSSParserTokenRange tokenRange, CSSDeferredParser& deferredParser, Vector<RefPtr<StyleRuleBase>>& childRules)
+{
+ if (!deferredParser.styleSheet())
+ return;
+ CSSParserImpl parser(deferredParser);
+ parser.consumeRuleList(tokenRange, RegularRuleList, [&childRules](const RefPtr<StyleRuleBase>& rule) {
+ childRules.append(rule);
+ });
+}
+
+void CSSParserImpl::parseDeferredKeyframeList(CSSParserTokenRange tokenRange, CSSDeferredParser& deferredParser, StyleRuleKeyframes& keyframeRule)
+{
+ if (!deferredParser.styleSheet())
+ return;
+ CSSParserImpl parser(deferredParser);
+ parser.consumeRuleList(tokenRange, KeyframesRuleList, [&keyframeRule](const RefPtr<StyleRuleBase>& keyframe) {
+ keyframeRule.parserAppendKeyframe(downcast<const StyleRuleKeyframe>(keyframe.get()));
+ });
+}
+
+bool CSSParserImpl::parseDeclarationList(MutableStyleProperties* declaration, const String& string, const CSSParserContext& context)
+{
+ CSSParserImpl parser(context, string);
+ StyleRule::Type ruleType = StyleRule::Style;
+#if ENABLE(CSS_DEVICE_ADAPTATION)
+ if (declaration->cssParserMode() == CSSViewportRuleMode)
+ ruleType = StyleRule::Viewport;
+#endif
+ parser.consumeDeclarationList(parser.tokenizer()->tokenRange(), ruleType);
+ if (parser.m_parsedProperties.isEmpty())
+ return false;
+
+ std::bitset<numCSSProperties> seenProperties;
+ size_t unusedEntries = parser.m_parsedProperties.size();
+ ParsedPropertyVector results(unusedEntries);
+ HashSet<AtomicString> seenCustomProperties;
+ filterProperties(true, parser.m_parsedProperties, results, unusedEntries, seenProperties, seenCustomProperties);
+ filterProperties(false, parser.m_parsedProperties, results, unusedEntries, seenProperties, seenCustomProperties);
+ if (unusedEntries)
+ results.remove(0, unusedEntries);
+ return declaration->addParsedProperties(results);
+}
+
+RefPtr<StyleRuleBase> CSSParserImpl::parseRule(const String& string, const CSSParserContext& context, StyleSheetContents* styleSheet, AllowedRulesType allowedRules)
+{
+ CSSParserImpl parser(context, string, styleSheet);
+ CSSParserTokenRange range = parser.tokenizer()->tokenRange();
+ range.consumeWhitespace();
+ if (range.atEnd())
+ return nullptr; // Parse error, empty rule
+ RefPtr<StyleRuleBase> rule;
+ if (range.peek().type() == AtKeywordToken)
+ rule = parser.consumeAtRule(range, allowedRules);
+ else
+ rule = parser.consumeQualifiedRule(range, allowedRules);
+ if (!rule)
+ return nullptr; // Parse error, failed to consume rule
+ range.consumeWhitespace();
+ if (!rule || !range.atEnd())
+ return nullptr; // Parse error, trailing garbage
+ return rule;
+}
+
+void CSSParserImpl::parseStyleSheet(const String& string, const CSSParserContext& context, StyleSheetContents* styleSheet, CSSParser::RuleParsing ruleParsing)
+{
+ CSSParserImpl parser(context, string, styleSheet, nullptr, ruleParsing);
+ bool firstRuleValid = parser.consumeRuleList(parser.tokenizer()->tokenRange(), TopLevelRuleList, [&styleSheet](RefPtr<StyleRuleBase> rule) {
+ if (rule->isCharsetRule())
+ return;
+ styleSheet->parserAppendRule(rule.releaseNonNull());
+ });
+ styleSheet->setHasSyntacticallyValidCSSHeader(firstRuleValid);
+ parser.adoptTokenizerEscapedStrings();
+}
+
+void CSSParserImpl::adoptTokenizerEscapedStrings()
+{
+ if (!m_deferredParser || !m_tokenizer)
+ return;
+ m_deferredParser->adoptTokenizerEscapedStrings(m_tokenizer->escapedStringsForAdoption());
+}
+
+CSSSelectorList CSSParserImpl::parsePageSelector(CSSParserTokenRange range, StyleSheetContents* styleSheet)
+{
+ // We only support a small subset of the css-page spec.
+ range.consumeWhitespace();
+ AtomicString typeSelector;
+ if (range.peek().type() == IdentToken)
+ typeSelector = range.consume().value().toAtomicString();
+
+ AtomicString pseudo;
+ if (range.peek().type() == ColonToken) {
+ range.consume();
+ if (range.peek().type() != IdentToken)
+ return CSSSelectorList();
+ pseudo = range.consume().value().toAtomicString();
+ }
+
+ range.consumeWhitespace();
+ if (!range.atEnd())
+ return CSSSelectorList(); // Parse error; extra tokens in @page selector
+
+ std::unique_ptr<CSSParserSelector> selector;
+ if (!typeSelector.isNull() && pseudo.isNull())
+ selector = std::unique_ptr<CSSParserSelector>(new CSSParserSelector(QualifiedName(nullAtom, typeSelector, styleSheet->defaultNamespace())));
+ else {
+ selector = std::unique_ptr<CSSParserSelector>(new CSSParserSelector);
+ if (!pseudo.isNull()) {
+ selector = std::unique_ptr<CSSParserSelector>(CSSParserSelector::parsePagePseudoSelector(pseudo));
+ if (!selector || selector->match() != CSSSelector::PagePseudoClass)
+ return CSSSelectorList();
+ }
+ if (!typeSelector.isNull())
+ selector->prependTagSelector(QualifiedName(nullAtom, typeSelector, styleSheet->defaultNamespace()));
+ }
+
+ selector->setForPage();
+ Vector<std::unique_ptr<CSSParserSelector>> selectorVector;
+ selectorVector.append(WTFMove(selector));
+ CSSSelectorList selectorList;
+ selectorList.adoptSelectorVector(selectorVector);
+ return selectorList;
+}
+
+std::unique_ptr<Vector<double>> CSSParserImpl::parseKeyframeKeyList(const String& keyList)
+{
+ return consumeKeyframeKeyList(CSSTokenizer(keyList).tokenRange());
+}
+
+bool CSSParserImpl::supportsDeclaration(CSSParserTokenRange& range)
+{
+ ASSERT(m_parsedProperties.isEmpty());
+ consumeDeclaration(range, StyleRule::Style);
+ bool result = !m_parsedProperties.isEmpty();
+ m_parsedProperties.clear();
+ return result;
+}
+
+void CSSParserImpl::parseDeclarationListForInspector(const String& declaration, const CSSParserContext& context, CSSParserObserver& observer)
+{
+ CSSParserObserverWrapper wrapper(observer);
+ CSSParserImpl parser(context, declaration, nullptr, &wrapper);
+ observer.startRuleHeader(StyleRule::Style, 0);
+ observer.endRuleHeader(1);
+ parser.consumeDeclarationList(parser.tokenizer()->tokenRange(), StyleRule::Style);
+}
+
+void CSSParserImpl::parseStyleSheetForInspector(const String& string, const CSSParserContext& context, StyleSheetContents* styleSheet, CSSParserObserver& observer)
+{
+ CSSParserObserverWrapper wrapper(observer);
+ CSSParserImpl parser(context, string, styleSheet, &wrapper);
+ bool firstRuleValid = parser.consumeRuleList(parser.tokenizer()->tokenRange(), TopLevelRuleList, [&styleSheet](RefPtr<StyleRuleBase> rule) {
+ if (rule->isCharsetRule())
+ return;
+ styleSheet->parserAppendRule(rule.releaseNonNull());
+ });
+ styleSheet->setHasSyntacticallyValidCSSHeader(firstRuleValid);
+}
+
+static CSSParserImpl::AllowedRulesType computeNewAllowedRules(CSSParserImpl::AllowedRulesType allowedRules, StyleRuleBase* rule)
+{
+ if (!rule || allowedRules == CSSParserImpl::KeyframeRules || allowedRules == CSSParserImpl::NoRules)
+ return allowedRules;
+ ASSERT(allowedRules <= CSSParserImpl::RegularRules);
+ if (rule->isCharsetRule() || rule->isImportRule())
+ return CSSParserImpl::AllowImportRules;
+ if (rule->isNamespaceRule())
+ return CSSParserImpl::AllowNamespaceRules;
+ return CSSParserImpl::RegularRules;
+}
+
+template<typename T>
+bool CSSParserImpl::consumeRuleList(CSSParserTokenRange range, RuleListType ruleListType, const T callback)
+{
+ AllowedRulesType allowedRules = RegularRules;
+ switch (ruleListType) {
+ case TopLevelRuleList:
+ allowedRules = AllowCharsetRules;
+ break;
+ case RegularRuleList:
+ allowedRules = RegularRules;
+ break;
+ case KeyframesRuleList:
+ allowedRules = KeyframeRules;
+ break;
+ default:
+ ASSERT_NOT_REACHED();
+ }
+
+ bool seenRule = false;
+ bool firstRuleValid = false;
+ while (!range.atEnd()) {
+ RefPtr<StyleRuleBase> rule;
+ switch (range.peek().type()) {
+ case WhitespaceToken:
+ range.consumeWhitespace();
+ continue;
+ case AtKeywordToken:
+ rule = consumeAtRule(range, allowedRules);
+ break;
+ case CDOToken:
+ case CDCToken:
+ if (ruleListType == TopLevelRuleList) {
+ range.consume();
+ continue;
+ }
+ FALLTHROUGH;
+ default:
+ rule = consumeQualifiedRule(range, allowedRules);
+ break;
+ }
+ if (!seenRule) {
+ seenRule = true;
+ firstRuleValid = rule;
+ }
+ if (rule) {
+ allowedRules = computeNewAllowedRules(allowedRules, rule.get());
+ callback(rule);
+ }
+ }
+
+ return firstRuleValid;
+}
+
+RefPtr<StyleRuleBase> CSSParserImpl::consumeAtRule(CSSParserTokenRange& range, AllowedRulesType allowedRules)
+{
+ ASSERT(range.peek().type() == AtKeywordToken);
+ const StringView name = range.consumeIncludingWhitespace().value();
+ const CSSParserToken* preludeStart = &range.peek();
+ while (!range.atEnd() && range.peek().type() != LeftBraceToken && range.peek().type() != SemicolonToken)
+ range.consumeComponentValue();
+
+ CSSParserTokenRange prelude = range.makeSubRange(preludeStart, &range.peek());
+ CSSAtRuleID id = cssAtRuleID(name);
+
+ if (range.atEnd() || range.peek().type() == SemicolonToken) {
+ range.consume();
+ if (allowedRules == AllowCharsetRules && id == CSSAtRuleCharset)
+ return consumeCharsetRule(prelude);
+ if (allowedRules <= AllowImportRules && id == CSSAtRuleImport)
+ return consumeImportRule(prelude);
+ if (allowedRules <= AllowNamespaceRules && id == CSSAtRuleNamespace)
+ return consumeNamespaceRule(prelude);
+ // FIXME-NEWPARSER: Support "apply"
+ /*if (allowedRules == ApplyRules && id == CSSAtRuleApply) {
+ consumeApplyRule(prelude);
+ return nullptr; // consumeApplyRule just updates m_parsedProperties
+ }*/
+ return nullptr; // Parse error, unrecognised at-rule without block
+ }
+
+ CSSParserTokenRange block = range.consumeBlock();
+ if (allowedRules == KeyframeRules)
+ return nullptr; // Parse error, no at-rules supported inside @keyframes
+ if (allowedRules == NoRules || allowedRules == ApplyRules)
+ return nullptr; // Parse error, no at-rules with blocks supported inside declaration lists
+
+ ASSERT(allowedRules <= RegularRules);
+
+ switch (id) {
+ case CSSAtRuleMedia:
+ return consumeMediaRule(prelude, block);
+ case CSSAtRuleSupports:
+ return consumeSupportsRule(prelude, block);
+#if ENABLE(CSS_DEVICE_ADAPTATION)
+ case CSSAtRuleViewport:
+ return consumeViewportRule(prelude, block);
+#endif
+ case CSSAtRuleFontFace:
+ return consumeFontFaceRule(prelude, block);
+ case CSSAtRuleWebkitKeyframes:
+ return consumeKeyframesRule(true, prelude, block);
+ case CSSAtRuleKeyframes:
+ return consumeKeyframesRule(false, prelude, block);
+ case CSSAtRulePage:
+ return consumePageRule(prelude, block);
+#if ENABLE(CSS_REGIONS)
+ case CSSAtRuleWebkitRegion:
+ return consumeRegionRule(prelude, block);
+#endif
+ default:
+ return nullptr; // Parse error, unrecognised at-rule with block
+ }
+}
+
+RefPtr<StyleRuleBase> CSSParserImpl::consumeQualifiedRule(CSSParserTokenRange& range, AllowedRulesType allowedRules)
+{
+ const CSSParserToken* preludeStart = &range.peek();
+ while (!range.atEnd() && range.peek().type() != LeftBraceToken)
+ range.consumeComponentValue();
+
+ if (range.atEnd())
+ return nullptr; // Parse error, EOF instead of qualified rule block
+
+ CSSParserTokenRange prelude = range.makeSubRange(preludeStart, &range.peek());
+ CSSParserTokenRange block = range.consumeBlockCheckingForEditability(m_styleSheet.get());
+
+ if (allowedRules <= RegularRules)
+ return consumeStyleRule(prelude, block);
+ if (allowedRules == KeyframeRules)
+ return consumeKeyframeStyleRule(prelude, block);
+
+ ASSERT_NOT_REACHED();
+ return nullptr;
+}
+
+// This may still consume tokens if it fails
+static AtomicString consumeStringOrURI(CSSParserTokenRange& range)
+{
+ const CSSParserToken& token = range.peek();
+
+ if (token.type() == StringToken || token.type() == UrlToken)
+ return range.consumeIncludingWhitespace().value().toAtomicString();
+
+ if (token.type() != FunctionToken || !equalIgnoringASCIICase(token.value(), "url"))
+ return AtomicString();
+
+ CSSParserTokenRange contents = range.consumeBlock();
+ const CSSParserToken& uri = contents.consumeIncludingWhitespace();
+ if (uri.type() == BadStringToken || !contents.atEnd())
+ return AtomicString();
+ return uri.value().toAtomicString();
+}
+
+RefPtr<StyleRuleCharset> CSSParserImpl::consumeCharsetRule(CSSParserTokenRange prelude)
+{
+ const CSSParserToken& string = prelude.consumeIncludingWhitespace();
+ if (string.type() != StringToken || !prelude.atEnd())
+ return nullptr; // Parse error, expected a single string
+ return StyleRuleCharset::create();
+}
+
+RefPtr<StyleRuleImport> CSSParserImpl::consumeImportRule(CSSParserTokenRange prelude)
+{
+ AtomicString uri(consumeStringOrURI(prelude));
+ if (uri.isNull())
+ return nullptr; // Parse error, expected string or URI
+
+ if (m_observerWrapper) {
+ unsigned endOffset = m_observerWrapper->endOffset(prelude);
+ m_observerWrapper->observer().startRuleHeader(StyleRule::Import, m_observerWrapper->startOffset(prelude));
+ m_observerWrapper->observer().endRuleHeader(endOffset);
+ m_observerWrapper->observer().startRuleBody(endOffset);
+ m_observerWrapper->observer().endRuleBody(endOffset);
+ }
+
+ return StyleRuleImport::create(uri, MediaQueryParser::parseMediaQuerySet(prelude).releaseNonNull());
+}
+
+RefPtr<StyleRuleNamespace> CSSParserImpl::consumeNamespaceRule(CSSParserTokenRange prelude)
+{
+ AtomicString namespacePrefix;
+ if (prelude.peek().type() == IdentToken)
+ namespacePrefix = prelude.consumeIncludingWhitespace().value().toAtomicString();
+
+ AtomicString uri(consumeStringOrURI(prelude));
+ if (uri.isNull() || !prelude.atEnd())
+ return nullptr; // Parse error, expected string or URI
+
+ return StyleRuleNamespace::create(namespacePrefix, uri);
+}
+
+RefPtr<StyleRuleMedia> CSSParserImpl::consumeMediaRule(CSSParserTokenRange prelude, CSSParserTokenRange block)
+{
+ if (m_deferredParser)
+ return StyleRuleMedia::create(MediaQueryParser::parseMediaQuerySet(prelude).releaseNonNull(), std::make_unique<DeferredStyleGroupRuleList>(block, *m_deferredParser));
+
+ Vector<RefPtr<StyleRuleBase>> rules;
+
+ if (m_observerWrapper) {
+ m_observerWrapper->observer().startRuleHeader(StyleRule::Media, m_observerWrapper->startOffset(prelude));
+ m_observerWrapper->observer().endRuleHeader(m_observerWrapper->endOffset(prelude));
+ m_observerWrapper->observer().startRuleBody(m_observerWrapper->previousTokenStartOffset(block));
+ }
+
+ consumeRuleList(block, RegularRuleList, [&rules](RefPtr<StyleRuleBase> rule) {
+ rules.append(rule);
+ });
+
+ if (m_observerWrapper)
+ m_observerWrapper->observer().endRuleBody(m_observerWrapper->endOffset(block));
+
+ return StyleRuleMedia::create(MediaQueryParser::parseMediaQuerySet(prelude).releaseNonNull(), rules);
+}
+
+RefPtr<StyleRuleSupports> CSSParserImpl::consumeSupportsRule(CSSParserTokenRange prelude, CSSParserTokenRange block)
+{
+ CSSSupportsParser::SupportsResult supported = CSSSupportsParser::supportsCondition(prelude, *this);
+ if (supported == CSSSupportsParser::Invalid)
+ return nullptr; // Parse error, invalid @supports condition
+
+ if (m_deferredParser)
+ return StyleRuleSupports::create(prelude.serialize().stripWhiteSpace(), supported, std::make_unique<DeferredStyleGroupRuleList>(block, *m_deferredParser));
+
+ if (m_observerWrapper) {
+ m_observerWrapper->observer().startRuleHeader(StyleRule::Supports, m_observerWrapper->startOffset(prelude));
+ m_observerWrapper->observer().endRuleHeader(m_observerWrapper->endOffset(prelude));
+ m_observerWrapper->observer().startRuleBody(m_observerWrapper->previousTokenStartOffset(block));
+ }
+
+ Vector<RefPtr<StyleRuleBase>> rules;
+ consumeRuleList(block, RegularRuleList, [&rules](RefPtr<StyleRuleBase> rule) {
+ rules.append(rule);
+ });
+
+ if (m_observerWrapper)
+ m_observerWrapper->observer().endRuleBody(m_observerWrapper->endOffset(block));
+
+ return StyleRuleSupports::create(prelude.serialize().stripWhiteSpace(), supported, rules);
+}
+
+#if ENABLE(CSS_DEVICE_ADAPTATION)
+RefPtr<StyleRuleViewport> CSSParserImpl::consumeViewportRule(CSSParserTokenRange prelude, CSSParserTokenRange block)
+{
+ if (!prelude.atEnd())
+ return nullptr; // Parser error; @viewport prelude should be empty
+
+ if (m_observerWrapper) {
+ unsigned endOffset = m_observerWrapper->endOffset(prelude);
+ m_observerWrapper->observer().startRuleHeader(StyleRule::Viewport, m_observerWrapper->startOffset(prelude));
+ m_observerWrapper->observer().endRuleHeader(endOffset);
+ m_observerWrapper->observer().startRuleBody(endOffset);
+ m_observerWrapper->observer().endRuleBody(endOffset);
+ }
+
+ consumeDeclarationList(block, StyleRule::Viewport);
+ return StyleRuleViewport::create(createStyleProperties(m_parsedProperties, CSSViewportRuleMode));
+}
+#endif
+
+RefPtr<StyleRuleFontFace> CSSParserImpl::consumeFontFaceRule(CSSParserTokenRange prelude, CSSParserTokenRange block)
+{
+ if (!prelude.atEnd())
+ return nullptr; // Parse error; @font-face prelude should be empty
+
+ if (m_observerWrapper) {
+ unsigned endOffset = m_observerWrapper->endOffset(prelude);
+ m_observerWrapper->observer().startRuleHeader(StyleRule::FontFace, m_observerWrapper->startOffset(prelude));
+ m_observerWrapper->observer().endRuleHeader(endOffset);
+ m_observerWrapper->observer().startRuleBody(endOffset);
+ m_observerWrapper->observer().endRuleBody(endOffset);
+ }
+
+ consumeDeclarationList(block, StyleRule::FontFace);
+ return StyleRuleFontFace::create(createStyleProperties(m_parsedProperties, m_context.mode));
+}
+
+RefPtr<StyleRuleKeyframes> CSSParserImpl::consumeKeyframesRule(bool webkitPrefixed, CSSParserTokenRange prelude, CSSParserTokenRange block)
+{
+ CSSParserTokenRange rangeCopy = prelude; // For inspector callbacks
+ const CSSParserToken& nameToken = prelude.consumeIncludingWhitespace();
+ if (!prelude.atEnd())
+ return nullptr; // Parse error; expected single non-whitespace token in @keyframes header
+
+ String name;
+ if (nameToken.type() == IdentToken) {
+ name = nameToken.value().toString();
+ } else if (nameToken.type() == StringToken && webkitPrefixed)
+ name = nameToken.value().toString();
+ else
+ return nullptr; // Parse error; expected ident token in @keyframes header
+
+ if (m_deferredParser)
+ return StyleRuleKeyframes::create(name, std::make_unique<DeferredStyleGroupRuleList>(block, *m_deferredParser));
+
+ if (m_observerWrapper) {
+ m_observerWrapper->observer().startRuleHeader(StyleRule::Keyframes, m_observerWrapper->startOffset(rangeCopy));
+ m_observerWrapper->observer().endRuleHeader(m_observerWrapper->endOffset(prelude));
+ m_observerWrapper->observer().startRuleBody(m_observerWrapper->previousTokenStartOffset(block));
+ m_observerWrapper->observer().endRuleBody(m_observerWrapper->endOffset(block));
+ }
+
+ RefPtr<StyleRuleKeyframes> keyframeRule = StyleRuleKeyframes::create(name);
+ consumeRuleList(block, KeyframesRuleList, [keyframeRule](const RefPtr<StyleRuleBase>& keyframe) {
+ keyframeRule->parserAppendKeyframe(downcast<const StyleRuleKeyframe>(keyframe.get()));
+ });
+
+ // FIXME-NEWPARSER: Find out why this is done. Behavior difference when prefixed?
+ // keyframeRule->setVendorPrefixed(webkitPrefixed);
+ return keyframeRule;
+}
+
+RefPtr<StyleRulePage> CSSParserImpl::consumePageRule(CSSParserTokenRange prelude, CSSParserTokenRange block)
+{
+ CSSSelectorList selectorList = parsePageSelector(prelude, m_styleSheet.get());
+ if (!selectorList.isValid())
+ return nullptr; // Parse error, invalid @page selector
+
+ if (m_observerWrapper) {
+ unsigned endOffset = m_observerWrapper->endOffset(prelude);
+ m_observerWrapper->observer().startRuleHeader(StyleRule::Page, m_observerWrapper->startOffset(prelude));
+ m_observerWrapper->observer().endRuleHeader(endOffset);
+ }
+
+ consumeDeclarationList(block, StyleRule::Style);
+
+ RefPtr<StyleRulePage> page = StyleRulePage::create(createStyleProperties(m_parsedProperties, m_context.mode));
+ page->wrapperAdoptSelectorList(selectorList);
+ return page;
+}
+
+#if ENABLE(CSS_REGIONS)
+RefPtr<StyleRuleRegion> CSSParserImpl::consumeRegionRule(CSSParserTokenRange prelude, CSSParserTokenRange block)
+{
+ CSSSelectorList selectorList = CSSSelectorParser::parseSelector(prelude, m_context, m_styleSheet.get());
+ if (!selectorList.isValid())
+ return nullptr; // Parse error, invalid selector list
+
+ if (m_observerWrapper) {
+ m_observerWrapper->observer().startRuleHeader(StyleRule::Region, m_observerWrapper->startOffset(prelude));
+ m_observerWrapper->observer().endRuleHeader(m_observerWrapper->endOffset(prelude));
+ m_observerWrapper->observer().startRuleBody(m_observerWrapper->previousTokenStartOffset(block));
+ }
+
+ Vector<RefPtr<StyleRuleBase>> rules;
+ consumeRuleList(block, RegularRuleList, [&rules](RefPtr<StyleRuleBase> rule) {
+ rules.append(rule);
+ });
+
+ if (m_observerWrapper)
+ m_observerWrapper->observer().endRuleBody(m_observerWrapper->endOffset(block));
+
+ return StyleRuleRegion::create(selectorList, rules);
+
+}
+#endif
+
+// FIXME-NEWPARSER: Support "apply"
+/*void CSSParserImpl::consumeApplyRule(CSSParserTokenRange prelude)
+{
+ const CSSParserToken& ident = prelude.consumeIncludingWhitespace();
+ if (!prelude.atEnd() || !CSSVariableParser::isValidVariableName(ident))
+ return; // Parse error, expected a single custom property name
+ m_parsedProperties.append(CSSProperty(
+ CSSPropertyApplyAtRule,
+ *CSSCustomIdentValue::create(ident.value().toString())));
+}
+*/
+
+RefPtr<StyleRuleKeyframe> CSSParserImpl::consumeKeyframeStyleRule(CSSParserTokenRange prelude, CSSParserTokenRange block)
+{
+ std::unique_ptr<Vector<double>> keyList = consumeKeyframeKeyList(prelude);
+ if (!keyList)
+ return nullptr;
+
+ if (m_observerWrapper) {
+ m_observerWrapper->observer().startRuleHeader(StyleRule::Keyframe, m_observerWrapper->startOffset(prelude));
+ m_observerWrapper->observer().endRuleHeader(m_observerWrapper->endOffset(prelude));
+ }
+
+ consumeDeclarationList(block, StyleRule::Keyframe);
+ return StyleRuleKeyframe::create(WTFMove(keyList), createStyleProperties(m_parsedProperties, m_context.mode));
+}
+
+static void observeSelectors(CSSParserObserverWrapper& wrapper, CSSParserTokenRange selectors)
+{
+ // This is easier than hooking into the CSSSelectorParser
+ selectors.consumeWhitespace();
+ CSSParserTokenRange originalRange = selectors;
+ wrapper.observer().startRuleHeader(StyleRule::Style, wrapper.startOffset(originalRange));
+
+ while (!selectors.atEnd()) {
+ const CSSParserToken* selectorStart = &selectors.peek();
+ while (!selectors.atEnd() && selectors.peek().type() != CommaToken)
+ selectors.consumeComponentValue();
+ CSSParserTokenRange selector = selectors.makeSubRange(selectorStart, &selectors.peek());
+ selectors.consumeIncludingWhitespace();
+
+ wrapper.observer().observeSelector(wrapper.startOffset(selector), wrapper.endOffset(selector));
+ }
+
+ wrapper.observer().endRuleHeader(wrapper.endOffset(originalRange));
+}
+
+RefPtr<StyleRule> CSSParserImpl::consumeStyleRule(CSSParserTokenRange prelude, CSSParserTokenRange block)
+{
+ CSSSelectorList selectorList = CSSSelectorParser::parseSelector(prelude, m_context, m_styleSheet.get());
+ if (!selectorList.isValid())
+ return nullptr; // Parse error, invalid selector list
+
+ RefPtr<StyleRule> rule;
+ if (m_observerWrapper)
+ observeSelectors(*m_observerWrapper, prelude);
+
+ if (m_deferredParser) {
+ // If a rule is empty (i.e., only whitespace), don't bother using
+ // deferred parsing. This allows the empty rule optimization in ElementRuleCollector
+ // to continue to work. Note we don't have to consider CommentTokens, since those
+ // are stripped out.
+ CSSParserTokenRange blockCopy = block;
+ blockCopy.consumeWhitespace();
+ if (!blockCopy.atEnd()) {
+ rule = StyleRule::create(createDeferredStyleProperties(block));
+ rule->wrapperAdoptSelectorList(selectorList);
+ return rule;
+ }
+ }
+
+ consumeDeclarationList(block, StyleRule::Style);
+ rule = StyleRule::create(createStyleProperties(m_parsedProperties, m_context.mode));
+ rule->wrapperAdoptSelectorList(selectorList);
+ return rule;
+}
+
+void CSSParserImpl::consumeDeclarationList(CSSParserTokenRange range, StyleRule::Type ruleType)
+{
+ ASSERT(m_parsedProperties.isEmpty());
+
+ bool useObserver = m_observerWrapper && (ruleType == StyleRule::Style || ruleType == StyleRule::Keyframe);
+ if (useObserver) {
+ m_observerWrapper->observer().startRuleBody(m_observerWrapper->previousTokenStartOffset(range));
+ m_observerWrapper->skipCommentsBefore(range, true);
+ }
+
+ while (!range.atEnd()) {
+ switch (range.peek().type()) {
+ case WhitespaceToken:
+ case SemicolonToken:
+ range.consume();
+ break;
+ case IdentToken: {
+ const CSSParserToken* declarationStart = &range.peek();
+
+ if (useObserver)
+ m_observerWrapper->yieldCommentsBefore(range);
+
+ while (!range.atEnd() && range.peek().type() != SemicolonToken)
+ range.consumeComponentValue();
+
+ consumeDeclaration(range.makeSubRange(declarationStart, &range.peek()), ruleType);
+
+ if (useObserver)
+ m_observerWrapper->skipCommentsBefore(range, false);
+ break;
+ }
+ case AtKeywordToken: {
+ // FIXME-NEWPARSER: Support apply
+ AllowedRulesType allowedRules = /* ruleType == StyleRule::Style && RuntimeEnabledFeatures::cssApplyAtRulesEnabled() ? ApplyRules :*/ NoRules;
+ RefPtr<StyleRuleBase> rule = consumeAtRule(range, allowedRules);
+ ASSERT_UNUSED(rule, !rule);
+ break;
+ }
+ default: // Parse error, unexpected token in declaration list
+ while (!range.atEnd() && range.peek().type() != SemicolonToken)
+ range.consumeComponentValue();
+ break;
+ }
+ }
+
+ // Yield remaining comments
+ if (useObserver) {
+ m_observerWrapper->yieldCommentsBefore(range);
+ m_observerWrapper->observer().endRuleBody(m_observerWrapper->endOffset(range));
+ }
+}
+
+void CSSParserImpl::consumeDeclaration(CSSParserTokenRange range, StyleRule::Type ruleType)
+{
+ CSSParserTokenRange rangeCopy = range; // For inspector callbacks
+
+ ASSERT(range.peek().type() == IdentToken);
+ const CSSParserToken& token = range.consumeIncludingWhitespace();
+ CSSPropertyID propertyID = token.parseAsCSSPropertyID();
+ if (range.consume().type() != ColonToken)
+ return; // Parse error
+
+ bool important = false;
+ const CSSParserToken* declarationValueEnd = range.end();
+ const CSSParserToken* last = range.end() - 1;
+ while (last->type() == WhitespaceToken)
+ --last;
+ if (last->type() == IdentToken && equalIgnoringASCIICase(last->value(), "important")) {
+ --last;
+ while (last->type() == WhitespaceToken)
+ --last;
+ if (last->type() == DelimiterToken && last->delimiter() == '!') {
+ important = true;
+ declarationValueEnd = last;
+ }
+ }
+
+ size_t propertiesCount = m_parsedProperties.size();
+ if (propertyID == CSSPropertyInvalid && CSSVariableParser::isValidVariableName(token)) {
+ AtomicString variableName = token.value().toAtomicString();
+ consumeCustomPropertyValue(range.makeSubRange(&range.peek(), declarationValueEnd), variableName, important);
+ }
+
+ if (important && (ruleType == StyleRule::FontFace || ruleType == StyleRule::Keyframe))
+ return;
+
+ if (propertyID != CSSPropertyInvalid)
+ consumeDeclarationValue(range.makeSubRange(&range.peek(), declarationValueEnd), propertyID, important, ruleType);
+
+ if (m_observerWrapper && (ruleType == StyleRule::Style || ruleType == StyleRule::Keyframe)) {
+ m_observerWrapper->observer().observeProperty(
+ m_observerWrapper->startOffset(rangeCopy), m_observerWrapper->endOffset(rangeCopy),
+ important, m_parsedProperties.size() != propertiesCount);
+ }
+}
+
+void CSSParserImpl::consumeCustomPropertyValue(CSSParserTokenRange range, const AtomicString& variableName, bool important)
+{
+ if (RefPtr<CSSCustomPropertyValue> value = CSSVariableParser::parseDeclarationValue(variableName, range))
+ m_parsedProperties.append(CSSProperty(CSSPropertyCustom, WTFMove(value), important));
+}
+
+void CSSParserImpl::consumeDeclarationValue(CSSParserTokenRange range, CSSPropertyID propertyID, bool important, StyleRule::Type ruleType)
+{
+ CSSPropertyParser::parseValue(propertyID, important, range, m_context, m_styleSheet.get(), m_parsedProperties, ruleType);
+}
+
+std::unique_ptr<Vector<double>> CSSParserImpl::consumeKeyframeKeyList(CSSParserTokenRange range)
+{
+ std::unique_ptr<Vector<double>> result = std::unique_ptr<Vector<double>>(new Vector<double>);
+ while (true) {
+ range.consumeWhitespace();
+ const CSSParserToken& token = range.consumeIncludingWhitespace();
+ if (token.type() == PercentageToken && token.numericValue() >= 0 && token.numericValue() <= 100)
+ result->append(token.numericValue() / 100);
+ else if (token.type() == IdentToken && equalIgnoringASCIICase(token.value(), "from"))
+ result->append(0);
+ else if (token.type() == IdentToken && equalIgnoringASCIICase(token.value(), "to"))
+ result->append(1);
+ else
+ return nullptr; // Parser error, invalid value in keyframe selector
+ if (range.atEnd())
+ return result;
+ if (range.consume().type() != CommaToken)
+ return nullptr; // Parser error
+ }
+}
+
+} // namespace WebCore