1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "third_party/blink/renderer/core/css/parser/sizes_attribute_parser.h"
6 
7 #include "third_party/blink/renderer/core/css/media_query_evaluator.h"
8 #include "third_party/blink/renderer/core/css/parser/css_tokenizer.h"
9 #include "third_party/blink/renderer/core/css/parser/sizes_math_function_parser.h"
10 #include "third_party/blink/renderer/core/media_type_names.h"
11 
12 namespace blink {
13 
SizesAttributeParser(MediaValues * media_values,const String & attribute,const ExecutionContext * execution_context)14 SizesAttributeParser::SizesAttributeParser(
15     MediaValues* media_values,
16     const String& attribute,
17     const ExecutionContext* execution_context)
18     : media_values_(media_values),
19       execution_context_(execution_context),
20       length_(0),
21       length_was_set_(false) {
22   DCHECK(media_values_);
23   is_valid_ =
24       Parse(CSSParserTokenRange(CSSTokenizer(attribute).TokenizeToEOF()));
25 }
26 
length()27 float SizesAttributeParser::length() {
28   if (is_valid_)
29     return EffectiveSize();
30   return EffectiveSizeDefaultValue();
31 }
32 
CalculateLengthInPixels(CSSParserTokenRange range,float & result)33 bool SizesAttributeParser::CalculateLengthInPixels(CSSParserTokenRange range,
34                                                    float& result) {
35   const CSSParserToken& start_token = range.Peek();
36   CSSParserTokenType type = start_token.GetType();
37   if (type == kDimensionToken) {
38     double length;
39     if (!CSSPrimitiveValue::IsLength(start_token.GetUnitType()))
40       return false;
41     if ((media_values_->ComputeLength(start_token.NumericValue(),
42                                       start_token.GetUnitType(), length)) &&
43         (length >= 0)) {
44       result = clampTo<float>(length);
45       return true;
46     }
47   } else if (type == kFunctionToken) {
48     SizesMathFunctionParser calc_parser(range, media_values_);
49     if (!calc_parser.IsValid())
50       return false;
51     result = calc_parser.Result();
52     return true;
53   } else if (type == kNumberToken && !start_token.NumericValue()) {
54     result = 0;
55     return true;
56   }
57 
58   return false;
59 }
60 
MediaConditionMatches(const MediaQuerySet & media_condition)61 bool SizesAttributeParser::MediaConditionMatches(
62     const MediaQuerySet& media_condition) {
63   // A Media Condition cannot have a media type other then screen.
64   MediaQueryEvaluator media_query_evaluator(*media_values_);
65   return media_query_evaluator.Eval(media_condition);
66 }
67 
Parse(CSSParserTokenRange range)68 bool SizesAttributeParser::Parse(CSSParserTokenRange range) {
69   // Split on a comma token and parse the result tokens as (media-condition,
70   // length) pairs
71   while (!range.AtEnd()) {
72     const CSSParserToken* media_condition_start = &range.Peek();
73     // The length is the last component value before the comma which isn't
74     // whitespace or a comment
75     const CSSParserToken* length_token_start = &range.Peek();
76     const CSSParserToken* length_token_end = &range.Peek();
77     while (!range.AtEnd() && range.Peek().GetType() != kCommaToken) {
78       length_token_start = &range.Peek();
79       range.ConsumeComponentValue();
80       length_token_end = &range.Peek();
81       range.ConsumeWhitespace();
82     }
83     range.Consume();
84 
85     float length;
86     if (!CalculateLengthInPixels(
87             range.MakeSubRange(length_token_start, length_token_end), length))
88       continue;
89     scoped_refptr<MediaQuerySet> media_condition =
90         MediaQueryParser::ParseMediaCondition(
91             range.MakeSubRange(media_condition_start, length_token_start),
92             execution_context_);
93     if (!media_condition || !MediaConditionMatches(*media_condition))
94       continue;
95     length_ = length;
96     length_was_set_ = true;
97     return true;
98   }
99   return false;
100 }
101 
EffectiveSize()102 float SizesAttributeParser::EffectiveSize() {
103   if (length_was_set_)
104     return length_;
105   return EffectiveSizeDefaultValue();
106 }
107 
EffectiveSizeDefaultValue()108 float SizesAttributeParser::EffectiveSizeDefaultValue() {
109   // Returning the equivalent of "100vw"
110   return clampTo<float>(media_values_->ViewportWidth());
111 }
112 
113 }  // namespace blink
114