root/Source/core/css/parser/MediaQueryParser.cpp

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. parse
  2. m_querySet
  3. setStateAndRestrict
  4. readRestrictor
  5. readMediaType
  6. readAnd
  7. readFeatureStart
  8. readFeature
  9. readFeatureColon
  10. readFeatureValue
  11. readFeatureEnd
  12. skipUntilComma
  13. skipUntilBlockEnd
  14. done
  15. popIfBlockMatches
  16. observeBlock
  17. observeBlocks
  18. processToken
  19. parseImpl
  20. m_mediaTypeSet
  21. clear
  22. takeMediaQuery
  23. addExpression
  24. addParserValue
  25. setMediaType

// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "config.h"
#include "core/css/parser/MediaQueryParser.h"

#include "MediaTypeNames.h"
#include "core/css/parser/CSSPropertyParser.h"
#include "core/css/parser/MediaQueryTokenizer.h"

namespace WebCore {

PassRefPtrWillBeRawPtr<MediaQuerySet> MediaQueryParser::parse(const String& queryString)
{
    return MediaQueryParser(queryString).parseImpl();
}

const MediaQueryParser::State MediaQueryParser::ReadRestrictor = &MediaQueryParser::readRestrictor;
const MediaQueryParser::State MediaQueryParser::ReadMediaType = &MediaQueryParser::readMediaType;
const MediaQueryParser::State MediaQueryParser::ReadAnd = &MediaQueryParser::readAnd;
const MediaQueryParser::State MediaQueryParser::ReadFeatureStart = &MediaQueryParser::readFeatureStart;
const MediaQueryParser::State MediaQueryParser::ReadFeature = &MediaQueryParser::readFeature;
const MediaQueryParser::State MediaQueryParser::ReadFeatureColon = &MediaQueryParser::readFeatureColon;
const MediaQueryParser::State MediaQueryParser::ReadFeatureValue = &MediaQueryParser::readFeatureValue;
const MediaQueryParser::State MediaQueryParser::ReadFeatureEnd = &MediaQueryParser::readFeatureEnd;
const MediaQueryParser::State MediaQueryParser::SkipUntilComma = &MediaQueryParser::skipUntilComma;
const MediaQueryParser::State MediaQueryParser::SkipUntilBlockEnd = &MediaQueryParser::skipUntilBlockEnd;
const MediaQueryParser::State MediaQueryParser::Done = &MediaQueryParser::done;

// FIXME: Replace the MediaQueryTokenizer with a generic CSSTokenizer, once there is one,
// or better yet, replace the MediaQueryParser with a generic thread-safe CSS parser.
MediaQueryParser::MediaQueryParser(const String& queryString)
    : m_state(&MediaQueryParser::readRestrictor)
    , m_querySet(MediaQuerySet::create())
{
    MediaQueryTokenizer::tokenize(queryString, m_tokens);
}

void MediaQueryParser::setStateAndRestrict(State state, MediaQuery::Restrictor restrictor)
{
    m_mediaQueryData.setRestrictor(restrictor);
    m_state = state;
}

// State machine member functions start here
void MediaQueryParser::readRestrictor(MediaQueryTokenType type, TokenIterator& token)
{
    readMediaType(type, token);
}

void MediaQueryParser::readMediaType(MediaQueryTokenType type, TokenIterator& token)
{
    if (type == LeftParenthesisToken) {
        m_state = ReadFeature;
    } else if (type == IdentToken) {
        if (m_state == ReadRestrictor && equalIgnoringCase(token->value(), "not")) {
            setStateAndRestrict(ReadMediaType, MediaQuery::Not);
        } else if (m_state == ReadRestrictor && equalIgnoringCase(token->value(), "only")) {
            setStateAndRestrict(ReadMediaType, MediaQuery::Only);
        } else {
            m_mediaQueryData.setMediaType(token->value());
            m_state = ReadAnd;
        }
    } else if (type == EOFToken && (!m_querySet->queryVector().size() || m_state != ReadRestrictor)) {
        m_state = Done;
    } else {
        if (type == CommaToken)
            --token;
        m_state = SkipUntilComma;
    }
}

void MediaQueryParser::readAnd(MediaQueryTokenType type, TokenIterator& token)
{
    if (type == IdentToken && equalIgnoringCase(token->value(), "and")) {
        m_state = ReadFeatureStart;
    } else if (type == CommaToken) {
        m_querySet->addMediaQuery(m_mediaQueryData.takeMediaQuery());
        m_state = ReadRestrictor;
    } else if (type == EOFToken) {
        m_state = Done;
    } else {
        m_state = SkipUntilComma;
    }
}

void MediaQueryParser::readFeatureStart(MediaQueryTokenType type, TokenIterator& token)
{
    if (type == LeftParenthesisToken)
        m_state = ReadFeature;
    else
        m_state = SkipUntilComma;
}

void MediaQueryParser::readFeature(MediaQueryTokenType type, TokenIterator& token)
{
    if (type == IdentToken) {
        m_mediaQueryData.setMediaFeature(token->value());
        m_state = ReadFeatureColon;
    } else {
        m_state = SkipUntilComma;
    }
}

void MediaQueryParser::readFeatureColon(MediaQueryTokenType type, TokenIterator& token)
{
    if (type == ColonToken) {
        m_state = ReadFeatureValue;
    } else if (type == RightParenthesisToken || type == EOFToken) {
        --token;
        m_state = ReadFeatureEnd;
    } else {
        m_state = SkipUntilBlockEnd;
    }
}

void MediaQueryParser::readFeatureValue(MediaQueryTokenType type, TokenIterator& token)
{
    if (type == DimensionToken && token->unitType() == CSSPrimitiveValue::CSS_UNKNOWN) {
        m_state = SkipUntilComma;
    } else {
        m_mediaQueryData.addParserValue(type, *token);
        m_state = ReadFeatureEnd;
    }
}

void MediaQueryParser::readFeatureEnd(MediaQueryTokenType type, TokenIterator& token)
{
    if (type == RightParenthesisToken || type == EOFToken) {
        if (m_mediaQueryData.addExpression())
            m_state = ReadAnd;
        else
            m_state = SkipUntilComma;
    } else if (type == DelimiterToken && token->delimiter() == '/') {
        m_mediaQueryData.addParserValue(type, *token);
        m_state = ReadFeatureValue;
    } else {
        m_state = SkipUntilBlockEnd;
    }
}

void MediaQueryParser::skipUntilComma(MediaQueryTokenType type, TokenIterator& token)
{
    if ((type == CommaToken && m_blockStack.isEmpty()) || type == EOFToken) {
        m_state = ReadRestrictor;
        m_mediaQueryData.clear();
        m_querySet->addMediaQuery(MediaQuery::createNotAll());
    }
}

void MediaQueryParser::skipUntilBlockEnd(MediaQueryTokenType type, TokenIterator& token)
{
    if (m_blockStack.isEmpty())
        m_state = SkipUntilComma;
}

void MediaQueryParser::done(MediaQueryTokenType type, TokenIterator& token) { }

void MediaQueryParser::popIfBlockMatches(Vector<BlockType>& blockStack, BlockType type)
{
    if (!blockStack.isEmpty() && blockStack.last() == type)
        blockStack.removeLast();
}

bool MediaQueryParser::observeBlock(BlockParameters& parameters, MediaQueryTokenType type)
{
    if (type == parameters.leftToken) {
        if (parameters.stateChange == ModifyState)
            m_state = SkipUntilBlockEnd;
        m_blockStack.append(parameters.blockType);
    } else if (type == parameters.rightToken) {
        popIfBlockMatches(m_blockStack, parameters.blockType);
    } else {
        return false;
    }
    return true;
}

void MediaQueryParser::observeBlocks(MediaQueryTokenType type)
{
    enum { BlockParametersCount = 4 };
    BlockParameters blockParameterSet[BlockParametersCount] = {
        { LeftParenthesisToken, RightParenthesisToken, ParenthesisBlock, DoNotModifyState },
        { FunctionToken, RightParenthesisToken, ParenthesisBlock, ModifyState },
        { LeftBracketToken, RightBracketToken, BracketsBlock, ModifyState },
        { LeftBraceToken, RightBraceToken, BracesBlock, ModifyState }
    };

    for (unsigned i = 0; i < BlockParametersCount; ++i) {
        if (observeBlock(blockParameterSet[i], type))
            break;
    }
}

void MediaQueryParser::processToken(TokenIterator& token)
{
    MediaQueryTokenType type = token->type();

    observeBlocks(type);

    // Call the function that handles current state
    if (type != WhitespaceToken && type != CommentToken)
        ((this)->*(m_state))(type, token);
}

// The state machine loop
PassRefPtrWillBeRawPtr<MediaQuerySet> MediaQueryParser::parseImpl()
{
    for (Vector<MediaQueryToken>::iterator token = m_tokens.begin(); token != m_tokens.end(); ++token)
        processToken(token);

    if (m_state != ReadAnd && m_state != ReadRestrictor && m_state != Done)
        m_querySet->addMediaQuery(MediaQuery::createNotAll());
    else if (m_mediaQueryData.currentMediaQueryChanged())
        m_querySet->addMediaQuery(m_mediaQueryData.takeMediaQuery());

    return m_querySet;
}

MediaQueryData::MediaQueryData()
    : m_restrictor(MediaQuery::None)
    , m_mediaType(MediaTypeNames::all)
    , m_expressions(adoptPtrWillBeNoop(new ExpressionHeapVector))
    , m_mediaTypeSet(false)
{
}

void MediaQueryData::clear()
{
    m_restrictor = MediaQuery::None;
    m_mediaType = MediaTypeNames::all;
    m_mediaTypeSet = false;
    m_mediaFeature = String();
    m_valueList.clear();
    m_expressions = adoptPtrWillBeNoop(new ExpressionHeapVector);
}

PassOwnPtrWillBeRawPtr<MediaQuery> MediaQueryData::takeMediaQuery()
{
    OwnPtrWillBeRawPtr<MediaQuery> mediaQuery = adoptPtrWillBeNoop(new MediaQuery(m_restrictor, m_mediaType, m_expressions.release()));
    clear();
    return mediaQuery.release();
}

bool MediaQueryData::addExpression()
{
    OwnPtrWillBeRawPtr<MediaQueryExp> expression = MediaQueryExp::createIfValid(m_mediaFeature, &m_valueList);
    bool isValid = !!expression;
    m_expressions->append(expression.release());
    m_valueList.clear();
    return isValid;
}

void MediaQueryData::addParserValue(MediaQueryTokenType type, MediaQueryToken& token)
{
    CSSParserValue value;
    if (type == NumberToken || type == PercentageToken || type == DimensionToken) {
        value.setFromNumber(token.numericValue(), token.unitType());
        value.isInt = (token.numericValueType() == IntegerValueType);
    } else if (type == DelimiterToken) {
        value.unit = CSSParserValue::Operator;
        value.iValue = token.delimiter();
    } else {
        CSSParserFunction* function = new CSSParserFunction;
        function->name.init(token.value());
        value.setFromFunction(function);
        CSSParserString tokenValue;
        tokenValue.init(token.value());
        value.id = cssValueKeywordID(tokenValue);
    }
    m_valueList.addValue(value);
}

void MediaQueryData::setMediaType(const String& mediaType)
{
    m_mediaType = mediaType;
    m_mediaTypeSet = true;
}

} // namespace WebCore

/* [<][>][^][v][top][bottom][index][help] */