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