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
13namespace blink {
14
15SizesAttributeParser::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
25unsigned SizesAttributeParser::length()
26{
27    if (m_isValid)
28        return effectiveSize();
29    return effectiveSizeDefaultValue();
30}
31
32bool 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
61static 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
70static 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
91bool 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
98bool 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
119bool 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
139unsigned SizesAttributeParser::effectiveSize()
140{
141    if (m_lengthWasSet)
142        return m_length;
143    return effectiveSizeDefaultValue();
144}
145
146unsigned SizesAttributeParser::effectiveSizeDefaultValue()
147{
148    // Returning the equivalent of "100vw"
149    m_viewportDependant = true;
150    return m_mediaValues->viewportWidth();
151}
152
153} // namespace
154
155