| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Copyright (C) 2016 Apple Inc. All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following disclaimer |
| // in the documentation and/or other materials provided with the |
| // distribution. |
| // * Neither the name of Google Inc. nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| #include "config.h" |
| #include "SizesAttributeParser.h" |
| |
| #include "CSSPrimitiveValue.h" |
| #include "CSSToLengthConversionData.h" |
| #include "CSSTokenizer.h" |
| #include "FontCascade.h" |
| #include "MediaList.h" |
| #include "MediaQueryEvaluator.h" |
| #include "MediaQueryParser.h" |
| #include "MediaQueryParserContext.h" |
| #include "RenderView.h" |
| #include "SizesCalcParser.h" |
| #include "StyleScope.h" |
| |
| namespace WebCore { |
| |
| float SizesAttributeParser::computeLength(double value, CSSPrimitiveValue::UnitType type, const Document& document) |
| { |
| auto* renderer = document.renderView(); |
| if (!renderer) |
| return 0; |
| auto& style = renderer->style(); |
| |
| CSSToLengthConversionData conversionData(&style, &style, renderer); |
| |
| // Because we evaluate "sizes" at parse time (before style has been resolved), the font metrics used for these specific units |
| // are not available. The font selector's internal consistency isn't guaranteed just yet, so we can just temporarily clear |
| // the pointer to it for the duration of the unit evaluation. This is acceptible because the style always comes from the |
| // RenderView, which has its font information hardcoded in resolveForDocument() to be -webkit-standard, whose operations |
| // don't require a font selector. |
| if (type == CSSPrimitiveValue::CSS_EXS || type == CSSPrimitiveValue::CSS_CHS) { |
| RefPtr<FontSelector> fontSelector = style.fontCascade().fontSelector(); |
| style.fontCascade().update(nullptr); |
| float result = CSSPrimitiveValue::computeNonCalcLengthDouble(conversionData, type, value); |
| style.fontCascade().update(fontSelector.get()); |
| return result; |
| } |
| |
| return clampTo<float>(CSSPrimitiveValue::computeNonCalcLengthDouble(conversionData, type, value)); |
| } |
| |
| SizesAttributeParser::SizesAttributeParser(const String& attribute, const Document& document) |
| : m_document(document) |
| , m_length(0) |
| , m_lengthWasSet(false) |
| { |
| m_isValid = parse(CSSTokenizer(attribute).tokenRange()); |
| } |
| |
| float SizesAttributeParser::length() |
| { |
| if (m_isValid) |
| return effectiveSize(); |
| return effectiveSizeDefaultValue(); |
| } |
| |
| bool SizesAttributeParser::calculateLengthInPixels(CSSParserTokenRange range, float& result) |
| { |
| const CSSParserToken& startToken = range.peek(); |
| CSSParserTokenType type = startToken.type(); |
| if (type == DimensionToken) { |
| if (!CSSPrimitiveValue::isLength(startToken.unitType())) |
| return false; |
| result = computeLength(startToken.numericValue(), startToken.unitType(), m_document); |
| if (result >= 0) |
| return true; |
| } else if (type == FunctionToken) { |
| SizesCalcParser calcParser(range, m_document); |
| if (!calcParser.isValid()) |
| return false; |
| result = calcParser.result(); |
| return true; |
| } else if (type == NumberToken && !startToken.numericValue()) { |
| result = 0; |
| return true; |
| } |
| |
| return false; |
| } |
| |
| bool SizesAttributeParser::mediaConditionMatches(const MediaQuerySet& mediaCondition) |
| { |
| // A Media Condition cannot have a media type other than screen. |
| auto* renderer = m_document.renderView(); |
| if (!renderer) |
| return false; |
| auto& style = renderer->style(); |
| return MediaQueryEvaluator { "screen", m_document, &style }.evaluate(mediaCondition, const_cast<Style::Scope&>(m_document.styleScope()).resolverIfExists()); |
| } |
| |
| bool SizesAttributeParser::parse(CSSParserTokenRange range) |
| { |
| // Split on a comma token and parse the result tokens as (media-condition, length) pairs |
| while (!range.atEnd()) { |
| const CSSParserToken* mediaConditionStart = &range.peek(); |
| // The length is the last component value before the comma which isn't whitespace or a comment |
| const CSSParserToken* lengthTokenStart = &range.peek(); |
| const CSSParserToken* lengthTokenEnd = &range.peek(); |
| while (!range.atEnd() && range.peek().type() != CommaToken) { |
| lengthTokenStart = &range.peek(); |
| range.consumeComponentValue(); |
| lengthTokenEnd = &range.peek(); |
| range.consumeWhitespace(); |
| } |
| range.consume(); |
| |
| float length; |
| if (!calculateLengthInPixels(range.makeSubRange(lengthTokenStart, lengthTokenEnd), length)) |
| continue; |
| RefPtr<MediaQuerySet> mediaCondition = MediaQueryParser::parseMediaCondition(range.makeSubRange(mediaConditionStart, lengthTokenStart), MediaQueryParserContext(m_document)); |
| if (!mediaCondition || !mediaConditionMatches(*mediaCondition)) |
| continue; |
| m_length = length; |
| m_lengthWasSet = true; |
| return true; |
| } |
| return false; |
| } |
| |
| float SizesAttributeParser::effectiveSize() |
| { |
| if (m_lengthWasSet) |
| return m_length; |
| return effectiveSizeDefaultValue(); |
| } |
| |
| unsigned SizesAttributeParser::effectiveSizeDefaultValue() |
| { |
| auto* renderer = m_document.renderView(); |
| if (!renderer) |
| return 0; |
| auto& style = renderer->style(); |
| return clampTo<float>(CSSPrimitiveValue::computeNonCalcLengthDouble({ &style, &style, renderer }, CSSPrimitiveValue::CSS_VW, 100.0)); |
| } |
| |
| } // namespace WebCore |