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 "HTMLNames.h" 31 #include "core/dom/Text.h" 32 #include "core/events/KeyboardEvent.h" 33 #include "platform/text/PlatformLocale.h" 34 #include "wtf/text/WTFString.h" 35 36 namespace WebCore { 37 38 using namespace HTMLNames; 39 40 static String emptyValueAXText() 41 { 42 return Locale::defaultLocale().queryString(blink::WebLocalizedString::AXDateTimeFieldEmptyValueText); 43 } 44 45 DateTimeFieldElement::FieldOwner::~FieldOwner() 46 { 47 } 48 49 DateTimeFieldElement::DateTimeFieldElement(Document& document, FieldOwner& fieldOwner) 50 : HTMLSpanElement(document) 51 , m_fieldOwner(&fieldOwner) 52 { 53 } 54 55 void DateTimeFieldElement::defaultEventHandler(Event* event) 56 { 57 if (event->type() == EventTypeNames::blur) 58 didBlur(); 59 60 if (event->type() == EventTypeNames::focus) 61 didFocus(); 62 63 if (event->isKeyboardEvent()) { 64 KeyboardEvent* keyboardEvent = toKeyboardEvent(event); 65 if (!isDisabled() && !isFieldOwnerDisabled() && !isFieldOwnerReadOnly()) { 66 handleKeyboardEvent(keyboardEvent); 67 if (keyboardEvent->defaultHandled()) 68 return; 69 } 70 defaultKeyboardEventHandler(keyboardEvent); 71 if (keyboardEvent->defaultHandled()) 72 return; 73 } 74 75 HTMLElement::defaultEventHandler(event); 76 } 77 78 void DateTimeFieldElement::defaultKeyboardEventHandler(KeyboardEvent* keyboardEvent) 79 { 80 if (keyboardEvent->type() != EventTypeNames::keydown) 81 return; 82 83 if (isDisabled() || isFieldOwnerDisabled()) 84 return; 85 86 const String& keyIdentifier = keyboardEvent->keyIdentifier(); 87 88 if (keyIdentifier == "Left") { 89 if (!m_fieldOwner) 90 return; 91 // FIXME: We'd like to use FocusController::advanceFocus(FocusDirectionLeft, ...) 92 // but it doesn't work for shadow nodes. webkit.org/b/104650 93 if (!localeForOwner().isRTL() && m_fieldOwner->focusOnPreviousField(*this)) 94 keyboardEvent->setDefaultHandled(); 95 return; 96 } 97 98 if (keyIdentifier == "Right") { 99 if (!m_fieldOwner) 100 return; 101 // FIXME: We'd like to use FocusController::advanceFocus(FocusDirectionRight, ...) 102 // but it doesn't work for shadow nodes. webkit.org/b/104650 103 if (!localeForOwner().isRTL() && m_fieldOwner->focusOnNextField(*this)) 104 keyboardEvent->setDefaultHandled(); 105 return; 106 } 107 108 if (isFieldOwnerReadOnly()) 109 return; 110 111 if (keyIdentifier == "Down") { 112 if (keyboardEvent->getModifierState("Alt")) 113 return; 114 keyboardEvent->setDefaultHandled(); 115 stepDown(); 116 return; 117 } 118 119 if (keyIdentifier == "Up") { 120 keyboardEvent->setDefaultHandled(); 121 stepUp(); 122 return; 123 } 124 125 if (keyIdentifier == "U+0008" || keyIdentifier == "U+007F") { 126 keyboardEvent->setDefaultHandled(); 127 setEmptyValue(DispatchEvent); 128 return; 129 } 130 } 131 132 void DateTimeFieldElement::didBlur() 133 { 134 if (m_fieldOwner) 135 m_fieldOwner->didBlurFromField(); 136 } 137 138 void DateTimeFieldElement::didFocus() 139 { 140 if (m_fieldOwner) 141 m_fieldOwner->didFocusOnField(); 142 } 143 144 void DateTimeFieldElement::focusOnNextField() 145 { 146 if (!m_fieldOwner) 147 return; 148 m_fieldOwner->focusOnNextField(*this); 149 } 150 151 void DateTimeFieldElement::initialize(const AtomicString& pseudo, const String& axHelpText, int axMinimum, int axMaximum) 152 { 153 // On accessibility, DateTimeFieldElement acts like spin button. 154 setAttribute(roleAttr, AtomicString("spinbutton", AtomicString::ConstructFromLiteral)); 155 setAttribute(aria_valuetextAttr, emptyValueAXText()); 156 setAttribute(aria_valueminAttr, String::number(axMinimum)); 157 setAttribute(aria_valuemaxAttr, String::number(axMaximum)); 158 159 setAttribute(aria_helpAttr, axHelpText); 160 setPseudo(pseudo); 161 appendChild(Text::create(document(), visibleValue())); 162 } 163 164 bool DateTimeFieldElement::isDateTimeFieldElement() const 165 { 166 return true; 167 } 168 169 bool DateTimeFieldElement::isFieldOwnerDisabled() const 170 { 171 return m_fieldOwner && m_fieldOwner->isFieldOwnerDisabled(); 172 } 173 174 bool DateTimeFieldElement::isFieldOwnerReadOnly() const 175 { 176 return m_fieldOwner && m_fieldOwner->isFieldOwnerReadOnly(); 177 } 178 179 bool DateTimeFieldElement::isDisabled() const 180 { 181 return fastHasAttribute(disabledAttr); 182 } 183 184 Locale& DateTimeFieldElement::localeForOwner() const 185 { 186 return document().getCachedLocale(localeIdentifier()); 187 } 188 189 AtomicString DateTimeFieldElement::localeIdentifier() const 190 { 191 return m_fieldOwner ? m_fieldOwner->localeIdentifier() : nullAtom; 192 } 193 194 float DateTimeFieldElement::maximumWidth(const Font&) 195 { 196 const float paddingLeftAndRight = 2; // This should match to html.css. 197 return paddingLeftAndRight; 198 } 199 200 void DateTimeFieldElement::setDisabled() 201 { 202 // Set HTML attribute disabled to change apperance. 203 setBooleanAttribute(disabledAttr, true); 204 setNeedsStyleRecalc(); 205 } 206 207 bool DateTimeFieldElement::supportsFocus() const 208 { 209 return !isDisabled() && !isFieldOwnerDisabled(); 210 } 211 212 void DateTimeFieldElement::updateVisibleValue(EventBehavior eventBehavior) 213 { 214 Text* const textNode = toText(firstChild()); 215 const String newVisibleValue = visibleValue(); 216 ASSERT(newVisibleValue.length() > 0); 217 218 if (textNode->wholeText() == newVisibleValue) 219 return; 220 221 textNode->replaceWholeText(newVisibleValue); 222 if (hasValue()) { 223 setAttribute(aria_valuetextAttr, newVisibleValue); 224 setAttribute(aria_valuenowAttr, String::number(valueForARIAValueNow())); 225 } else { 226 setAttribute(aria_valuetextAttr, emptyValueAXText()); 227 removeAttribute(aria_valuenowAttr); 228 } 229 230 if (eventBehavior == DispatchEvent && m_fieldOwner) 231 m_fieldOwner->fieldValueChanged(); 232 } 233 234 int DateTimeFieldElement::valueForARIAValueNow() const 235 { 236 return valueAsInteger(); 237 } 238 239 } // namespace WebCore 240 241 #endif 242