1 /* 2 * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). 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 23 #include "core/html/HTMLMeterElement.h" 24 25 #include "HTMLNames.h" 26 #include "bindings/v8/ExceptionState.h" 27 #include "bindings/v8/ExceptionStatePlaceholder.h" 28 #include "core/dom/ExceptionCode.h" 29 #include "core/dom/NodeRenderingContext.h" 30 #include "core/dom/shadow/ShadowRoot.h" 31 #include "core/html/parser/HTMLParserIdioms.h" 32 #include "core/html/shadow/MeterShadowElement.h" 33 #include "core/page/Page.h" 34 #include "core/rendering/RenderMeter.h" 35 #include "core/rendering/RenderTheme.h" 36 37 namespace WebCore { 38 39 using namespace HTMLNames; 40 41 HTMLMeterElement::HTMLMeterElement(const QualifiedName& tagName, Document* document) 42 : LabelableElement(tagName, document) 43 { 44 ASSERT(hasTagName(meterTag)); 45 ScriptWrappable::init(this); 46 } 47 48 HTMLMeterElement::~HTMLMeterElement() 49 { 50 } 51 52 PassRefPtr<HTMLMeterElement> HTMLMeterElement::create(const QualifiedName& tagName, Document* document) 53 { 54 RefPtr<HTMLMeterElement> meter = adoptRef(new HTMLMeterElement(tagName, document)); 55 meter->ensureUserAgentShadowRoot(); 56 return meter; 57 } 58 59 RenderObject* HTMLMeterElement::createRenderer(RenderStyle* style) 60 { 61 if (hasAuthorShadowRoot() || !document()->page()->theme()->supportsMeter(style->appearance())) 62 return RenderObject::createObject(this, style); 63 64 return new RenderMeter(this); 65 } 66 67 void HTMLMeterElement::parseAttribute(const QualifiedName& name, const AtomicString& value) 68 { 69 if (name == valueAttr || name == minAttr || name == maxAttr || name == lowAttr || name == highAttr || name == optimumAttr) 70 didElementStateChange(); 71 else 72 LabelableElement::parseAttribute(name, value); 73 } 74 75 double HTMLMeterElement::min() const 76 { 77 return parseToDoubleForNumberType(getAttribute(minAttr), 0); 78 } 79 80 void HTMLMeterElement::setMin(double min, ExceptionState& es) 81 { 82 if (!std::isfinite(min)) { 83 es.throwDOMException(NotSupportedError); 84 return; 85 } 86 setAttribute(minAttr, String::number(min)); 87 } 88 89 double HTMLMeterElement::max() const 90 { 91 return std::max(parseToDoubleForNumberType(getAttribute(maxAttr), std::max(1.0, min())), min()); 92 } 93 94 void HTMLMeterElement::setMax(double max, ExceptionState& es) 95 { 96 if (!std::isfinite(max)) { 97 es.throwDOMException(NotSupportedError); 98 return; 99 } 100 setAttribute(maxAttr, String::number(max)); 101 } 102 103 double HTMLMeterElement::value() const 104 { 105 double value = parseToDoubleForNumberType(getAttribute(valueAttr), 0); 106 return std::min(std::max(value, min()), max()); 107 } 108 109 void HTMLMeterElement::setValue(double value, ExceptionState& es) 110 { 111 if (!std::isfinite(value)) { 112 es.throwDOMException(NotSupportedError); 113 return; 114 } 115 setAttribute(valueAttr, String::number(value)); 116 } 117 118 double HTMLMeterElement::low() const 119 { 120 double low = parseToDoubleForNumberType(getAttribute(lowAttr), min()); 121 return std::min(std::max(low, min()), max()); 122 } 123 124 void HTMLMeterElement::setLow(double low, ExceptionState& es) 125 { 126 if (!std::isfinite(low)) { 127 es.throwDOMException(NotSupportedError); 128 return; 129 } 130 setAttribute(lowAttr, String::number(low)); 131 } 132 133 double HTMLMeterElement::high() const 134 { 135 double high = parseToDoubleForNumberType(getAttribute(highAttr), max()); 136 return std::min(std::max(high, low()), max()); 137 } 138 139 void HTMLMeterElement::setHigh(double high, ExceptionState& es) 140 { 141 if (!std::isfinite(high)) { 142 es.throwDOMException(NotSupportedError); 143 return; 144 } 145 setAttribute(highAttr, String::number(high)); 146 } 147 148 double HTMLMeterElement::optimum() const 149 { 150 double optimum = parseToDoubleForNumberType(getAttribute(optimumAttr), (max() + min()) / 2); 151 return std::min(std::max(optimum, min()), max()); 152 } 153 154 void HTMLMeterElement::setOptimum(double optimum, ExceptionState& es) 155 { 156 if (!std::isfinite(optimum)) { 157 es.throwDOMException(NotSupportedError); 158 return; 159 } 160 setAttribute(optimumAttr, String::number(optimum)); 161 } 162 163 HTMLMeterElement::GaugeRegion HTMLMeterElement::gaugeRegion() const 164 { 165 double lowValue = low(); 166 double highValue = high(); 167 double theValue = value(); 168 double optimumValue = optimum(); 169 170 if (optimumValue < lowValue) { 171 // The optimum range stays under low 172 if (theValue <= lowValue) 173 return GaugeRegionOptimum; 174 if (theValue <= highValue) 175 return GaugeRegionSuboptimal; 176 return GaugeRegionEvenLessGood; 177 } 178 179 if (highValue < optimumValue) { 180 // The optimum range stays over high 181 if (highValue <= theValue) 182 return GaugeRegionOptimum; 183 if (lowValue <= theValue) 184 return GaugeRegionSuboptimal; 185 return GaugeRegionEvenLessGood; 186 } 187 188 // The optimum range stays between high and low. 189 // According to the standard, <meter> never show GaugeRegionEvenLessGood in this case 190 // because the value is never less or greater than min or max. 191 if (lowValue <= theValue && theValue <= highValue) 192 return GaugeRegionOptimum; 193 return GaugeRegionSuboptimal; 194 } 195 196 double HTMLMeterElement::valueRatio() const 197 { 198 double min = this->min(); 199 double max = this->max(); 200 double value = this->value(); 201 202 if (max <= min) 203 return 0; 204 return (value - min) / (max - min); 205 } 206 207 void HTMLMeterElement::didElementStateChange() 208 { 209 m_value->setWidthPercentage(valueRatio()*100); 210 m_value->updatePseudo(); 211 if (RenderMeter* render = renderMeter()) 212 render->updateFromElement(); 213 } 214 215 RenderMeter* HTMLMeterElement::renderMeter() const 216 { 217 if (renderer() && renderer()->isMeter()) 218 return static_cast<RenderMeter*>(renderer()); 219 220 RenderObject* renderObject = userAgentShadowRoot()->firstChild()->renderer(); 221 ASSERT(!renderObject || renderObject->isMeter()); 222 return static_cast<RenderMeter*>(renderObject); 223 } 224 225 void HTMLMeterElement::didAddUserAgentShadowRoot(ShadowRoot* root) 226 { 227 ASSERT(!m_value); 228 229 RefPtr<MeterInnerElement> inner = MeterInnerElement::create(document()); 230 root->appendChild(inner); 231 232 RefPtr<MeterBarElement> bar = MeterBarElement::create(document()); 233 m_value = MeterValueElement::create(document()); 234 m_value->setWidthPercentage(0); 235 m_value->updatePseudo(); 236 bar->appendChild(m_value, ASSERT_NO_EXCEPTION); 237 238 inner->appendChild(bar, ASSERT_NO_EXCEPTION); 239 } 240 241 } // namespace 242