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#if ENABLE(PROGRESS_ELEMENT)
23#include "HTMLProgressElement.h"
24
25#include "Attribute.h"
26#include "EventNames.h"
27#include "ExceptionCode.h"
28#include "HTMLDivElement.h"
29#include "HTMLNames.h"
30#include "HTMLParserIdioms.h"
31#include "NodeRenderingContext.h"
32#include "ProgressShadowElement.h"
33#include "RenderProgress.h"
34#include "ShadowRoot.h"
35#include <wtf/StdLibExtras.h>
36
37namespace WebCore {
38
39using namespace HTMLNames;
40
41const double HTMLProgressElement::IndeterminatePosition = -1;
42const double HTMLProgressElement::InvalidPosition = -2;
43
44HTMLProgressElement::HTMLProgressElement(const QualifiedName& tagName, Document* document)
45    : LabelableElement(tagName, document)
46    , m_value(0)
47{
48    ASSERT(hasTagName(progressTag));
49}
50
51HTMLProgressElement::~HTMLProgressElement()
52{
53}
54
55PassRefPtr<HTMLProgressElement> HTMLProgressElement::create(const QualifiedName& tagName, Document* document)
56{
57    RefPtr<HTMLProgressElement> progress = adoptRef(new HTMLProgressElement(tagName, document));
58    progress->ensureUserAgentShadowRoot();
59    return progress.release();
60}
61
62RenderObject* HTMLProgressElement::createRenderer(RenderArena* arena, RenderStyle* style)
63{
64    if (!style->hasAppearance() || hasAuthorShadowRoot())
65        return RenderObject::createObject(this, style);
66
67    return new (arena) RenderProgress(this);
68}
69
70bool HTMLProgressElement::childShouldCreateRenderer(const NodeRenderingContext& childContext) const
71{
72    return childContext.isOnUpperEncapsulationBoundary() && HTMLElement::childShouldCreateRenderer(childContext);
73}
74
75RenderProgress* HTMLProgressElement::renderProgress() const
76{
77    if (renderer() && renderer()->isProgress())
78        return static_cast<RenderProgress*>(renderer());
79
80    RenderObject* renderObject = userAgentShadowRoot()->firstChild()->renderer();
81    ASSERT_WITH_SECURITY_IMPLICATION(!renderObject || renderObject->isProgress());
82    return static_cast<RenderProgress*>(renderObject);
83}
84
85void HTMLProgressElement::parseAttribute(const QualifiedName& name, const AtomicString& value)
86{
87    if (name == valueAttr)
88        didElementStateChange();
89    else if (name == maxAttr)
90        didElementStateChange();
91    else
92        LabelableElement::parseAttribute(name, value);
93}
94
95void HTMLProgressElement::attach(const AttachContext& context)
96{
97    LabelableElement::attach(context);
98    if (RenderProgress* render = renderProgress())
99        render->updateFromElement();
100}
101
102double HTMLProgressElement::value() const
103{
104    double value = parseToDoubleForNumberType(fastGetAttribute(valueAttr));
105    return !std::isfinite(value) || value < 0 ? 0 : std::min(value, max());
106}
107
108void HTMLProgressElement::setValue(double value, ExceptionCode& ec)
109{
110    if (!std::isfinite(value)) {
111        ec = NOT_SUPPORTED_ERR;
112        return;
113    }
114    setAttribute(valueAttr, String::number(value >= 0 ? value : 0));
115}
116
117double HTMLProgressElement::max() const
118{
119    double max = parseToDoubleForNumberType(getAttribute(maxAttr));
120    return !std::isfinite(max) || max <= 0 ? 1 : max;
121}
122
123void HTMLProgressElement::setMax(double max, ExceptionCode& ec)
124{
125    if (!std::isfinite(max)) {
126        ec = NOT_SUPPORTED_ERR;
127        return;
128    }
129    setAttribute(maxAttr, String::number(max > 0 ? max : 1));
130}
131
132double HTMLProgressElement::position() const
133{
134    if (!isDeterminate())
135        return HTMLProgressElement::IndeterminatePosition;
136    return value() / max();
137}
138
139bool HTMLProgressElement::isDeterminate() const
140{
141    return fastHasAttribute(valueAttr);
142}
143
144void HTMLProgressElement::didElementStateChange()
145{
146    m_value->setWidthPercentage(position() * 100);
147    if (RenderProgress* render = renderProgress()) {
148        bool wasDeterminate = render->isDeterminate();
149        render->updateFromElement();
150        if (wasDeterminate != isDeterminate())
151            didAffectSelector(AffectedSelectorIndeterminate);
152    }
153}
154
155void HTMLProgressElement::didAddUserAgentShadowRoot(ShadowRoot* root)
156{
157    ASSERT(!m_value);
158
159    RefPtr<ProgressInnerElement> inner = ProgressInnerElement::create(document());
160    root->appendChild(inner);
161
162    RefPtr<ProgressBarElement> bar = ProgressBarElement::create(document());
163    RefPtr<ProgressValueElement> value = ProgressValueElement::create(document());
164    m_value = value.get();
165    m_value->setWidthPercentage(HTMLProgressElement::IndeterminatePosition * 100);
166    bar->appendChild(m_value, ASSERT_NO_EXCEPTION);
167
168    inner->appendChild(bar, ASSERT_NO_EXCEPTION);
169}
170
171bool HTMLProgressElement::shouldAppearIndeterminate() const
172{
173    return !isDeterminate();
174}
175
176} // namespace
177#endif
178