Chromium Code Reviews| Index: third_party/WebKit/Source/core/css/parser/CSSParserTokenStream.h |
| diff --git a/third_party/WebKit/Source/core/css/parser/CSSParserTokenStream.h b/third_party/WebKit/Source/core/css/parser/CSSParserTokenStream.h |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..b78fd0a0dc04cd3b850024a10cf525f598daf600 |
| --- /dev/null |
| +++ b/third_party/WebKit/Source/core/css/parser/CSSParserTokenStream.h |
| @@ -0,0 +1,184 @@ |
| +// Copyright 2016 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. |
| + |
| +#ifndef CSSParserTokenStream_h |
| +#define CSSParserTokenStream_h |
| + |
| +#include "core/css/parser/CSSParserToken.h" |
| +#include "core/css/parser/CSSParserTokenRange.h" |
| +#include "core/css/parser/CSSTokenizer.h" |
| + |
| +namespace blink { |
| + |
| +class CSSParserObserver; |
| + |
| +// This class is a lazily tokenized stream of CSSParserTokens. It provides only |
| +// a single token of lookahead. When this is not sufficient, the functions |
| +// index(), consumeUntilAtEndOrPeekedTypeIs() and makeSubRangeFrom() are used |
| +// to make a CSSParserTokenRange. |
| +class CSSParserTokenStream { |
| + public: |
| + enum MakeSubStreamTag { MakeSubStream }; |
|
Charlie Harrison
2017/01/09 21:35:07
optional: Put this right above the constructor tha
|
| + |
| + explicit CSSParserTokenStream(CSSTokenizer& tokenizer) |
| + : m_tokenizer(tokenizer), |
| + m_startIndex(0), |
| + m_currentIndex(0), |
| + m_isSubStream(false) { |
| + DCHECK(tokenizer.m_tokens.isEmpty()); |
| + } |
| + |
| + // This constructor can be called when the next token is a LeftBraceToken. |
| + // The new stream will end at the end of the block and clean up by deleting |
| + // all the tokens in the block. The original stream cannot be used until the |
| + // sub-stream is destroyed. |
| + CSSParserTokenStream(CSSParserTokenStream& stream, MakeSubStreamTag) |
| + : m_tokenizer(stream.m_tokenizer), |
| + m_startIndex(stream.m_currentIndex), |
| + m_currentIndex(stream.m_currentIndex + 1), |
| + m_isSubStream(true) { |
| + DCHECK_EQ(m_tokenizer.m_tokens.size(), m_currentIndex); |
| + DCHECK_EQ(m_tokenizer.m_tokens.back().type(), LeftBraceToken); |
| + } |
| + |
| + ~CSSParserTokenStream(); |
| + |
| + // Delete all the tokens this stream has consumed. This is used to ensure |
| + // the tokenizer doesn't take up much memory. |
| + void clean() { |
| + m_tokenizer.m_tokens.remove(m_startIndex, m_currentIndex - m_startIndex); |
|
Charlie Harrison
2017/01/09 21:35:07
I'll need to look at all usage, but it seems like
|
| + m_currentIndex = m_startIndex; |
| + } |
| + |
| + const CSSParserToken& peek() { |
| + const CSSParserToken& result = peekInternal(); |
| + if (result.getBlockType() == CSSParserToken::BlockEnd) |
| + return staticEOFToken; |
| + return result; |
| + } |
| + |
| + bool atEnd() { return peek().type() == EOFToken; } |
| + |
| + // Consumes component values until the next token is a listed type or EOF. |
| + template <CSSParserTokenType... types> |
| + void consumeUntilAtEndOrPeekedTypeIs(); |
| + |
| + // Only for single-length component values. |
| + void consume() { |
| + DCHECK(hasLookedAhead()); |
| + DCHECK(peekInternal().getBlockType() == CSSParserToken::NotBlock); |
| + DCHECK(!atEnd()); |
| + m_currentIndex++; |
| + } |
| + |
| + // These do not create tokens |
| + void skipWhitespaceAndComments() { |
| + DCHECK_EQ(m_currentIndex, m_tokenizer.m_tokens.size()); |
| + m_tokenizer.skipWhitespaceAndComments(); |
| + } |
| + void skipBlock() { |
| + DCHECK(hasLookedAhead()); |
| + DCHECK(peek().type() == LeftBraceToken); |
| + m_tokenizer.skipToBlockEnd(); |
| + } |
| + |
| + // This is the current character index in the original string. |
| + // This can only be called if we have not looked ahead. After calling peek(), |
| + // atEnd(), or consumeUntilAtEndOrPeekedTypeIs(), we can only call offset() |
| + // if we're at the end of the stream. |
| + // After calling consume() or making a substream we can call offset(). |
| + size_t offset() { |
| + if (hasLookedAhead()) { |
| + DCHECK(atEnd()); |
| + return previousOffset(); |
| + } |
| + return m_tokenizer.m_input.offset(); |
| + } |
| + // This is the character index in the original string of where we have |
| + // consumed up to, when we have looked ahead. This is only correct when |
| + // the look-ahead token is a single character or EOF. |
| + size_t previousOffset() { |
| + if (m_tokenizer.m_finishedTokenizing) { |
| + DCHECK_EQ(m_currentIndex, m_tokenizer.m_tokens.size()); |
| + return m_tokenizer.m_input.offset(); |
| + } |
| + DCHECK_EQ(m_currentIndex, m_tokenizer.m_tokens.size() - 1); |
| + return m_tokenizer.m_input.offset() - 1; |
| + } |
| + |
| + size_t offsetAfterComments() { |
| + if (!hasLookedAhead()) |
| + m_tokenizer.skipComments(); |
| + return offset(); |
| + } |
| + |
| + // CSSParserTokenRange objects are created by calling index(), consuming from |
| + // the stream, then calling makeSubRangeFrom. |
| + size_t index() { return m_currentIndex; } |
| + CSSParserTokenRange makeSubRangeFrom(size_t startIndex) const { |
| + return CSSParserTokenRange(m_tokenizer.m_tokens) |
| + .makeSubRange(m_tokenizer.m_tokens.begin() + startIndex, |
| + m_tokenizer.m_tokens.begin() + m_currentIndex); |
| + } |
| + |
| + void yieldComments(CSSParserObserver& observer) { |
| + if (hasLookedAhead()) |
| + DCHECK(atEnd()); |
| + else |
| + m_tokenizer.yieldComments(observer); |
| + } |
| + |
| + private: |
| + bool hasLookedAhead() { |
|
Charlie Harrison
2017/01/09 21:35:07
Maybe replace this method with:
DCHECK(m_currentI
|
| + if (m_currentIndex == m_tokenizer.m_tokens.size()) |
| + return false; |
| + DCHECK_EQ(m_currentIndex, m_tokenizer.m_tokens.size() - 1); |
| + return true; |
| + } |
| + |
| + const CSSParserToken& peekInternal(); |
| + |
| + CSSTokenizer& m_tokenizer; |
| + size_t m_startIndex; |
| + size_t m_currentIndex; |
| + bool m_isSubStream; |
| +}; |
| + |
| +template <typename... emptyBaseCase> |
| +inline bool typeIs(CSSParserTokenType type) { |
| + return false; |
| +} |
| + |
| +template <CSSParserTokenType head, CSSParserTokenType... tail> |
| +inline bool typeIs(CSSParserTokenType type) { |
| + return type == head || typeIs<tail...>(type); |
| +} |
| + |
| +template <CSSParserTokenType... types> |
| +void CSSParserTokenStream::consumeUntilAtEndOrPeekedTypeIs() { |
| + unsigned nestingLevel = 0; |
| + const CSSParserToken* token = &peekInternal(); |
| + if (token->type() == EOFToken) |
| + return; |
| + |
| + while (nestingLevel || !typeIs<types...>(token->type())) { |
| + if (token->getBlockType() == CSSParserToken::BlockStart) { |
| + nestingLevel++; |
| + } else if (token->getBlockType() == CSSParserToken::BlockEnd) { |
| + if (nestingLevel == 0) |
| + return; |
| + nestingLevel--; |
| + } |
| + |
| + m_currentIndex++; |
| + m_tokenizer.tokenizeSingle(); |
| + if (m_tokenizer.m_finishedTokenizing) |
| + return; |
| + token = &m_tokenizer.m_tokens[m_currentIndex]; |
| + } |
| +} |
| + |
| +} // namespace blink |
| + |
| +#endif // CSSParserTokenStream_h |