blob: c1b93a2c39e6492cc236c0a1f3d9f3b5e3afb16d [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 "third_party/blink/renderer/core/html/forms/date_time_field_element.h"
#include "third_party/blink/renderer/core/css/style_change_reason.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/text.h"
#include "third_party/blink/renderer/core/events/keyboard_event.h"
#include "third_party/blink/renderer/core/html_names.h"
#include "third_party/blink/renderer/core/layout/text_run_constructor.h"
#include "third_party/blink/renderer/core/style/computed_style.h"
#include "third_party/blink/renderer/platform/text/platform_locale.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
namespace blink {
using namespace html_names;
DateTimeFieldElement::FieldOwner::~FieldOwner() = default;
DateTimeFieldElement::DateTimeFieldElement(Document& document,
FieldOwner& field_owner)
: HTMLSpanElement(document), field_owner_(&field_owner) {}
void DateTimeFieldElement::Trace(Visitor* visitor) {
visitor->Trace(field_owner_);
HTMLSpanElement::Trace(visitor);
}
float DateTimeFieldElement::ComputeTextWidth(const ComputedStyle& style,
const String& text) {
return style.GetFont().Width(ConstructTextRun(style.GetFont(), text, style));
}
void DateTimeFieldElement::DefaultEventHandler(Event& event) {
if (event.IsKeyboardEvent()) {
auto& keyboard_event = ToKeyboardEvent(event);
if (!IsDisabled() && !IsFieldOwnerDisabled() && !IsFieldOwnerReadOnly()) {
HandleKeyboardEvent(keyboard_event);
if (keyboard_event.DefaultHandled()) {
if (field_owner_)
field_owner_->FieldDidChangeValueByKeyboard();
return;
}
}
DefaultKeyboardEventHandler(keyboard_event);
if (field_owner_)
field_owner_->FieldDidChangeValueByKeyboard();
if (keyboard_event.DefaultHandled())
return;
}
HTMLElement::DefaultEventHandler(event);
}
void DateTimeFieldElement::DefaultKeyboardEventHandler(
KeyboardEvent& keyboard_event) {
if (keyboard_event.type() != event_type_names::kKeydown)
return;
if (IsDisabled() || IsFieldOwnerDisabled())
return;
const String& key = keyboard_event.key();
if (key == "ArrowLeft") {
if (!field_owner_)
return;
// FIXME: We'd like to use FocusController::advanceFocus(FocusDirectionLeft,
// ...) but it doesn't work for shadow nodes. webkit.org/b/104650
if (!LocaleForOwner().IsRTL() && field_owner_->FocusOnPreviousField(*this))
keyboard_event.SetDefaultHandled();
return;
}
if (key == "ArrowRight") {
if (!field_owner_)
return;
// FIXME: We'd like to use
// FocusController::advanceFocus(FocusDirectionRight, ...)
// but it doesn't work for shadow nodes. webkit.org/b/104650
if (!LocaleForOwner().IsRTL() && field_owner_->FocusOnNextField(*this))
keyboard_event.SetDefaultHandled();
return;
}
if (IsFieldOwnerReadOnly())
return;
if (key == "ArrowDown") {
if (keyboard_event.getModifierState("Alt"))
return;
keyboard_event.SetDefaultHandled();
StepDown();
return;
}
if (key == "ArrowUp") {
keyboard_event.SetDefaultHandled();
StepUp();
return;
}
if (key == "Backspace" || key == "Delete") {
keyboard_event.SetDefaultHandled();
SetEmptyValue(kDispatchEvent);
return;
}
}
void DateTimeFieldElement::SetFocused(bool value, WebFocusType focus_type) {
if (field_owner_) {
if (value) {
field_owner_->DidFocusOnField(focus_type);
} else {
field_owner_->DidBlurFromField(focus_type);
}
}
ContainerNode::SetFocused(value, focus_type);
}
void DateTimeFieldElement::FocusOnNextField() {
if (!field_owner_)
return;
field_owner_->FocusOnNextField(*this);
}
void DateTimeFieldElement::Initialize(const AtomicString& pseudo,
const String& ax_help_text,
int ax_minimum,
int ax_maximum) {
// On accessibility, DateTimeFieldElement acts like spin button.
setAttribute(kRoleAttr, AtomicString("spinbutton"));
setAttribute(kAriaPlaceholderAttr, AtomicString(Placeholder()));
setAttribute(kAriaValueminAttr, AtomicString::Number(ax_minimum));
setAttribute(kAriaValuemaxAttr, AtomicString::Number(ax_maximum));
setAttribute(kAriaLabelAttr, AtomicString(ax_help_text));
SetShadowPseudoId(pseudo);
AppendChild(Text::Create(GetDocument(), VisibleValue()));
}
bool DateTimeFieldElement::IsDateTimeFieldElement() const {
return true;
}
bool DateTimeFieldElement::IsFieldOwnerDisabled() const {
return field_owner_ && field_owner_->IsFieldOwnerDisabled();
}
bool DateTimeFieldElement::IsFieldOwnerReadOnly() const {
return field_owner_ && field_owner_->IsFieldOwnerReadOnly();
}
bool DateTimeFieldElement::IsDisabled() const {
return FastHasAttribute(kDisabledAttr);
}
Locale& DateTimeFieldElement::LocaleForOwner() const {
return GetDocument().GetCachedLocale(LocaleIdentifier());
}
AtomicString DateTimeFieldElement::LocaleIdentifier() const {
return field_owner_ ? field_owner_->LocaleIdentifier() : g_null_atom;
}
float DateTimeFieldElement::MaximumWidth(const ComputedStyle&) {
const float kPaddingLeftAndRight = 2; // This should match to html.css.
return kPaddingLeftAndRight;
}
void DateTimeFieldElement::SetDisabled() {
// Set HTML attribute disabled to change apperance.
SetBooleanAttribute(kDisabledAttr, true);
SetNeedsStyleRecalc(kSubtreeStyleChange,
StyleChangeReasonForTracing::CreateWithExtraData(
style_change_reason::kPseudoClass,
style_change_extra_data::g_disabled));
}
bool DateTimeFieldElement::SupportsFocus() const {
return !IsDisabled() && !IsFieldOwnerDisabled();
}
void DateTimeFieldElement::UpdateVisibleValue(EventBehavior event_behavior) {
Text* const text_node = ToText(firstChild());
const String new_visible_value = VisibleValue();
DCHECK_GT(new_visible_value.length(), 0u);
if (text_node->wholeText() == new_visible_value)
return;
text_node->ReplaceWholeText(new_visible_value);
if (HasValue()) {
setAttribute(kAriaValuenowAttr,
AtomicString::Number(ValueForARIAValueNow()));
setAttribute(kAriaValuetextAttr, AtomicString(new_visible_value));
} else {
removeAttribute(kAriaValuenowAttr);
removeAttribute(kAriaValuetextAttr);
}
if (event_behavior == kDispatchEvent && field_owner_)
field_owner_->FieldValueChanged();
}
int DateTimeFieldElement::ValueForARIAValueNow() const {
return ValueAsInteger();
}
} // namespace blink