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/HTMLProgressElement.h"
24
25#include "HTMLNames.h"
26#include "bindings/v8/ExceptionMessages.h"
27#include "bindings/v8/ExceptionState.h"
28#include "bindings/v8/ExceptionStatePlaceholder.h"
29#include "core/dom/ExceptionCode.h"
30#include "core/dom/shadow/ShadowRoot.h"
31#include "core/html/parser/HTMLParserIdioms.h"
32#include "core/html/shadow/ProgressShadowElement.h"
33#include "core/rendering/RenderProgress.h"
34
35namespace WebCore {
36
37using namespace HTMLNames;
38
39const double HTMLProgressElement::IndeterminatePosition = -1;
40const double HTMLProgressElement::InvalidPosition = -2;
41
42HTMLProgressElement::HTMLProgressElement(Document& document)
43    : LabelableElement(progressTag, document)
44    , m_value(0)
45{
46    ScriptWrappable::init(this);
47}
48
49HTMLProgressElement::~HTMLProgressElement()
50{
51}
52
53PassRefPtr<HTMLProgressElement> HTMLProgressElement::create(Document& document)
54{
55    RefPtr<HTMLProgressElement> progress = adoptRef(new HTMLProgressElement(document));
56    progress->ensureUserAgentShadowRoot();
57    return progress.release();
58}
59
60RenderObject* HTMLProgressElement::createRenderer(RenderStyle* style)
61{
62    if (!style->hasAppearance() || hasAuthorShadowRoot())
63        return RenderObject::createObject(this, style);
64
65    return new RenderProgress(this);
66}
67
68RenderProgress* HTMLProgressElement::renderProgress() const
69{
70    if (renderer() && renderer()->isProgress())
71        return toRenderProgress(renderer());
72
73    RenderObject* renderObject = userAgentShadowRoot()->firstChild()->renderer();
74    ASSERT_WITH_SECURITY_IMPLICATION(!renderObject || renderObject->isProgress());
75    return toRenderProgress(renderObject);
76}
77
78void HTMLProgressElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
79{
80    if (name == valueAttr)
81        didElementStateChange();
82    else if (name == maxAttr)
83        didElementStateChange();
84    else
85        LabelableElement::parseAttribute(name, value);
86}
87
88void HTMLProgressElement::attach(const AttachContext& context)
89{
90    LabelableElement::attach(context);
91    if (RenderProgress* render = renderProgress())
92        render->updateFromElement();
93}
94
95double HTMLProgressElement::value() const
96{
97    double value = getFloatingPointAttribute(valueAttr);
98    return !std::isfinite(value) || value < 0 ? 0 : std::min(value, max());
99}
100
101void HTMLProgressElement::setValue(double value, ExceptionState& exceptionState)
102{
103    if (!std::isfinite(value)) {
104        exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::notAFiniteNumber(value));
105        return;
106    }
107    setFloatingPointAttribute(valueAttr, std::max(value, 0.));
108}
109
110double HTMLProgressElement::max() const
111{
112    double max = getFloatingPointAttribute(maxAttr);
113    return !std::isfinite(max) || max <= 0 ? 1 : max;
114}
115
116void HTMLProgressElement::setMax(double max, ExceptionState& exceptionState)
117{
118    if (!std::isfinite(max)) {
119        exceptionState.throwDOMException(NotSupportedError, ExceptionMessages::notAFiniteNumber(max));
120        return;
121    }
122    // FIXME: The specification says we should ignore the input value if it is inferior or equal to 0.
123    setFloatingPointAttribute(maxAttr, max > 0 ? max : 1);
124}
125
126double HTMLProgressElement::position() const
127{
128    if (!isDeterminate())
129        return HTMLProgressElement::IndeterminatePosition;
130    return value() / max();
131}
132
133bool HTMLProgressElement::isDeterminate() const
134{
135    return fastHasAttribute(valueAttr);
136}
137
138void HTMLProgressElement::didElementStateChange()
139{
140    m_value->setWidthPercentage(position() * 100);
141    if (RenderProgress* render = renderProgress()) {
142        bool wasDeterminate = render->isDeterminate();
143        render->updateFromElement();
144        if (wasDeterminate != isDeterminate())
145            didAffectSelector(AffectedSelectorIndeterminate);
146    }
147}
148
149void HTMLProgressElement::didAddUserAgentShadowRoot(ShadowRoot& root)
150{
151    ASSERT(!m_value);
152
153    RefPtr<ProgressInnerElement> inner = ProgressInnerElement::create(document());
154    inner->setPseudo(AtomicString("-webkit-progress-inner-element", AtomicString::ConstructFromLiteral));
155    root.appendChild(inner);
156
157    RefPtr<ProgressBarElement> bar = ProgressBarElement::create(document());
158    bar->setPseudo(AtomicString("-webkit-progress-bar", AtomicString::ConstructFromLiteral));
159    RefPtr<ProgressValueElement> value = ProgressValueElement::create(document());
160    m_value = value.get();
161    m_value->setPseudo(AtomicString("-webkit-progress-value", AtomicString::ConstructFromLiteral));
162    m_value->setWidthPercentage(HTMLProgressElement::IndeterminatePosition * 100);
163    bar->appendChild(m_value);
164
165    inner->appendChild(bar);
166}
167
168bool HTMLProgressElement::shouldAppearIndeterminate() const
169{
170    return !isDeterminate();
171}
172
173} // namespace
174