blob: 431cd6cc06f9784c320528b634c08d2badaebfef [file] [log] [blame]
// Copyright 2019 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 "ui/base/metadata/base_type_conversion.h"
#include <cmath>
#include <string>
#include "base/containers/fixed_flat_set.h"
#include "base/no_destructor.h"
#include "base/numerics/ranges.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/string_tokenizer.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time_delta_from_string.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/skia/include/core/SkScalar.h"
#include "ui/gfx/color_utils.h"
#include "ui/gfx/geometry/rect.h"
namespace ui {
namespace metadata {
const char kNoPrefix[] = "";
const char kSkColorPrefix[] = "--";
std::u16string PointerToString(const void* pointer_val) {
return pointer_val ? u"(assigned)" : u"(not assigned)";
}
const std::u16string& GetNullOptStr() {
static const base::NoDestructor<std::u16string> kNullOptStr(u"<Empty>");
return *kNullOptStr;
}
/***** String Conversions *****/
#define CONVERT_NUMBER_TO_STRING(T) \
std::u16string TypeConverter<T>::ToString(T source_value) { \
return base::NumberToString16(source_value); \
}
CONVERT_NUMBER_TO_STRING(int8_t)
CONVERT_NUMBER_TO_STRING(int16_t)
CONVERT_NUMBER_TO_STRING(int32_t)
CONVERT_NUMBER_TO_STRING(int64_t)
CONVERT_NUMBER_TO_STRING(uint8_t)
CONVERT_NUMBER_TO_STRING(uint16_t)
CONVERT_NUMBER_TO_STRING(uint32_t)
CONVERT_NUMBER_TO_STRING(uint64_t)
CONVERT_NUMBER_TO_STRING(float)
CONVERT_NUMBER_TO_STRING(double)
std::u16string TypeConverter<bool>::ToString(bool source_value) {
return source_value ? u"true" : u"false";
}
ValidStrings TypeConverter<bool>::GetValidStrings() {
return {u"false", u"true"};
}
std::u16string TypeConverter<const char*>::ToString(const char* source_value) {
return base::UTF8ToUTF16(source_value);
}
std::u16string TypeConverter<base::FilePath>::ToString(
const base::FilePath& source_value) {
return source_value.AsUTF16Unsafe();
}
std::u16string TypeConverter<std::u16string>::ToString(
const std::u16string& source_value) {
return source_value;
}
std::u16string TypeConverter<base::TimeDelta>::ToString(
const base::TimeDelta& source_value) {
return base::NumberToString16(source_value.InSecondsF()) + u"s";
}
std::u16string TypeConverter<gfx::Insets>::ToString(
const gfx::Insets& source_value) {
return base::ASCIIToUTF16(source_value.ToString());
}
std::u16string TypeConverter<gfx::Point>::ToString(
const gfx::Point& source_value) {
return base::ASCIIToUTF16(source_value.ToString());
}
std::u16string TypeConverter<gfx::PointF>::ToString(
const gfx::PointF& source_value) {
return base::ASCIIToUTF16(source_value.ToString());
}
std::u16string TypeConverter<gfx::Range>::ToString(
const gfx::Range& source_value) {
return base::ASCIIToUTF16(source_value.ToString());
}
std::u16string TypeConverter<gfx::Rect>::ToString(
const gfx::Rect& source_value) {
return base::ASCIIToUTF16(source_value.ToString());
}
std::u16string TypeConverter<gfx::RectF>::ToString(
const gfx::RectF& source_value) {
return base::ASCIIToUTF16(source_value.ToString());
}
std::u16string TypeConverter<gfx::ShadowValues>::ToString(
const gfx::ShadowValues& source_value) {
std::u16string ret = u"[";
for (auto shadow_value : source_value) {
ret += u" " + base::ASCIIToUTF16(shadow_value.ToString()) + u";";
}
ret[ret.length() - 1] = ' ';
ret += u"]";
return ret;
}
std::u16string TypeConverter<gfx::Size>::ToString(
const gfx::Size& source_value) {
return base::ASCIIToUTF16(source_value.ToString());
}
std::u16string TypeConverter<gfx::SizeF>::ToString(
const gfx::SizeF& source_value) {
return base::ASCIIToUTF16(source_value.ToString());
}
std::u16string TypeConverter<std::string>::ToString(
const std::string& source_value) {
return base::UTF8ToUTF16(source_value);
}
std::u16string TypeConverter<url::Component>::ToString(
const url::Component& source_value) {
return base::ASCIIToUTF16(
base::StringPrintf("{%d,%d}", source_value.begin, source_value.len));
}
absl::optional<int8_t> TypeConverter<int8_t>::FromString(
const std::u16string& source_value) {
int32_t ret = 0;
if (base::StringToInt(source_value, &ret) &&
base::IsValueInRangeForNumericType<int8_t>(ret)) {
return static_cast<int8_t>(ret);
}
return absl::nullopt;
}
absl::optional<int16_t> TypeConverter<int16_t>::FromString(
const std::u16string& source_value) {
int32_t ret = 0;
if (base::StringToInt(source_value, &ret) &&
base::IsValueInRangeForNumericType<int16_t>(ret)) {
return static_cast<int16_t>(ret);
}
return absl::nullopt;
}
absl::optional<int32_t> TypeConverter<int32_t>::FromString(
const std::u16string& source_value) {
int value;
return base::StringToInt(source_value, &value) ? absl::make_optional(value)
: absl::nullopt;
}
absl::optional<int64_t> TypeConverter<int64_t>::FromString(
const std::u16string& source_value) {
int64_t value;
return base::StringToInt64(source_value, &value) ? absl::make_optional(value)
: absl::nullopt;
}
absl::optional<uint8_t> TypeConverter<uint8_t>::FromString(
const std::u16string& source_value) {
unsigned ret = 0;
if (base::StringToUint(source_value, &ret) &&
base::IsValueInRangeForNumericType<uint8_t>(ret)) {
return static_cast<uint8_t>(ret);
}
return absl::nullopt;
}
absl::optional<uint16_t> TypeConverter<uint16_t>::FromString(
const std::u16string& source_value) {
unsigned ret = 0;
if (base::StringToUint(source_value, &ret) &&
base::IsValueInRangeForNumericType<uint16_t>(ret)) {
return static_cast<uint16_t>(ret);
}
return absl::nullopt;
}
absl::optional<uint32_t> TypeConverter<uint32_t>::FromString(
const std::u16string& source_value) {
unsigned value;
return base::StringToUint(source_value, &value) ? absl::make_optional(value)
: absl::nullopt;
}
absl::optional<uint64_t> TypeConverter<uint64_t>::FromString(
const std::u16string& source_value) {
uint64_t value;
return base::StringToUint64(source_value, &value) ? absl::make_optional(value)
: absl::nullopt;
}
absl::optional<float> TypeConverter<float>::FromString(
const std::u16string& source_value) {
if (absl::optional<double> temp =
TypeConverter<double>::FromString(source_value))
return static_cast<float>(temp.value());
return absl::nullopt;
}
absl::optional<double> TypeConverter<double>::FromString(
const std::u16string& source_value) {
double value;
return base::StringToDouble(base::UTF16ToUTF8(source_value), &value)
? absl::make_optional(value)
: absl::nullopt;
}
absl::optional<bool> TypeConverter<bool>::FromString(
const std::u16string& source_value) {
const bool is_true = source_value == u"true";
if (is_true || source_value == u"false")
return is_true;
return absl::nullopt;
}
absl::optional<std::u16string> TypeConverter<std::u16string>::FromString(
const std::u16string& source_value) {
return source_value;
}
absl::optional<base::FilePath> TypeConverter<base::FilePath>::FromString(
const std::u16string& source_value) {
return base::FilePath::FromUTF16Unsafe(source_value);
}
absl::optional<base::TimeDelta> TypeConverter<base::TimeDelta>::FromString(
const std::u16string& source_value) {
std::string source = base::UTF16ToUTF8(source_value);
return base::TimeDeltaFromString(source);
}
absl::optional<gfx::Insets> TypeConverter<gfx::Insets>::FromString(
const std::u16string& source_value) {
const auto values = base::SplitStringPiece(
source_value, u",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
int top, left, bottom, right;
if ((values.size() == 4) && base::StringToInt(values[0], &top) &&
base::StringToInt(values[1], &left) &&
base::StringToInt(values[2], &bottom) &&
base::StringToInt(values[3], &right)) {
return gfx::Insets(top, left, bottom, right);
}
return absl::nullopt;
}
absl::optional<gfx::Point> TypeConverter<gfx::Point>::FromString(
const std::u16string& source_value) {
const auto values = base::SplitStringPiece(
source_value, u",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
int x, y;
if ((values.size() == 2) && base::StringToInt(values[0], &x) &&
base::StringToInt(values[1], &y)) {
return gfx::Point(x, y);
}
return absl::nullopt;
}
absl::optional<gfx::PointF> TypeConverter<gfx::PointF>::FromString(
const std::u16string& source_value) {
const auto values = base::SplitStringPiece(
source_value, u",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
double x, y;
if ((values.size() == 2) && base::StringToDouble(values[0], &x) &&
base::StringToDouble(values[1], &y)) {
return gfx::PointF(x, y);
}
return absl::nullopt;
}
absl::optional<gfx::Range> TypeConverter<gfx::Range>::FromString(
const std::u16string& source_value) {
const auto values = base::SplitStringPiece(
source_value, u"{,}", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
unsigned min, max;
if ((values.size() == 2) && base::StringToUint(values[0], &min) &&
base::StringToUint(values[1], &max)) {
return gfx::Range(min, max);
}
return absl::nullopt;
}
absl::optional<gfx::Rect> TypeConverter<gfx::Rect>::FromString(
const std::u16string& source_value) {
const auto values = base::SplitString(
source_value, u" ", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
if (values.size() != 2)
return absl::nullopt;
const absl::optional<gfx::Point> origin =
TypeConverter<gfx::Point>::FromString(values[0]);
const absl::optional<gfx::Size> size =
TypeConverter<gfx::Size>::FromString(values[1]);
if (origin && size)
return gfx::Rect(*origin, *size);
return absl::nullopt;
}
absl::optional<gfx::RectF> TypeConverter<gfx::RectF>::FromString(
const std::u16string& source_value) {
const auto values = base::SplitString(
source_value, u" ", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
if (values.size() != 2)
return absl::nullopt;
const absl::optional<gfx::PointF> origin =
TypeConverter<gfx::PointF>::FromString(values[0]);
const absl::optional<gfx::SizeF> size =
TypeConverter<gfx::SizeF>::FromString(values[1]);
if (origin && size)
return gfx::RectF(*origin, *size);
return absl::nullopt;
}
absl::optional<gfx::ShadowValues> TypeConverter<gfx::ShadowValues>::FromString(
const std::u16string& source_value) {
gfx::ShadowValues ret;
const auto shadow_value_strings = base::SplitStringPiece(
source_value, u"[;]", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
for (auto v : shadow_value_strings) {
std::u16string value = std::u16string(v);
base::String16Tokenizer tokenizer(
value, u"(,)", base::String16Tokenizer::WhitespacePolicy::kSkipOver);
tokenizer.set_options(base::String16Tokenizer::RETURN_DELIMS);
int x, y;
double blur;
if (tokenizer.GetNext() && tokenizer.token_piece() == u"(" &&
tokenizer.GetNext() && base::StringToInt(tokenizer.token_piece(), &x) &&
tokenizer.GetNext() && tokenizer.token_piece() == u"," &&
tokenizer.GetNext() && base::StringToInt(tokenizer.token_piece(), &y) &&
tokenizer.GetNext() && tokenizer.token_piece() == u")" &&
tokenizer.GetNext() && tokenizer.token_piece() == u"," &&
tokenizer.GetNext() &&
base::StringToDouble(tokenizer.token_piece(), &blur) &&
tokenizer.GetNext() && tokenizer.token_piece() == u"," &&
tokenizer.GetNext()) {
const auto color =
SkColorConverter::GetNextColor(tokenizer.token_begin(), value.cend());
if (color)
ret.emplace_back(gfx::Vector2d(x, y), blur, color.value());
}
}
return ret;
}
absl::optional<gfx::Size> TypeConverter<gfx::Size>::FromString(
const std::u16string& source_value) {
const auto values = base::SplitStringPiece(
source_value, u"x", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
int width, height;
if ((values.size() == 2) && base::StringToInt(values[0], &width) &&
base::StringToInt(values[1], &height)) {
return gfx::Size(width, height);
}
return absl::nullopt;
}
absl::optional<gfx::SizeF> TypeConverter<gfx::SizeF>::FromString(
const std::u16string& source_value) {
const auto values = base::SplitStringPiece(
source_value, u"x", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
double width, height;
if ((values.size() == 2) && base::StringToDouble(values[0], &width) &&
base::StringToDouble(values[1], &height)) {
return gfx::SizeF(width, height);
}
return absl::nullopt;
}
absl::optional<std::string> TypeConverter<std::string>::FromString(
const std::u16string& source_value) {
return base::UTF16ToUTF8(source_value);
}
absl::optional<url::Component> TypeConverter<url::Component>::FromString(
const std::u16string& source_value) {
const auto values = base::SplitStringPiece(
source_value, u"{,}", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
int begin, len;
if ((values.size() == 2) && base::StringToInt(values[0], &begin) &&
base::StringToInt(values[1], &len) && len >= -1) {
return url::Component(begin, len);
}
return absl::nullopt;
}
std::u16string TypeConverter<UNIQUE_TYPE_NAME(SkColor)>::ToString(
SkColor source_value) {
return base::UTF8ToUTF16(color_utils::SkColorToRgbaString(source_value));
}
absl::optional<SkColor> TypeConverter<UNIQUE_TYPE_NAME(SkColor)>::FromString(
const std::u16string& source_value) {
return GetNextColor(source_value.cbegin(), source_value.cend());
}
ValidStrings TypeConverter<UNIQUE_TYPE_NAME(SkColor)>::GetValidStrings() {
return {};
}
bool TypeConverter<UNIQUE_TYPE_NAME(SkColor)>::GetNextColor(
std::u16string::const_iterator start,
std::u16string::const_iterator end,
std::u16string& color,
std::u16string::const_iterator& next_token) {
static const auto open_paren = u'(';
static const auto close_paren = u')';
static constexpr auto schemes = base::MakeFixedFlatSet<base::StringPiece16>(
{u"hsl", u"hsla", u"rgb", u"rgba"});
base::String16Tokenizer tokenizer(
start, end, u"(,)", base::String16Tokenizer::WhitespacePolicy::kSkipOver);
tokenizer.set_options(base::String16Tokenizer::RETURN_DELIMS);
for (; tokenizer.GetNext();) {
if (!tokenizer.token_is_delim()) {
base::StringPiece16 token = tokenizer.token_piece();
std::u16string::const_iterator start_color = tokenizer.token_begin();
if (base::ranges::find(schemes.begin(), schemes.end(), token) !=
schemes.end()) {
if (!tokenizer.GetNext() || *tokenizer.token_begin() != open_paren)
return false;
for (;
tokenizer.GetNext() && *tokenizer.token_begin() != close_paren;) {
}
if (*tokenizer.token_begin() != close_paren)
return false;
}
next_token = tokenizer.token_end();
color = std::u16string(start_color, next_token);
return true;
}
}
return false;
}
bool TypeConverter<UNIQUE_TYPE_NAME(SkColor)>::GetNextColor(
std::u16string::const_iterator start,
std::u16string::const_iterator end,
std::u16string& color) {
std::u16string::const_iterator next_token;
return GetNextColor(start, end, color, next_token);
}
absl::optional<SkColor> TypeConverter<UNIQUE_TYPE_NAME(SkColor)>::GetNextColor(
std::u16string::const_iterator start,
std::u16string::const_iterator end,
std::u16string::const_iterator& next_token) {
std::u16string color;
if (GetNextColor(start, end, color, next_token)) {
if (base::StartsWith(color, u"hsl", base::CompareCase::SENSITIVE))
return ParseHslString(color);
if (base::StartsWith(color, u"rgb", base::CompareCase::SENSITIVE))
return ParseRgbString(color);
if (base::StartsWith(color, u"0x", base::CompareCase::INSENSITIVE_ASCII))
return ParseHexString(color);
SkColor value;
if (base::StringToUint(color, &value))
return absl::make_optional(value);
}
return absl::nullopt;
}
absl::optional<SkColor> TypeConverter<UNIQUE_TYPE_NAME(SkColor)>::GetNextColor(
std::u16string::const_iterator start,
std::u16string::const_iterator end) {
std::u16string::const_iterator next_token;
return GetNextColor(start, end, next_token);
}
absl::optional<SkColor>
TypeConverter<UNIQUE_TYPE_NAME(SkColor)>::RgbaPiecesToSkColor(
const std::vector<base::StringPiece16>& pieces,
size_t start_piece) {
int r, g, b;
double a;
return ((pieces.size() >= start_piece + 4) &&
base::StringToInt(pieces[start_piece], &r) &&
base::IsValueInRangeForNumericType<uint8_t>(r) &&
base::StringToInt(pieces[start_piece + 1], &g) &&
base::IsValueInRangeForNumericType<uint8_t>(g) &&
base::StringToInt(pieces[start_piece + 2], &b) &&
base::IsValueInRangeForNumericType<uint8_t>(b) &&
base::StringToDouble(pieces[start_piece + 3], &a) && a >= 0.0 &&
a <= 1.0)
? absl::make_optional(SkColorSetARGB(
base::ClampRound<SkAlpha>(a * SK_AlphaOPAQUE), r, g, b))
: absl::nullopt;
}
absl::optional<SkColor>
TypeConverter<UNIQUE_TYPE_NAME(SkColor)>::ParseHexString(
const std::u16string& hex_string) {
SkColor value;
if (base::HexStringToUInt(base::UTF16ToUTF8(hex_string), &value)) {
// Add in a 1.0 alpha channel if it wasn't included in the input.
if (hex_string.length() <= 8)
value = SkColorSetA(value, 0xFF);
return absl::make_optional(value);
}
return absl::nullopt;
}
absl::optional<SkColor>
TypeConverter<UNIQUE_TYPE_NAME(SkColor)>::ParseHslString(
const std::u16string& hsl_string) {
std::u16string pruned_string;
base::RemoveChars(hsl_string, u"(%)hsla", &pruned_string);
const auto values = base::SplitStringPiece(
pruned_string, u", ", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
double h, s, v;
double a = 1.0;
if (values.size() >= 3 && values.size() <= 4 &&
base::StringToDouble(values[0], &h) &&
base::StringToDouble(values[1], &s) &&
base::StringToDouble(values[2], &v) &&
(values.size() == 3 ||
(base::StringToDouble(values[3], &a) && a >= 0.0 && a <= 1.0))) {
SkScalar hsv[3];
hsv[0] = base::ClampToRange(std::fmod(h, 360.0), 0.0, 360.0);
hsv[1] = s > 1.0 ? base::ClampToRange(s, 0.0, 100.0) / 100.0
: base::ClampToRange(s, 0.0, 1.0);
hsv[2] = v > 1.0 ? base::ClampToRange(v, 0.0, 100.0) / 100.0
: base::ClampToRange(v, 0.0, 1.0);
return absl::make_optional(
SkHSVToColor(base::ClampRound<SkAlpha>(a * SK_AlphaOPAQUE), hsv));
}
return absl::nullopt;
}
absl::optional<SkColor>
TypeConverter<UNIQUE_TYPE_NAME(SkColor)>::ParseRgbString(
const std::u16string& rgb_string) {
// Declare a constant string here for use below since it might trigger an
// ASAN error due to the stack temp going out of scope before the call to
// RgbaPiecesToSkColor.
std::u16string pruned_string;
base::RemoveChars(rgb_string, u"()rgba", &pruned_string);
auto values = base::SplitStringPiece(
pruned_string, u", ", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
// if it was just an rgb string, add the 1.0 alpha
if (values.size() == 3)
values.push_back(u"1.0");
return RgbaPiecesToSkColor(values, 0);
}
} // namespace metadata
} // namespace ui
DEFINE_ENUM_CONVERTERS(gfx::HorizontalAlignment,
{gfx::HorizontalAlignment::ALIGN_LEFT, u"ALIGN_LEFT"},
{gfx::HorizontalAlignment::ALIGN_CENTER,
u"ALIGN_CENTER"},
{gfx::HorizontalAlignment::ALIGN_RIGHT, u"ALIGN_RIGHT"},
{gfx::HorizontalAlignment::ALIGN_TO_HEAD,
u"ALIGN_TO_HEAD"})
DEFINE_ENUM_CONVERTERS(gfx::VerticalAlignment,
{gfx::VerticalAlignment::ALIGN_TOP, u"ALIGN_TOP"},
{gfx::VerticalAlignment::ALIGN_MIDDLE, u"ALIGN_MIDDLE"},
{gfx::VerticalAlignment::ALIGN_BOTTOM, u"ALIGN_BOTTOM"})
DEFINE_ENUM_CONVERTERS(gfx::ElideBehavior,
{gfx::ElideBehavior::NO_ELIDE, u"NO_ELIDE"},
{gfx::ElideBehavior::TRUNCATE, u"TRUNCATE"},
{gfx::ElideBehavior::ELIDE_HEAD, u"ELIDE_HEAD"},
{gfx::ElideBehavior::ELIDE_MIDDLE, u"ELIDE_MIDDLE"},
{gfx::ElideBehavior::ELIDE_TAIL, u"ELIDE_TAIL"},
{gfx::ElideBehavior::ELIDE_EMAIL, u"ELIDE_EMAIL"},
{gfx::ElideBehavior::FADE_TAIL, u"FADE_TAIL"})
DEFINE_ENUM_CONVERTERS(
ui::MenuSeparatorType,
{ui::MenuSeparatorType::NORMAL_SEPARATOR, u"NORMAL_SEPARATOR"},
{ui::MenuSeparatorType::DOUBLE_SEPARATOR, u"DOUBLE_SEPARATOR"},
{ui::MenuSeparatorType::UPPER_SEPARATOR, u"UPPER_SEPARATOR"},
{ui::MenuSeparatorType::LOWER_SEPARATOR, u"LOWER_SEPARATOR"},
{ui::MenuSeparatorType::SPACING_SEPARATOR, u"SPACING_SEPARATOR"},
{ui::MenuSeparatorType::VERTICAL_SEPARATOR, u"VERTICAL_SEPARATOR"},
{ui::MenuSeparatorType::PADDED_SEPARATOR, u"PADDED_SEPARATOR"})