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 "config.h"
6 #include "core/css/parser/SizesAttributeParser.h"
7
8 #include "core/MediaTypeNames.h"
9 #include "core/css/MediaQueryEvaluator.h"
10 #include "core/css/parser/MediaQueryTokenizer.h"
11 #include "core/css/parser/SizesCalcParser.h"
12
13 namespace blink {
14
SizesAttributeParser(PassRefPtr<MediaValues> mediaValues,const String & attribute)15 SizesAttributeParser::SizesAttributeParser(PassRefPtr<MediaValues> mediaValues, const String& attribute)
16 : m_mediaValues(mediaValues)
17 , m_length(0)
18 , m_lengthWasSet(false)
19 , m_viewportDependant(false)
20 {
21 MediaQueryTokenizer::tokenize(attribute, m_tokens);
22 m_isValid = parse(m_tokens);
23 }
24
length()25 unsigned SizesAttributeParser::length()
26 {
27 if (m_isValid)
28 return effectiveSize();
29 return effectiveSizeDefaultValue();
30 }
31
calculateLengthInPixels(MediaQueryTokenIterator startToken,MediaQueryTokenIterator endToken,unsigned & result)32 bool SizesAttributeParser::calculateLengthInPixels(MediaQueryTokenIterator startToken, MediaQueryTokenIterator endToken, unsigned& result)
33 {
34 if (startToken == endToken)
35 return false;
36 MediaQueryTokenType type = startToken->type();
37 if (type == DimensionToken) {
38 int length;
39 if (!CSSPrimitiveValue::isLength(startToken->unitType()))
40 return false;
41 m_viewportDependant = CSSPrimitiveValue::isViewportPercentageLength(startToken->unitType());
42 if ((m_mediaValues->computeLength(startToken->numericValue(), startToken->unitType(), length)) && (length > 0)) {
43 result = (unsigned)length;
44 return true;
45 }
46 } else if (type == FunctionToken) {
47 SizesCalcParser calcParser(startToken, endToken, m_mediaValues);
48 if (!calcParser.isValid())
49 return false;
50 m_viewportDependant = calcParser.viewportDependant();
51 result = calcParser.result();
52 return true;
53 } else if (type == NumberToken && !startToken->numericValue()) {
54 result = 0;
55 return true;
56 }
57
58 return false;
59 }
60
reverseSkipIrrelevantTokens(MediaQueryTokenIterator & token,MediaQueryTokenIterator startToken)61 static void reverseSkipIrrelevantTokens(MediaQueryTokenIterator& token, MediaQueryTokenIterator startToken)
62 {
63 MediaQueryTokenIterator endToken = token;
64 while (token != startToken && (token->type() == WhitespaceToken || token->type() == CommentToken || token->type() == EOFToken))
65 --token;
66 if (token != endToken)
67 ++token;
68 }
69
reverseSkipUntilComponentStart(MediaQueryTokenIterator & token,MediaQueryTokenIterator startToken)70 static void reverseSkipUntilComponentStart(MediaQueryTokenIterator& token, MediaQueryTokenIterator startToken)
71 {
72 if (token == startToken)
73 return;
74 --token;
75 if (token->blockType() != MediaQueryToken::BlockEnd)
76 return;
77 unsigned blockLevel = 0;
78 while (token != startToken) {
79 if (token->blockType() == MediaQueryToken::BlockEnd) {
80 ++blockLevel;
81 } else if (token->blockType() == MediaQueryToken::BlockStart) {
82 --blockLevel;
83 if (!blockLevel)
84 break;
85 }
86
87 --token;
88 }
89 }
90
mediaConditionMatches(PassRefPtrWillBeRawPtr<MediaQuerySet> mediaCondition)91 bool SizesAttributeParser::mediaConditionMatches(PassRefPtrWillBeRawPtr<MediaQuerySet> mediaCondition)
92 {
93 // A Media Condition cannot have a media type other then screen.
94 MediaQueryEvaluator mediaQueryEvaluator(*m_mediaValues);
95 return mediaQueryEvaluator.eval(mediaCondition.get());
96 }
97
parseMediaConditionAndLength(MediaQueryTokenIterator startToken,MediaQueryTokenIterator endToken)98 bool SizesAttributeParser::parseMediaConditionAndLength(MediaQueryTokenIterator startToken, MediaQueryTokenIterator endToken)
99 {
100 MediaQueryTokenIterator lengthTokenStart;
101 MediaQueryTokenIterator lengthTokenEnd;
102
103 reverseSkipIrrelevantTokens(endToken, startToken);
104 lengthTokenEnd = endToken;
105 reverseSkipUntilComponentStart(endToken, startToken);
106 lengthTokenStart = endToken;
107 unsigned length;
108 if (!calculateLengthInPixels(lengthTokenStart, lengthTokenEnd, length))
109 return false;
110 RefPtrWillBeRawPtr<MediaQuerySet> mediaCondition = MediaQueryParser::parseMediaCondition(startToken, endToken);
111 if (mediaCondition && mediaConditionMatches(mediaCondition)) {
112 m_length = length;
113 m_lengthWasSet = true;
114 return true;
115 }
116 return false;
117 }
118
parse(Vector<MediaQueryToken> & tokens)119 bool SizesAttributeParser::parse(Vector<MediaQueryToken>& tokens)
120 {
121 if (tokens.isEmpty())
122 return false;
123 MediaQueryTokenIterator startToken = tokens.begin();
124 MediaQueryTokenIterator endToken;
125 // Split on a comma token, and send the result tokens to be parsed as (media-condition, length) pairs
126 for (MediaQueryTokenIterator token = tokens.begin(); token != tokens.end(); ++token) {
127 if (token->type() == CommaToken) {
128 endToken = token;
129 if (parseMediaConditionAndLength(startToken, endToken))
130 return true;
131 startToken = token;
132 ++startToken;
133 }
134 }
135 endToken = tokens.end();
136 return parseMediaConditionAndLength(startToken, --endToken);
137 }
138
effectiveSize()139 unsigned SizesAttributeParser::effectiveSize()
140 {
141 if (m_lengthWasSet)
142 return m_length;
143 return effectiveSizeDefaultValue();
144 }
145
effectiveSizeDefaultValue()146 unsigned SizesAttributeParser::effectiveSizeDefaultValue()
147 {
148 // Returning the equivalent of "100vw"
149 m_viewportDependant = true;
150 return m_mediaValues->viewportWidth();
151 }
152
153 } // namespace
154
155