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 | |
45 | namespace WebCore { |
46 | |
47 | float 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 | |
72 | SizesAttributeParser::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 | |
80 | float SizesAttributeParser::length() |
81 | { |
82 | if (m_isValid) |
83 | return effectiveSize(); |
84 | return effectiveSizeDefaultValue(); |
85 | } |
86 | |
87 | bool 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 | |
111 | bool 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 | |
121 | bool 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 | |
150 | float SizesAttributeParser::effectiveSize() |
151 | { |
152 | if (m_lengthWasSet) |
153 | return m_length; |
154 | return effectiveSizeDefaultValue(); |
155 | } |
156 | |
157 | unsigned 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 | |