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