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