blob: f179d019ad63cd6b3ef74ac5931240e667860ff1 [file] [log] [blame]
// Copyright 2015 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/css_variable_data.h"
#include "third_party/blink/renderer/core/css/css_syntax_definition.h"
#include "third_party/blink/renderer/core/css/parser/css_parser_context.h"
#include "third_party/blink/renderer/platform/wtf/text/character_names.h"
#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
#include "third_party/blink/renderer/platform/wtf/text/string_view.h"
namespace blink {
template <typename CharacterType>
static void UpdateTokens(const CSSParserTokenRange& range,
const String& backing_string,
Vector<CSSParserToken>& result) {
const CharacterType* current_offset =
backing_string.GetCharacters<CharacterType>();
for (const CSSParserToken& token : range) {
if (token.HasStringBacking()) {
unsigned length = token.Value().length();
StringView string(current_offset, length);
result.push_back(token.CopyWithUpdatedString(string));
current_offset += length;
} else {
result.push_back(token);
}
}
DCHECK(current_offset == backing_string.GetCharacters<CharacterType>() +
backing_string.length());
}
static bool IsFontUnitToken(CSSParserToken token) {
if (token.GetType() != kDimensionToken)
return false;
switch (token.GetUnitType()) {
case CSSPrimitiveValue::UnitType::kEms:
case CSSPrimitiveValue::UnitType::kChs:
case CSSPrimitiveValue::UnitType::kExs:
return true;
default:
return false;
}
}
static bool IsRootFontUnitToken(CSSParserToken token) {
return token.GetType() == kDimensionToken &&
token.GetUnitType() == CSSPrimitiveValue::UnitType::kRems;
}
String CSSVariableData::Serialize() const {
if (original_text_) {
if (original_text_.EndsWith('\\')) {
// https://drafts.csswg.org/css-syntax/#consume-escaped-code-point
// '\' followed by EOF is consumed as U+FFFD.
// https://drafts.csswg.org/css-syntax/#consume-string-token
// '\' followed by EOF in a string token is ignored.
//
// The tokenizer handles both of these cases when returning tokens, but
// since we're working with the original string, we need to deal with them
// ourselves.
StringBuilder serialized_text;
serialized_text.Append(original_text_);
serialized_text.Resize(serialized_text.length() - 1);
DCHECK(!tokens_.IsEmpty());
const CSSParserToken& last = tokens_.back();
if (last.GetType() != kStringToken)
serialized_text.Append(kReplacementCharacter);
// Certain token types implicitly include terminators when serialized.
// https://drafts.csswg.org/cssom/#common-serializing-idioms
if (last.GetType() == kStringToken)
serialized_text.Append('"');
if (last.GetType() == kUrlToken)
serialized_text.Append(')');
return serialized_text.ToString();
}
return original_text_;
}
return TokenRange().Serialize();
}
bool CSSVariableData::operator==(const CSSVariableData& other) const {
return Tokens() == other.Tokens();
}
void CSSVariableData::ConsumeAndUpdateTokens(const CSSParserTokenRange& range) {
DCHECK_EQ(tokens_.size(), 0u);
DCHECK_EQ(backing_strings_.size(), 0u);
StringBuilder string_builder;
CSSParserTokenRange local_range = range;
while (!local_range.AtEnd()) {
CSSParserToken token = local_range.Consume();
if (token.HasStringBacking())
string_builder.Append(token.Value());
has_font_units_ |= IsFontUnitToken(token);
has_root_font_units_ |= IsRootFontUnitToken(token);
}
String backing_string = string_builder.ToString();
backing_strings_.push_back(backing_string);
if (backing_string.Is8Bit())
UpdateTokens<LChar>(range, backing_string, tokens_);
else
UpdateTokens<UChar>(range, backing_string, tokens_);
}
CSSVariableData::CSSVariableData(const CSSTokenizedValue& tokenized_value,
bool is_animation_tainted,
bool needs_variable_resolution,
const KURL& base_url,
const WTF::TextEncoding& charset)
: original_text_(tokenized_value.text.ToString()),
is_animation_tainted_(is_animation_tainted),
needs_variable_resolution_(needs_variable_resolution),
base_url_(base_url.IsValid() ? base_url.GetString() : String()),
charset_(charset) {
DCHECK(!tokenized_value.range.AtEnd());
ConsumeAndUpdateTokens(tokenized_value.range);
}
const CSSValue* CSSVariableData::ParseForSyntax(
const CSSSyntaxDefinition& syntax,
SecureContextMode secure_context_mode) const {
DCHECK(!NeedsVariableResolution());
// TODO(timloh): This probably needs a proper parser context for
// relative URL resolution.
return syntax.Parse(TokenRange(),
*StrictCSSParserContext(secure_context_mode),
is_animation_tainted_);
}
} // namespace blink