mirror of
https://github.com/roytam1/UXP.git
synced 2026-05-26 13:58:49 +00:00
93899c0157
Prereq for parts of #2404
579 lines
16 KiB
C++
579 lines
16 KiB
C++
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
/* representation of length values in computed style data */
|
|
|
|
#include "nsStyleCoord.h"
|
|
#include "mozilla/HashFunctions.h"
|
|
#include "mozilla/PodOperations.h"
|
|
#include <algorithm>
|
|
|
|
already_AddRefed<nsStyleCoord::CalcNode>
|
|
nsStyleCoord::CalcNode::CreateLeaf(nscoord aLength, float aPercent,
|
|
bool aHasPercent)
|
|
{
|
|
RefPtr<CalcNode> node = new CalcNode(Type::Leaf);
|
|
node->mLength = aLength;
|
|
node->mPercent = aPercent;
|
|
node->mHasPercent = aHasPercent;
|
|
return node.forget();
|
|
}
|
|
|
|
already_AddRefed<nsStyleCoord::CalcNode>
|
|
nsStyleCoord::CalcNode::Create(Type aType)
|
|
{
|
|
RefPtr<CalcNode> node = new CalcNode(aType);
|
|
return node.forget();
|
|
}
|
|
|
|
nsStyleCoord::CalcNode::CalcNode(Type aType)
|
|
: mType(aType)
|
|
, mLength(0)
|
|
, mPercent(0.0f)
|
|
, mNumber(0.0f)
|
|
, mHasPercent(false)
|
|
{
|
|
}
|
|
|
|
bool
|
|
nsStyleCoord::CalcNode::HasPercent() const
|
|
{
|
|
if (mHasPercent) {
|
|
return true;
|
|
}
|
|
for (const RefPtr<CalcNode>& child : mChildren) {
|
|
if (child->HasPercent()) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
nsStyleCoord::CalcNode::Equals(const CalcNode& aOther) const
|
|
{
|
|
if (mType != aOther.mType ||
|
|
mLength != aOther.mLength ||
|
|
mPercent != aOther.mPercent ||
|
|
mNumber != aOther.mNumber ||
|
|
mHasPercent != aOther.mHasPercent ||
|
|
mChildren.Length() != aOther.mChildren.Length()) {
|
|
return false;
|
|
}
|
|
|
|
for (uint32_t i = 0; i < mChildren.Length(); ++i) {
|
|
if (!mChildren[i]->Equals(*aOther.mChildren[i])) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
uint32_t
|
|
nsStyleCoord::CalcNode::HashValue(uint32_t aHash) const
|
|
{
|
|
aHash = mozilla::AddToHash(aHash, uint8_t(mType), mLength, mPercent,
|
|
mNumber, mHasPercent);
|
|
for (const RefPtr<CalcNode>& child : mChildren) {
|
|
aHash = child->HashValue(aHash);
|
|
}
|
|
return aHash;
|
|
}
|
|
|
|
static nscoord
|
|
ResolveCalcNode(const nsStyleCoord::CalcNode& aNode, nscoord aPercentageBasis)
|
|
{
|
|
using Type = nsStyleCoord::CalcNode::Type;
|
|
|
|
switch (aNode.mType) {
|
|
case Type::Leaf:
|
|
return aNode.mLength +
|
|
NSToCoordFloorClamped(aPercentageBasis * aNode.mPercent);
|
|
case Type::Add:
|
|
MOZ_ASSERT(aNode.mChildren.Length() == 2, "unexpected child count");
|
|
return NSCoordSaturatingAdd(
|
|
ResolveCalcNode(*aNode.mChildren[0], aPercentageBasis),
|
|
ResolveCalcNode(*aNode.mChildren[1], aPercentageBasis));
|
|
case Type::Subtract:
|
|
MOZ_ASSERT(aNode.mChildren.Length() == 2, "unexpected child count");
|
|
return NSCoordSaturatingSubtract(
|
|
ResolveCalcNode(*aNode.mChildren[0], aPercentageBasis),
|
|
ResolveCalcNode(*aNode.mChildren[1], aPercentageBasis), 0);
|
|
case Type::Multiply:
|
|
MOZ_ASSERT(aNode.mChildren.Length() == 1, "unexpected child count");
|
|
return NSCoordSaturatingMultiply(
|
|
ResolveCalcNode(*aNode.mChildren[0], aPercentageBasis),
|
|
aNode.mNumber);
|
|
case Type::Divide:
|
|
MOZ_ASSERT(aNode.mChildren.Length() == 1, "unexpected child count");
|
|
return NSCoordSaturatingMultiply(
|
|
ResolveCalcNode(*aNode.mChildren[0], aPercentageBasis),
|
|
1.0f / aNode.mNumber);
|
|
case Type::Min: {
|
|
MOZ_ASSERT(!aNode.mChildren.IsEmpty(), "unexpected child count");
|
|
nscoord result = ResolveCalcNode(*aNode.mChildren[0], aPercentageBasis);
|
|
for (uint32_t i = 1; i < aNode.mChildren.Length(); ++i) {
|
|
result = std::min(result,
|
|
ResolveCalcNode(*aNode.mChildren[i],
|
|
aPercentageBasis));
|
|
}
|
|
return result;
|
|
}
|
|
case Type::Max: {
|
|
MOZ_ASSERT(!aNode.mChildren.IsEmpty(), "unexpected child count");
|
|
nscoord result = ResolveCalcNode(*aNode.mChildren[0], aPercentageBasis);
|
|
for (uint32_t i = 1; i < aNode.mChildren.Length(); ++i) {
|
|
result = std::max(result,
|
|
ResolveCalcNode(*aNode.mChildren[i],
|
|
aPercentageBasis));
|
|
}
|
|
return result;
|
|
}
|
|
case Type::Clamp:
|
|
MOZ_ASSERT(aNode.mChildren.Length() == 3, "unexpected child count");
|
|
return std::max(ResolveCalcNode(*aNode.mChildren[0], aPercentageBasis),
|
|
std::min(ResolveCalcNode(*aNode.mChildren[1],
|
|
aPercentageBasis),
|
|
ResolveCalcNode(*aNode.mChildren[2],
|
|
aPercentageBasis)));
|
|
}
|
|
|
|
MOZ_ASSERT_UNREACHABLE("unexpected calc node type");
|
|
return 0;
|
|
}
|
|
|
|
nscoord
|
|
nsStyleCoord::Calc::Resolve(nscoord aPercentageBasis) const
|
|
{
|
|
if (mNode) {
|
|
return ResolveCalcNode(*mNode, aPercentageBasis);
|
|
}
|
|
return mLength + NSToCoordFloorClamped(aPercentageBasis * mPercent);
|
|
}
|
|
|
|
nsStyleCoord::nsStyleCoord(nsStyleUnit aUnit)
|
|
: mUnit(aUnit)
|
|
{
|
|
NS_ASSERTION(aUnit < eStyleUnit_Percent, "not a valueless unit");
|
|
if (aUnit >= eStyleUnit_Percent) {
|
|
mUnit = eStyleUnit_Null;
|
|
}
|
|
mValue.mInt = 0;
|
|
}
|
|
|
|
nsStyleCoord::nsStyleCoord(int32_t aValue, nsStyleUnit aUnit)
|
|
: mUnit(aUnit)
|
|
{
|
|
//if you want to pass in eStyleUnit_Coord, don't. instead, use the
|
|
//constructor just above this one... MMP
|
|
NS_ASSERTION((aUnit == eStyleUnit_Enumerated) ||
|
|
(aUnit == eStyleUnit_Integer), "not an int value");
|
|
if ((aUnit == eStyleUnit_Enumerated) ||
|
|
(aUnit == eStyleUnit_Integer)) {
|
|
mValue.mInt = aValue;
|
|
}
|
|
else {
|
|
mUnit = eStyleUnit_Null;
|
|
mValue.mInt = 0;
|
|
}
|
|
}
|
|
|
|
nsStyleCoord::nsStyleCoord(float aValue, nsStyleUnit aUnit)
|
|
: mUnit(aUnit)
|
|
{
|
|
if (aUnit < eStyleUnit_Percent || aUnit >= eStyleUnit_Coord) {
|
|
NS_NOTREACHED("not a float value");
|
|
mUnit = eStyleUnit_Null;
|
|
mValue.mInt = 0;
|
|
} else {
|
|
mValue.mFloat = aValue;
|
|
}
|
|
}
|
|
|
|
bool nsStyleCoord::operator==(const nsStyleCoord& aOther) const
|
|
{
|
|
if (mUnit != aOther.mUnit) {
|
|
return false;
|
|
}
|
|
switch (mUnit) {
|
|
case eStyleUnit_Null:
|
|
case eStyleUnit_Normal:
|
|
case eStyleUnit_Auto:
|
|
case eStyleUnit_None:
|
|
return true;
|
|
case eStyleUnit_Percent:
|
|
case eStyleUnit_Factor:
|
|
case eStyleUnit_Degree:
|
|
case eStyleUnit_Grad:
|
|
case eStyleUnit_Radian:
|
|
case eStyleUnit_Turn:
|
|
case eStyleUnit_FlexFraction:
|
|
return mValue.mFloat == aOther.mValue.mFloat;
|
|
case eStyleUnit_Coord:
|
|
case eStyleUnit_Integer:
|
|
case eStyleUnit_Enumerated:
|
|
return mValue.mInt == aOther.mValue.mInt;
|
|
case eStyleUnit_Calc: {
|
|
Calc* thisCalc = GetCalcValue();
|
|
Calc* otherCalc = aOther.GetCalcValue();
|
|
if (thisCalc->HasCalcNode() || otherCalc->HasCalcNode()) {
|
|
return thisCalc->HasCalcNode() == otherCalc->HasCalcNode() &&
|
|
thisCalc->mNode->Equals(*otherCalc->mNode);
|
|
}
|
|
return *thisCalc == *otherCalc;
|
|
}
|
|
}
|
|
MOZ_ASSERT(false, "unexpected unit");
|
|
return false;
|
|
}
|
|
|
|
uint32_t nsStyleCoord::HashValue(uint32_t aHash = 0) const
|
|
{
|
|
aHash = mozilla::AddToHash(aHash, mUnit);
|
|
|
|
switch (mUnit) {
|
|
case eStyleUnit_Null:
|
|
case eStyleUnit_Normal:
|
|
case eStyleUnit_Auto:
|
|
case eStyleUnit_None:
|
|
return mozilla::AddToHash(aHash, true);
|
|
case eStyleUnit_Percent:
|
|
case eStyleUnit_Factor:
|
|
case eStyleUnit_Degree:
|
|
case eStyleUnit_Grad:
|
|
case eStyleUnit_Radian:
|
|
case eStyleUnit_Turn:
|
|
case eStyleUnit_FlexFraction:
|
|
return mozilla::AddToHash(aHash, mValue.mFloat);
|
|
case eStyleUnit_Coord:
|
|
case eStyleUnit_Integer:
|
|
case eStyleUnit_Enumerated:
|
|
return mozilla::AddToHash(aHash, mValue.mInt);
|
|
case eStyleUnit_Calc: {
|
|
Calc* calcValue = GetCalcValue();
|
|
if (calcValue->HasCalcNode()) {
|
|
return calcValue->mNode->HashValue(aHash);
|
|
}
|
|
aHash = mozilla::AddToHash(aHash, calcValue->mLength);
|
|
if (HasPercent()) {
|
|
return mozilla::AddToHash(aHash, calcValue->mPercent);
|
|
}
|
|
return aHash;
|
|
}
|
|
}
|
|
MOZ_ASSERT(false, "unexpected unit");
|
|
return aHash;
|
|
}
|
|
|
|
void nsStyleCoord::Reset()
|
|
{
|
|
Reset(mUnit, mValue);
|
|
}
|
|
|
|
void nsStyleCoord::SetCoordValue(nscoord aValue)
|
|
{
|
|
Reset();
|
|
mUnit = eStyleUnit_Coord;
|
|
mValue.mInt = aValue;
|
|
}
|
|
|
|
void nsStyleCoord::SetIntValue(int32_t aValue, nsStyleUnit aUnit)
|
|
{
|
|
NS_ASSERTION((aUnit == eStyleUnit_Enumerated) ||
|
|
(aUnit == eStyleUnit_Integer), "not an int value");
|
|
Reset();
|
|
if ((aUnit == eStyleUnit_Enumerated) ||
|
|
(aUnit == eStyleUnit_Integer)) {
|
|
mUnit = aUnit;
|
|
mValue.mInt = aValue;
|
|
}
|
|
}
|
|
|
|
void nsStyleCoord::SetPercentValue(float aValue)
|
|
{
|
|
Reset();
|
|
mUnit = eStyleUnit_Percent;
|
|
mValue.mFloat = aValue;
|
|
}
|
|
|
|
void nsStyleCoord::SetFactorValue(float aValue)
|
|
{
|
|
Reset();
|
|
mUnit = eStyleUnit_Factor;
|
|
mValue.mFloat = aValue;
|
|
}
|
|
|
|
void nsStyleCoord::SetAngleValue(float aValue, nsStyleUnit aUnit)
|
|
{
|
|
Reset();
|
|
if (aUnit == eStyleUnit_Degree ||
|
|
aUnit == eStyleUnit_Grad ||
|
|
aUnit == eStyleUnit_Radian ||
|
|
aUnit == eStyleUnit_Turn) {
|
|
mUnit = aUnit;
|
|
mValue.mFloat = aValue;
|
|
} else {
|
|
NS_NOTREACHED("not an angle value");
|
|
}
|
|
}
|
|
|
|
void nsStyleCoord::SetFlexFractionValue(float aValue)
|
|
{
|
|
Reset();
|
|
mUnit = eStyleUnit_FlexFraction;
|
|
mValue.mFloat = aValue;
|
|
}
|
|
|
|
void nsStyleCoord::SetCalcValue(Calc* aValue)
|
|
{
|
|
Reset();
|
|
mUnit = eStyleUnit_Calc;
|
|
mValue.mPointer = aValue;
|
|
aValue->AddRef();
|
|
}
|
|
|
|
void nsStyleCoord::SetNormalValue()
|
|
{
|
|
Reset();
|
|
mUnit = eStyleUnit_Normal;
|
|
mValue.mInt = 0;
|
|
}
|
|
|
|
void nsStyleCoord::SetAutoValue()
|
|
{
|
|
Reset();
|
|
mUnit = eStyleUnit_Auto;
|
|
mValue.mInt = 0;
|
|
}
|
|
|
|
void nsStyleCoord::SetNoneValue()
|
|
{
|
|
Reset();
|
|
mUnit = eStyleUnit_None;
|
|
mValue.mInt = 0;
|
|
}
|
|
|
|
// accessors that are not inlined
|
|
|
|
double
|
|
nsStyleCoord::GetAngleValueInDegrees() const
|
|
{
|
|
return GetAngleValueInRadians() * (180.0 / M_PI);
|
|
}
|
|
|
|
double
|
|
nsStyleCoord::GetAngleValueInRadians() const
|
|
{
|
|
double angle = mValue.mFloat;
|
|
|
|
switch (GetUnit()) {
|
|
case eStyleUnit_Radian: return angle;
|
|
case eStyleUnit_Turn: return angle * 2 * M_PI;
|
|
case eStyleUnit_Degree: return angle * M_PI / 180.0;
|
|
case eStyleUnit_Grad: return angle * M_PI / 200.0;
|
|
|
|
default:
|
|
NS_NOTREACHED("unrecognized angular unit");
|
|
return 0.0;
|
|
}
|
|
}
|
|
|
|
nsStyleSides::nsStyleSides()
|
|
{
|
|
NS_FOR_CSS_SIDES(i) {
|
|
mUnits[i] = eStyleUnit_Null;
|
|
}
|
|
mozilla::PodArrayZero(mValues);
|
|
}
|
|
|
|
nsStyleSides::nsStyleSides(const nsStyleSides& aOther)
|
|
{
|
|
NS_FOR_CSS_SIDES(i) {
|
|
mUnits[i] = eStyleUnit_Null;
|
|
}
|
|
*this = aOther;
|
|
}
|
|
|
|
nsStyleSides::~nsStyleSides()
|
|
{
|
|
Reset();
|
|
}
|
|
|
|
nsStyleSides&
|
|
nsStyleSides::operator=(const nsStyleSides& aCopy)
|
|
{
|
|
if (this != &aCopy) {
|
|
NS_FOR_CSS_SIDES(i) {
|
|
nsStyleCoord::SetValue(mUnits[i], mValues[i],
|
|
aCopy.mUnits[i], aCopy.mValues[i]);
|
|
}
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
bool nsStyleSides::operator==(const nsStyleSides& aOther) const
|
|
{
|
|
NS_FOR_CSS_SIDES(i) {
|
|
if (nsStyleCoord(mValues[i], (nsStyleUnit)mUnits[i]) !=
|
|
nsStyleCoord(aOther.mValues[i], (nsStyleUnit)aOther.mUnits[i])) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void nsStyleSides::Reset()
|
|
{
|
|
NS_FOR_CSS_SIDES(i) {
|
|
nsStyleCoord::Reset(mUnits[i], mValues[i]);
|
|
}
|
|
}
|
|
|
|
nsStyleCorners::nsStyleCorners()
|
|
{
|
|
NS_FOR_CSS_HALF_CORNERS(i) {
|
|
mUnits[i] = eStyleUnit_Null;
|
|
}
|
|
mozilla::PodArrayZero(mValues);
|
|
}
|
|
|
|
nsStyleCorners::nsStyleCorners(const nsStyleCorners& aOther)
|
|
{
|
|
NS_FOR_CSS_HALF_CORNERS(i) {
|
|
mUnits[i] = eStyleUnit_Null;
|
|
}
|
|
*this = aOther;
|
|
}
|
|
|
|
nsStyleCorners::~nsStyleCorners()
|
|
{
|
|
Reset();
|
|
}
|
|
|
|
nsStyleCorners&
|
|
nsStyleCorners::operator=(const nsStyleCorners& aCopy)
|
|
{
|
|
if (this != &aCopy) {
|
|
NS_FOR_CSS_HALF_CORNERS(i) {
|
|
nsStyleCoord::SetValue(mUnits[i], mValues[i],
|
|
aCopy.mUnits[i], aCopy.mValues[i]);
|
|
}
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
bool
|
|
nsStyleCorners::operator==(const nsStyleCorners& aOther) const
|
|
{
|
|
NS_FOR_CSS_HALF_CORNERS(i) {
|
|
if (nsStyleCoord(mValues[i], (nsStyleUnit)mUnits[i]) !=
|
|
nsStyleCoord(aOther.mValues[i], (nsStyleUnit)aOther.mUnits[i])) {
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void nsStyleCorners::Reset()
|
|
{
|
|
NS_FOR_CSS_HALF_CORNERS(i) {
|
|
nsStyleCoord::Reset(mUnits[i], mValues[i]);
|
|
}
|
|
}
|
|
|
|
// Validation of SideIsVertical.
|
|
#define CASE(side, result) \
|
|
static_assert(SideIsVertical(side) == result, \
|
|
"SideIsVertical is wrong")
|
|
CASE(eSideTop, false);
|
|
CASE(eSideRight, true);
|
|
CASE(eSideBottom, false);
|
|
CASE(eSideLeft, true);
|
|
#undef CASE
|
|
|
|
// Validation of HalfCornerIsX.
|
|
#define CASE(corner, result) \
|
|
static_assert(HalfCornerIsX(corner) == result, \
|
|
"HalfCornerIsX is wrong")
|
|
CASE(eCornerTopLeftX, true);
|
|
CASE(eCornerTopLeftY, false);
|
|
CASE(eCornerTopRightX, true);
|
|
CASE(eCornerTopRightY, false);
|
|
CASE(eCornerBottomRightX, true);
|
|
CASE(eCornerBottomRightY, false);
|
|
CASE(eCornerBottomLeftX, true);
|
|
CASE(eCornerBottomLeftY, false);
|
|
#undef CASE
|
|
|
|
// Validation of HalfToFullCorner.
|
|
#define CASE(corner, result) \
|
|
static_assert(HalfToFullCorner(corner) == result, \
|
|
"HalfToFullCorner is wrong")
|
|
CASE(eCornerTopLeftX, eCornerTopLeft);
|
|
CASE(eCornerTopLeftY, eCornerTopLeft);
|
|
CASE(eCornerTopRightX, eCornerTopRight);
|
|
CASE(eCornerTopRightY, eCornerTopRight);
|
|
CASE(eCornerBottomRightX, eCornerBottomRight);
|
|
CASE(eCornerBottomRightY, eCornerBottomRight);
|
|
CASE(eCornerBottomLeftX, eCornerBottomLeft);
|
|
CASE(eCornerBottomLeftY, eCornerBottomLeft);
|
|
#undef CASE
|
|
|
|
// Validation of FullToHalfCorner.
|
|
#define CASE(corner, vert, result) \
|
|
static_assert(FullToHalfCorner(corner, vert) == result, \
|
|
"FullToHalfCorner is wrong")
|
|
CASE(eCornerTopLeft, false, eCornerTopLeftX);
|
|
CASE(eCornerTopLeft, true, eCornerTopLeftY);
|
|
CASE(eCornerTopRight, false, eCornerTopRightX);
|
|
CASE(eCornerTopRight, true, eCornerTopRightY);
|
|
CASE(eCornerBottomRight, false, eCornerBottomRightX);
|
|
CASE(eCornerBottomRight, true, eCornerBottomRightY);
|
|
CASE(eCornerBottomLeft, false, eCornerBottomLeftX);
|
|
CASE(eCornerBottomLeft, true, eCornerBottomLeftY);
|
|
#undef CASE
|
|
|
|
// Validation of SideToFullCorner.
|
|
#define CASE(side, second, result) \
|
|
static_assert(SideToFullCorner(side, second) == result, \
|
|
"SideToFullCorner is wrong")
|
|
CASE(eSideTop, false, eCornerTopLeft);
|
|
CASE(eSideTop, true, eCornerTopRight);
|
|
|
|
CASE(eSideRight, false, eCornerTopRight);
|
|
CASE(eSideRight, true, eCornerBottomRight);
|
|
|
|
CASE(eSideBottom, false, eCornerBottomRight);
|
|
CASE(eSideBottom, true, eCornerBottomLeft);
|
|
|
|
CASE(eSideLeft, false, eCornerBottomLeft);
|
|
CASE(eSideLeft, true, eCornerTopLeft);
|
|
#undef CASE
|
|
|
|
//Validation of SideToHalfCorner.
|
|
#define CASE(side, second, parallel, result) \
|
|
static_assert(SideToHalfCorner(side, second, parallel) == result, \
|
|
"SideToHalfCorner is wrong")
|
|
CASE(eSideTop, false, true, eCornerTopLeftX);
|
|
CASE(eSideTop, false, false, eCornerTopLeftY);
|
|
CASE(eSideTop, true, true, eCornerTopRightX);
|
|
CASE(eSideTop, true, false, eCornerTopRightY);
|
|
|
|
CASE(eSideRight, false, false, eCornerTopRightX);
|
|
CASE(eSideRight, false, true, eCornerTopRightY);
|
|
CASE(eSideRight, true, false, eCornerBottomRightX);
|
|
CASE(eSideRight, true, true, eCornerBottomRightY);
|
|
|
|
CASE(eSideBottom, false, true, eCornerBottomRightX);
|
|
CASE(eSideBottom, false, false, eCornerBottomRightY);
|
|
CASE(eSideBottom, true, true, eCornerBottomLeftX);
|
|
CASE(eSideBottom, true, false, eCornerBottomLeftY);
|
|
|
|
CASE(eSideLeft, false, false, eCornerBottomLeftX);
|
|
CASE(eSideLeft, false, true, eCornerBottomLeftY);
|
|
CASE(eSideLeft, true, false, eCornerTopLeftX);
|
|
CASE(eSideLeft, true, true, eCornerTopLeftY);
|
|
#undef CASE
|