blob: 22b5044bf10d508b9a5b02eb71ed305b663d51b8 [file] [log] [blame]
// 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.
#include "core/css/CSSSyntaxDescriptor.h"
#include "core/css/CSSCustomPropertyDeclaration.h"
#include "core/css/CSSURIValue.h"
#include "core/css/CSSValueList.h"
#include "core/css/CSSVariableReferenceValue.h"
#include "core/css/parser/CSSParserIdioms.h"
#include "core/css/parser/CSSPropertyParserHelpers.h"
#include "core/css/parser/CSSVariableParser.h"
#include "core/html/parser/HTMLParserIdioms.h"
namespace blink {
void consumeWhitespace(const String& string, size_t& offset) {
while (isHTMLSpace(string[offset]))
offset++;
}
bool consumeCharacterAndWhitespace(const String& string,
char character,
size_t& offset) {
if (string[offset] != character)
return false;
offset++;
consumeWhitespace(string, offset);
return true;
}
CSSSyntaxType parseSyntaxType(String type) {
// TODO(timloh): Are these supposed to be case sensitive?
if (type == "length")
return CSSSyntaxType::Length;
if (type == "number")
return CSSSyntaxType::Number;
if (type == "percentage")
return CSSSyntaxType::Percentage;
if (type == "length-percentage")
return CSSSyntaxType::LengthPercentage;
if (type == "color")
return CSSSyntaxType::Color;
if (type == "image")
return CSSSyntaxType::Image;
if (type == "url")
return CSSSyntaxType::Url;
if (type == "integer")
return CSSSyntaxType::Integer;
if (type == "angle")
return CSSSyntaxType::Angle;
if (type == "time")
return CSSSyntaxType::Time;
if (type == "resolution")
return CSSSyntaxType::Resolution;
if (type == "transform-function")
return CSSSyntaxType::TransformFunction;
if (type == "custom-ident")
return CSSSyntaxType::CustomIdent;
// Not an Ident, just used to indicate failure
return CSSSyntaxType::Ident;
}
bool consumeSyntaxType(const String& input,
size_t& offset,
CSSSyntaxType& type) {
DCHECK_EQ(input[offset], '<');
offset++;
size_t typeStart = offset;
while (offset < input.length() && input[offset] != '>')
offset++;
if (offset == input.length())
return false;
type = parseSyntaxType(input.substring(typeStart, offset - typeStart));
if (type == CSSSyntaxType::Ident)
return false;
offset++;
return true;
}
bool consumeSyntaxIdent(const String& input, size_t& offset, String& ident) {
size_t identStart = offset;
while (isNameCodePoint(input[offset]))
offset++;
if (offset == identStart)
return false;
ident = input.substring(identStart, offset - identStart);
return !CSSPropertyParserHelpers::isCSSWideKeyword(ident);
}
CSSSyntaxDescriptor::CSSSyntaxDescriptor(String input) {
size_t offset = 0;
consumeWhitespace(input, offset);
if (consumeCharacterAndWhitespace(input, '*', offset)) {
if (offset != input.length())
return;
m_syntaxComponents.append(
CSSSyntaxComponent(CSSSyntaxType::TokenStream, emptyString(), false));
return;
}
do {
CSSSyntaxType type;
String ident;
bool success;
if (input[offset] == '<') {
success = consumeSyntaxType(input, offset, type);
} else {
type = CSSSyntaxType::Ident;
success = consumeSyntaxIdent(input, offset, ident);
}
if (!success) {
m_syntaxComponents.clear();
return;
}
bool repeatable = consumeCharacterAndWhitespace(input, '+', offset);
consumeWhitespace(input, offset);
m_syntaxComponents.append(CSSSyntaxComponent(type, ident, repeatable));
} while (consumeCharacterAndWhitespace(input, '|', offset));
if (offset != input.length())
m_syntaxComponents.clear();
}
const CSSValue* consumeSingleType(const CSSSyntaxComponent& syntax,
CSSParserTokenRange& range) {
using namespace CSSPropertyParserHelpers;
// TODO(timloh): Calc values need to be normalized
switch (syntax.m_type) {
case CSSSyntaxType::Ident:
if (range.peek().type() == IdentToken &&
range.peek().value() == syntax.m_string) {
range.consumeIncludingWhitespace();
return CSSCustomIdentValue::create(AtomicString(syntax.m_string));
}
return nullptr;
case CSSSyntaxType::Length:
return consumeLength(range, HTMLStandardMode, ValueRange::ValueRangeAll);
case CSSSyntaxType::Number:
return consumeNumber(range, ValueRange::ValueRangeAll);
case CSSSyntaxType::Percentage:
return consumePercent(range, ValueRange::ValueRangeAll);
case CSSSyntaxType::LengthPercentage:
return consumeLengthOrPercent(range, HTMLStandardMode,
ValueRange::ValueRangeAll);
case CSSSyntaxType::Color:
return consumeColor(range, HTMLStandardMode);
case CSSSyntaxType::Image:
// TODO(timloh): This probably needs a proper parser context for relative
// URL resolution.
return consumeImage(range, strictCSSParserContext());
case CSSSyntaxType::Url:
return consumeUrl(range);
case CSSSyntaxType::Integer:
return consumeInteger(range);
case CSSSyntaxType::Angle:
return consumeAngle(range);
case CSSSyntaxType::Time:
return consumeTime(range, ValueRange::ValueRangeAll);
case CSSSyntaxType::Resolution:
return nullptr; // TODO(timloh): Implement this.
case CSSSyntaxType::TransformFunction:
return nullptr; // TODO(timloh): Implement this.
case CSSSyntaxType::CustomIdent:
return consumeCustomIdent(range);
default:
NOTREACHED();
return nullptr;
}
}
const CSSValue* consumeSyntaxComponent(const CSSSyntaxComponent& syntax,
CSSParserTokenRange range) {
// CSS-wide keywords are already handled by the CSSPropertyParser
if (syntax.m_repeatable) {
CSSValueList* list = CSSValueList::createSpaceSeparated();
while (!range.atEnd()) {
const CSSValue* value = consumeSingleType(syntax, range);
if (!value)
return nullptr;
list->append(*value);
}
return list;
}
const CSSValue* result = consumeSingleType(syntax, range);
if (!range.atEnd())
return nullptr;
return result;
}
const CSSValue* CSSSyntaxDescriptor::parse(CSSParserTokenRange range,
bool isAnimationTainted) const {
if (isTokenStream()) {
return CSSVariableParser::parseRegisteredPropertyValue(range, false,
isAnimationTainted);
}
range.consumeWhitespace();
for (const CSSSyntaxComponent& component : m_syntaxComponents) {
if (const CSSValue* result = consumeSyntaxComponent(component, range))
return result;
}
return CSSVariableParser::parseRegisteredPropertyValue(range, true,
isAnimationTainted);
}
} // namespace blink