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#include "HTMLProgressElement.h"
23
24#include "Attribute.h"
25#include "ElementIterator.h"
26#include "EventNames.h"
27#include "ExceptionCode.h"
28#include "HTMLNames.h"
29#include "HTMLParserIdioms.h"
30#include "ProgressShadowElement.h"
31#include "RenderProgress.h"
32#include "ShadowRoot.h"
33
34namespace WebCore {
35
36using namespace HTMLNames;
37
38const double HTMLProgressElement::IndeterminatePosition = -1;
39const double HTMLProgressElement::InvalidPosition = -2;
40
41HTMLProgressElement::HTMLProgressElement(const QualifiedName& tagName, Document& document)
42    : LabelableElement(tagName, document)
43    , m_value(0)
44{
45    ASSERT(hasTagName(progressTag));
46    setHasCustomStyleResolveCallbacks();
47}
48
49HTMLProgressElement::~HTMLProgressElement()
50{
51}
52
53PassRefPtr<HTMLProgressElement> HTMLProgressElement::create(const QualifiedName& tagName, Document& document)
54{
55    RefPtr<HTMLProgressElement> progress = adoptRef(new HTMLProgressElement(tagName, document));
56    progress->ensureUserAgentShadowRoot();
57    return progress.release();
58}
59
60RenderPtr<RenderElement> HTMLProgressElement::createElementRenderer(PassRef<RenderStyle> style)
61{
62    if (!style.get().hasAppearance())
63        return RenderElement::createFor(*this, WTF::move(style));
64
65    return createRenderer<RenderProgress>(*this, WTF::move(style));
66}
67
68bool HTMLProgressElement::childShouldCreateRenderer(const Node& child) const
69{
70    return hasShadowRootParent(child) && HTMLElement::childShouldCreateRenderer(child);
71}
72
73RenderProgress* HTMLProgressElement::renderProgress() const
74{
75    if (renderer() && renderer()->isProgress())
76        return toRenderProgress(renderer());
77    return toRenderProgress(descendantsOfType<Element>(*userAgentShadowRoot()).first()->renderer());
78}
79
80void HTMLProgressElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
81{
82    if (name == valueAttr)
83        didElementStateChange();
84    else if (name == maxAttr)
85        didElementStateChange();
86    else
87        LabelableElement::parseAttribute(name, value);
88}
89
90void HTMLProgressElement::didAttachRenderers()
91{
92    if (RenderProgress* render = renderProgress())
93        render->updateFromElement();
94}
95
96double HTMLProgressElement::value() const
97{
98    double value = parseToDoubleForNumberType(fastGetAttribute(valueAttr));
99    return !std::isfinite(value) || value < 0 ? 0 : std::min(value, max());
100}
101
102void HTMLProgressElement::setValue(double value, ExceptionCode& ec)
103{
104    if (!std::isfinite(value)) {
105        ec = NOT_SUPPORTED_ERR;
106        return;
107    }
108    setAttribute(valueAttr, AtomicString::number(value >= 0 ? value : 0));
109}
110
111double HTMLProgressElement::max() const
112{
113    double max = parseToDoubleForNumberType(getAttribute(maxAttr));
114    return !std::isfinite(max) || max <= 0 ? 1 : max;
115}
116
117void HTMLProgressElement::setMax(double max, ExceptionCode& ec)
118{
119    if (!std::isfinite(max)) {
120        ec = NOT_SUPPORTED_ERR;
121        return;
122    }
123    setAttribute(maxAttr, AtomicString::number(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    root->appendChild(inner);
155
156    RefPtr<ProgressBarElement> bar = ProgressBarElement::create(document());
157    RefPtr<ProgressValueElement> value = ProgressValueElement::create(document());
158    m_value = value.get();
159    m_value->setWidthPercentage(HTMLProgressElement::IndeterminatePosition * 100);
160    bar->appendChild(m_value, ASSERT_NO_EXCEPTION);
161
162    inner->appendChild(bar, ASSERT_NO_EXCEPTION);
163}
164
165bool HTMLProgressElement::shouldAppearIndeterminate() const
166{
167    return !isDeterminate();
168}
169
170} // namespace
171