1cad810f21b803229eb11403f9209855525a25d57Steve Block/* 2cad810f21b803229eb11403f9209855525a25d57Steve Block * Copyright (C) 2005, 2011 Apple Inc. All rights reserved. 3cad810f21b803229eb11403f9209855525a25d57Steve Block * Copyright (C) 2010 Google Inc. All rights reserved. 4cad810f21b803229eb11403f9209855525a25d57Steve Block * 5cad810f21b803229eb11403f9209855525a25d57Steve Block * This library is free software; you can redistribute it and/or 6cad810f21b803229eb11403f9209855525a25d57Steve Block * modify it under the terms of the GNU Library General Public 7cad810f21b803229eb11403f9209855525a25d57Steve Block * License as published by the Free Software Foundation; either 8cad810f21b803229eb11403f9209855525a25d57Steve Block * version 2 of the License, or (at your option) any later version. 9cad810f21b803229eb11403f9209855525a25d57Steve Block * 10cad810f21b803229eb11403f9209855525a25d57Steve Block * This library is distributed in the hope that it will be useful, 11cad810f21b803229eb11403f9209855525a25d57Steve Block * but WITHOUT ANY WARRANTY; without even the implied warranty of 12cad810f21b803229eb11403f9209855525a25d57Steve Block * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13cad810f21b803229eb11403f9209855525a25d57Steve Block * Library General Public License for more details. 14cad810f21b803229eb11403f9209855525a25d57Steve Block * 15cad810f21b803229eb11403f9209855525a25d57Steve Block * You should have received a copy of the GNU Library General Public License 16cad810f21b803229eb11403f9209855525a25d57Steve Block * along with this library; see the file COPYING.LIB. If not, write to 17cad810f21b803229eb11403f9209855525a25d57Steve Block * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 18cad810f21b803229eb11403f9209855525a25d57Steve Block * Boston, MA 02110-1301, USA. 19cad810f21b803229eb11403f9209855525a25d57Steve Block * 20cad810f21b803229eb11403f9209855525a25d57Steve Block */ 21cad810f21b803229eb11403f9209855525a25d57Steve Block 22cad810f21b803229eb11403f9209855525a25d57Steve Block#include "config.h" 23cad810f21b803229eb11403f9209855525a25d57Steve Block#include "RadioInputType.h" 24cad810f21b803229eb11403f9209855525a25d57Steve Block 25cad810f21b803229eb11403f9209855525a25d57Steve Block#include "Frame.h" 26cad810f21b803229eb11403f9209855525a25d57Steve Block#include "HTMLInputElement.h" 27cad810f21b803229eb11403f9209855525a25d57Steve Block#include "HTMLNames.h" 28cad810f21b803229eb11403f9209855525a25d57Steve Block#include "KeyboardEvent.h" 29cad810f21b803229eb11403f9209855525a25d57Steve Block#include "LocalizedStrings.h" 30cad810f21b803229eb11403f9209855525a25d57Steve Block#include "MouseEvent.h" 31cad810f21b803229eb11403f9209855525a25d57Steve Block#include "Settings.h" 32cad810f21b803229eb11403f9209855525a25d57Steve Block#include "SpatialNavigation.h" 33cad810f21b803229eb11403f9209855525a25d57Steve Block#include <wtf/PassOwnPtr.h> 34cad810f21b803229eb11403f9209855525a25d57Steve Block 35cad810f21b803229eb11403f9209855525a25d57Steve Blocknamespace WebCore { 36cad810f21b803229eb11403f9209855525a25d57Steve Block 37cad810f21b803229eb11403f9209855525a25d57Steve Blockusing namespace HTMLNames; 38cad810f21b803229eb11403f9209855525a25d57Steve Block 39cad810f21b803229eb11403f9209855525a25d57Steve BlockPassOwnPtr<InputType> RadioInputType::create(HTMLInputElement* element) 40cad810f21b803229eb11403f9209855525a25d57Steve Block{ 41cad810f21b803229eb11403f9209855525a25d57Steve Block return adoptPtr(new RadioInputType(element)); 42cad810f21b803229eb11403f9209855525a25d57Steve Block} 43cad810f21b803229eb11403f9209855525a25d57Steve Block 44cad810f21b803229eb11403f9209855525a25d57Steve Blockconst AtomicString& RadioInputType::formControlType() const 45cad810f21b803229eb11403f9209855525a25d57Steve Block{ 46cad810f21b803229eb11403f9209855525a25d57Steve Block return InputTypeNames::radio(); 47cad810f21b803229eb11403f9209855525a25d57Steve Block} 48cad810f21b803229eb11403f9209855525a25d57Steve Block 49cad810f21b803229eb11403f9209855525a25d57Steve Blockbool RadioInputType::valueMissing(const String&) const 50cad810f21b803229eb11403f9209855525a25d57Steve Block{ 51cad810f21b803229eb11403f9209855525a25d57Steve Block return !element()->checkedRadioButtons().checkedButtonForGroup(element()->name()); 52cad810f21b803229eb11403f9209855525a25d57Steve Block} 53cad810f21b803229eb11403f9209855525a25d57Steve Block 54cad810f21b803229eb11403f9209855525a25d57Steve BlockString RadioInputType::valueMissingText() const 55cad810f21b803229eb11403f9209855525a25d57Steve Block{ 56cad810f21b803229eb11403f9209855525a25d57Steve Block return validationMessageValueMissingForRadioText(); 57cad810f21b803229eb11403f9209855525a25d57Steve Block} 58cad810f21b803229eb11403f9209855525a25d57Steve Block 59cad810f21b803229eb11403f9209855525a25d57Steve Blockvoid RadioInputType::handleClickEvent(MouseEvent* event) 60cad810f21b803229eb11403f9209855525a25d57Steve Block{ 61cad810f21b803229eb11403f9209855525a25d57Steve Block event->setDefaultHandled(); 62cad810f21b803229eb11403f9209855525a25d57Steve Block} 63cad810f21b803229eb11403f9209855525a25d57Steve Block 64cad810f21b803229eb11403f9209855525a25d57Steve Blockvoid RadioInputType::handleKeydownEvent(KeyboardEvent* event) 65cad810f21b803229eb11403f9209855525a25d57Steve Block{ 66cad810f21b803229eb11403f9209855525a25d57Steve Block BaseCheckableInputType::handleKeydownEvent(event); 67cad810f21b803229eb11403f9209855525a25d57Steve Block if (event->defaultHandled()) 68cad810f21b803229eb11403f9209855525a25d57Steve Block return; 69cad810f21b803229eb11403f9209855525a25d57Steve Block const String& key = event->keyIdentifier(); 70cad810f21b803229eb11403f9209855525a25d57Steve Block if (key != "Up" && key != "Down" && key != "Left" && key != "Right") 71cad810f21b803229eb11403f9209855525a25d57Steve Block return; 72cad810f21b803229eb11403f9209855525a25d57Steve Block 73cad810f21b803229eb11403f9209855525a25d57Steve Block // Left and up mean "previous radio button". 74cad810f21b803229eb11403f9209855525a25d57Steve Block // Right and down mean "next radio button". 75cad810f21b803229eb11403f9209855525a25d57Steve Block // Tested in WinIE, and even for RTL, left still means previous radio button (and so moves 76cad810f21b803229eb11403f9209855525a25d57Steve Block // to the right). Seems strange, but we'll match it. 77cad810f21b803229eb11403f9209855525a25d57Steve Block // However, when using Spatial Navigation, we need to be able to navigate without changing the selection. 78cad810f21b803229eb11403f9209855525a25d57Steve Block Document* document = element()->document(); 79cad810f21b803229eb11403f9209855525a25d57Steve Block if (isSpatialNavigationEnabled(document->frame())) 80cad810f21b803229eb11403f9209855525a25d57Steve Block return; 81cad810f21b803229eb11403f9209855525a25d57Steve Block bool forward = (key == "Down" || key == "Right"); 82cad810f21b803229eb11403f9209855525a25d57Steve Block 83cad810f21b803229eb11403f9209855525a25d57Steve Block // We can only stay within the form's children if the form hasn't been demoted to a leaf because 84cad810f21b803229eb11403f9209855525a25d57Steve Block // of malformed HTML. 85cad810f21b803229eb11403f9209855525a25d57Steve Block Node* node = element(); 86cad810f21b803229eb11403f9209855525a25d57Steve Block while ((node = (forward ? node->traverseNextNode() : node->traversePreviousNode()))) { 87cad810f21b803229eb11403f9209855525a25d57Steve Block // Once we encounter a form element, we know we're through. 88cad810f21b803229eb11403f9209855525a25d57Steve Block if (node->hasTagName(formTag)) 89cad810f21b803229eb11403f9209855525a25d57Steve Block break; 90cad810f21b803229eb11403f9209855525a25d57Steve Block // Look for more radio buttons. 91cad810f21b803229eb11403f9209855525a25d57Steve Block if (!node->hasTagName(inputTag)) 92cad810f21b803229eb11403f9209855525a25d57Steve Block continue; 93cad810f21b803229eb11403f9209855525a25d57Steve Block HTMLInputElement* inputElement = static_cast<HTMLInputElement*>(node); 94cad810f21b803229eb11403f9209855525a25d57Steve Block if (inputElement->form() != element()->form()) 95cad810f21b803229eb11403f9209855525a25d57Steve Block break; 96cad810f21b803229eb11403f9209855525a25d57Steve Block if (inputElement->isRadioButton() && inputElement->name() == element()->name() && inputElement->isFocusable()) { 97cad810f21b803229eb11403f9209855525a25d57Steve Block inputElement->setChecked(true); 98cad810f21b803229eb11403f9209855525a25d57Steve Block document->setFocusedNode(inputElement); 99cad810f21b803229eb11403f9209855525a25d57Steve Block inputElement->dispatchSimulatedClick(event, false, false); 100cad810f21b803229eb11403f9209855525a25d57Steve Block event->setDefaultHandled(); 101cad810f21b803229eb11403f9209855525a25d57Steve Block return; 102cad810f21b803229eb11403f9209855525a25d57Steve Block } 103cad810f21b803229eb11403f9209855525a25d57Steve Block } 104cad810f21b803229eb11403f9209855525a25d57Steve Block} 105cad810f21b803229eb11403f9209855525a25d57Steve Block 106cad810f21b803229eb11403f9209855525a25d57Steve Blockvoid RadioInputType::handleKeyupEvent(KeyboardEvent* event) 107cad810f21b803229eb11403f9209855525a25d57Steve Block{ 108cad810f21b803229eb11403f9209855525a25d57Steve Block const String& key = event->keyIdentifier(); 109cad810f21b803229eb11403f9209855525a25d57Steve Block if (key != "U+0020") 110cad810f21b803229eb11403f9209855525a25d57Steve Block return; 111cad810f21b803229eb11403f9209855525a25d57Steve Block // If an unselected radio is tabbed into (because the entire group has nothing 112cad810f21b803229eb11403f9209855525a25d57Steve Block // checked, or because of some explicit .focus() call), then allow space to check it. 113cad810f21b803229eb11403f9209855525a25d57Steve Block if (element()->checked()) 114cad810f21b803229eb11403f9209855525a25d57Steve Block return; 115cad810f21b803229eb11403f9209855525a25d57Steve Block dispatchSimulatedClickIfActive(event); 116cad810f21b803229eb11403f9209855525a25d57Steve Block} 117cad810f21b803229eb11403f9209855525a25d57Steve Block 118cad810f21b803229eb11403f9209855525a25d57Steve Blockbool RadioInputType::isKeyboardFocusable() const 119cad810f21b803229eb11403f9209855525a25d57Steve Block{ 120cad810f21b803229eb11403f9209855525a25d57Steve Block // When using Spatial Navigation, every radio button should be focusable. 121cad810f21b803229eb11403f9209855525a25d57Steve Block if (isSpatialNavigationEnabled(element()->document()->frame())) 122cad810f21b803229eb11403f9209855525a25d57Steve Block return true; 123cad810f21b803229eb11403f9209855525a25d57Steve Block 124cad810f21b803229eb11403f9209855525a25d57Steve Block // Never allow keyboard tabbing to leave you in the same radio group. Always 125cad810f21b803229eb11403f9209855525a25d57Steve Block // skip any other elements in the group. 126cad810f21b803229eb11403f9209855525a25d57Steve Block Node* currentFocusedNode = element()->document()->focusedNode(); 127cad810f21b803229eb11403f9209855525a25d57Steve Block if (currentFocusedNode && currentFocusedNode->hasTagName(inputTag)) { 128cad810f21b803229eb11403f9209855525a25d57Steve Block HTMLInputElement* focusedInput = static_cast<HTMLInputElement*>(currentFocusedNode); 129cad810f21b803229eb11403f9209855525a25d57Steve Block if (focusedInput->isRadioButton() && focusedInput->form() == element()->form() && focusedInput->name() == element()->name()) 130cad810f21b803229eb11403f9209855525a25d57Steve Block return false; 131cad810f21b803229eb11403f9209855525a25d57Steve Block } 132cad810f21b803229eb11403f9209855525a25d57Steve Block 133cad810f21b803229eb11403f9209855525a25d57Steve Block // Allow keyboard focus if we're checked or if nothing in the group is checked. 134cad810f21b803229eb11403f9209855525a25d57Steve Block return element()->checked() || !element()->checkedRadioButtons().checkedButtonForGroup(element()->name()); 135cad810f21b803229eb11403f9209855525a25d57Steve Block} 136cad810f21b803229eb11403f9209855525a25d57Steve Block 137cad810f21b803229eb11403f9209855525a25d57Steve Blockvoid RadioInputType::attach() 138cad810f21b803229eb11403f9209855525a25d57Steve Block{ 139cad810f21b803229eb11403f9209855525a25d57Steve Block InputType::attach(); 140cad810f21b803229eb11403f9209855525a25d57Steve Block element()->updateCheckedRadioButtons(); 141cad810f21b803229eb11403f9209855525a25d57Steve Block} 142cad810f21b803229eb11403f9209855525a25d57Steve Block 143cad810f21b803229eb11403f9209855525a25d57Steve Blockbool RadioInputType::shouldSendChangeEventAfterCheckedChanged() 144cad810f21b803229eb11403f9209855525a25d57Steve Block{ 145cad810f21b803229eb11403f9209855525a25d57Steve Block // Don't send a change event for a radio button that's getting unchecked. 146cad810f21b803229eb11403f9209855525a25d57Steve Block // This was done to match the behavior of other browsers. 147cad810f21b803229eb11403f9209855525a25d57Steve Block return element()->checked(); 148cad810f21b803229eb11403f9209855525a25d57Steve Block} 149cad810f21b803229eb11403f9209855525a25d57Steve Block 150cad810f21b803229eb11403f9209855525a25d57Steve BlockPassOwnPtr<ClickHandlingState> RadioInputType::willDispatchClick() 151cad810f21b803229eb11403f9209855525a25d57Steve Block{ 152cad810f21b803229eb11403f9209855525a25d57Steve Block // An event handler can use preventDefault or "return false" to reverse the selection we do here. 153cad810f21b803229eb11403f9209855525a25d57Steve Block // The ClickHandlingState object contains what we need to undo what we did here in didDispatchClick. 154cad810f21b803229eb11403f9209855525a25d57Steve Block 155cad810f21b803229eb11403f9209855525a25d57Steve Block // We want radio groups to end up in sane states, i.e., to have something checked. 156cad810f21b803229eb11403f9209855525a25d57Steve Block // Therefore if nothing is currently selected, we won't allow the upcoming action to be "undone", since 157cad810f21b803229eb11403f9209855525a25d57Steve Block // we want some object in the radio group to actually get selected. 158cad810f21b803229eb11403f9209855525a25d57Steve Block 159cad810f21b803229eb11403f9209855525a25d57Steve Block OwnPtr<ClickHandlingState> state = adoptPtr(new ClickHandlingState); 160cad810f21b803229eb11403f9209855525a25d57Steve Block 161cad810f21b803229eb11403f9209855525a25d57Steve Block state->checked = element()->checked(); 162cad810f21b803229eb11403f9209855525a25d57Steve Block state->indeterminate = element()->indeterminate(); 163cad810f21b803229eb11403f9209855525a25d57Steve Block state->checkedRadioButton = element()->checkedRadioButtons().checkedButtonForGroup(element()->name()); 164cad810f21b803229eb11403f9209855525a25d57Steve Block 165cad810f21b803229eb11403f9209855525a25d57Steve Block if (element()->indeterminate()) 166cad810f21b803229eb11403f9209855525a25d57Steve Block element()->setIndeterminate(false); 167cad810f21b803229eb11403f9209855525a25d57Steve Block element()->setChecked(true, true); 168cad810f21b803229eb11403f9209855525a25d57Steve Block 169cad810f21b803229eb11403f9209855525a25d57Steve Block return state.release(); 170cad810f21b803229eb11403f9209855525a25d57Steve Block} 171cad810f21b803229eb11403f9209855525a25d57Steve Block 172cad810f21b803229eb11403f9209855525a25d57Steve Blockvoid RadioInputType::didDispatchClick(Event* event, const ClickHandlingState& state) 173cad810f21b803229eb11403f9209855525a25d57Steve Block{ 174cad810f21b803229eb11403f9209855525a25d57Steve Block if (event->defaultPrevented() || event->defaultHandled()) { 175cad810f21b803229eb11403f9209855525a25d57Steve Block // Restore the original selected radio button if possible. 176cad810f21b803229eb11403f9209855525a25d57Steve Block // Make sure it is still a radio button and only do the restoration if it still belongs to our group. 177cad810f21b803229eb11403f9209855525a25d57Steve Block HTMLInputElement* checkedRadioButton = state.checkedRadioButton.get(); 178cad810f21b803229eb11403f9209855525a25d57Steve Block if (checkedRadioButton 179cad810f21b803229eb11403f9209855525a25d57Steve Block && checkedRadioButton->isRadioButton() 180cad810f21b803229eb11403f9209855525a25d57Steve Block && checkedRadioButton->form() == element()->form() 181cad810f21b803229eb11403f9209855525a25d57Steve Block && checkedRadioButton->name() == element()->name()) { 182cad810f21b803229eb11403f9209855525a25d57Steve Block checkedRadioButton->setChecked(true); 183cad810f21b803229eb11403f9209855525a25d57Steve Block } 184cad810f21b803229eb11403f9209855525a25d57Steve Block element()->setIndeterminate(state.indeterminate); 185cad810f21b803229eb11403f9209855525a25d57Steve Block } 186cad810f21b803229eb11403f9209855525a25d57Steve Block 187cad810f21b803229eb11403f9209855525a25d57Steve Block // The work we did in willDispatchClick was default handling. 188cad810f21b803229eb11403f9209855525a25d57Steve Block event->setDefaultHandled(); 189cad810f21b803229eb11403f9209855525a25d57Steve Block} 190cad810f21b803229eb11403f9209855525a25d57Steve Block 191cad810f21b803229eb11403f9209855525a25d57Steve Blockbool RadioInputType::isRadioButton() const 192cad810f21b803229eb11403f9209855525a25d57Steve Block{ 193cad810f21b803229eb11403f9209855525a25d57Steve Block return true; 194cad810f21b803229eb11403f9209855525a25d57Steve Block} 195cad810f21b803229eb11403f9209855525a25d57Steve Block 196cad810f21b803229eb11403f9209855525a25d57Steve Block} // namespace WebCore 197