blob: 4b9d13afe37b94f0e22397096a1eda1a068de3db [file] [log] [blame]
/*
* Copyright (C) 2012 Google Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "core/html/shadow/DateTimeEditElement.h"
#if ENABLE(INPUT_MULTIPLE_FIELDS_UI)
#include "bindings/core/v8/ExceptionStatePlaceholder.h"
#include "core/HTMLNames.h"
#include "core/dom/Document.h"
#include "core/dom/Text.h"
#include "core/events/MouseEvent.h"
#include "core/html/forms/DateTimeFieldsState.h"
#include "core/html/shadow/DateTimeFieldElements.h"
#include "core/html/shadow/ShadowElementNames.h"
#include "core/style/ComputedStyle.h"
#include "core/style/StyleInheritedData.h"
#include "platform/fonts/FontCache.h"
#include "platform/text/DateTimeFormat.h"
#include "platform/text/PlatformLocale.h"
#include "wtf/DateMath.h"
namespace blink {
using namespace HTMLNames;
using namespace WTF::Unicode;
class DateTimeEditBuilder : private DateTimeFormat::TokenHandler {
public:
// The argument objects must be alive until this object dies.
DateTimeEditBuilder(DateTimeEditElement&, const DateTimeEditElement::LayoutParameters&, const DateComponents&);
bool build(const String&);
private:
bool needMillisecondField() const;
bool shouldAMPMFieldDisabled() const;
bool shouldDayOfMonthFieldDisabled() const;
bool shouldHourFieldDisabled() const;
bool shouldMillisecondFieldDisabled() const;
bool shouldMinuteFieldDisabled() const;
bool shouldSecondFieldDisabled() const;
bool shouldYearFieldDisabled() const;
inline const StepRange& stepRange() const { return m_parameters.stepRange; }
DateTimeNumericFieldElement::Step createStep(double msPerFieldUnit, double msPerFieldSize) const;
// DateTimeFormat::TokenHandler functions.
void visitField(DateTimeFormat::FieldType, int) final;
void visitLiteral(const String&) final;
DateTimeEditElement& editElement() const;
RawPtrWillBeMember<DateTimeEditElement> m_editElement;
const DateComponents m_dateValue;
const DateTimeEditElement::LayoutParameters& m_parameters;
DateTimeNumericFieldElement::Range m_dayRange;
DateTimeNumericFieldElement::Range m_hour23Range;
DateTimeNumericFieldElement::Range m_minuteRange;
DateTimeNumericFieldElement::Range m_secondRange;
DateTimeNumericFieldElement::Range m_millisecondRange;
};
DateTimeEditBuilder::DateTimeEditBuilder(DateTimeEditElement& element, const DateTimeEditElement::LayoutParameters& layoutParameters, const DateComponents& dateValue)
: m_editElement(&element)
, m_dateValue(dateValue)
, m_parameters(layoutParameters)
, m_dayRange(1, 31)
, m_hour23Range(0, 23)
, m_minuteRange(0, 59)
, m_secondRange(0, 59)
, m_millisecondRange(0, 999)
{
if (m_dateValue.type() == DateComponents::Date || m_dateValue.type() == DateComponents::DateTimeLocal) {
if (m_parameters.minimum.type() != DateComponents::Invalid
&& m_parameters.maximum.type() != DateComponents::Invalid
&& m_parameters.minimum.fullYear() == m_parameters.maximum.fullYear()
&& m_parameters.minimum.month() == m_parameters.maximum.month()
&& m_parameters.minimum.monthDay() <= m_parameters.maximum.monthDay()) {
m_dayRange.minimum = m_parameters.minimum.monthDay();
m_dayRange.maximum = m_parameters.maximum.monthDay();
}
}
if (m_dateValue.type() == DateComponents::Time || m_dayRange.isSingleton()) {
if (m_parameters.minimum.type() != DateComponents::Invalid
&& m_parameters.maximum.type() != DateComponents::Invalid
&& m_parameters.minimum.hour() <= m_parameters.maximum.hour()) {
m_hour23Range.minimum = m_parameters.minimum.hour();
m_hour23Range.maximum = m_parameters.maximum.hour();
}
}
if (m_hour23Range.isSingleton() && m_parameters.minimum.minute() <= m_parameters.maximum.minute()) {
m_minuteRange.minimum = m_parameters.minimum.minute();
m_minuteRange.maximum = m_parameters.maximum.minute();
}
if (m_minuteRange.isSingleton() && m_parameters.minimum.second() <= m_parameters.maximum.second()) {
m_secondRange.minimum = m_parameters.minimum.second();
m_secondRange.maximum = m_parameters.maximum.second();
}
if (m_secondRange.isSingleton() && m_parameters.minimum.millisecond() <= m_parameters.maximum.millisecond()) {
m_millisecondRange.minimum = m_parameters.minimum.millisecond();
m_millisecondRange.maximum = m_parameters.maximum.millisecond();
}
}
bool DateTimeEditBuilder::build(const String& formatString)
{
editElement().resetFields();
return DateTimeFormat::parse(formatString, *this);
}
bool DateTimeEditBuilder::needMillisecondField() const
{
return m_dateValue.millisecond()
|| !stepRange().minimum().remainder(static_cast<int>(msPerSecond)).isZero()
|| !stepRange().step().remainder(static_cast<int>(msPerSecond)).isZero();
}
void DateTimeEditBuilder::visitField(DateTimeFormat::FieldType fieldType, int count)
{
const int countForAbbreviatedMonth = 3;
const int countForFullMonth = 4;
const int countForNarrowMonth = 5;
Document& document = editElement().document();
switch (fieldType) {
case DateTimeFormat::FieldTypeDayOfMonth: {
RefPtrWillBeRawPtr<DateTimeFieldElement> field = DateTimeDayFieldElement::create(document, editElement(), m_parameters.placeholderForDay, m_dayRange);
editElement().addField(field);
if (shouldDayOfMonthFieldDisabled()) {
field->setValueAsDate(m_dateValue);
field->setDisabled();
}
return;
}
case DateTimeFormat::FieldTypeHour11: {
DateTimeNumericFieldElement::Step step = createStep(msPerHour, msPerHour * 12);
RefPtrWillBeRawPtr<DateTimeFieldElement> field = DateTimeHour11FieldElement::create(document, editElement(), m_hour23Range, step);
editElement().addField(field);
if (shouldHourFieldDisabled()) {
field->setValueAsDate(m_dateValue);
field->setDisabled();
}
return;
}
case DateTimeFormat::FieldTypeHour12: {
DateTimeNumericFieldElement::Step step = createStep(msPerHour, msPerHour * 12);
RefPtrWillBeRawPtr<DateTimeFieldElement> field = DateTimeHour12FieldElement::create(document, editElement(), m_hour23Range, step);
editElement().addField(field);
if (shouldHourFieldDisabled()) {
field->setValueAsDate(m_dateValue);
field->setDisabled();
}
return;
}
case DateTimeFormat::FieldTypeHour23: {
DateTimeNumericFieldElement::Step step = createStep(msPerHour, msPerDay);
RefPtrWillBeRawPtr<DateTimeFieldElement> field = DateTimeHour23FieldElement::create(document, editElement(), m_hour23Range, step);
editElement().addField(field);
if (shouldHourFieldDisabled()) {
field->setValueAsDate(m_dateValue);
field->setDisabled();
}
return;
}
case DateTimeFormat::FieldTypeHour24: {
DateTimeNumericFieldElement::Step step = createStep(msPerHour, msPerDay);
RefPtrWillBeRawPtr<DateTimeFieldElement> field = DateTimeHour24FieldElement::create(document, editElement(), m_hour23Range, step);
editElement().addField(field);
if (shouldHourFieldDisabled()) {
field->setValueAsDate(m_dateValue);
field->setDisabled();
}
return;
}
case DateTimeFormat::FieldTypeMinute: {
DateTimeNumericFieldElement::Step step = createStep(msPerMinute, msPerHour);
RefPtrWillBeRawPtr<DateTimeNumericFieldElement> field = DateTimeMinuteFieldElement::create(document, editElement(), m_minuteRange, step);
editElement().addField(field);
if (shouldMinuteFieldDisabled()) {
field->setValueAsDate(m_dateValue);
field->setDisabled();
}
return;
}
case DateTimeFormat::FieldTypeMonth: // Fallthrough.
case DateTimeFormat::FieldTypeMonthStandAlone: {
int minMonth = 0, maxMonth = 11;
if (m_parameters.minimum.type() != DateComponents::Invalid
&& m_parameters.maximum.type() != DateComponents::Invalid
&& m_parameters.minimum.fullYear() == m_parameters.maximum.fullYear()
&& m_parameters.minimum.month() <= m_parameters.maximum.month()) {
minMonth = m_parameters.minimum.month();
maxMonth = m_parameters.maximum.month();
}
RefPtrWillBeRawPtr<DateTimeFieldElement> field;
switch (count) {
case countForNarrowMonth: // Fallthrough.
case countForAbbreviatedMonth:
field = DateTimeSymbolicMonthFieldElement::create(document, editElement(), fieldType == DateTimeFormat::FieldTypeMonth ? m_parameters.locale.shortMonthLabels() : m_parameters.locale.shortStandAloneMonthLabels(), minMonth, maxMonth);
break;
case countForFullMonth:
field = DateTimeSymbolicMonthFieldElement::create(document, editElement(), fieldType == DateTimeFormat::FieldTypeMonth ? m_parameters.locale.monthLabels() : m_parameters.locale.standAloneMonthLabels(), minMonth, maxMonth);
break;
default:
field = DateTimeMonthFieldElement::create(document, editElement(), m_parameters.placeholderForMonth, DateTimeNumericFieldElement::Range(minMonth + 1, maxMonth + 1));
break;
}
editElement().addField(field);
if (minMonth == maxMonth && minMonth == m_dateValue.month() && m_dateValue.type() != DateComponents::Month) {
field->setValueAsDate(m_dateValue);
field->setDisabled();
}
return;
}
case DateTimeFormat::FieldTypePeriod: {
RefPtrWillBeRawPtr<DateTimeFieldElement> field = DateTimeAMPMFieldElement::create(document, editElement(), m_parameters.locale.timeAMPMLabels());
editElement().addField(field);
if (shouldAMPMFieldDisabled()) {
field->setValueAsDate(m_dateValue);
field->setDisabled();
}
return;
}
case DateTimeFormat::FieldTypeSecond: {
DateTimeNumericFieldElement::Step step = createStep(msPerSecond, msPerMinute);
RefPtrWillBeRawPtr<DateTimeNumericFieldElement> field = DateTimeSecondFieldElement::create(document, editElement(), m_secondRange, step);
editElement().addField(field);
if (shouldSecondFieldDisabled()) {
field->setValueAsDate(m_dateValue);
field->setDisabled();
}
if (needMillisecondField()) {
visitLiteral(m_parameters.locale.localizedDecimalSeparator());
visitField(DateTimeFormat::FieldTypeFractionalSecond, 3);
}
return;
}
case DateTimeFormat::FieldTypeFractionalSecond: {
DateTimeNumericFieldElement::Step step = createStep(1, msPerSecond);
RefPtrWillBeRawPtr<DateTimeNumericFieldElement> field = DateTimeMillisecondFieldElement::create(document, editElement(), m_millisecondRange, step);
editElement().addField(field);
if (shouldMillisecondFieldDisabled()) {
field->setValueAsDate(m_dateValue);
field->setDisabled();
}
return;
}
case DateTimeFormat::FieldTypeWeekOfYear: {
DateTimeNumericFieldElement::Range range(DateComponents::minimumWeekNumber, DateComponents::maximumWeekNumber);
if (m_parameters.minimum.type() != DateComponents::Invalid
&& m_parameters.maximum.type() != DateComponents::Invalid
&& m_parameters.minimum.fullYear() == m_parameters.maximum.fullYear()
&& m_parameters.minimum.week() <= m_parameters.maximum.week()) {
range.minimum = m_parameters.minimum.week();
range.maximum = m_parameters.maximum.week();
}
editElement().addField(DateTimeWeekFieldElement::create(document, editElement(), range));
return;
}
case DateTimeFormat::FieldTypeYear: {
DateTimeYearFieldElement::Parameters yearParams;
if (m_parameters.minimum.type() == DateComponents::Invalid) {
yearParams.minimumYear = DateComponents::minimumYear();
yearParams.minIsSpecified = false;
} else {
yearParams.minimumYear = m_parameters.minimum.fullYear();
yearParams.minIsSpecified = true;
}
if (m_parameters.maximum.type() == DateComponents::Invalid) {
yearParams.maximumYear = DateComponents::maximumYear();
yearParams.maxIsSpecified = false;
} else {
yearParams.maximumYear = m_parameters.maximum.fullYear();
yearParams.maxIsSpecified = true;
}
if (yearParams.minimumYear > yearParams.maximumYear) {
std::swap(yearParams.minimumYear, yearParams.maximumYear);
std::swap(yearParams.minIsSpecified, yearParams.maxIsSpecified);
}
yearParams.placeholder = m_parameters.placeholderForYear;
RefPtrWillBeRawPtr<DateTimeFieldElement> field = DateTimeYearFieldElement::create(document, editElement(), yearParams);
editElement().addField(field);
if (shouldYearFieldDisabled()) {
field->setValueAsDate(m_dateValue);
field->setDisabled();
}
return;
}
default:
return;
}
}
bool DateTimeEditBuilder::shouldAMPMFieldDisabled() const
{
return shouldHourFieldDisabled()
|| (m_hour23Range.minimum < 12 && m_hour23Range.maximum < 12 && m_dateValue.hour() < 12)
|| (m_hour23Range.minimum >= 12 && m_hour23Range.maximum >= 12 && m_dateValue.hour() >= 12);
}
bool DateTimeEditBuilder::shouldDayOfMonthFieldDisabled() const
{
return m_dayRange.isSingleton() && m_dayRange.minimum == m_dateValue.monthDay() && m_dateValue.type() != DateComponents::Date;
}
bool DateTimeEditBuilder::shouldHourFieldDisabled() const
{
if (m_hour23Range.isSingleton() && m_hour23Range.minimum == m_dateValue.hour()
&& !(shouldMinuteFieldDisabled() && shouldSecondFieldDisabled() && shouldMillisecondFieldDisabled()))
return true;
if (m_dateValue.type() == DateComponents::Time)
return false;
ASSERT(m_dateValue.type() == DateComponents::DateTimeLocal);
if (shouldDayOfMonthFieldDisabled()) {
ASSERT(m_parameters.minimum.fullYear() == m_parameters.maximum.fullYear());
ASSERT(m_parameters.minimum.month() == m_parameters.maximum.month());
return false;
}
const Decimal decimalMsPerDay(static_cast<int>(msPerDay));
Decimal hourPartOfMinimum = (stepRange().stepBase().abs().remainder(decimalMsPerDay) / static_cast<int>(msPerHour)).floor();
return hourPartOfMinimum == m_dateValue.hour() && stepRange().step().remainder(decimalMsPerDay).isZero();
}
bool DateTimeEditBuilder::shouldMillisecondFieldDisabled() const
{
if (m_millisecondRange.isSingleton() && m_millisecondRange.minimum == m_dateValue.millisecond())
return true;
const Decimal decimalMsPerSecond(static_cast<int>(msPerSecond));
return stepRange().stepBase().abs().remainder(decimalMsPerSecond) == m_dateValue.millisecond() && stepRange().step().remainder(decimalMsPerSecond).isZero();
}
bool DateTimeEditBuilder::shouldMinuteFieldDisabled() const
{
if (m_minuteRange.isSingleton() && m_minuteRange.minimum == m_dateValue.minute())
return true;
const Decimal decimalMsPerHour(static_cast<int>(msPerHour));
Decimal minutePartOfMinimum = (stepRange().stepBase().abs().remainder(decimalMsPerHour) / static_cast<int>(msPerMinute)).floor();
return minutePartOfMinimum == m_dateValue.minute() && stepRange().step().remainder(decimalMsPerHour).isZero();
}
bool DateTimeEditBuilder::shouldSecondFieldDisabled() const
{
if (m_secondRange.isSingleton() && m_secondRange.minimum == m_dateValue.second())
return true;
const Decimal decimalMsPerMinute(static_cast<int>(msPerMinute));
Decimal secondPartOfMinimum = (stepRange().stepBase().abs().remainder(decimalMsPerMinute) / static_cast<int>(msPerSecond)).floor();
return secondPartOfMinimum == m_dateValue.second() && stepRange().step().remainder(decimalMsPerMinute).isZero();
}
bool DateTimeEditBuilder::shouldYearFieldDisabled() const
{
return m_parameters.minimum.type() != DateComponents::Invalid
&& m_parameters.maximum.type() != DateComponents::Invalid
&& m_parameters.minimum.fullYear() == m_parameters.maximum.fullYear()
&& m_parameters.minimum.fullYear() == m_dateValue.fullYear();
}
void DateTimeEditBuilder::visitLiteral(const String& text)
{
DEFINE_STATIC_LOCAL(AtomicString, textPseudoId, ("-webkit-datetime-edit-text", AtomicString::ConstructFromLiteral));
ASSERT(text.length());
RefPtrWillBeRawPtr<HTMLDivElement> element = HTMLDivElement::create(editElement().document());
element->setShadowPseudoId(textPseudoId);
if (m_parameters.locale.isRTL() && text.length()) {
Direction dir = direction(text[0]);
if (dir == SegmentSeparator || dir == WhiteSpaceNeutral || dir == OtherNeutral)
element->appendChild(Text::create(editElement().document(), String(&rightToLeftMarkCharacter, 1)));
}
element->appendChild(Text::create(editElement().document(), text));
editElement().fieldsWrapperElement()->appendChild(element);
}
DateTimeEditElement& DateTimeEditBuilder::editElement() const
{
return *m_editElement;
}
DateTimeNumericFieldElement::Step DateTimeEditBuilder::createStep(double msPerFieldUnit, double msPerFieldSize) const
{
const Decimal msPerFieldUnitDecimal(static_cast<int>(msPerFieldUnit));
const Decimal msPerFieldSizeDecimal(static_cast<int>(msPerFieldSize));
Decimal stepMilliseconds = stepRange().step();
ASSERT(!msPerFieldUnitDecimal.isZero());
ASSERT(!msPerFieldSizeDecimal.isZero());
ASSERT(!stepMilliseconds.isZero());
DateTimeNumericFieldElement::Step step(1, 0);
if (stepMilliseconds.remainder(msPerFieldSizeDecimal).isZero())
stepMilliseconds = msPerFieldSizeDecimal;
if (msPerFieldSizeDecimal.remainder(stepMilliseconds).isZero() && stepMilliseconds.remainder(msPerFieldUnitDecimal).isZero()) {
step.step = static_cast<int>((stepMilliseconds / msPerFieldUnitDecimal).toDouble());
step.stepBase = static_cast<int>((stepRange().stepBase() / msPerFieldUnitDecimal).floor().remainder(msPerFieldSizeDecimal / msPerFieldUnitDecimal).toDouble());
}
return step;
}
// ----------------------------
DateTimeEditElement::EditControlOwner::~EditControlOwner()
{
}
DateTimeEditElement::DateTimeEditElement(Document& document, EditControlOwner& editControlOwner)
: HTMLDivElement(document)
, m_editControlOwner(&editControlOwner)
{
setHasCustomStyleCallbacks();
}
DateTimeEditElement::~DateTimeEditElement()
{
#if !ENABLE(OILPAN)
for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex)
m_fields[fieldIndex]->removeEventHandler();
#endif
}
DEFINE_TRACE(DateTimeEditElement)
{
#if ENABLE(OILPAN)
visitor->trace(m_fields);
#endif
visitor->trace(m_editControlOwner);
HTMLDivElement::trace(visitor);
}
inline Element* DateTimeEditElement::fieldsWrapperElement() const
{
ASSERT(firstChild());
return toElement(firstChild());
}
void DateTimeEditElement::addField(PassRefPtrWillBeRawPtr<DateTimeFieldElement> field)
{
if (m_fields.size() == m_fields.capacity())
return;
m_fields.append(field.get());
fieldsWrapperElement()->appendChild(field);
}
bool DateTimeEditElement::anyEditableFieldsHaveValues() const
{
for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex) {
if (!m_fields[fieldIndex]->isDisabled() && m_fields[fieldIndex]->hasValue())
return true;
}
return false;
}
void DateTimeEditElement::blurByOwner()
{
if (DateTimeFieldElement* field = focusedField())
field->blur();
}
PassRefPtrWillBeRawPtr<DateTimeEditElement> DateTimeEditElement::create(Document& document, EditControlOwner& editControlOwner)
{
RefPtrWillBeRawPtr<DateTimeEditElement> container = adoptRefWillBeNoop(new DateTimeEditElement(document, editControlOwner));
container->setShadowPseudoId(AtomicString("-webkit-datetime-edit", AtomicString::ConstructFromLiteral));
container->setAttribute(idAttr, ShadowElementNames::dateTimeEdit());
return container.release();
}
PassRefPtr<ComputedStyle> DateTimeEditElement::customStyleForLayoutObject()
{
// FIXME: This is a kind of layout. We might want to introduce new layoutObject.
RefPtr<ComputedStyle> originalStyle = originalStyleForLayoutObject();
RefPtr<ComputedStyle> style = ComputedStyle::clone(*originalStyle);
float width = 0;
for (Node* child = fieldsWrapperElement()->firstChild(); child; child = child->nextSibling()) {
if (!child->isElementNode())
continue;
Element* childElement = toElement(child);
if (childElement->isDateTimeFieldElement()) {
// We need to pass the ComputedStyle of this element because child
// elements can't resolve inherited style at this timing.
width += static_cast<DateTimeFieldElement*>(childElement)->maximumWidth(*style);
} else {
// ::-webkit-datetime-edit-text case. It has no
// border/padding/margin in html.css.
width += DateTimeFieldElement::computeTextWidth(*style, childElement->textContent());
}
}
style->setWidth(Length(ceilf(width), Fixed));
style->setUnique();
return style.release();
}
void DateTimeEditElement::didBlurFromField()
{
if (m_editControlOwner)
m_editControlOwner->didBlurFromControl();
}
void DateTimeEditElement::didFocusOnField()
{
if (m_editControlOwner)
m_editControlOwner->didFocusOnControl();
}
void DateTimeEditElement::disabledStateChanged()
{
updateUIState();
}
DateTimeFieldElement* DateTimeEditElement::fieldAt(size_t fieldIndex) const
{
return fieldIndex < m_fields.size() ? m_fields[fieldIndex].get() : 0;
}
size_t DateTimeEditElement::fieldIndexOf(const DateTimeFieldElement& field) const
{
for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex) {
if (m_fields[fieldIndex] == &field)
return fieldIndex;
}
return invalidFieldIndex;
}
void DateTimeEditElement::focusIfNoFocus()
{
if (focusedFieldIndex() != invalidFieldIndex)
return;
focusOnNextFocusableField(0);
}
void DateTimeEditElement::focusByOwner(Element* oldFocusedElement)
{
if (oldFocusedElement && oldFocusedElement->isDateTimeFieldElement()) {
DateTimeFieldElement* oldFocusedField = static_cast<DateTimeFieldElement*>(oldFocusedElement);
size_t index = fieldIndexOf(*oldFocusedField);
if (index != invalidFieldIndex && oldFocusedField->isFocusable()) {
oldFocusedField->focus();
return;
}
}
focusOnNextFocusableField(0);
}
DateTimeFieldElement* DateTimeEditElement::focusedField() const
{
return fieldAt(focusedFieldIndex());
}
size_t DateTimeEditElement::focusedFieldIndex() const
{
Element* const focusedFieldElement = document().focusedElement();
for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex) {
if (m_fields[fieldIndex] == focusedFieldElement)
return fieldIndex;
}
return invalidFieldIndex;
}
void DateTimeEditElement::fieldValueChanged()
{
if (m_editControlOwner)
m_editControlOwner->editControlValueChanged();
}
bool DateTimeEditElement::focusOnNextFocusableField(size_t startIndex)
{
for (size_t fieldIndex = startIndex; fieldIndex < m_fields.size(); ++fieldIndex) {
if (m_fields[fieldIndex]->isFocusable()) {
m_fields[fieldIndex]->focus();
return true;
}
}
return false;
}
bool DateTimeEditElement::focusOnNextField(const DateTimeFieldElement& field)
{
const size_t startFieldIndex = fieldIndexOf(field);
if (startFieldIndex == invalidFieldIndex)
return false;
return focusOnNextFocusableField(startFieldIndex + 1);
}
bool DateTimeEditElement::focusOnPreviousField(const DateTimeFieldElement& field)
{
const size_t startFieldIndex = fieldIndexOf(field);
if (startFieldIndex == invalidFieldIndex)
return false;
size_t fieldIndex = startFieldIndex;
while (fieldIndex > 0) {
--fieldIndex;
if (m_fields[fieldIndex]->isFocusable()) {
m_fields[fieldIndex]->focus();
return true;
}
}
return false;
}
bool DateTimeEditElement::isDateTimeEditElement() const
{
return true;
}
bool DateTimeEditElement::isDisabled() const
{
return m_editControlOwner && m_editControlOwner->isEditControlOwnerDisabled();
}
bool DateTimeEditElement::isFieldOwnerDisabled() const
{
return isDisabled();
}
bool DateTimeEditElement::isFieldOwnerReadOnly() const
{
return isReadOnly();
}
bool DateTimeEditElement::isReadOnly() const
{
return m_editControlOwner && m_editControlOwner->isEditControlOwnerReadOnly();
}
void DateTimeEditElement::layout(const LayoutParameters& layoutParameters, const DateComponents& dateValue)
{
DEFINE_STATIC_LOCAL(AtomicString, fieldsWrapperPseudoId, ("-webkit-datetime-edit-fields-wrapper", AtomicString::ConstructFromLiteral));
if (!hasChildren()) {
RefPtrWillBeRawPtr<HTMLDivElement> element = HTMLDivElement::create(document());
element->setShadowPseudoId(fieldsWrapperPseudoId);
appendChild(element.get());
}
Element* fieldsWrapper = fieldsWrapperElement();
size_t focusedFieldIndex = this->focusedFieldIndex();
DateTimeFieldElement* const focusedField = fieldAt(focusedFieldIndex);
const AtomicString focusedFieldId = focusedField ? focusedField->shadowPseudoId() : nullAtom;
DateTimeEditBuilder builder(*this, layoutParameters, dateValue);
Node* lastChildToBeRemoved = fieldsWrapper->lastChild();
if (!builder.build(layoutParameters.dateTimeFormat) || m_fields.isEmpty()) {
lastChildToBeRemoved = fieldsWrapper->lastChild();
builder.build(layoutParameters.fallbackDateTimeFormat);
}
if (focusedFieldIndex != invalidFieldIndex) {
for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex) {
if (m_fields[fieldIndex]->shadowPseudoId() == focusedFieldId) {
focusedFieldIndex = fieldIndex;
break;
}
}
if (DateTimeFieldElement* field = fieldAt(std::min(focusedFieldIndex, m_fields.size() - 1)))
field->focus();
}
if (lastChildToBeRemoved) {
for (Node* childNode = fieldsWrapper->firstChild(); childNode; childNode = fieldsWrapper->firstChild()) {
fieldsWrapper->removeChild(childNode);
if (childNode == lastChildToBeRemoved)
break;
}
setNeedsStyleRecalc(SubtreeStyleChange, StyleChangeReasonForTracing::create(StyleChangeReason::Control));
}
}
AtomicString DateTimeEditElement::localeIdentifier() const
{
return m_editControlOwner ? m_editControlOwner->localeIdentifier() : nullAtom;
}
void DateTimeEditElement::fieldDidChangeValueByKeyboard()
{
if (m_editControlOwner)
m_editControlOwner->editControlDidChangeValueByKeyboard();
}
void DateTimeEditElement::readOnlyStateChanged()
{
updateUIState();
}
void DateTimeEditElement::resetFields()
{
for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex)
m_fields[fieldIndex]->removeEventHandler();
m_fields.shrink(0);
}
void DateTimeEditElement::defaultEventHandler(Event* event)
{
// In case of control owner forward event to control, e.g. DOM
// dispatchEvent method.
if (DateTimeFieldElement* field = focusedField()) {
field->defaultEventHandler(event);
if (event->defaultHandled())
return;
}
HTMLDivElement::defaultEventHandler(event);
}
void DateTimeEditElement::setValueAsDate(const LayoutParameters& layoutParameters, const DateComponents& date)
{
layout(layoutParameters, date);
for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex)
m_fields[fieldIndex]->setValueAsDate(date);
}
void DateTimeEditElement::setValueAsDateTimeFieldsState(const DateTimeFieldsState& dateTimeFieldsState)
{
for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex)
m_fields[fieldIndex]->setValueAsDateTimeFieldsState(dateTimeFieldsState);
}
void DateTimeEditElement::setEmptyValue(const LayoutParameters& layoutParameters, const DateComponents& dateForReadOnlyField)
{
layout(layoutParameters, dateForReadOnlyField);
for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex)
m_fields[fieldIndex]->setEmptyValue(DateTimeFieldElement::DispatchNoEvent);
}
bool DateTimeEditElement::hasFocusedField()
{
return focusedFieldIndex() != invalidFieldIndex;
}
void DateTimeEditElement::setOnlyYearMonthDay(const DateComponents& date)
{
ASSERT(date.type() == DateComponents::Date);
if (!m_editControlOwner)
return;
DateTimeFieldsState dateTimeFieldsState = valueAsDateTimeFieldsState();
dateTimeFieldsState.setYear(date.fullYear());
dateTimeFieldsState.setMonth(date.month() + 1);
dateTimeFieldsState.setDayOfMonth(date.monthDay());
setValueAsDateTimeFieldsState(dateTimeFieldsState);
m_editControlOwner->editControlValueChanged();
}
void DateTimeEditElement::stepDown()
{
if (DateTimeFieldElement* const field = focusedField())
field->stepDown();
}
void DateTimeEditElement::stepUp()
{
if (DateTimeFieldElement* const field = focusedField())
field->stepUp();
}
void DateTimeEditElement::updateUIState()
{
if (isDisabled()) {
if (DateTimeFieldElement* field = focusedField())
field->blur();
}
}
String DateTimeEditElement::value() const
{
if (!m_editControlOwner)
return emptyString();
return m_editControlOwner->formatDateTimeFieldsState(valueAsDateTimeFieldsState());
}
DateTimeFieldsState DateTimeEditElement::valueAsDateTimeFieldsState() const
{
DateTimeFieldsState dateTimeFieldsState;
for (size_t fieldIndex = 0; fieldIndex < m_fields.size(); ++fieldIndex)
m_fields[fieldIndex]->populateDateTimeFieldsState(dateTimeFieldsState);
return dateTimeFieldsState;
}
} // namespace blink
#endif