1/* 2 * Copyright (C) 2006, 2007, 2008, 2009, 2010 Apple Inc. All rights reserved. 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Library General Public 6 * License as published by the Free Software Foundation; either 7 * version 2 of the License, or (at your option) any later version. 8 * 9 * This library is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * Library General Public License for more details. 13 * 14 * You should have received a copy of the GNU Library General Public License 15 * along with this library; see the file COPYING.LIB. If not, write to 16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 17 * Boston, MA 02110-1301, USA. 18 * 19 */ 20 21#include "config.h" 22#include "StepRange.h" 23 24#include "HTMLNames.h" 25#include "HTMLParserIdioms.h" 26#include <wtf/MathExtras.h> 27#include <wtf/text/WTFString.h> 28 29using namespace std; 30 31namespace WebCore { 32 33using namespace HTMLNames; 34 35StepRange::StepRange() 36 : m_maximum(100) 37 , m_minimum(0) 38 , m_step(1) 39 , m_stepBase(0) 40 , m_hasStep(false) 41{ 42} 43 44StepRange::StepRange(const StepRange& stepRange) 45 : m_maximum(stepRange.m_maximum) 46 , m_minimum(stepRange.m_minimum) 47 , m_step(stepRange.m_step) 48 , m_stepBase(stepRange.m_stepBase) 49 , m_stepDescription(stepRange.m_stepDescription) 50 , m_hasStep(stepRange.m_hasStep) 51{ 52} 53 54StepRange::StepRange(const Decimal& stepBase, const Decimal& minimum, const Decimal& maximum, const Decimal& step, const StepDescription& stepDescription) 55 : m_maximum(maximum) 56 , m_minimum(minimum) 57 , m_step(step.isFinite() ? step : 1) 58 , m_stepBase(stepBase.isFinite() ? stepBase : 1) 59 , m_stepDescription(stepDescription) 60 , m_hasStep(step.isFinite()) 61{ 62 ASSERT(m_maximum.isFinite()); 63 ASSERT(m_minimum.isFinite()); 64 ASSERT(m_step.isFinite()); 65 ASSERT(m_stepBase.isFinite()); 66} 67 68Decimal StepRange::acceptableError() const 69{ 70 // FIXME: We should use DBL_MANT_DIG instead of FLT_MANT_DIG regarding to HTML5 specification. 71 DEFINE_STATIC_LOCAL(const Decimal, twoPowerOfFloatMantissaBits, (Decimal::Positive, 0, UINT64_C(1) << FLT_MANT_DIG)); 72 return m_stepDescription.stepValueShouldBe == StepValueShouldBeReal ? m_step / twoPowerOfFloatMantissaBits : Decimal(0); 73} 74 75Decimal StepRange::alignValueForStep(const Decimal& currentValue, const Decimal& newValue) const 76{ 77 DEFINE_STATIC_LOCAL(const Decimal, tenPowerOf21, (Decimal::Positive, 21, 1)); 78 if (newValue >= tenPowerOf21) 79 return newValue; 80 81 return stepMismatch(currentValue) ? newValue : roundByStep(newValue, m_stepBase); 82} 83 84Decimal StepRange::clampValue(const Decimal& value) const 85{ 86 const Decimal inRangeValue = max(m_minimum, min(value, m_maximum)); 87 if (!m_hasStep) 88 return inRangeValue; 89 // Rounds inRangeValue to minimum + N * step. 90 const Decimal roundedValue = roundByStep(inRangeValue, m_minimum); 91 const Decimal clampedValue = roundedValue > m_maximum ? roundedValue - m_step : roundedValue; 92 ASSERT(clampedValue >= m_minimum); 93 ASSERT(clampedValue <= m_maximum); 94 return clampedValue; 95} 96 97Decimal StepRange::parseStep(AnyStepHandling anyStepHandling, const StepDescription& stepDescription, const String& stepString) 98{ 99 if (stepString.isEmpty()) 100 return stepDescription.defaultValue(); 101 102 if (equalIgnoringCase(stepString, "any")) { 103 switch (anyStepHandling) { 104 case RejectAny: 105 return Decimal::nan(); 106 case AnyIsDefaultStep: 107 return stepDescription.defaultValue(); 108 default: 109 ASSERT_NOT_REACHED(); 110 } 111 } 112 113 Decimal step = parseToDecimalForNumberType(stepString); 114 if (!step.isFinite() || step <= 0) 115 return stepDescription.defaultValue(); 116 117 switch (stepDescription.stepValueShouldBe) { 118 case StepValueShouldBeReal: 119 step *= stepDescription.stepScaleFactor; 120 break; 121 case ParsedStepValueShouldBeInteger: 122 // For date, month, and week, the parsed value should be an integer for some types. 123 step = max(step.round(), Decimal(1)); 124 step *= stepDescription.stepScaleFactor; 125 break; 126 case ScaledStepValueShouldBeInteger: 127 // For datetime, datetime-local, time, the result should be an integer. 128 step *= stepDescription.stepScaleFactor; 129 step = max(step.round(), Decimal(1)); 130 break; 131 default: 132 ASSERT_NOT_REACHED(); 133 } 134 135 ASSERT(step > 0); 136 return step; 137} 138 139Decimal StepRange::roundByStep(const Decimal& value, const Decimal& base) const 140{ 141 return base + ((value - base) / m_step).round() * m_step; 142} 143 144bool StepRange::stepMismatch(const Decimal& valueForCheck) const 145{ 146 if (!m_hasStep) 147 return false; 148 if (!valueForCheck.isFinite()) 149 return false; 150 const Decimal value = (valueForCheck - m_stepBase).abs(); 151 if (!value.isFinite()) 152 return false; 153 // Decimal's fractional part size is DBL_MAN_DIG-bit. If the current value 154 // is greater than step*2^DBL_MANT_DIG, the following computation for 155 // remainder makes no sense. 156 DEFINE_STATIC_LOCAL(const Decimal, twoPowerOfDoubleMantissaBits, (Decimal::Positive, 0, UINT64_C(1) << DBL_MANT_DIG)); 157 if (value / twoPowerOfDoubleMantissaBits > m_step) 158 return false; 159 // The computation follows HTML5 4.10.7.2.10 `The step attribute' : 160 // ... that number subtracted from the step base is not an integral multiple 161 // of the allowed value step, the element is suffering from a step mismatch. 162 const Decimal remainder = (value - m_step * (value / m_step).round()).abs(); 163 // Accepts erros in lower fractional part which IEEE 754 single-precision 164 // can't represent. 165 const Decimal computedAcceptableError = acceptableError(); 166 return computedAcceptableError < remainder && remainder < (m_step - computedAcceptableError); 167} 168 169} // namespace WebCore 170