blob: 9902b71ad21578bb7b43b70f4cd70b8b40a4e9dd [file] [log] [blame]
/*
* Copyright (C) 1999 Lars Knoll (knoll@kde.org)
* (C) 1999 Antti Koivisto (koivisto@kde.org)
* (C) 2001 Dirk Mueller ( mueller@kde.org )
* Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights
* reserved.
* Copyright (C) 2006 Andrew Wellington (proton@wiretapped.net)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
*/
#include "third_party/blink/renderer/platform/geometry/length.h"
#include <array>
#include "third_party/blink/renderer/platform/geometry/blend.h"
#include "third_party/blink/renderer/platform/geometry/calculation_value.h"
#include "third_party/blink/renderer/platform/heap/collection_support/heap_hash_map.h"
#include "third_party/blink/renderer/platform/wtf/allocator/allocator.h"
#include "third_party/blink/renderer/platform/wtf/size_assertions.h"
#include "third_party/blink/renderer/platform/wtf/static_constructors.h"
#include "third_party/blink/renderer/platform/wtf/text/string_builder.h"
#include "third_party/blink/renderer/platform/wtf/wtf.h"
namespace blink {
DEFINE_GLOBAL(PLATFORM_EXPORT, Length, g_auto_length);
DEFINE_GLOBAL(PLATFORM_EXPORT, Length, g_stretch_length);
DEFINE_GLOBAL(PLATFORM_EXPORT, Length, g_fit_content_length);
DEFINE_GLOBAL(PLATFORM_EXPORT, Length, g_max_content_length);
DEFINE_GLOBAL(PLATFORM_EXPORT, Length, g_min_content_length);
DEFINE_GLOBAL(PLATFORM_EXPORT, Length, g_min_intrinsic_length);
// static
void Length::Initialize() {
new (base::NotNullTag::kNotNull, (void*)&g_auto_length) Length(kAuto);
new (base::NotNullTag::kNotNull, (void*)&g_stretch_length) Length(kStretch);
new (base::NotNullTag::kNotNull, (void*)&g_fit_content_length)
Length(kFitContent);
new (base::NotNullTag::kNotNull, (void*)&g_max_content_length)
Length(kMaxContent);
new (base::NotNullTag::kNotNull, (void*)&g_min_content_length)
Length(kMinContent);
new (base::NotNullTag::kNotNull, (void*)&g_min_intrinsic_length)
Length(kMinIntrinsic);
}
class CalculationValueHandleMap
: public GarbageCollected<CalculationValueHandleMap> {
public:
CalculationValueHandleMap() = default;
CalculationValueHandleMap(const CalculationValueHandleMap&) = delete;
CalculationValueHandleMap& operator=(const CalculationValueHandleMap&) =
delete;
struct MemberWithCount {
DISALLOW_NEW();
public:
void Trace(Visitor* visitor) const { visitor->Trace(value); }
Member<const CalculationValue> value;
unsigned count = 1u;
};
void Trace(Visitor* visitor) const { visitor->Trace(map_); }
int insert(const CalculationValue* calc_value) {
DCHECK(index_);
// FIXME calc(): https://bugs.webkit.org/show_bug.cgi?id=80489
// This monotonically increasing handle generation scheme is potentially
// wasteful of the handle space. Consider reusing empty handles.
while (map_.Contains(index_))
index_++;
map_.Set(index_, MemberWithCount(calc_value, 1u));
return index_;
}
const CalculationValue& Get(int index) const {
DCHECK(map_.Contains(index));
return *map_.at(index).value;
}
unsigned GetCount(int index) const {
DCHECK(map_.Contains(index));
return map_.at(index).count;
}
wtf_size_t GetMapSize() const { return map_.size(); }
void DecrementCount(int index) {
DCHECK(map_.Contains(index));
auto iter = map_.find(index);
--iter->value.count;
if (iter->value.count == 0u) {
map_.erase(index);
}
}
void IncrementCount(int index) {
DCHECK(map_.Contains(index));
auto iter = map_.find(index);
++iter->value.count;
}
private:
int index_ = 1;
HeapHashMap<int, MemberWithCount> map_;
};
static CalculationValueHandleMap& CalcHandles() {
DCHECK(IsMainThread());
DEFINE_STATIC_LOCAL(Persistent<CalculationValueHandleMap>, handle_map,
(MakeGarbageCollected<CalculationValueHandleMap>()));
return *handle_map;
}
Length::Length(const CalculationValue* calc)
: quirk_(false), type_(kCalculated) {
calculation_handle_ = CalcHandles().insert(calc);
}
Length Length::BlendMixedTypes(const Length& from,
double progress,
ValueRange range) const {
DCHECK(from.CanConvertToCalculation());
DCHECK(CanConvertToCalculation());
return Length(
AsCalculationValue()->Blend(*from.AsCalculationValue(), progress, range));
}
Length Length::BlendSameTypes(const Length& from,
double progress,
ValueRange range) const {
Length::Type result_type = GetType();
if (IsZero())
result_type = from.GetType();
float blended_value =
blink::Blend(from.GetFloatValue(), GetFloatValue(), progress);
if (range == ValueRange::kNonNegative)
blended_value = ClampTo<float>(blended_value, 0);
return Length(blended_value, result_type);
}
PixelsAndPercent Length::GetPixelsAndPercent() const {
switch (GetType()) {
case kFixed:
return PixelsAndPercent(Pixels());
case kPercent:
return PixelsAndPercent(0.0f, Percent(), /*has_explicit_pixels=*/false,
/*has_explicit_percent=*/true);
case kCalculated:
return GetCalculationValue().GetPixelsAndPercent();
default:
NOTREACHED();
}
}
const CalculationValue* Length::AsCalculationValue() const {
if (IsCalculated())
return &GetCalculationValue();
return MakeGarbageCollected<CalculationValue>(GetPixelsAndPercent(),
ValueRange::kAll);
}
Length Length::SubtractFromOneHundredPercent() const {
if (IsPercent())
return Length::Percent(100 - Percent());
DCHECK(CanConvertToCalculation());
return Length(AsCalculationValue()->SubtractFromOneHundredPercent());
}
Length Length::Add(const Length& other) const {
CHECK(CanConvertToCalculation());
if (IsFixed() && other.IsFixed()) {
return Length::Fixed(Pixels() + other.Pixels());
}
if (IsPercent() && other.IsPercent()) {
return Length::Percent(Percent() + other.Percent());
}
return Length(AsCalculationValue()->Add(*other.AsCalculationValue()));
}
Length Length::Zoom(double factor) const {
switch (GetType()) {
case kFixed:
return Length::Fixed(GetFloatValue() * factor);
case kCalculated:
return Length(GetCalculationValue().Zoom(factor));
default:
return *this;
}
}
const CalculationValue& Length::GetCalculationValue() const {
DCHECK(IsCalculated());
return CalcHandles().Get(CalculationHandle());
}
void Length::IncrementCalculatedCount() const {
DCHECK(IsCalculated());
CalcHandles().IncrementCount(CalculationHandle());
}
void Length::DecrementCalculatedCount() const {
DCHECK(IsCalculated());
CalcHandles().DecrementCount(CalculationHandle());
}
unsigned Length::GetCalculatedCountForTest() const {
DCHECK(IsCalculated());
return CalcHandles().GetCount(CalculationHandle());
}
wtf_size_t Length::GetCalcHandleMapSizeForTest() {
return CalcHandles().GetMapSize();
}
float Length::NonNanCalculatedValue(float max_value,
const EvaluationInput& input) const {
DCHECK(IsCalculated());
float result = GetCalculationValue().Evaluate(max_value, input);
if (std::isnan(result))
return 0;
return result;
}
bool Length::HasOnlyFixedAndPercent() const {
if (GetType() == kFixed || GetType() == kPercent) {
return true;
}
if (GetType() == kCalculated) {
return GetCalculationValue().HasOnlyFixedAndPercent();
}
return false;
}
bool Length::HasAuto() const {
if (GetType() == kCalculated) {
return GetCalculationValue().HasAuto();
}
return GetType() == kAuto;
}
bool Length::HasContentOrIntrinsic() const {
if (GetType() == kCalculated) {
return GetCalculationValue().HasContentOrIntrinsicSize();
}
return GetType() == kMinContent || GetType() == kMaxContent ||
GetType() == kFitContent || GetType() == kMinIntrinsic ||
GetType() == kContent;
}
bool Length::HasAutoOrContentOrIntrinsic() const {
if (GetType() == kCalculated) {
return GetCalculationValue().HasAutoOrContentOrIntrinsicSize();
}
return GetType() == kAuto || HasContentOrIntrinsic();
}
bool Length::HasPercent() const {
if (GetType() == kCalculated) {
return GetCalculationValue().HasPercent();
}
return GetType() == kPercent;
}
bool Length::HasPercentOrStretch() const {
if (GetType() == kCalculated) {
return GetCalculationValue().HasPercentOrStretch();
}
return GetType() == kPercent || GetType() == kStretch;
}
bool Length::HasStretch() const {
if (GetType() == kCalculated) {
return GetCalculationValue().HasStretch();
}
return GetType() == kStretch;
}
bool Length::HasMinContent() const {
if (GetType() == kCalculated) {
return GetCalculationValue().HasMinContent();
}
return GetType() == kMinContent;
}
bool Length::HasMaxContent() const {
if (GetType() == kCalculated) {
return GetCalculationValue().HasMaxContent();
}
return GetType() == kMaxContent;
}
bool Length::HasFitContent() const {
if (GetType() == kCalculated) {
return GetCalculationValue().HasFitContent();
}
return GetType() == kFitContent;
}
bool Length::IsCalculatedEqual(const Length& o) const {
return IsCalculated() &&
(&GetCalculationValue() == &o.GetCalculationValue() ||
GetCalculationValue() == o.GetCalculationValue());
}
String Length::ToString() const {
StringBuilder builder;
builder.Append("Length(");
static const auto kTypeNames = std::to_array<const char* const>(
{"Auto", "Percent", "Fixed", "MinContent", "MaxContent", "MinIntrinsic",
"FillAvailable", "Stretch", "FitContent", "Calculated", "Flex",
"ExtendToZoom", "DeviceWidth", "DeviceHeight", "None", "Content"});
if (type_ < std::size(kTypeNames))
builder.Append(kTypeNames[type_]);
else
builder.Append("?");
builder.Append(", ");
if (IsCalculated()) {
builder.AppendNumber(calculation_handle_);
} else {
builder.AppendNumber(value_);
}
if (quirk_)
builder.Append(", Quirk");
builder.Append(")");
return builder.ToString();
}
unsigned Length::GetHash() const {
unsigned hash = 0;
AddFloatToHash(hash, value_);
AddIntToHash(hash, type_);
AddIntToHash(hash, quirk_);
AddIntToHash(hash, calculation_handle_);
return hash;
}
std::ostream& operator<<(std::ostream& ostream, const Length& value) {
return ostream << value.ToString();
}
struct SameSizeAsLength {
int32_t value;
int32_t meta_data;
};
ASSERT_SIZE(Length, SameSizeAsLength);
} // namespace blink