1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Copyright (C) 2016 Apple Inc. All rights reserved.
3//
4// Redistribution and use in source and binary forms, with or without
5// modification, are permitted provided that the following conditions are
6// met:
7//
8// * Redistributions of source code must retain the above copyright
9// notice, this list of conditions and the following disclaimer.
10// * Redistributions in binary form must reproduce the above
11// copyright notice, this list of conditions and the following disclaimer
12// in the documentation and/or other materials provided with the
13// distribution.
14// * Neither the name of Google Inc. nor the names of its
15// contributors may be used to endorse or promote products derived from
16// this software without specific prior written permission.
17//
18// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30#include "config.h"
31#include "CSSParserImpl.h"
32
33#include "CSSAtRuleID.h"
34#include "CSSCustomPropertyValue.h"
35#include "CSSDeferredParser.h"
36#include "CSSKeyframeRule.h"
37#include "CSSKeyframesRule.h"
38#include "CSSParserObserver.h"
39#include "CSSParserObserverWrapper.h"
40#include "CSSParserSelector.h"
41#include "CSSPropertyParser.h"
42#include "CSSSelectorParser.h"
43#include "CSSStyleSheet.h"
44#include "CSSSupportsParser.h"
45#include "CSSTokenizer.h"
46#include "CSSVariableParser.h"
47#include "Document.h"
48#include "Element.h"
49#include "MediaList.h"
50#include "MediaQueryParser.h"
51#include "MediaQueryParserContext.h"
52#include "StyleProperties.h"
53#include "StyleRuleImport.h"
54#include "StyleSheetContents.h"
55
56#include <bitset>
57#include <memory>
58
59namespace WebCore {
60
61CSSParserImpl::CSSParserImpl(const CSSParserContext& context, StyleSheetContents* styleSheet)
62 : m_context(context)
63 , m_styleSheet(styleSheet)
64{
65
66}
67
68CSSParserImpl::CSSParserImpl(CSSDeferredParser& deferredParser)
69 : m_context(deferredParser.context())
70 , m_styleSheet(deferredParser.styleSheet())
71 , m_deferredParser(&deferredParser)
72{
73}
74
75CSSParserImpl::CSSParserImpl(const CSSParserContext& context, const String& string, StyleSheetContents* styleSheet, CSSParserObserverWrapper* wrapper, CSSParser::RuleParsing ruleParsing)
76 : m_context(context)
77 , m_styleSheet(styleSheet)
78 , m_observerWrapper(wrapper)
79{
80 m_tokenizer = wrapper ? std::make_unique<CSSTokenizer>(string, *wrapper) : std::make_unique<CSSTokenizer>(string);
81 if (context.deferredCSSParserEnabled && !wrapper && styleSheet && ruleParsing == CSSParser::RuleParsing::Deferred)
82 m_deferredParser = CSSDeferredParser::create(context, string, *styleSheet);
83}
84
85CSSParser::ParseResult CSSParserImpl::parseValue(MutableStyleProperties* declaration, CSSPropertyID propertyID, const String& string, bool important, const CSSParserContext& context)
86{
87 CSSParserImpl parser(context, string);
88 StyleRule::Type ruleType = StyleRule::Style;
89#if ENABLE(CSS_DEVICE_ADAPTATION)
90 if (declaration->cssParserMode() == CSSViewportRuleMode)
91 ruleType = StyleRule::Viewport;
92#endif
93 parser.consumeDeclarationValue(parser.tokenizer()->tokenRange(), propertyID, important, ruleType);
94 if (parser.m_parsedProperties.isEmpty())
95 return CSSParser::ParseResult::Error;
96 return declaration->addParsedProperties(parser.m_parsedProperties) ? CSSParser::ParseResult::Changed : CSSParser::ParseResult::Unchanged;
97}
98
99CSSParser::ParseResult CSSParserImpl::parseCustomPropertyValue(MutableStyleProperties* declaration, const AtomicString& propertyName, const String& string, bool important, const CSSParserContext& context)
100{
101 CSSParserImpl parser(context, string);
102 parser.consumeCustomPropertyValue(parser.tokenizer()->tokenRange(), propertyName, important);
103 if (parser.m_parsedProperties.isEmpty())
104 return CSSParser::ParseResult::Error;
105 return declaration->addParsedProperties(parser.m_parsedProperties) ? CSSParser::ParseResult::Changed : CSSParser::ParseResult::Unchanged;
106}
107
108static inline void filterProperties(bool important, const ParsedPropertyVector& input, ParsedPropertyVector& output, size_t& unusedEntries, std::bitset<numCSSProperties>& seenProperties, HashSet<AtomicString>& seenCustomProperties)
109{
110 // Add properties in reverse order so that highest priority definitions are reached first. Duplicate definitions can then be ignored when found.
111 for (size_t i = input.size(); i--; ) {
112 const CSSProperty& property = input[i];
113 if (property.isImportant() != important)
114 continue;
115 const unsigned propertyIDIndex = property.id() - firstCSSProperty;
116
117 if (property.id() == CSSPropertyCustom) {
118 auto& name = downcast<CSSCustomPropertyValue>(*property.value()).name();
119 if (!seenCustomProperties.add(name).isNewEntry)
120 continue;
121 output[--unusedEntries] = property;
122 continue;
123 }
124
125 // FIXME-NEWPARSER: We won't support @apply yet.
126 /*else if (property.id() == CSSPropertyApplyAtRule) {
127 // FIXME: Do we need to do anything here?
128 } */
129
130 if (seenProperties.test(propertyIDIndex))
131 continue;
132 seenProperties.set(propertyIDIndex);
133
134 output[--unusedEntries] = property;
135 }
136}
137
138Ref<DeferredStyleProperties> CSSParserImpl::createDeferredStyleProperties(const CSSParserTokenRange& propertyRange)
139{
140 ASSERT(m_deferredParser);
141 return DeferredStyleProperties::create(propertyRange, *m_deferredParser);
142}
143
144static Ref<ImmutableStyleProperties> createStyleProperties(ParsedPropertyVector& parsedProperties, CSSParserMode mode)
145{
146 std::bitset<numCSSProperties> seenProperties;
147 size_t unusedEntries = parsedProperties.size();
148 ParsedPropertyVector results(unusedEntries);
149 HashSet<AtomicString> seenCustomProperties;
150
151 filterProperties(true, parsedProperties, results, unusedEntries, seenProperties, seenCustomProperties);
152 filterProperties(false, parsedProperties, results, unusedEntries, seenProperties, seenCustomProperties);
153
154 Ref<ImmutableStyleProperties> result = ImmutableStyleProperties::create(results.data() + unusedEntries, results.size() - unusedEntries, mode);
155 parsedProperties.clear();
156 return result;
157}
158
159Ref<ImmutableStyleProperties> CSSParserImpl::parseInlineStyleDeclaration(const String& string, const Element* element)
160{
161 CSSParserContext context(element->document());
162 context.mode = strictToCSSParserMode(element->isHTMLElement() && !element->document().inQuirksMode());
163
164 CSSParserImpl parser(context, string);
165 parser.consumeDeclarationList(parser.tokenizer()->tokenRange(), StyleRule::Style);
166 return createStyleProperties(parser.m_parsedProperties, context.mode);
167}
168
169Ref<ImmutableStyleProperties> CSSParserImpl::parseDeferredDeclaration(CSSParserTokenRange tokenRange, const CSSParserContext& context, StyleSheetContents* styleSheet)
170{
171 if (!styleSheet) {
172 ParsedPropertyVector properties;
173 return createStyleProperties(properties, context.mode);
174 }
175 CSSParserImpl parser(context, styleSheet);
176 parser.consumeDeclarationList(tokenRange, StyleRule::Style);
177 return createStyleProperties(parser.m_parsedProperties, context.mode);
178}
179
180void CSSParserImpl::parseDeferredRuleList(CSSParserTokenRange tokenRange, CSSDeferredParser& deferredParser, Vector<RefPtr<StyleRuleBase>>& childRules)
181{
182 if (!deferredParser.styleSheet())
183 return;
184 CSSParserImpl parser(deferredParser);
185 parser.consumeRuleList(tokenRange, RegularRuleList, [&childRules](const RefPtr<StyleRuleBase>& rule) {
186 childRules.append(rule);
187 });
188 childRules.shrinkToFit();
189}
190
191void CSSParserImpl::parseDeferredKeyframeList(CSSParserTokenRange tokenRange, CSSDeferredParser& deferredParser, StyleRuleKeyframes& keyframeRule)
192{
193 if (!deferredParser.styleSheet())
194 return;
195 CSSParserImpl parser(deferredParser);
196 parser.consumeRuleList(tokenRange, KeyframesRuleList, [&keyframeRule](const RefPtr<StyleRuleBase>& keyframe) {
197 keyframeRule.parserAppendKeyframe(downcast<const StyleRuleKeyframe>(keyframe.get()));
198 });
199}
200
201bool CSSParserImpl::parseDeclarationList(MutableStyleProperties* declaration, const String& string, const CSSParserContext& context)
202{
203 CSSParserImpl parser(context, string);
204 StyleRule::Type ruleType = StyleRule::Style;
205#if ENABLE(CSS_DEVICE_ADAPTATION)
206 if (declaration->cssParserMode() == CSSViewportRuleMode)
207 ruleType = StyleRule::Viewport;
208#endif
209 parser.consumeDeclarationList(parser.tokenizer()->tokenRange(), ruleType);
210 if (parser.m_parsedProperties.isEmpty())
211 return false;
212
213 std::bitset<numCSSProperties> seenProperties;
214 size_t unusedEntries = parser.m_parsedProperties.size();
215 ParsedPropertyVector results(unusedEntries);
216 HashSet<AtomicString> seenCustomProperties;
217 filterProperties(true, parser.m_parsedProperties, results, unusedEntries, seenProperties, seenCustomProperties);
218 filterProperties(false, parser.m_parsedProperties, results, unusedEntries, seenProperties, seenCustomProperties);
219 if (unusedEntries)
220 results.remove(0, unusedEntries);
221 return declaration->addParsedProperties(results);
222}
223
224RefPtr<StyleRuleBase> CSSParserImpl::parseRule(const String& string, const CSSParserContext& context, StyleSheetContents* styleSheet, AllowedRulesType allowedRules)
225{
226 CSSParserImpl parser(context, string, styleSheet);
227 CSSParserTokenRange range = parser.tokenizer()->tokenRange();
228 range.consumeWhitespace();
229 if (range.atEnd())
230 return nullptr; // Parse error, empty rule
231 RefPtr<StyleRuleBase> rule;
232 if (range.peek().type() == AtKeywordToken)
233 rule = parser.consumeAtRule(range, allowedRules);
234 else
235 rule = parser.consumeQualifiedRule(range, allowedRules);
236 if (!rule)
237 return nullptr; // Parse error, failed to consume rule
238 range.consumeWhitespace();
239 if (!rule || !range.atEnd())
240 return nullptr; // Parse error, trailing garbage
241 return rule;
242}
243
244void CSSParserImpl::parseStyleSheet(const String& string, const CSSParserContext& context, StyleSheetContents* styleSheet, CSSParser::RuleParsing ruleParsing)
245{
246 CSSParserImpl parser(context, string, styleSheet, nullptr, ruleParsing);
247 bool firstRuleValid = parser.consumeRuleList(parser.tokenizer()->tokenRange(), TopLevelRuleList, [&styleSheet](RefPtr<StyleRuleBase> rule) {
248 if (rule->isCharsetRule())
249 return;
250 styleSheet->parserAppendRule(rule.releaseNonNull());
251 });
252 styleSheet->setHasSyntacticallyValidCSSHeader(firstRuleValid);
253 parser.adoptTokenizerEscapedStrings();
254}
255
256void CSSParserImpl::adoptTokenizerEscapedStrings()
257{
258 if (!m_deferredParser || !m_tokenizer)
259 return;
260 m_deferredParser->adoptTokenizerEscapedStrings(m_tokenizer->escapedStringsForAdoption());
261}
262
263CSSSelectorList CSSParserImpl::parsePageSelector(CSSParserTokenRange range, StyleSheetContents* styleSheet)
264{
265 // We only support a small subset of the css-page spec.
266 range.consumeWhitespace();
267 AtomicString typeSelector;
268 if (range.peek().type() == IdentToken)
269 typeSelector = range.consume().value().toAtomicString();
270
271 AtomicString pseudo;
272 if (range.peek().type() == ColonToken) {
273 range.consume();
274 if (range.peek().type() != IdentToken)
275 return CSSSelectorList();
276 pseudo = range.consume().value().toAtomicString();
277 }
278
279 range.consumeWhitespace();
280 if (!range.atEnd())
281 return CSSSelectorList(); // Parse error; extra tokens in @page selector
282
283 std::unique_ptr<CSSParserSelector> selector;
284 if (!typeSelector.isNull() && pseudo.isNull())
285 selector = std::make_unique<CSSParserSelector>(QualifiedName(nullAtom(), typeSelector, styleSheet->defaultNamespace()));
286 else {
287 selector = std::make_unique<CSSParserSelector>();
288 if (!pseudo.isNull()) {
289 selector = std::unique_ptr<CSSParserSelector>(CSSParserSelector::parsePagePseudoSelector(pseudo));
290 if (!selector || selector->match() != CSSSelector::PagePseudoClass)
291 return CSSSelectorList();
292 }
293 if (!typeSelector.isNull())
294 selector->prependTagSelector(QualifiedName(nullAtom(), typeSelector, styleSheet->defaultNamespace()));
295 }
296
297 selector->setForPage();
298 return CSSSelectorList { Vector<std::unique_ptr<CSSParserSelector>>::from(WTFMove(selector)) };
299}
300
301std::unique_ptr<Vector<double>> CSSParserImpl::parseKeyframeKeyList(const String& keyList)
302{
303 return consumeKeyframeKeyList(CSSTokenizer(keyList).tokenRange());
304}
305
306bool CSSParserImpl::supportsDeclaration(CSSParserTokenRange& range)
307{
308 ASSERT(m_parsedProperties.isEmpty());
309 consumeDeclaration(range, StyleRule::Style);
310 bool result = !m_parsedProperties.isEmpty();
311 m_parsedProperties.clear();
312 return result;
313}
314
315void CSSParserImpl::parseDeclarationListForInspector(const String& declaration, const CSSParserContext& context, CSSParserObserver& observer)
316{
317 CSSParserObserverWrapper wrapper(observer);
318 CSSParserImpl parser(context, declaration, nullptr, &wrapper);
319 observer.startRuleHeader(StyleRule::Style, 0);
320 observer.endRuleHeader(1);
321 parser.consumeDeclarationList(parser.tokenizer()->tokenRange(), StyleRule::Style);
322}
323
324void CSSParserImpl::parseStyleSheetForInspector(const String& string, const CSSParserContext& context, StyleSheetContents* styleSheet, CSSParserObserver& observer)
325{
326 CSSParserObserverWrapper wrapper(observer);
327 CSSParserImpl parser(context, string, styleSheet, &wrapper);
328 bool firstRuleValid = parser.consumeRuleList(parser.tokenizer()->tokenRange(), TopLevelRuleList, [&styleSheet](RefPtr<StyleRuleBase> rule) {
329 if (rule->isCharsetRule())
330 return;
331 styleSheet->parserAppendRule(rule.releaseNonNull());
332 });
333 styleSheet->setHasSyntacticallyValidCSSHeader(firstRuleValid);
334}
335
336static CSSParserImpl::AllowedRulesType computeNewAllowedRules(CSSParserImpl::AllowedRulesType allowedRules, StyleRuleBase* rule)
337{
338 if (!rule || allowedRules == CSSParserImpl::KeyframeRules || allowedRules == CSSParserImpl::NoRules)
339 return allowedRules;
340 ASSERT(allowedRules <= CSSParserImpl::RegularRules);
341 if (rule->isCharsetRule() || rule->isImportRule())
342 return CSSParserImpl::AllowImportRules;
343 if (rule->isNamespaceRule())
344 return CSSParserImpl::AllowNamespaceRules;
345 return CSSParserImpl::RegularRules;
346}
347
348template<typename T>
349bool CSSParserImpl::consumeRuleList(CSSParserTokenRange range, RuleListType ruleListType, const T callback)
350{
351 AllowedRulesType allowedRules = RegularRules;
352 switch (ruleListType) {
353 case TopLevelRuleList:
354 allowedRules = AllowCharsetRules;
355 break;
356 case RegularRuleList:
357 allowedRules = RegularRules;
358 break;
359 case KeyframesRuleList:
360 allowedRules = KeyframeRules;
361 break;
362 default:
363 ASSERT_NOT_REACHED();
364 }
365
366 bool seenRule = false;
367 bool firstRuleValid = false;
368 while (!range.atEnd()) {
369 RefPtr<StyleRuleBase> rule;
370 switch (range.peek().type()) {
371 case WhitespaceToken:
372 range.consumeWhitespace();
373 continue;
374 case AtKeywordToken:
375 rule = consumeAtRule(range, allowedRules);
376 break;
377 case CDOToken:
378 case CDCToken:
379 if (ruleListType == TopLevelRuleList) {
380 range.consume();
381 continue;
382 }
383 FALLTHROUGH;
384 default:
385 rule = consumeQualifiedRule(range, allowedRules);
386 break;
387 }
388 if (!seenRule) {
389 seenRule = true;
390 firstRuleValid = rule;
391 }
392 if (rule) {
393 allowedRules = computeNewAllowedRules(allowedRules, rule.get());
394 callback(rule);
395 }
396 }
397
398 return firstRuleValid;
399}
400
401RefPtr<StyleRuleBase> CSSParserImpl::consumeAtRule(CSSParserTokenRange& range, AllowedRulesType allowedRules)
402{
403 ASSERT(range.peek().type() == AtKeywordToken);
404 const StringView name = range.consumeIncludingWhitespace().value();
405 const CSSParserToken* preludeStart = &range.peek();
406 while (!range.atEnd() && range.peek().type() != LeftBraceToken && range.peek().type() != SemicolonToken)
407 range.consumeComponentValue();
408
409 CSSParserTokenRange prelude = range.makeSubRange(preludeStart, &range.peek());
410 CSSAtRuleID id = cssAtRuleID(name);
411
412 if (range.atEnd() || range.peek().type() == SemicolonToken) {
413 range.consume();
414 if (allowedRules == AllowCharsetRules && id == CSSAtRuleCharset)
415 return consumeCharsetRule(prelude);
416 if (allowedRules <= AllowImportRules && id == CSSAtRuleImport)
417 return consumeImportRule(prelude);
418 if (allowedRules <= AllowNamespaceRules && id == CSSAtRuleNamespace)
419 return consumeNamespaceRule(prelude);
420 // FIXME-NEWPARSER: Support "apply"
421 /*if (allowedRules == ApplyRules && id == CSSAtRuleApply) {
422 consumeApplyRule(prelude);
423 return nullptr; // consumeApplyRule just updates m_parsedProperties
424 }*/
425 return nullptr; // Parse error, unrecognised at-rule without block
426 }
427
428 CSSParserTokenRange block = range.consumeBlock();
429 if (allowedRules == KeyframeRules)
430 return nullptr; // Parse error, no at-rules supported inside @keyframes
431 if (allowedRules == NoRules || allowedRules == ApplyRules)
432 return nullptr; // Parse error, no at-rules with blocks supported inside declaration lists
433
434 ASSERT(allowedRules <= RegularRules);
435
436 switch (id) {
437 case CSSAtRuleMedia:
438 return consumeMediaRule(prelude, block);
439 case CSSAtRuleSupports:
440 return consumeSupportsRule(prelude, block);
441#if ENABLE(CSS_DEVICE_ADAPTATION)
442 case CSSAtRuleViewport:
443 return consumeViewportRule(prelude, block);
444#endif
445 case CSSAtRuleFontFace:
446 return consumeFontFaceRule(prelude, block);
447 case CSSAtRuleWebkitKeyframes:
448 return consumeKeyframesRule(true, prelude, block);
449 case CSSAtRuleKeyframes:
450 return consumeKeyframesRule(false, prelude, block);
451 case CSSAtRulePage:
452 return consumePageRule(prelude, block);
453 default:
454 return nullptr; // Parse error, unrecognised at-rule with block
455 }
456}
457
458RefPtr<StyleRuleBase> CSSParserImpl::consumeQualifiedRule(CSSParserTokenRange& range, AllowedRulesType allowedRules)
459{
460 const CSSParserToken* preludeStart = &range.peek();
461 while (!range.atEnd() && range.peek().type() != LeftBraceToken)
462 range.consumeComponentValue();
463
464 if (range.atEnd())
465 return nullptr; // Parse error, EOF instead of qualified rule block
466
467 CSSParserTokenRange prelude = range.makeSubRange(preludeStart, &range.peek());
468 CSSParserTokenRange block = range.consumeBlockCheckingForEditability(m_styleSheet.get());
469
470 if (allowedRules <= RegularRules)
471 return consumeStyleRule(prelude, block);
472 if (allowedRules == KeyframeRules)
473 return consumeKeyframeStyleRule(prelude, block);
474
475 ASSERT_NOT_REACHED();
476 return nullptr;
477}
478
479// This may still consume tokens if it fails
480static AtomicString consumeStringOrURI(CSSParserTokenRange& range)
481{
482 const CSSParserToken& token = range.peek();
483
484 if (token.type() == StringToken || token.type() == UrlToken)
485 return range.consumeIncludingWhitespace().value().toAtomicString();
486
487 if (token.type() != FunctionToken || !equalIgnoringASCIICase(token.value(), "url"))
488 return AtomicString();
489
490 CSSParserTokenRange contents = range.consumeBlock();
491 const CSSParserToken& uri = contents.consumeIncludingWhitespace();
492 if (uri.type() == BadStringToken || !contents.atEnd())
493 return AtomicString();
494 return uri.value().toAtomicString();
495}
496
497RefPtr<StyleRuleCharset> CSSParserImpl::consumeCharsetRule(CSSParserTokenRange prelude)
498{
499 const CSSParserToken& string = prelude.consumeIncludingWhitespace();
500 if (string.type() != StringToken || !prelude.atEnd())
501 return nullptr; // Parse error, expected a single string
502 return StyleRuleCharset::create();
503}
504
505RefPtr<StyleRuleImport> CSSParserImpl::consumeImportRule(CSSParserTokenRange prelude)
506{
507 AtomicString uri(consumeStringOrURI(prelude));
508 if (uri.isNull())
509 return nullptr; // Parse error, expected string or URI
510
511 if (m_observerWrapper) {
512 unsigned endOffset = m_observerWrapper->endOffset(prelude);
513 m_observerWrapper->observer().startRuleHeader(StyleRule::Import, m_observerWrapper->startOffset(prelude));
514 m_observerWrapper->observer().endRuleHeader(endOffset);
515 m_observerWrapper->observer().startRuleBody(endOffset);
516 m_observerWrapper->observer().endRuleBody(endOffset);
517 }
518
519 return StyleRuleImport::create(uri, MediaQueryParser::parseMediaQuerySet(prelude, MediaQueryParserContext(m_context)).releaseNonNull());
520}
521
522RefPtr<StyleRuleNamespace> CSSParserImpl::consumeNamespaceRule(CSSParserTokenRange prelude)
523{
524 AtomicString namespacePrefix;
525 if (prelude.peek().type() == IdentToken)
526 namespacePrefix = prelude.consumeIncludingWhitespace().value().toAtomicString();
527
528 AtomicString uri(consumeStringOrURI(prelude));
529 if (uri.isNull() || !prelude.atEnd())
530 return nullptr; // Parse error, expected string or URI
531
532 return StyleRuleNamespace::create(namespacePrefix, uri);
533}
534
535RefPtr<StyleRuleMedia> CSSParserImpl::consumeMediaRule(CSSParserTokenRange prelude, CSSParserTokenRange block)
536{
537 if (m_deferredParser)
538 return StyleRuleMedia::create(MediaQueryParser::parseMediaQuerySet(prelude, MediaQueryParserContext(m_context)).releaseNonNull(), std::make_unique<DeferredStyleGroupRuleList>(block, *m_deferredParser));
539
540 Vector<RefPtr<StyleRuleBase>> rules;
541
542 if (m_observerWrapper) {
543 m_observerWrapper->observer().startRuleHeader(StyleRule::Media, m_observerWrapper->startOffset(prelude));
544 m_observerWrapper->observer().endRuleHeader(m_observerWrapper->endOffset(prelude));
545 m_observerWrapper->observer().startRuleBody(m_observerWrapper->previousTokenStartOffset(block));
546 }
547
548 consumeRuleList(block, RegularRuleList, [&rules](RefPtr<StyleRuleBase> rule) {
549 rules.append(rule);
550 });
551 rules.shrinkToFit();
552
553 if (m_observerWrapper)
554 m_observerWrapper->observer().endRuleBody(m_observerWrapper->endOffset(block));
555
556 return StyleRuleMedia::create(MediaQueryParser::parseMediaQuerySet(prelude, MediaQueryParserContext(m_context)).releaseNonNull(), rules);
557}
558
559RefPtr<StyleRuleSupports> CSSParserImpl::consumeSupportsRule(CSSParserTokenRange prelude, CSSParserTokenRange block)
560{
561 CSSSupportsParser::SupportsResult supported = CSSSupportsParser::supportsCondition(prelude, *this, CSSSupportsParser::ForAtRule);
562 if (supported == CSSSupportsParser::Invalid)
563 return nullptr; // Parse error, invalid @supports condition
564
565 if (m_deferredParser)
566 return StyleRuleSupports::create(prelude.serialize().stripWhiteSpace(), supported, std::make_unique<DeferredStyleGroupRuleList>(block, *m_deferredParser));
567
568 if (m_observerWrapper) {
569 m_observerWrapper->observer().startRuleHeader(StyleRule::Supports, m_observerWrapper->startOffset(prelude));
570 m_observerWrapper->observer().endRuleHeader(m_observerWrapper->endOffset(prelude));
571 m_observerWrapper->observer().startRuleBody(m_observerWrapper->previousTokenStartOffset(block));
572 }
573
574 Vector<RefPtr<StyleRuleBase>> rules;
575 consumeRuleList(block, RegularRuleList, [&rules](RefPtr<StyleRuleBase> rule) {
576 rules.append(rule);
577 });
578 rules.shrinkToFit();
579
580 if (m_observerWrapper)
581 m_observerWrapper->observer().endRuleBody(m_observerWrapper->endOffset(block));
582
583 return StyleRuleSupports::create(prelude.serialize().stripWhiteSpace(), supported, rules);
584}
585
586#if ENABLE(CSS_DEVICE_ADAPTATION)
587RefPtr<StyleRuleViewport> CSSParserImpl::consumeViewportRule(CSSParserTokenRange prelude, CSSParserTokenRange block)
588{
589 if (!prelude.atEnd())
590 return nullptr; // Parser error; @viewport prelude should be empty
591
592 if (m_observerWrapper) {
593 unsigned endOffset = m_observerWrapper->endOffset(prelude);
594 m_observerWrapper->observer().startRuleHeader(StyleRule::Viewport, m_observerWrapper->startOffset(prelude));
595 m_observerWrapper->observer().endRuleHeader(endOffset);
596 m_observerWrapper->observer().startRuleBody(endOffset);
597 m_observerWrapper->observer().endRuleBody(endOffset);
598 }
599
600 consumeDeclarationList(block, StyleRule::Viewport);
601 return StyleRuleViewport::create(createStyleProperties(m_parsedProperties, CSSViewportRuleMode));
602}
603#endif
604
605RefPtr<StyleRuleFontFace> CSSParserImpl::consumeFontFaceRule(CSSParserTokenRange prelude, CSSParserTokenRange block)
606{
607 if (!prelude.atEnd())
608 return nullptr; // Parse error; @font-face prelude should be empty
609
610 if (m_observerWrapper) {
611 unsigned endOffset = m_observerWrapper->endOffset(prelude);
612 m_observerWrapper->observer().startRuleHeader(StyleRule::FontFace, m_observerWrapper->startOffset(prelude));
613 m_observerWrapper->observer().endRuleHeader(endOffset);
614 m_observerWrapper->observer().startRuleBody(endOffset);
615 m_observerWrapper->observer().endRuleBody(endOffset);
616 }
617
618 consumeDeclarationList(block, StyleRule::FontFace);
619 return StyleRuleFontFace::create(createStyleProperties(m_parsedProperties, m_context.mode));
620}
621
622RefPtr<StyleRuleKeyframes> CSSParserImpl::consumeKeyframesRule(bool webkitPrefixed, CSSParserTokenRange prelude, CSSParserTokenRange block)
623{
624 CSSParserTokenRange rangeCopy = prelude; // For inspector callbacks
625 const CSSParserToken& nameToken = prelude.consumeIncludingWhitespace();
626 if (!prelude.atEnd())
627 return nullptr; // Parse error; expected single non-whitespace token in @keyframes header
628
629 String name;
630 if (nameToken.type() == IdentToken) {
631 name = nameToken.value().toString();
632 } else if (nameToken.type() == StringToken && webkitPrefixed)
633 name = nameToken.value().toString();
634 else
635 return nullptr; // Parse error; expected ident token in @keyframes header
636
637 if (m_deferredParser)
638 return StyleRuleKeyframes::create(name, std::make_unique<DeferredStyleGroupRuleList>(block, *m_deferredParser));
639
640 if (m_observerWrapper) {
641 m_observerWrapper->observer().startRuleHeader(StyleRule::Keyframes, m_observerWrapper->startOffset(rangeCopy));
642 m_observerWrapper->observer().endRuleHeader(m_observerWrapper->endOffset(prelude));
643 m_observerWrapper->observer().startRuleBody(m_observerWrapper->previousTokenStartOffset(block));
644 m_observerWrapper->observer().endRuleBody(m_observerWrapper->endOffset(block));
645 }
646
647 RefPtr<StyleRuleKeyframes> keyframeRule = StyleRuleKeyframes::create(name);
648 consumeRuleList(block, KeyframesRuleList, [keyframeRule](const RefPtr<StyleRuleBase>& keyframe) {
649 keyframeRule->parserAppendKeyframe(downcast<const StyleRuleKeyframe>(keyframe.get()));
650 });
651
652 // FIXME-NEWPARSER: Find out why this is done. Behavior difference when prefixed?
653 // keyframeRule->setVendorPrefixed(webkitPrefixed);
654 return keyframeRule;
655}
656
657RefPtr<StyleRulePage> CSSParserImpl::consumePageRule(CSSParserTokenRange prelude, CSSParserTokenRange block)
658{
659 CSSSelectorList selectorList = parsePageSelector(prelude, m_styleSheet.get());
660 if (!selectorList.isValid())
661 return nullptr; // Parse error, invalid @page selector
662
663 if (m_observerWrapper) {
664 unsigned endOffset = m_observerWrapper->endOffset(prelude);
665 m_observerWrapper->observer().startRuleHeader(StyleRule::Page, m_observerWrapper->startOffset(prelude));
666 m_observerWrapper->observer().endRuleHeader(endOffset);
667 }
668
669 consumeDeclarationList(block, StyleRule::Style);
670
671 return StyleRulePage::create(createStyleProperties(m_parsedProperties, m_context.mode), WTFMove(selectorList));
672}
673
674// FIXME-NEWPARSER: Support "apply"
675/*void CSSParserImpl::consumeApplyRule(CSSParserTokenRange prelude)
676{
677 const CSSParserToken& ident = prelude.consumeIncludingWhitespace();
678 if (!prelude.atEnd() || !CSSVariableParser::isValidVariableName(ident))
679 return; // Parse error, expected a single custom property name
680 m_parsedProperties.append(CSSProperty(
681 CSSPropertyApplyAtRule,
682 *CSSCustomIdentValue::create(ident.value().toString())));
683}
684*/
685
686RefPtr<StyleRuleKeyframe> CSSParserImpl::consumeKeyframeStyleRule(CSSParserTokenRange prelude, CSSParserTokenRange block)
687{
688 std::unique_ptr<Vector<double>> keyList = consumeKeyframeKeyList(prelude);
689 if (!keyList)
690 return nullptr;
691
692 if (m_observerWrapper) {
693 m_observerWrapper->observer().startRuleHeader(StyleRule::Keyframe, m_observerWrapper->startOffset(prelude));
694 m_observerWrapper->observer().endRuleHeader(m_observerWrapper->endOffset(prelude));
695 }
696
697 consumeDeclarationList(block, StyleRule::Keyframe);
698 return StyleRuleKeyframe::create(WTFMove(keyList), createStyleProperties(m_parsedProperties, m_context.mode));
699}
700
701static void observeSelectors(CSSParserObserverWrapper& wrapper, CSSParserTokenRange selectors)
702{
703 // This is easier than hooking into the CSSSelectorParser
704 selectors.consumeWhitespace();
705 CSSParserTokenRange originalRange = selectors;
706 wrapper.observer().startRuleHeader(StyleRule::Style, wrapper.startOffset(originalRange));
707
708 while (!selectors.atEnd()) {
709 const CSSParserToken* selectorStart = &selectors.peek();
710 while (!selectors.atEnd() && selectors.peek().type() != CommaToken)
711 selectors.consumeComponentValue();
712 CSSParserTokenRange selector = selectors.makeSubRange(selectorStart, &selectors.peek());
713 selectors.consumeIncludingWhitespace();
714
715 wrapper.observer().observeSelector(wrapper.startOffset(selector), wrapper.endOffset(selector));
716 }
717
718 wrapper.observer().endRuleHeader(wrapper.endOffset(originalRange));
719}
720
721RefPtr<StyleRule> CSSParserImpl::consumeStyleRule(CSSParserTokenRange prelude, CSSParserTokenRange block)
722{
723 CSSSelectorList selectorList = CSSSelectorParser::parseSelector(prelude, m_context, m_styleSheet.get());
724 if (!selectorList.isValid())
725 return nullptr; // Parse error, invalid selector list
726
727 if (m_observerWrapper)
728 observeSelectors(*m_observerWrapper, prelude);
729
730 if (m_deferredParser) {
731 // If a rule is empty (i.e., only whitespace), don't bother using
732 // deferred parsing. This allows the empty rule optimization in ElementRuleCollector
733 // to continue to work. Note we don't have to consider CommentTokens, since those
734 // are stripped out.
735 CSSParserTokenRange blockCopy = block;
736 blockCopy.consumeWhitespace();
737 if (!blockCopy.atEnd()) {
738 return StyleRule::create(createDeferredStyleProperties(block), m_context.hasDocumentSecurityOrigin, WTFMove(selectorList));
739 }
740 }
741
742 consumeDeclarationList(block, StyleRule::Style);
743 return StyleRule::create(createStyleProperties(m_parsedProperties, m_context.mode), m_context.hasDocumentSecurityOrigin, WTFMove(selectorList));
744}
745
746void CSSParserImpl::consumeDeclarationList(CSSParserTokenRange range, StyleRule::Type ruleType)
747{
748 ASSERT(m_parsedProperties.isEmpty());
749
750 bool useObserver = m_observerWrapper && (ruleType == StyleRule::Style || ruleType == StyleRule::Keyframe);
751 if (useObserver) {
752 m_observerWrapper->observer().startRuleBody(m_observerWrapper->previousTokenStartOffset(range));
753 m_observerWrapper->skipCommentsBefore(range, true);
754 }
755
756 while (!range.atEnd()) {
757 switch (range.peek().type()) {
758 case WhitespaceToken:
759 case SemicolonToken:
760 range.consume();
761 break;
762 case IdentToken: {
763 const CSSParserToken* declarationStart = &range.peek();
764
765 if (useObserver)
766 m_observerWrapper->yieldCommentsBefore(range);
767
768 while (!range.atEnd() && range.peek().type() != SemicolonToken)
769 range.consumeComponentValue();
770
771 consumeDeclaration(range.makeSubRange(declarationStart, &range.peek()), ruleType);
772
773 if (useObserver)
774 m_observerWrapper->skipCommentsBefore(range, false);
775 break;
776 }
777 case AtKeywordToken: {
778 // FIXME-NEWPARSER: Support apply
779 AllowedRulesType allowedRules = /* ruleType == StyleRule::Style && RuntimeEnabledFeatures::cssApplyAtRulesEnabled() ? ApplyRules :*/ NoRules;
780 RefPtr<StyleRuleBase> rule = consumeAtRule(range, allowedRules);
781 ASSERT_UNUSED(rule, !rule);
782 break;
783 }
784 default: // Parse error, unexpected token in declaration list
785 while (!range.atEnd() && range.peek().type() != SemicolonToken)
786 range.consumeComponentValue();
787 break;
788 }
789 }
790
791 // Yield remaining comments
792 if (useObserver) {
793 m_observerWrapper->yieldCommentsBefore(range);
794 m_observerWrapper->observer().endRuleBody(m_observerWrapper->endOffset(range));
795 }
796}
797
798void CSSParserImpl::consumeDeclaration(CSSParserTokenRange range, StyleRule::Type ruleType)
799{
800 CSSParserTokenRange rangeCopy = range; // For inspector callbacks
801
802 ASSERT(range.peek().type() == IdentToken);
803 const CSSParserToken& token = range.consumeIncludingWhitespace();
804 CSSPropertyID propertyID = token.parseAsCSSPropertyID();
805 if (range.consume().type() != ColonToken)
806 return; // Parse error
807
808 bool important = false;
809 const CSSParserToken* declarationValueEnd = range.end();
810 const CSSParserToken* last = range.end() - 1;
811 while (last->type() == WhitespaceToken)
812 --last;
813 if (last->type() == IdentToken && equalIgnoringASCIICase(last->value(), "important")) {
814 --last;
815 while (last->type() == WhitespaceToken)
816 --last;
817 if (last->type() == DelimiterToken && last->delimiter() == '!') {
818 important = true;
819 declarationValueEnd = last;
820 }
821 }
822
823 size_t propertiesCount = m_parsedProperties.size();
824 if (propertyID == CSSPropertyInvalid && CSSVariableParser::isValidVariableName(token)) {
825 AtomicString variableName = token.value().toAtomicString();
826 consumeCustomPropertyValue(range.makeSubRange(&range.peek(), declarationValueEnd), variableName, important);
827 }
828
829 if (important && (ruleType == StyleRule::FontFace || ruleType == StyleRule::Keyframe))
830 return;
831
832 if (propertyID != CSSPropertyInvalid)
833 consumeDeclarationValue(range.makeSubRange(&range.peek(), declarationValueEnd), propertyID, important, ruleType);
834
835 if (m_observerWrapper && (ruleType == StyleRule::Style || ruleType == StyleRule::Keyframe)) {
836 m_observerWrapper->observer().observeProperty(
837 m_observerWrapper->startOffset(rangeCopy), m_observerWrapper->endOffset(rangeCopy),
838 important, m_parsedProperties.size() != propertiesCount);
839 }
840}
841
842void CSSParserImpl::consumeCustomPropertyValue(CSSParserTokenRange range, const AtomicString& variableName, bool important)
843{
844 if (RefPtr<CSSCustomPropertyValue> value = CSSVariableParser::parseDeclarationValue(variableName, range, m_context))
845 m_parsedProperties.append(CSSProperty(CSSPropertyCustom, WTFMove(value), important));
846}
847
848void CSSParserImpl::consumeDeclarationValue(CSSParserTokenRange range, CSSPropertyID propertyID, bool important, StyleRule::Type ruleType)
849{
850 CSSPropertyParser::parseValue(propertyID, important, range, m_context, m_parsedProperties, ruleType);
851}
852
853std::unique_ptr<Vector<double>> CSSParserImpl::consumeKeyframeKeyList(CSSParserTokenRange range)
854{
855 std::unique_ptr<Vector<double>> result = std::unique_ptr<Vector<double>>(new Vector<double>);
856 while (true) {
857 range.consumeWhitespace();
858 const CSSParserToken& token = range.consumeIncludingWhitespace();
859 if (token.type() == PercentageToken && token.numericValue() >= 0 && token.numericValue() <= 100)
860 result->append(token.numericValue() / 100);
861 else if (token.type() == IdentToken && equalIgnoringASCIICase(token.value(), "from"))
862 result->append(0);
863 else if (token.type() == IdentToken && equalIgnoringASCIICase(token.value(), "to"))
864 result->append(1);
865 else
866 return nullptr; // Parser error, invalid value in keyframe selector
867 if (range.atEnd())
868 return result;
869 if (range.consume().type() != CommaToken)
870 return nullptr; // Parser error
871 }
872}
873
874} // namespace WebCore
875