1 /* 2 * Copyright (C) 2012 Google Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 19 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 20 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 */ 25 26 #include "config.h" 27 #if ENABLE(INPUT_MULTIPLE_FIELDS_UI) 28 #include "core/html/shadow/DateTimeFieldElement.h" 29 30 #include "core/HTMLNames.h" 31 #include "core/dom/Document.h" 32 #include "core/dom/Text.h" 33 #include "core/events/KeyboardEvent.h" 34 #include "platform/text/PlatformLocale.h" 35 #include "wtf/text/WTFString.h" 36 37 namespace blink { 38 39 using namespace HTMLNames; 40 41 static String emptyValueAXText() 42 { 43 return Locale::defaultLocale().queryString(blink::WebLocalizedString::AXDateTimeFieldEmptyValueText); 44 } 45 46 DateTimeFieldElement::FieldOwner::~FieldOwner() 47 { 48 } 49 50 DateTimeFieldElement::DateTimeFieldElement(Document& document, FieldOwner& fieldOwner) 51 : HTMLSpanElement(document) 52 , m_fieldOwner(&fieldOwner) 53 { 54 } 55 56 void DateTimeFieldElement::trace(Visitor* visitor) 57 { 58 visitor->trace(m_fieldOwner); 59 HTMLSpanElement::trace(visitor); 60 } 61 62 void DateTimeFieldElement::defaultEventHandler(Event* event) 63 { 64 if (event->isKeyboardEvent()) { 65 KeyboardEvent* keyboardEvent = toKeyboardEvent(event); 66 if (!isDisabled() && !isFieldOwnerDisabled() && !isFieldOwnerReadOnly()) { 67 handleKeyboardEvent(keyboardEvent); 68 if (keyboardEvent->defaultHandled()) { 69 if (m_fieldOwner) 70 m_fieldOwner->fieldDidChangeValueByKeyboard(); 71 return; 72 } 73 } 74 defaultKeyboardEventHandler(keyboardEvent); 75 if (m_fieldOwner) 76 m_fieldOwner->fieldDidChangeValueByKeyboard(); 77 if (keyboardEvent->defaultHandled()) 78 return; 79 } 80 81 HTMLElement::defaultEventHandler(event); 82 } 83 84 void DateTimeFieldElement::defaultKeyboardEventHandler(KeyboardEvent* keyboardEvent) 85 { 86 if (keyboardEvent->type() != EventTypeNames::keydown) 87 return; 88 89 if (isDisabled() || isFieldOwnerDisabled()) 90 return; 91 92 const String& keyIdentifier = keyboardEvent->keyIdentifier(); 93 94 if (keyIdentifier == "Left") { 95 if (!m_fieldOwner) 96 return; 97 // FIXME: We'd like to use FocusController::advanceFocus(FocusDirectionLeft, ...) 98 // but it doesn't work for shadow nodes. webkit.org/b/104650 99 if (!localeForOwner().isRTL() && m_fieldOwner->focusOnPreviousField(*this)) 100 keyboardEvent->setDefaultHandled(); 101 return; 102 } 103 104 if (keyIdentifier == "Right") { 105 if (!m_fieldOwner) 106 return; 107 // FIXME: We'd like to use FocusController::advanceFocus(FocusDirectionRight, ...) 108 // but it doesn't work for shadow nodes. webkit.org/b/104650 109 if (!localeForOwner().isRTL() && m_fieldOwner->focusOnNextField(*this)) 110 keyboardEvent->setDefaultHandled(); 111 return; 112 } 113 114 if (isFieldOwnerReadOnly()) 115 return; 116 117 if (keyIdentifier == "Down") { 118 if (keyboardEvent->getModifierState("Alt")) 119 return; 120 keyboardEvent->setDefaultHandled(); 121 stepDown(); 122 return; 123 } 124 125 if (keyIdentifier == "Up") { 126 keyboardEvent->setDefaultHandled(); 127 stepUp(); 128 return; 129 } 130 131 if (keyIdentifier == "U+0008" || keyIdentifier == "U+007F") { 132 keyboardEvent->setDefaultHandled(); 133 setEmptyValue(DispatchEvent); 134 return; 135 } 136 } 137 138 void DateTimeFieldElement::setFocus(bool value) 139 { 140 if (m_fieldOwner) 141 value ? m_fieldOwner->didFocusOnField() : m_fieldOwner->didBlurFromField(); 142 ContainerNode::setFocus(value); 143 } 144 145 void DateTimeFieldElement::focusOnNextField() 146 { 147 if (!m_fieldOwner) 148 return; 149 m_fieldOwner->focusOnNextField(*this); 150 } 151 152 void DateTimeFieldElement::initialize(const AtomicString& pseudo, const String& axHelpText, int axMinimum, int axMaximum) 153 { 154 // On accessibility, DateTimeFieldElement acts like spin button. 155 setAttribute(roleAttr, AtomicString("spinbutton", AtomicString::ConstructFromLiteral)); 156 setAttribute(aria_valuetextAttr, AtomicString(emptyValueAXText())); 157 setAttribute(aria_valueminAttr, AtomicString::number(axMinimum)); 158 setAttribute(aria_valuemaxAttr, AtomicString::number(axMaximum)); 159 160 setAttribute(aria_helpAttr, AtomicString(axHelpText)); 161 setShadowPseudoId(pseudo); 162 appendChild(Text::create(document(), visibleValue())); 163 } 164 165 bool DateTimeFieldElement::isDateTimeFieldElement() const 166 { 167 return true; 168 } 169 170 bool DateTimeFieldElement::isFieldOwnerDisabled() const 171 { 172 return m_fieldOwner && m_fieldOwner->isFieldOwnerDisabled(); 173 } 174 175 bool DateTimeFieldElement::isFieldOwnerReadOnly() const 176 { 177 return m_fieldOwner && m_fieldOwner->isFieldOwnerReadOnly(); 178 } 179 180 bool DateTimeFieldElement::isDisabled() const 181 { 182 return fastHasAttribute(disabledAttr); 183 } 184 185 Locale& DateTimeFieldElement::localeForOwner() const 186 { 187 return document().getCachedLocale(localeIdentifier()); 188 } 189 190 AtomicString DateTimeFieldElement::localeIdentifier() const 191 { 192 return m_fieldOwner ? m_fieldOwner->localeIdentifier() : nullAtom; 193 } 194 195 float DateTimeFieldElement::maximumWidth(const Font&) 196 { 197 const float paddingLeftAndRight = 2; // This should match to html.css. 198 return paddingLeftAndRight; 199 } 200 201 void DateTimeFieldElement::setDisabled() 202 { 203 // Set HTML attribute disabled to change apperance. 204 setBooleanAttribute(disabledAttr, true); 205 setNeedsStyleRecalc(SubtreeStyleChange); 206 } 207 208 bool DateTimeFieldElement::supportsFocus() const 209 { 210 return !isDisabled() && !isFieldOwnerDisabled(); 211 } 212 213 void DateTimeFieldElement::updateVisibleValue(EventBehavior eventBehavior) 214 { 215 Text* const textNode = toText(firstChild()); 216 const String newVisibleValue = visibleValue(); 217 ASSERT(newVisibleValue.length() > 0); 218 219 if (textNode->wholeText() == newVisibleValue) 220 return; 221 222 textNode->replaceWholeText(newVisibleValue); 223 if (hasValue()) { 224 setAttribute(aria_valuetextAttr, AtomicString(newVisibleValue)); 225 setAttribute(aria_valuenowAttr, AtomicString::number(valueForARIAValueNow())); 226 } else { 227 setAttribute(aria_valuetextAttr, AtomicString(emptyValueAXText())); 228 removeAttribute(aria_valuenowAttr); 229 } 230 231 if (eventBehavior == DispatchEvent && m_fieldOwner) 232 m_fieldOwner->fieldValueChanged(); 233 } 234 235 int DateTimeFieldElement::valueForARIAValueNow() const 236 { 237 return valueAsInteger(); 238 } 239 240 } // namespace blink 241 242 #endif 243