Home | History | Annotate | Download | only in platform
      1 /*
      2  * Copyright (C) 1999 Lars Knoll (knoll (at) kde.org)
      3  *           (C) 1999 Antti Koivisto (koivisto (at) kde.org)
      4  *           (C) 2001 Dirk Mueller ( mueller (at) kde.org )
      5  * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
      6  * Copyright (C) 2006 Andrew Wellington (proton (at) wiretapped.net)
      7  *
      8  * This library is free software; you can redistribute it and/or
      9  * modify it under the terms of the GNU Library General Public
     10  * License as published by the Free Software Foundation; either
     11  * version 2 of the License, or (at your option) any later version.
     12  *
     13  * This library is distributed in the hope that it will be useful,
     14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     16  * Library General Public License for more details.
     17  *
     18  * You should have received a copy of the GNU Library General Public License
     19  * along with this library; see the file COPYING.LIB.  If not, write to
     20  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     21  * Boston, MA 02110-1301, USA.
     22  *
     23  */
     24 
     25 #include "config.h"
     26 #include "platform/Length.h"
     27 
     28 #include "platform/CalculationValue.h"
     29 #include "platform/animation/AnimationUtilities.h"
     30 #include "wtf/ASCIICType.h"
     31 #include "wtf/text/StringBuffer.h"
     32 #include "wtf/text/WTFString.h"
     33 
     34 using namespace WTF;
     35 
     36 namespace blink {
     37 
     38 template<typename CharType>
     39 static unsigned splitLength(const CharType* data, unsigned length, unsigned& intLength, unsigned& doubleLength)
     40 {
     41     ASSERT(length);
     42 
     43     unsigned i = 0;
     44     while (i < length && isSpaceOrNewline(data[i]))
     45         ++i;
     46     if (i < length && (data[i] == '+' || data[i] == '-'))
     47         ++i;
     48     while (i < length && isASCIIDigit(data[i]))
     49         ++i;
     50     intLength = i;
     51     while (i < length && (isASCIIDigit(data[i]) || data[i] == '.'))
     52         ++i;
     53     doubleLength = i;
     54 
     55     // IE quirk: Skip whitespace between the number and the % character (20 % => 20%).
     56     while (i < length && isSpaceOrNewline(data[i]))
     57         ++i;
     58 
     59     return i;
     60 }
     61 
     62 template<typename CharType>
     63 static Length parseHTMLAreaCoordinate(const CharType* data, unsigned length)
     64 {
     65     unsigned intLength;
     66     unsigned doubleLength;
     67     splitLength(data, length, intLength, doubleLength);
     68 
     69     bool ok;
     70     int r = charactersToIntStrict(data, intLength, &ok);
     71     if (ok)
     72         return Length(r, Fixed);
     73     return Length(0, Fixed);
     74 }
     75 
     76 // FIXME: Per HTML5, this should follow the "rules for parsing a list of integers".
     77 Vector<Length> parseHTMLAreaElementCoords(const String& string)
     78 {
     79     unsigned length = string.length();
     80     StringBuffer<LChar> spacified(length);
     81     for (unsigned i = 0; i < length; i++) {
     82         UChar cc = string[i];
     83         if (cc > '9' || (cc < '0' && cc != '-' && cc != '.'))
     84             spacified[i] = ' ';
     85         else
     86             spacified[i] = cc;
     87     }
     88     RefPtr<StringImpl> str = spacified.release();
     89     str = str->simplifyWhiteSpace();
     90     ASSERT(str->is8Bit());
     91 
     92     if (!str->length())
     93         return Vector<Length>();
     94 
     95     unsigned len = str->count(' ') + 1;
     96     Vector<Length> r(len);
     97 
     98     unsigned i = 0;
     99     unsigned pos = 0;
    100     size_t pos2;
    101 
    102     while ((pos2 = str->find(' ', pos)) != kNotFound) {
    103         r[i++] = parseHTMLAreaCoordinate(str->characters8() + pos, pos2 - pos);
    104         pos = pos2 + 1;
    105     }
    106     r[i] = parseHTMLAreaCoordinate(str->characters8() + pos, str->length() - pos);
    107 
    108     ASSERT(i == len - 1);
    109 
    110     return r;
    111 }
    112 
    113 class CalculationValueHandleMap {
    114     WTF_MAKE_FAST_ALLOCATED;
    115 public:
    116     CalculationValueHandleMap()
    117         : m_index(1)
    118     {
    119     }
    120 
    121     int insert(PassRefPtr<CalculationValue> calcValue)
    122     {
    123         ASSERT(m_index);
    124         // FIXME calc(): https://bugs.webkit.org/show_bug.cgi?id=80489
    125         // This monotonically increasing handle generation scheme is potentially wasteful
    126         // of the handle space. Consider reusing empty handles.
    127         while (m_map.contains(m_index))
    128             m_index++;
    129 
    130         m_map.set(m_index, calcValue);
    131 
    132         return m_index;
    133     }
    134 
    135     void remove(int index)
    136     {
    137         ASSERT(m_map.contains(index));
    138         m_map.remove(index);
    139     }
    140 
    141     CalculationValue& get(int index)
    142     {
    143         ASSERT(m_map.contains(index));
    144         return *m_map.get(index);
    145     }
    146 
    147     void decrementRef(int index)
    148     {
    149         ASSERT(m_map.contains(index));
    150         CalculationValue* value = m_map.get(index);
    151         if (value->hasOneRef()) {
    152             // Force the CalculationValue destructor early to avoid a potential recursive call inside HashMap remove().
    153             m_map.set(index, nullptr);
    154             m_map.remove(index);
    155         } else {
    156             value->deref();
    157         }
    158     }
    159 
    160 private:
    161     int m_index;
    162     HashMap<int, RefPtr<CalculationValue> > m_map;
    163 };
    164 
    165 static CalculationValueHandleMap& calcHandles()
    166 {
    167     DEFINE_STATIC_LOCAL(CalculationValueHandleMap, handleMap, ());
    168     return handleMap;
    169 }
    170 
    171 Length::Length(PassRefPtr<CalculationValue> calc)
    172     : m_quirk(false)
    173     , m_type(Calculated)
    174     , m_isFloat(false)
    175 {
    176     m_intValue = calcHandles().insert(calc);
    177 }
    178 
    179 Length Length::blendMixedTypes(const Length& from, double progress, ValueRange range) const
    180 {
    181     ASSERT(from.isSpecified());
    182     ASSERT(isSpecified());
    183     PixelsAndPercent fromPixelsAndPercent = from.pixelsAndPercent();
    184     PixelsAndPercent toPixelsAndPercent = pixelsAndPercent();
    185     const float pixels = blink::blend(fromPixelsAndPercent.pixels, toPixelsAndPercent.pixels, progress);
    186     const float percent = blink::blend(fromPixelsAndPercent.percent, toPixelsAndPercent.percent, progress);
    187     return Length(CalculationValue::create(PixelsAndPercent(pixels, percent), range));
    188 }
    189 
    190 PixelsAndPercent Length::pixelsAndPercent() const
    191 {
    192     switch (type()) {
    193     case Fixed:
    194         return PixelsAndPercent(value(), 0);
    195     case Percent:
    196         return PixelsAndPercent(0, value());
    197     case Calculated:
    198         return calculationValue().pixelsAndPercent();
    199     default:
    200         ASSERT_NOT_REACHED();
    201         return PixelsAndPercent(0, 0);
    202     }
    203 }
    204 
    205 Length Length::subtractFromOneHundredPercent() const
    206 {
    207     PixelsAndPercent result = pixelsAndPercent();
    208     result.pixels = -result.pixels;
    209     result.percent = 100 - result.percent;
    210     if (result.pixels && result.percent)
    211         return Length(CalculationValue::create(result, ValueRangeAll));
    212     if (result.percent)
    213         return Length(result.percent, Percent);
    214     return Length(result.pixels, Fixed);
    215 }
    216 
    217 CalculationValue& Length::calculationValue() const
    218 {
    219     ASSERT(isCalculated());
    220     return calcHandles().get(calculationHandle());
    221 }
    222 
    223 void Length::incrementCalculatedRef() const
    224 {
    225     ASSERT(isCalculated());
    226     calculationValue().ref();
    227 }
    228 
    229 void Length::decrementCalculatedRef() const
    230 {
    231     ASSERT(isCalculated());
    232     calcHandles().decrementRef(calculationHandle());
    233 }
    234 
    235 float Length::nonNanCalculatedValue(int maxValue) const
    236 {
    237     ASSERT(isCalculated());
    238     float result = calculationValue().evaluate(maxValue);
    239     if (std::isnan(result))
    240         return 0;
    241     return result;
    242 }
    243 
    244 bool Length::isCalculatedEqual(const Length& o) const
    245 {
    246     return isCalculated() && (&calculationValue() == &o.calculationValue() || calculationValue() == o.calculationValue());
    247 }
    248 
    249 struct SameSizeAsLength {
    250     int32_t value;
    251     int32_t metaData;
    252 };
    253 COMPILE_ASSERT(sizeof(Length) == sizeof(SameSizeAsLength), length_should_stay_small);
    254 
    255 } // namespace blink
    256