blob: 6f85bf2fca051e08bf26ee1c818b5e34a3427cfc [file] [log] [blame]
// 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 "third_party/blink/renderer/core/css/parser/css_parser_token.h"
#include <limits.h>
#include "third_party/blink/renderer/core/css/css_markup.h"
#include "third_party/blink/renderer/core/css/css_primitive_value.h"
#include "third_party/blink/renderer/core/css/parser/css_property_parser.h"
#include "third_party/blink/renderer/core/css_value_keywords.h"
#include "third_party/blink/renderer/platform/wtf/hash_map.h"
#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
namespace blink {
// Just a helper used for Delimiter tokens.
CSSParserToken::CSSParserToken(CSSParserTokenType type, UChar c)
: type_(type), block_type_(kNotBlock), delimiter_(c) {
DCHECK_EQ(type_, static_cast<unsigned>(kDelimiterToken));
}
CSSParserToken::CSSParserToken(CSSParserTokenType type,
double numeric_value,
NumericValueType numeric_value_type,
NumericSign sign)
: type_(type),
block_type_(kNotBlock),
numeric_value_type_(numeric_value_type),
numeric_sign_(sign),
unit_(static_cast<unsigned>(CSSPrimitiveValue::UnitType::kNumber)) {
DCHECK_EQ(type, kNumberToken);
numeric_value_ =
clampTo<double>(numeric_value, -std::numeric_limits<float>::max(),
std::numeric_limits<float>::max());
}
CSSParserToken::CSSParserToken(CSSParserTokenType type,
UChar32 start,
UChar32 end)
: type_(kUnicodeRangeToken), block_type_(kNotBlock) {
DCHECK_EQ(type, kUnicodeRangeToken);
unicode_range_.start = start;
unicode_range_.end = end;
}
CSSParserToken::CSSParserToken(HashTokenType type, StringView value)
: type_(kHashToken), block_type_(kNotBlock), hash_token_type_(type) {
InitValueFromStringView(value);
}
void CSSParserToken::ConvertToDimensionWithUnit(StringView unit) {
DCHECK_EQ(type_, static_cast<unsigned>(kNumberToken));
type_ = kDimensionToken;
InitValueFromStringView(unit);
unit_ = static_cast<unsigned>(CSSPrimitiveValue::StringToUnitType(unit));
}
void CSSParserToken::ConvertToPercentage() {
DCHECK_EQ(type_, static_cast<unsigned>(kNumberToken));
type_ = kPercentageToken;
unit_ = static_cast<unsigned>(CSSPrimitiveValue::UnitType::kPercentage);
}
UChar CSSParserToken::Delimiter() const {
DCHECK_EQ(type_, static_cast<unsigned>(kDelimiterToken));
return delimiter_;
}
NumericSign CSSParserToken::GetNumericSign() const {
// This is valid for DimensionToken and PercentageToken, but only used
// in <an+b> parsing on NumberTokens.
DCHECK_EQ(type_, static_cast<unsigned>(kNumberToken));
return static_cast<NumericSign>(numeric_sign_);
}
NumericValueType CSSParserToken::GetNumericValueType() const {
DCHECK(type_ == kNumberToken || type_ == kPercentageToken ||
type_ == kDimensionToken);
return static_cast<NumericValueType>(numeric_value_type_);
}
double CSSParserToken::NumericValue() const {
DCHECK(type_ == kNumberToken || type_ == kPercentageToken ||
type_ == kDimensionToken);
return numeric_value_;
}
CSSPropertyID CSSParserToken::ParseAsUnresolvedCSSPropertyID(
const ExecutionContext* execution_context,
CSSParserMode mode) const {
DCHECK_EQ(type_, static_cast<unsigned>(kIdentToken));
return UnresolvedCSSPropertyID(execution_context, Value(), mode);
}
AtRuleDescriptorID CSSParserToken::ParseAsAtRuleDescriptorID() const {
DCHECK_EQ(type_, static_cast<unsigned>(kIdentToken));
return AsAtRuleDescriptorID(Value());
}
CSSValueID CSSParserToken::Id() const {
if (type_ != kIdentToken)
return CSSValueID::kInvalid;
if (id_ < 0)
id_ = static_cast<int>(CssValueKeywordID(Value()));
return static_cast<CSSValueID>(id_);
}
CSSValueID CSSParserToken::FunctionId() const {
if (type_ != kFunctionToken)
return CSSValueID::kInvalid;
if (id_ < 0)
id_ = static_cast<int>(CssValueKeywordID(Value()));
return static_cast<CSSValueID>(id_);
}
bool CSSParserToken::HasStringBacking() const {
CSSParserTokenType token_type = GetType();
return token_type == kIdentToken || token_type == kFunctionToken ||
token_type == kAtKeywordToken || token_type == kHashToken ||
token_type == kUrlToken || token_type == kDimensionToken ||
token_type == kStringToken;
}
CSSParserToken CSSParserToken::CopyWithUpdatedString(
const StringView& string) const {
CSSParserToken copy(*this);
copy.InitValueFromStringView(string);
return copy;
}
bool CSSParserToken::ValueDataCharRawEqual(const CSSParserToken& other) const {
if (value_length_ != other.value_length_)
return false;
if (value_data_char_raw_ == other.value_data_char_raw_ &&
value_is_8bit_ == other.value_is_8bit_)
return true;
if (value_is_8bit_) {
return other.value_is_8bit_
? Equal(static_cast<const LChar*>(value_data_char_raw_),
static_cast<const LChar*>(other.value_data_char_raw_),
value_length_)
: Equal(static_cast<const LChar*>(value_data_char_raw_),
static_cast<const UChar*>(other.value_data_char_raw_),
value_length_);
} else {
return other.value_is_8bit_
? Equal(static_cast<const UChar*>(value_data_char_raw_),
static_cast<const LChar*>(other.value_data_char_raw_),
value_length_)
: Equal(static_cast<const UChar*>(value_data_char_raw_),
static_cast<const UChar*>(other.value_data_char_raw_),
value_length_);
}
}
bool CSSParserToken::operator==(const CSSParserToken& other) const {
if (type_ != other.type_)
return false;
switch (type_) {
case kDelimiterToken:
return Delimiter() == other.Delimiter();
case kHashToken:
if (hash_token_type_ != other.hash_token_type_)
return false;
FALLTHROUGH;
case kIdentToken:
case kFunctionToken:
case kStringToken:
case kUrlToken:
return ValueDataCharRawEqual(other);
case kDimensionToken:
if (!ValueDataCharRawEqual(other))
return false;
FALLTHROUGH;
case kNumberToken:
case kPercentageToken:
return numeric_sign_ == other.numeric_sign_ &&
numeric_value_ == other.numeric_value_ &&
numeric_value_type_ == other.numeric_value_type_;
case kUnicodeRangeToken:
return unicode_range_.start == other.unicode_range_.start &&
unicode_range_.end == other.unicode_range_.end;
default:
return true;
}
}
void CSSParserToken::Serialize(StringBuilder& builder) const {
// This is currently only used for @supports CSSOM. To keep our implementation
// simple we handle some of the edge cases incorrectly (see comments below).
switch (GetType()) {
case kIdentToken:
SerializeIdentifier(Value().ToString(), builder);
break;
case kFunctionToken:
SerializeIdentifier(Value().ToString(), builder);
return builder.Append('(');
case kAtKeywordToken:
builder.Append('@');
SerializeIdentifier(Value().ToString(), builder);
break;
case kHashToken:
builder.Append('#');
SerializeIdentifier(Value().ToString(), builder,
(GetHashTokenType() == kHashTokenUnrestricted));
break;
case kUrlToken:
builder.Append("url(");
SerializeIdentifier(Value().ToString(), builder);
return builder.Append(')');
case kDelimiterToken:
if (Delimiter() == '\\')
return builder.Append("\\\n");
return builder.Append(Delimiter());
case kNumberToken:
// These won't properly preserve the NumericValueType flag
return builder.AppendNumber(NumericValue());
case kPercentageToken:
builder.AppendNumber(NumericValue());
return builder.Append('%');
case kDimensionToken:
// This will incorrectly serialize e.g. 4e3e2 as 4000e2
builder.AppendNumber(NumericValue());
SerializeIdentifier(Value().ToString(), builder);
break;
case kUnicodeRangeToken:
return builder.Append(
String::Format("U+%X-%X", UnicodeRangeStart(), UnicodeRangeEnd()));
case kStringToken:
return SerializeString(Value().ToString(), builder);
case kIncludeMatchToken:
return builder.Append("~=");
case kDashMatchToken:
return builder.Append("|=");
case kPrefixMatchToken:
return builder.Append("^=");
case kSuffixMatchToken:
return builder.Append("$=");
case kSubstringMatchToken:
return builder.Append("*=");
case kColumnToken:
return builder.Append("||");
case kCDOToken:
return builder.Append("<!--");
case kCDCToken:
return builder.Append("-->");
case kBadStringToken:
return builder.Append("'\n");
case kBadUrlToken:
return builder.Append("url(()");
case kWhitespaceToken:
return builder.Append(' ');
case kColonToken:
return builder.Append(':');
case kSemicolonToken:
return builder.Append(';');
case kCommaToken:
return builder.Append(',');
case kLeftParenthesisToken:
return builder.Append('(');
case kRightParenthesisToken:
return builder.Append(')');
case kLeftBracketToken:
return builder.Append('[');
case kRightBracketToken:
return builder.Append(']');
case kLeftBraceToken:
return builder.Append('{');
case kRightBraceToken:
return builder.Append('}');
case kEOFToken:
case kCommentToken:
NOTREACHED();
return;
}
}
} // namespace blink