| /* |
| Copyright (C) 1999 Lars Knoll (knoll@kde.org) |
| Copyright (C) 2006, 2008 Apple Inc. All rights reserved. |
| Copyright (C) 2011 Rik Cabanier (cabanier@adobe.com) |
| Copyright (C) 2011 Adobe Systems Incorporated. All rights reserved. |
| |
| 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. |
| */ |
| |
| #ifndef THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_LENGTH_H_ |
| #define THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_LENGTH_H_ |
| |
| #include <cmath> |
| #include <cstring> |
| #include <optional> |
| |
| #include "base/check_op.h" |
| #include "base/compiler_specific.h" |
| #include "base/memory/stack_allocated.h" |
| #include "base/notreached.h" |
| #include "third_party/blink/renderer/platform/geometry/layout_unit.h" |
| #include "third_party/blink/renderer/platform/platform_export.h" |
| #include "third_party/blink/renderer/platform/wtf/allocator/allocator.h" |
| #include "third_party/blink/renderer/platform/wtf/forward.h" |
| #include "third_party/blink/renderer/platform/wtf/math_extras.h" |
| |
| namespace blink { |
| |
| struct EvaluationInput; |
| |
| struct PixelsAndPercent { |
| DISALLOW_NEW(); |
| |
| // The default constructor places this in an invalid state. |
| PixelsAndPercent() = default; |
| |
| explicit PixelsAndPercent(float pixels) |
| : pixels(pixels), |
| percent(0.0f), |
| has_explicit_pixels(true), |
| has_explicit_percent(false) {} |
| PixelsAndPercent(float pixels, |
| float percent, |
| bool has_explicit_pixels, |
| bool has_explicit_percent) |
| : pixels(pixels), |
| percent(percent), |
| has_explicit_pixels(has_explicit_pixels), |
| has_explicit_percent(has_explicit_percent) {} |
| |
| PixelsAndPercent& operator+=(const PixelsAndPercent& rhs) { |
| pixels += rhs.pixels; |
| percent += rhs.percent; |
| has_explicit_pixels |= rhs.has_explicit_pixels; |
| has_explicit_percent |= rhs.has_explicit_percent; |
| return *this; |
| } |
| friend PixelsAndPercent operator+(PixelsAndPercent lhs, |
| const PixelsAndPercent& rhs) { |
| lhs += rhs; |
| return lhs; |
| } |
| PixelsAndPercent& operator-=(const PixelsAndPercent& rhs) { |
| pixels -= rhs.pixels; |
| percent -= rhs.percent; |
| has_explicit_pixels |= rhs.has_explicit_pixels; |
| has_explicit_percent |= rhs.has_explicit_percent; |
| return *this; |
| } |
| PixelsAndPercent& operator*=(float number) { |
| pixels *= number; |
| percent *= number; |
| return *this; |
| } |
| |
| float pixels = 0.f; |
| float percent = 0.f; |
| bool has_explicit_pixels = false; |
| bool has_explicit_percent = false; |
| }; |
| |
| class CalculationValue; |
| class Length; |
| |
| PLATFORM_EXPORT extern const Length& g_auto_length; |
| PLATFORM_EXPORT extern const Length& g_stretch_length; |
| PLATFORM_EXPORT extern const Length& g_fit_content_length; |
| PLATFORM_EXPORT extern const Length& g_max_content_length; |
| PLATFORM_EXPORT extern const Length& g_min_content_length; |
| PLATFORM_EXPORT extern const Length& g_min_intrinsic_length; |
| |
| class PLATFORM_EXPORT Length { |
| DISALLOW_NEW(); |
| |
| public: |
| // Initializes global instances. |
| static void Initialize(); |
| |
| enum class ValueRange { kAll, kNonNegative }; |
| |
| // FIXME: This enum makes it hard to tell in general what values may be |
| // appropriate for any given Length. |
| enum Type : unsigned char { |
| kAuto, |
| kPercent, |
| kFixed, |
| kMinContent, |
| kMaxContent, |
| kMinIntrinsic, |
| kStretch, |
| kFitContent, |
| kCalculated, |
| kFlex, |
| kExtendToZoom, |
| kDeviceWidth, |
| kDeviceHeight, |
| kNone, // only valid for max-width, max-height, or contain-intrinsic-size |
| kContent // only valid for flex-basis |
| }; |
| |
| Length() : value_(0), type_(kAuto) {} |
| |
| explicit Length(Length::Type t) : value_(0), type_(t) { |
| DCHECK_NE(t, kCalculated); |
| } |
| |
| Length(int v, Length::Type t) : value_(v), type_(t) { |
| DCHECK_NE(t, kCalculated); |
| } |
| |
| Length(LayoutUnit v, Length::Type t) : value_(v.ToFloat()), type_(t) { |
| DCHECK(std::isfinite(v.ToFloat())); |
| DCHECK_NE(t, kCalculated); |
| } |
| |
| Length(float v, Length::Type t) : value_(v), type_(t) { |
| DCHECK(std::isfinite(v)); |
| DCHECK_NE(t, kCalculated); |
| } |
| |
| Length(double v, Length::Type t) : type_(t) { |
| DCHECK(std::isfinite(v)); |
| value_ = ClampTo<float>(v); |
| } |
| |
| explicit Length(const CalculationValue*); |
| |
| Length(const Length& length) { |
| UNSAFE_TODO(memcpy(this, &length, sizeof(Length))); |
| if (IsCalculated()) |
| IncrementCalculatedCount(); |
| } |
| |
| Length& operator=(const Length& length) { |
| if (length.IsCalculated()) |
| length.IncrementCalculatedCount(); |
| if (IsCalculated()) |
| DecrementCalculatedCount(); |
| UNSAFE_TODO(memcpy(this, &length, sizeof(Length))); |
| return *this; |
| } |
| |
| ~Length() { |
| if (IsCalculated()) |
| DecrementCalculatedCount(); |
| } |
| |
| bool operator==(const Length& o) const { |
| if (type_ != o.type_ || quirk_ != o.quirk_) { |
| return false; |
| } |
| if (type_ == kCalculated) { |
| return IsCalculatedEqual(o); |
| } else { |
| // For everything that doesn't use value_, it is defined to be zero, |
| // so we can compare here unconditionally. |
| return value_ == o.value_; |
| } |
| } |
| |
| static const Length& Auto() { return g_auto_length; } |
| static const Length& Stretch() { return g_stretch_length; } |
| static const Length& FitContent() { return g_fit_content_length; } |
| static const Length& MaxContent() { return g_max_content_length; } |
| static const Length& MinContent() { return g_min_content_length; } |
| static const Length& MinIntrinsic() { return g_min_intrinsic_length; } |
| |
| static Length Content() { return Length(kContent); } |
| static Length Fixed() { return Length(kFixed); } |
| static Length None() { return Length(kNone); } |
| |
| static Length ExtendToZoom() { return Length(kExtendToZoom); } |
| static Length DeviceWidth() { return Length(kDeviceWidth); } |
| static Length DeviceHeight() { return Length(kDeviceHeight); } |
| |
| template <typename NUMBER_TYPE> |
| static Length Fixed(NUMBER_TYPE number) { |
| return Length(number, kFixed); |
| } |
| template <typename NUMBER_TYPE> |
| static Length Percent(NUMBER_TYPE number) { |
| return Length(number, kPercent); |
| } |
| static Length Flex(float value) { return Length(value, kFlex); } |
| |
| int IntValue() const { |
| if (IsCalculated()) { |
| NOTREACHED(); |
| } |
| DCHECK(!IsNone()); |
| return static_cast<int>(value_); |
| } |
| |
| float Pixels() const { |
| DCHECK_EQ(GetType(), kFixed); |
| return GetFloatValue(); |
| } |
| float Percent() const { |
| DCHECK_EQ(GetType(), kPercent); |
| return GetFloatValue(); |
| } |
| float Flex() const { |
| DCHECK_EQ(GetType(), kFlex); |
| return GetFloatValue(); |
| } |
| |
| PixelsAndPercent GetPixelsAndPercent() const; |
| |
| const CalculationValue& GetCalculationValue() const; |
| |
| // If |this| is calculated, returns the underlying |CalculationValue|. If not, |
| // returns a |CalculationValue| constructed from |GetPixelsAndPercent()|. Hits |
| // a DCHECK if |this| is not a specified value (e.g., 'auto'). |
| const CalculationValue* AsCalculationValue() const; |
| |
| Length::Type GetType() const { return static_cast<Length::Type>(type_); } |
| bool Quirk() const { return quirk_; } |
| |
| void SetQuirk(bool quirk) { quirk_ = quirk; } |
| |
| bool IsNone() const { return GetType() == kNone; } |
| |
| // FIXME calc: https://bugs.webkit.org/show_bug.cgi?id=80357. A calculated |
| // Length always contains a percentage, and without a maxValue passed to these |
| // functions it's impossible to determine the sign or zero-ness. We assume all |
| // calc values are positive and non-zero for now. |
| bool IsZero() const { |
| DCHECK(!IsNone()); |
| if (IsCalculated()) |
| return false; |
| |
| return !value_; |
| } |
| |
| // If this is a length in a property that accepts calc-size(), use |
| // |HasAuto()|. If this |Length| is a block-axis size |
| // |HasAutoOrContentOrIntrinsic()| is usually a better choice. |
| bool IsAuto() const { return GetType() == kAuto; } |
| bool IsFixed() const { return GetType() == kFixed; } |
| |
| // For the block axis, intrinsic sizes such as `min-content` behave the same |
| // as `auto`. https://www.w3.org/TR/css-sizing-3/#valdef-width-min-content |
| // This includes content-based sizes in calc-size(). |
| bool HasAuto() const; |
| bool HasContentOrIntrinsic() const; |
| bool HasAutoOrContentOrIntrinsic() const; |
| // HasPercent and HasPercentOrStretch refer to whether the toplevel value |
| // should be treated as a percentage type for web-exposed behavior |
| // decisions. However, a value can still depend on a percentage when |
| // HasPercent() is false: for example, calc-size(any, 20%). |
| bool HasPercent() const; |
| bool HasPercentOrStretch() const; |
| bool HasStretch() const; |
| |
| bool HasMinContent() const; |
| bool HasMaxContent() const; |
| bool HasMinIntrinsic() const { return IsMinIntrinsic(); } |
| bool HasFitContent() const; |
| |
| // CanConvertToCalculation() is true for any Lengths that are a fixed length, |
| // a percent, or a calc() expression. Note that this *includes* calc-size() |
| // expressions that contain sizing keywords, which may not be what you want. |
| // |
| // Compare to HasOnlyFixedAndPercent. (The difference is relevant only when |
| // sizing keywords may be present.) |
| // |
| // Note that in some contexts sizing keywords can be converted to |
| // calculation expressions, but this function does *not* return true for |
| // those cases; the caller is required to convert appropriately. |
| bool CanConvertToCalculation() const { |
| return GetType() == kFixed || GetType() == kPercent || |
| GetType() == kCalculated; |
| } |
| |
| // HasOnlyFixedAndPercent() is true for any Lengths that are a fixed length, |
| // a percent, or calc() expressions that consist only of those. (This |
| // excludes calc() expressions with calc-size() that depend on sizing |
| // keywords.) |
| // Compare to CanConvertToCalculation. (The difference is relevant only |
| // when sizing keywords may be present.) |
| bool HasOnlyFixedAndPercent() const; |
| |
| bool IsCalculated() const { return GetType() == kCalculated; } |
| bool IsCalculatedEqual(const Length&) const; |
| |
| // These type checking methods should be used with extreme caution; |
| // many uses probably want the Has* methods above to work correctly |
| // with calc-size(). |
| bool IsMinContent() const { return GetType() == kMinContent; } |
| bool IsMaxContent() const { return GetType() == kMaxContent; } |
| bool IsMinIntrinsic() const { return GetType() == kMinIntrinsic; } |
| bool IsStretch() const { return GetType() == kStretch; } |
| bool IsFitContent() const { return GetType() == kFitContent; } |
| bool IsPercent() const { return GetType() == kPercent; } |
| // MayHavePercentDependence should be used to decide whether to optimize |
| // away computing the value on which percentages depend or optimize away |
| // recomputation that results from changes to that value. It is intended to |
| // be used *only* in cases where the implementation could be changed to one |
| // that returns true only if there are percentage values somewhere in the |
| // expression (that is, one that still returns true for calc-size(any, 30%) |
| // for which HasPercent() is false, but is false for calc-size(any, 30px)). |
| // |
| // We could (if we want) make this exact and remove "May" from the name. |
| // But this would require looking into the calculation value like HasPercent |
| // does. However, it needs to be different from HasPercent because of cases |
| // where calc-size() erases percentage-ness from the type, like |
| // calc-size(any, 20%). |
| // |
| // For properties that cannot have calc-size in them, we currently use |
| // HasPercent() rather than MayHavePercentDependence() since it's a |
| // shorter/simpler function name, and the two functions are equivalent in |
| // that case. |
| bool MayHavePercentDependence() const { |
| return GetType() == kPercent || GetType() == kCalculated; |
| } |
| bool IsFlex() const { return GetType() == kFlex; } |
| bool IsExtendToZoom() const { return GetType() == kExtendToZoom; } |
| bool IsDeviceWidth() const { return GetType() == kDeviceWidth; } |
| bool IsDeviceHeight() const { return GetType() == kDeviceHeight; } |
| |
| Length Blend(const Length& from, double progress, ValueRange range) const { |
| DCHECK(CanConvertToCalculation()); |
| DCHECK(from.CanConvertToCalculation()); |
| |
| if (progress == 0.0) |
| return from; |
| |
| if (progress == 1.0) |
| return *this; |
| |
| if (from.GetType() == kCalculated || GetType() == kCalculated) |
| return BlendMixedTypes(from, progress, range); |
| |
| if (!from.IsZero() && !IsZero() && from.GetType() != GetType()) |
| return BlendMixedTypes(from, progress, range); |
| |
| if (from.IsZero() && IsZero()) |
| return *this; |
| |
| return BlendSameTypes(from, progress, range); |
| } |
| |
| float NonNanCalculatedValue(float max_value, const EvaluationInput&) const; |
| |
| Length SubtractFromOneHundredPercent() const; |
| |
| Length Add(const Length& other) const; |
| |
| Length Zoom(double factor) const; |
| |
| unsigned GetCalculatedCountForTest() const; |
| |
| static wtf_size_t GetCalcHandleMapSizeForTest(); |
| |
| String ToString() const; |
| |
| unsigned GetHash() const; |
| |
| private: |
| float GetFloatValue() const { |
| DCHECK(!IsNone()); |
| DCHECK(!IsCalculated()); |
| return value_; |
| } |
| |
| Length BlendMixedTypes(const Length& from, double progress, ValueRange) const; |
| |
| Length BlendSameTypes(const Length& from, double progress, ValueRange) const; |
| |
| int CalculationHandle() const { |
| DCHECK(IsCalculated()); |
| return calculation_handle_; |
| } |
| void IncrementCalculatedCount() const; |
| void DecrementCalculatedCount() const; |
| |
| union { |
| // If kType == kCalculated. |
| int calculation_handle_; |
| |
| // Otherwise. Must be zero if not in use (e.g., for kAuto or kNone). |
| float value_; |
| }; |
| bool quirk_ = false; |
| unsigned char type_; |
| }; |
| |
| PLATFORM_EXPORT std::ostream& operator<<(std::ostream&, const Length&); |
| |
| } // namespace blink |
| |
| WTF_ALLOW_CLEAR_UNUSED_SLOTS_WITH_MEM_FUNCTIONS(blink::Length) |
| |
| #endif // THIRD_PARTY_BLINK_RENDERER_PLATFORM_GEOMETRY_LENGTH_H_ |