| 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 |  |