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 "SizesAttributeParser.h"
32
33#include "CSSPrimitiveValue.h"
34#include "CSSToLengthConversionData.h"
35#include "CSSTokenizer.h"
36#include "FontCascade.h"
37#include "MediaList.h"
38#include "MediaQueryEvaluator.h"
39#include "MediaQueryParser.h"
40#include "MediaQueryParserContext.h"
41#include "RenderView.h"
42#include "SizesCalcParser.h"
43#include "StyleScope.h"
44
45namespace WebCore {
46
47float SizesAttributeParser::computeLength(double value, CSSPrimitiveValue::UnitType type, const Document& document)
48{
49 auto* renderer = document.renderView();
50 if (!renderer)
51 return 0;
52 auto& style = renderer->style();
53
54 CSSToLengthConversionData conversionData(&style, &style, renderer);
55
56 // Because we evaluate "sizes" at parse time (before style has been resolved), the font metrics used for these specific units
57 // are not available. The font selector's internal consistency isn't guaranteed just yet, so we can just temporarily clear
58 // the pointer to it for the duration of the unit evaluation. This is acceptible because the style always comes from the
59 // RenderView, which has its font information hardcoded in resolveForDocument() to be -webkit-standard, whose operations
60 // don't require a font selector.
61 if (type == CSSPrimitiveValue::CSS_EXS || type == CSSPrimitiveValue::CSS_CHS) {
62 RefPtr<FontSelector> fontSelector = style.fontCascade().fontSelector();
63 style.fontCascade().update(nullptr);
64 float result = CSSPrimitiveValue::computeNonCalcLengthDouble(conversionData, type, value);
65 style.fontCascade().update(fontSelector.get());
66 return result;
67 }
68
69 return clampTo<float>(CSSPrimitiveValue::computeNonCalcLengthDouble(conversionData, type, value));
70}
71
72SizesAttributeParser::SizesAttributeParser(const String& attribute, const Document& document)
73 : m_document(document)
74 , m_length(0)
75 , m_lengthWasSet(false)
76{
77 m_isValid = parse(CSSTokenizer(attribute).tokenRange());
78}
79
80float SizesAttributeParser::length()
81{
82 if (m_isValid)
83 return effectiveSize();
84 return effectiveSizeDefaultValue();
85}
86
87bool SizesAttributeParser::calculateLengthInPixels(CSSParserTokenRange range, float& result)
88{
89 const CSSParserToken& startToken = range.peek();
90 CSSParserTokenType type = startToken.type();
91 if (type == DimensionToken) {
92 if (!CSSPrimitiveValue::isLength(startToken.unitType()))
93 return false;
94 result = computeLength(startToken.numericValue(), startToken.unitType(), m_document);
95 if (result >= 0)
96 return true;
97 } else if (type == FunctionToken) {
98 SizesCalcParser calcParser(range, m_document);
99 if (!calcParser.isValid())
100 return false;
101 result = calcParser.result();
102 return true;
103 } else if (type == NumberToken && !startToken.numericValue()) {
104 result = 0;
105 return true;
106 }
107
108 return false;
109}
110
111bool SizesAttributeParser::mediaConditionMatches(const MediaQuerySet& mediaCondition)
112{
113 // A Media Condition cannot have a media type other than screen.
114 auto* renderer = m_document.renderView();
115 if (!renderer)
116 return false;
117 auto& style = renderer->style();
118 return MediaQueryEvaluator { "screen", m_document, &style }.evaluate(mediaCondition, const_cast<Style::Scope&>(m_document.styleScope()).resolverIfExists());
119}
120
121bool SizesAttributeParser::parse(CSSParserTokenRange range)
122{
123 // Split on a comma token and parse the result tokens as (media-condition, length) pairs
124 while (!range.atEnd()) {
125 const CSSParserToken* mediaConditionStart = &range.peek();
126 // The length is the last component value before the comma which isn't whitespace or a comment
127 const CSSParserToken* lengthTokenStart = &range.peek();
128 const CSSParserToken* lengthTokenEnd = &range.peek();
129 while (!range.atEnd() && range.peek().type() != CommaToken) {
130 lengthTokenStart = &range.peek();
131 range.consumeComponentValue();
132 lengthTokenEnd = &range.peek();
133 range.consumeWhitespace();
134 }
135 range.consume();
136
137 float length;
138 if (!calculateLengthInPixels(range.makeSubRange(lengthTokenStart, lengthTokenEnd), length))
139 continue;
140 RefPtr<MediaQuerySet> mediaCondition = MediaQueryParser::parseMediaCondition(range.makeSubRange(mediaConditionStart, lengthTokenStart), MediaQueryParserContext(m_document));
141 if (!mediaCondition || !mediaConditionMatches(*mediaCondition))
142 continue;
143 m_length = length;
144 m_lengthWasSet = true;
145 return true;
146 }
147 return false;
148}
149
150float SizesAttributeParser::effectiveSize()
151{
152 if (m_lengthWasSet)
153 return m_length;
154 return effectiveSizeDefaultValue();
155}
156
157unsigned SizesAttributeParser::effectiveSizeDefaultValue()
158{
159 auto* renderer = m_document.renderView();
160 if (!renderer)
161 return 0;
162 auto& style = renderer->style();
163 return clampTo<float>(CSSPrimitiveValue::computeNonCalcLengthDouble({ &style, &style, renderer }, CSSPrimitiveValue::CSS_VW, 100.0));
164}
165
166} // namespace WebCore
167