1/* 2 Copyright (C) 1999 Lars Knoll (knoll@kde.org) 3 Copyright (C) 2006, 2008, 2014 Apple Inc. All rights reserved. 4 Copyright (C) 2011 Rik Cabanier (cabanier@adobe.com) 5 Copyright (C) 2011 Adobe Systems Incorporated. All rights reserved. 6 7 This library is free software; you can redistribute it and/or 8 modify it under the terms of the GNU Library General Public 9 License as published by the Free Software Foundation; either 10 version 2 of the License, or (at your option) any later version. 11 12 This library is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 Library General Public License for more details. 16 17 You should have received a copy of the GNU Library General Public License 18 along with this library; see the file COPYING.LIB. If not, write to 19 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 20 Boston, MA 02110-1301, USA. 21*/ 22 23#ifndef Length_h 24#define Length_h 25 26#include "AnimationUtilities.h" 27#include <memory> 28#include <string.h> 29#include <wtf/Assertions.h> 30#include <wtf/FastMalloc.h> 31#include <wtf/Forward.h> 32 33namespace WebCore { 34 35enum LengthType { 36 Auto, Relative, Percent, Fixed, 37 Intrinsic, MinIntrinsic, 38 MinContent, MaxContent, FillAvailable, FitContent, 39 Calculated, 40 Undefined 41}; 42 43class CalculationValue; 44 45struct Length { 46 WTF_MAKE_FAST_ALLOCATED; 47public: 48 Length(LengthType = Auto); 49 50 Length(int value, LengthType, bool hasQuirk = false); 51 Length(LayoutUnit value, LengthType, bool hasQuirk = false); 52 Length(float value, LengthType, bool hasQuirk = false); 53 Length(double value, LengthType, bool hasQuirk = false); 54 55 explicit Length(PassRef<CalculationValue>); 56 57 Length(const Length&); 58 Length(Length&&); 59 Length& operator=(const Length&); 60 Length& operator=(Length&&); 61 62 ~Length(); 63 64 void setValue(LengthType, int value); 65 void setValue(LengthType, float value); 66 void setValue(LengthType, LayoutUnit value); 67 Length& operator*=(float); 68 69 void setHasQuirk(bool); 70 71 bool operator==(const Length&) const; 72 bool operator!=(const Length&) const; 73 74 float value() const; 75 int intValue() const; 76 float percent() const; 77 float viewportPercentageLength() const; 78 CalculationValue& calculationValue() const; 79 80 LengthType type() const; 81 82 bool isAuto() const; 83 bool isCalculated() const; 84 bool isFixed() const; 85 bool isMaxContent() const; 86 bool isMinContent() const; 87 bool isPercentNotCalculated() const; // FIXME: Rename to isPercent. 88 bool isRelative() const; 89 bool isUndefined() const; 90 91 bool hasQuirk() const; 92 93 // FIXME calc: https://bugs.webkit.org/show_bug.cgi?id=80357. A calculated Length 94 // always contains a percentage, and without a maxValue passed to these functions 95 // it's impossible to determine the sign or zero-ness. The following three functions 96 // act as if all calculated values are positive. 97 bool isZero() const; 98 bool isPositive() const; 99 bool isNegative() const; 100 101 bool isPercent() const; // Returns true for both Percent and Calculated. FIXME: Find a better name for this. 102 103 bool isIntrinsic() const; 104 bool isIntrinsicOrAuto() const; 105 bool isSpecified() const; 106 bool isSpecifiedOrIntrinsic() const; 107 108 // Blend two lengths to produce a new length that is in between them. Used for animation. 109 // FIXME: Why is this a member function? 110 Length blend(const Length& from, double progress) const; 111 112 float nonNanCalculatedValue(int maxValue) const; 113 114private: 115 bool isLegacyIntrinsic() const; 116 117 bool isCalculatedEqual(const Length&) const; 118 Length blendMixedTypes(const Length& from, double progress) const; 119 120 void ref() const; 121 void deref() const; 122 123 union { 124 int m_intValue; 125 float m_floatValue; 126 unsigned m_calculationValueHandle; 127 }; 128 bool m_hasQuirk; 129 unsigned char m_type; 130 bool m_isFloat; 131}; 132 133std::unique_ptr<Length[]> newCoordsArray(const String&, int& length); 134std::unique_ptr<Length[]> newLengthArray(const String&, int& length); 135 136inline Length::Length(LengthType type) 137 : m_intValue(0), m_hasQuirk(false), m_type(type), m_isFloat(false) 138{ 139 ASSERT(type != Calculated); 140} 141 142inline Length::Length(int value, LengthType type, bool hasQuirk) 143 : m_intValue(value), m_hasQuirk(hasQuirk), m_type(type), m_isFloat(false) 144{ 145 ASSERT(type != Calculated); 146} 147 148inline Length::Length(LayoutUnit value, LengthType type, bool hasQuirk) 149 : m_floatValue(value.toFloat()), m_hasQuirk(hasQuirk), m_type(type), m_isFloat(true) 150{ 151 ASSERT(type != Calculated); 152} 153 154inline Length::Length(float value, LengthType type, bool hasQuirk) 155 : m_floatValue(value), m_hasQuirk(hasQuirk), m_type(type), m_isFloat(true) 156{ 157 ASSERT(type != Calculated); 158} 159 160inline Length::Length(double value, LengthType type, bool hasQuirk) 161 : m_floatValue(static_cast<float>(value)), m_hasQuirk(hasQuirk), m_type(type), m_isFloat(true) 162{ 163 ASSERT(type != Calculated); 164} 165 166inline Length::Length(const Length& other) 167{ 168 if (other.isCalculated()) 169 other.ref(); 170 171 memcpy(this, &other, sizeof(Length)); 172} 173 174inline Length::Length(Length&& other) 175{ 176 memcpy(this, &other, sizeof(Length)); 177 other.m_type = Auto; 178} 179 180inline Length& Length::operator=(const Length& other) 181{ 182 if (other.isCalculated()) 183 other.ref(); 184 if (isCalculated()) 185 deref(); 186 187 memcpy(this, &other, sizeof(Length)); 188 return *this; 189} 190 191inline Length& Length::operator=(Length&& other) 192{ 193 if (this == &other) 194 return *this; 195 196 if (isCalculated()) 197 deref(); 198 199 memcpy(this, &other, sizeof(Length)); 200 other.m_type = Auto; 201 return *this; 202} 203 204inline Length::~Length() 205{ 206 if (isCalculated()) 207 deref(); 208} 209 210inline bool Length::operator==(const Length& other) const 211{ 212 // FIXME: This might be too long to be inline. 213 if (type() != other.type() || hasQuirk() != other.hasQuirk()) 214 return false; 215 if (isUndefined()) 216 return true; 217 if (isCalculated()) 218 return isCalculatedEqual(other); 219 return value() == other.value(); 220} 221 222inline bool Length::operator!=(const Length& other) const 223{ 224 return !(*this == other); 225} 226 227inline Length& Length::operator*=(float value) 228{ 229 ASSERT(!isCalculated()); 230 if (isCalculated()) 231 return *this; 232 233 if (m_isFloat) 234 m_floatValue *= value; 235 else 236 m_intValue *= value; 237 238 return *this; 239} 240 241inline float Length::value() const 242{ 243 ASSERT(!isUndefined()); 244 ASSERT(!isCalculated()); 245 return m_isFloat ? m_floatValue : m_intValue; 246} 247 248inline int Length::intValue() const 249{ 250 ASSERT(!isUndefined()); 251 ASSERT(!isCalculated()); 252 // FIXME: Makes no sense to return 0 here but not in the value() function above. 253 if (isCalculated()) 254 return 0; 255 return m_isFloat ? static_cast<int>(m_floatValue) : m_intValue; 256} 257 258inline float Length::percent() const 259{ 260 ASSERT(isPercentNotCalculated()); 261 return value(); 262} 263 264inline LengthType Length::type() const 265{ 266 return static_cast<LengthType>(m_type); 267} 268 269inline bool Length::hasQuirk() const 270{ 271 return m_hasQuirk; 272} 273 274inline void Length::setHasQuirk(bool hasQuirk) 275{ 276 m_hasQuirk = hasQuirk; 277} 278 279inline void Length::setValue(LengthType type, int value) 280{ 281 ASSERT(m_type != Calculated); 282 ASSERT(type != Calculated); 283 m_type = type; 284 m_intValue = value; 285 m_isFloat = false; 286} 287 288inline void Length::setValue(LengthType type, float value) 289{ 290 ASSERT(m_type != Calculated); 291 ASSERT(type != Calculated); 292 m_type = type; 293 m_floatValue = value; 294 m_isFloat = true; 295} 296 297inline void Length::setValue(LengthType type, LayoutUnit value) 298{ 299 ASSERT(m_type != Calculated); 300 ASSERT(type != Calculated); 301 m_type = type; 302 m_floatValue = value; 303 m_isFloat = true; 304} 305 306inline bool Length::isAuto() const 307{ 308 return type() == Auto; 309} 310 311inline bool Length::isFixed() const 312{ 313 return type() == Fixed; 314} 315 316inline bool Length::isMaxContent() const 317{ 318 return type() == MaxContent; 319} 320 321inline bool Length::isMinContent() const 322{ 323 return type() == MinContent; 324} 325 326inline bool Length::isNegative() const 327{ 328 if (isUndefined() || isCalculated()) 329 return false; 330 return m_isFloat ? (m_floatValue < 0) : (m_intValue < 0); 331} 332 333inline bool Length::isPercentNotCalculated() const 334{ 335 return type() == Percent; 336} 337 338inline bool Length::isRelative() const 339{ 340 return type() == Relative; 341} 342 343inline bool Length::isUndefined() const 344{ 345 return type() == Undefined; 346} 347 348inline bool Length::isPercent() const 349{ 350 return isPercentNotCalculated() || isCalculated(); 351} 352 353inline bool Length::isPositive() const 354{ 355 if (isUndefined()) 356 return false; 357 if (isCalculated()) 358 return true; 359 return m_isFloat ? (m_floatValue > 0) : (m_intValue > 0); 360} 361 362inline bool Length::isZero() const 363{ 364 ASSERT(!isUndefined()); 365 if (isCalculated()) 366 return false; 367 return m_isFloat ? !m_floatValue : !m_intValue; 368} 369 370inline bool Length::isCalculated() const 371{ 372 return type() == Calculated; 373} 374 375inline bool Length::isLegacyIntrinsic() const 376{ 377 return type() == Intrinsic || type() == MinIntrinsic; 378} 379 380inline bool Length::isIntrinsic() const 381{ 382 return type() == MinContent || type() == MaxContent || type() == FillAvailable || type() == FitContent; 383} 384 385inline bool Length::isIntrinsicOrAuto() const 386{ 387 return isAuto() || isIntrinsic() || isLegacyIntrinsic(); 388} 389 390inline bool Length::isSpecified() const 391{ 392 return isFixed() || isPercent(); 393} 394 395inline bool Length::isSpecifiedOrIntrinsic() const 396{ 397 return isSpecified() || isIntrinsic(); 398} 399 400// FIXME: Does this need to be in the header? Is it valuable to inline? Does it get inlined? 401inline Length Length::blend(const Length& from, double progress) const 402{ 403 if (from.type() == Calculated || type() == Calculated) 404 return blendMixedTypes(from, progress); 405 406 if (!from.isZero() && !isZero() && from.type() != type()) 407 return blendMixedTypes(from, progress); 408 409 if (from.isZero() && isZero()) 410 return *this; 411 412 LengthType resultType = type(); 413 if (isZero()) 414 resultType = from.type(); 415 416 if (resultType == Percent) { 417 float fromPercent = from.isZero() ? 0 : from.percent(); 418 float toPercent = isZero() ? 0 : percent(); 419 return Length(WebCore::blend(fromPercent, toPercent, progress), Percent); 420 } 421 422 float fromValue = from.isZero() ? 0 : from.value(); 423 float toValue = isZero() ? 0 : value(); 424 return Length(WebCore::blend(fromValue, toValue, progress), resultType); 425} 426 427} // namespace WebCore 428 429#endif // Length_h 430