253 lines
7.8 KiB
C++
253 lines
7.8 KiB
C++
/*
|
|
* Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public License
|
|
* along with this library; see the file COPYING.LIB. If not, write to
|
|
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
*
|
|
*/
|
|
|
|
#include "config.h"
|
|
#include "HTMLMeterElement.h"
|
|
|
|
#include "Attribute.h"
|
|
#include "ElementIterator.h"
|
|
#include "HTMLDivElement.h"
|
|
#include "HTMLFormElement.h"
|
|
#include "HTMLNames.h"
|
|
#include "HTMLParserIdioms.h"
|
|
#include "HTMLStyleElement.h"
|
|
#include "Page.h"
|
|
#include "RenderMeter.h"
|
|
#include "RenderTheme.h"
|
|
#include "ShadowRoot.h"
|
|
#include "UserAgentStyleSheets.h"
|
|
#include <wtf/IsoMallocInlines.h>
|
|
|
|
namespace WebCore {
|
|
|
|
WTF_MAKE_ISO_ALLOCATED_IMPL(HTMLMeterElement);
|
|
|
|
using namespace HTMLNames;
|
|
|
|
HTMLMeterElement::HTMLMeterElement(const QualifiedName& tagName, Document& document)
|
|
: LabelableElement(tagName, document)
|
|
{
|
|
ASSERT(hasTagName(meterTag));
|
|
}
|
|
|
|
HTMLMeterElement::~HTMLMeterElement() = default;
|
|
|
|
Ref<HTMLMeterElement> HTMLMeterElement::create(const QualifiedName& tagName, Document& document)
|
|
{
|
|
Ref<HTMLMeterElement> meter = adoptRef(*new HTMLMeterElement(tagName, document));
|
|
meter->ensureUserAgentShadowRoot();
|
|
return meter;
|
|
}
|
|
|
|
RenderPtr<RenderElement> HTMLMeterElement::createElementRenderer(RenderStyle&& style, const RenderTreePosition&)
|
|
{
|
|
if (!RenderTheme::singleton().supportsMeter(style.appearance(), *this))
|
|
return RenderElement::createFor(*this, WTFMove(style));
|
|
|
|
return createRenderer<RenderMeter>(*this, WTFMove(style));
|
|
}
|
|
|
|
bool HTMLMeterElement::childShouldCreateRenderer(const Node& child) const
|
|
{
|
|
return !is<RenderMeter>(renderer()) && HTMLElement::childShouldCreateRenderer(child);
|
|
}
|
|
|
|
void HTMLMeterElement::parseAttribute(const QualifiedName& name, const AtomString& value)
|
|
{
|
|
if (name == valueAttr || name == minAttr || name == maxAttr || name == lowAttr || name == highAttr || name == optimumAttr)
|
|
didElementStateChange();
|
|
else
|
|
LabelableElement::parseAttribute(name, value);
|
|
}
|
|
|
|
double HTMLMeterElement::min() const
|
|
{
|
|
return parseToDoubleForNumberType(attributeWithoutSynchronization(minAttr), 0);
|
|
}
|
|
|
|
void HTMLMeterElement::setMin(double min)
|
|
{
|
|
setAttributeWithoutSynchronization(minAttr, AtomString::number(min));
|
|
}
|
|
|
|
double HTMLMeterElement::max() const
|
|
{
|
|
return std::max(parseToDoubleForNumberType(attributeWithoutSynchronization(maxAttr), std::max(1.0, min())), min());
|
|
}
|
|
|
|
void HTMLMeterElement::setMax(double max)
|
|
{
|
|
setAttributeWithoutSynchronization(maxAttr, AtomString::number(max));
|
|
}
|
|
|
|
double HTMLMeterElement::value() const
|
|
{
|
|
double value = parseToDoubleForNumberType(attributeWithoutSynchronization(valueAttr), 0);
|
|
return std::min(std::max(value, min()), max());
|
|
}
|
|
|
|
void HTMLMeterElement::setValue(double value)
|
|
{
|
|
setAttributeWithoutSynchronization(valueAttr, AtomString::number(value));
|
|
}
|
|
|
|
double HTMLMeterElement::low() const
|
|
{
|
|
double low = parseToDoubleForNumberType(attributeWithoutSynchronization(lowAttr), min());
|
|
return std::min(std::max(low, min()), max());
|
|
}
|
|
|
|
void HTMLMeterElement::setLow(double low)
|
|
{
|
|
setAttributeWithoutSynchronization(lowAttr, AtomString::number(low));
|
|
}
|
|
|
|
double HTMLMeterElement::high() const
|
|
{
|
|
double high = parseToDoubleForNumberType(attributeWithoutSynchronization(highAttr), max());
|
|
return std::min(std::max(high, low()), max());
|
|
}
|
|
|
|
void HTMLMeterElement::setHigh(double high)
|
|
{
|
|
setAttributeWithoutSynchronization(highAttr, AtomString::number(high));
|
|
}
|
|
|
|
double HTMLMeterElement::optimum() const
|
|
{
|
|
double optimum = parseToDoubleForNumberType(attributeWithoutSynchronization(optimumAttr), (max() + min()) / 2);
|
|
return std::min(std::max(optimum, min()), max());
|
|
}
|
|
|
|
void HTMLMeterElement::setOptimum(double optimum)
|
|
{
|
|
setAttributeWithoutSynchronization(optimumAttr, AtomString::number(optimum));
|
|
}
|
|
|
|
HTMLMeterElement::GaugeRegion HTMLMeterElement::gaugeRegion() const
|
|
{
|
|
double lowValue = low();
|
|
double highValue = high();
|
|
double theValue = value();
|
|
double optimumValue = optimum();
|
|
|
|
if (optimumValue < lowValue) {
|
|
// The optimum range stays under low
|
|
if (theValue <= lowValue)
|
|
return GaugeRegionOptimum;
|
|
if (theValue <= highValue)
|
|
return GaugeRegionSuboptimal;
|
|
return GaugeRegionEvenLessGood;
|
|
}
|
|
|
|
if (highValue < optimumValue) {
|
|
// The optimum range stays over high
|
|
if (highValue <= theValue)
|
|
return GaugeRegionOptimum;
|
|
if (lowValue <= theValue)
|
|
return GaugeRegionSuboptimal;
|
|
return GaugeRegionEvenLessGood;
|
|
}
|
|
|
|
// The optimum range stays between high and low.
|
|
// According to the standard, <meter> never show GaugeRegionEvenLessGood in this case
|
|
// because the value is never less or greater than min or max.
|
|
if (lowValue <= theValue && theValue <= highValue)
|
|
return GaugeRegionOptimum;
|
|
return GaugeRegionSuboptimal;
|
|
}
|
|
|
|
double HTMLMeterElement::valueRatio() const
|
|
{
|
|
double min = this->min();
|
|
double max = this->max();
|
|
double value = this->value();
|
|
|
|
if (max <= min)
|
|
return 0;
|
|
return (value - min) / (max - min);
|
|
}
|
|
|
|
static void setValueClass(HTMLElement& element, HTMLMeterElement::GaugeRegion gaugeRegion)
|
|
{
|
|
switch (gaugeRegion) {
|
|
case HTMLMeterElement::GaugeRegionOptimum:
|
|
element.setAttribute(HTMLNames::classAttr, "optimum");
|
|
element.setPseudo("-webkit-meter-optimum-value");
|
|
return;
|
|
case HTMLMeterElement::GaugeRegionSuboptimal:
|
|
element.setAttribute(HTMLNames::classAttr, "suboptimum");
|
|
element.setPseudo("-webkit-meter-suboptimum-value");
|
|
return;
|
|
case HTMLMeterElement::GaugeRegionEvenLessGood:
|
|
element.setAttribute(HTMLNames::classAttr, "even-less-good");
|
|
element.setPseudo("-webkit-meter-even-less-good-value");
|
|
return;
|
|
default:
|
|
ASSERT_NOT_REACHED();
|
|
}
|
|
}
|
|
|
|
void HTMLMeterElement::didElementStateChange()
|
|
{
|
|
m_value->setInlineStyleProperty(CSSPropertyWidth, valueRatio()*100, CSSUnitType::CSS_PERCENTAGE);
|
|
setValueClass(*m_value, gaugeRegion());
|
|
|
|
if (RenderMeter* render = renderMeter())
|
|
render->updateFromElement();
|
|
}
|
|
|
|
RenderMeter* HTMLMeterElement::renderMeter() const
|
|
{
|
|
if (is<RenderMeter>(renderer()))
|
|
return downcast<RenderMeter>(renderer());
|
|
return nullptr;
|
|
}
|
|
|
|
void HTMLMeterElement::didAddUserAgentShadowRoot(ShadowRoot& root)
|
|
{
|
|
ASSERT(!m_value);
|
|
|
|
static MainThreadNeverDestroyed<const String> shadowStyle(StringImpl::createWithoutCopying(meterElementShadowUserAgentStyleSheet, sizeof(meterElementShadowUserAgentStyleSheet)));
|
|
|
|
auto style = HTMLStyleElement::create(HTMLNames::styleTag, document(), false);
|
|
style->setTextContent(shadowStyle);
|
|
root.appendChild(style);
|
|
|
|
// Pseudos are set to allow author styling.
|
|
auto inner = HTMLDivElement::create(document());
|
|
inner->setIdAttribute("inner");
|
|
inner->setPseudo("-webkit-meter-inner-element");
|
|
root.appendChild(inner);
|
|
|
|
auto bar = HTMLDivElement::create(document());
|
|
bar->setIdAttribute("bar");
|
|
bar->setPseudo("-webkit-meter-bar");
|
|
inner->appendChild(bar);
|
|
|
|
m_value = HTMLDivElement::create(document());
|
|
m_value->setIdAttribute("value");
|
|
bar->appendChild(*m_value);
|
|
|
|
didElementStateChange();
|
|
}
|
|
|
|
} // namespace
|