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 "ElementIterator.h"
25#include "HTMLNames.h"
26#include "HTMLParserIdioms.h"
27#include "ProgressShadowElement.h"
28#include "RenderProgress.h"
29#include "ShadowRoot.h"
30#include <wtf/IsoMallocInlines.h>
31
32namespace WebCore {
33
34WTF_MAKE_ISO_ALLOCATED_IMPL(HTMLProgressElement);
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() = default;
50
51Ref<HTMLProgressElement> HTMLProgressElement::create(const QualifiedName& tagName, Document& document)
52{
53 Ref<HTMLProgressElement> progress = adoptRef(*new HTMLProgressElement(tagName, document));
54 progress->ensureUserAgentShadowRoot();
55 return progress;
56}
57
58RenderPtr<RenderElement> HTMLProgressElement::createElementRenderer(RenderStyle&& style, const RenderTreePosition&)
59{
60 if (!style.hasAppearance())
61 return RenderElement::createFor(*this, WTFMove(style));
62
63 return createRenderer<RenderProgress>(*this, WTFMove(style));
64}
65
66bool HTMLProgressElement::childShouldCreateRenderer(const Node& child) const
67{
68 return hasShadowRootParent(child) && HTMLElement::childShouldCreateRenderer(child);
69}
70
71RenderProgress* HTMLProgressElement::renderProgress() const
72{
73 if (is<RenderProgress>(renderer()))
74 return downcast<RenderProgress>(renderer());
75 return downcast<RenderProgress>(descendantsOfType<Element>(*userAgentShadowRoot()).first()->renderer());
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::didAttachRenderers()
89{
90 if (RenderProgress* render = renderProgress())
91 render->updateFromElement();
92}
93
94double HTMLProgressElement::value() const
95{
96 double value = parseToDoubleForNumberType(attributeWithoutSynchronization(valueAttr));
97 return !std::isfinite(value) || value < 0 ? 0 : std::min(value, max());
98}
99
100void HTMLProgressElement::setValue(double value)
101{
102 setAttributeWithoutSynchronization(valueAttr, AtomicString::number(value));
103}
104
105double HTMLProgressElement::max() const
106{
107 double max = parseToDoubleForNumberType(attributeWithoutSynchronization(maxAttr));
108 return !std::isfinite(max) || max <= 0 ? 1 : max;
109}
110
111void HTMLProgressElement::setMax(double max)
112{
113 if (max > 0)
114 setAttributeWithoutSynchronization(maxAttr, AtomicString::number(max));
115}
116
117double HTMLProgressElement::position() const
118{
119 if (!isDeterminate())
120 return HTMLProgressElement::IndeterminatePosition;
121 return value() / max();
122}
123
124bool HTMLProgressElement::isDeterminate() const
125{
126 return hasAttributeWithoutSynchronization(valueAttr);
127}
128
129void HTMLProgressElement::didElementStateChange()
130{
131 m_value->setWidthPercentage(position() * 100);
132 if (RenderProgress* render = renderProgress()) {
133 bool wasDeterminate = render->isDeterminate();
134 render->updateFromElement();
135 if (wasDeterminate != isDeterminate())
136 invalidateStyleForSubtree();
137 }
138}
139
140void HTMLProgressElement::didAddUserAgentShadowRoot(ShadowRoot& root)
141{
142 ASSERT(!m_value);
143
144 auto inner = ProgressInnerElement::create(document());
145 root.appendChild(inner);
146
147 auto bar = ProgressBarElement::create(document());
148 auto value = ProgressValueElement::create(document());
149 m_value = value.ptr();
150 m_value->setWidthPercentage(HTMLProgressElement::IndeterminatePosition * 100);
151 bar->appendChild(value);
152
153 inner->appendChild(bar);
154}
155
156bool HTMLProgressElement::shouldAppearIndeterminate() const
157{
158 return !isDeterminate();
159}
160
161} // namespace
162