2 * Copyright (C) 1999 Lars Knoll (knoll@kde.org)
3 * (C) 1999 Antti Koivisto (koivisto@kde.org)
4 * (C) 2001 Dirk Mueller (mueller@kde.org)
5 * Copyright (C) 2004, 2005, 2006, 2007, 2010 Apple Inc. All rights reserved.
6 * (C) 2006 Alexey Proskuryakov (ap@nypop.com)
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
26 #include "core/html/HTMLLabelElement.h"
28 #include "core/HTMLNames.h"
29 #include "core/dom/Document.h"
30 #include "core/dom/ElementTraversal.h"
31 #include "core/editing/EditingUtilities.h"
32 #include "core/editing/FrameSelection.h"
33 #include "core/editing/SelectionController.h"
34 #include "core/events/MouseEvent.h"
35 #include "core/frame/LocalFrame.h"
36 #include "core/html/FormAssociatedElement.h"
37 #include "core/input/EventHandler.h"
41 using namespace HTMLNames
;
43 inline HTMLLabelElement::HTMLLabelElement(Document
& document
, HTMLFormElement
* form
)
44 : HTMLElement(labelTag
, document
)
45 , m_processingClick(false)
47 FormAssociatedElement::associateByParser(form
);
50 PassRefPtrWillBeRawPtr
<HTMLLabelElement
> HTMLLabelElement::create(Document
& document
, HTMLFormElement
* form
)
52 RefPtrWillBeRawPtr
<HTMLLabelElement
> labelElement
= adoptRefWillBeNoop(new HTMLLabelElement(document
, form
));
53 return labelElement
.release();
56 bool HTMLLabelElement::layoutObjectIsFocusable() const
58 HTMLLabelElement
* that
= const_cast<HTMLLabelElement
*>(this);
59 return that
->isContentEditable();
62 LabelableElement
* HTMLLabelElement::control() const
64 const AtomicString
& controlId
= getAttribute(forAttr
);
65 if (controlId
.isNull()) {
66 // Search the children and descendants of the label element for a form element.
67 // per http://dev.w3.org/html5/spec/Overview.html#the-label-element
68 // the form element must be "labelable form-associated element".
69 for (LabelableElement
& element
: Traversal
<LabelableElement
>::descendantsOf(*this)) {
70 if (element
.supportLabels())
76 if (Element
* element
= treeScope().getElementById(controlId
)) {
77 if (isLabelableElement(*element
) && toLabelableElement(*element
).supportLabels())
78 return toLabelableElement(element
);
84 HTMLFormElement
* HTMLLabelElement::formOwner() const
86 return FormAssociatedElement::form();
89 void HTMLLabelElement::setActive(bool down
)
91 if (down
!= active()) {
92 // Update our status first.
93 HTMLElement::setActive(down
);
96 // Also update our corresponding control.
97 HTMLElement
* controlElement
= control();
98 if (controlElement
&& controlElement
->active() != active())
99 controlElement
->setActive(active());
102 void HTMLLabelElement::setHovered(bool over
)
104 if (over
!= hovered()) {
105 // Update our status first.
106 HTMLElement::setHovered(over
);
109 // Also update our corresponding control.
110 HTMLElement
* element
= control();
111 if (element
&& element
->hovered() != hovered())
112 element
->setHovered(hovered());
115 bool HTMLLabelElement::isInteractiveContent() const
120 bool HTMLLabelElement::isInInteractiveContent(Node
* node
) const
122 if (!containsIncludingShadowDOM(node
))
124 while (node
&& this != node
) {
125 if (node
->isHTMLElement() && toHTMLElement(node
)->isInteractiveContent())
127 node
= node
->parentOrShadowHostNode();
132 void HTMLLabelElement::defaultEventHandler(Event
* evt
)
134 if (evt
->type() == EventTypeNames::click
&& !m_processingClick
) {
135 RefPtrWillBeRawPtr
<HTMLElement
> element
= control();
137 // If we can't find a control or if the control received the click
138 // event, then there's no need for us to do anything.
139 if (!element
|| (evt
->target() && element
->containsIncludingShadowDOM(evt
->target()->toNode())))
142 if (evt
->target() && isInInteractiveContent(evt
->target()->toNode()))
145 // Behaviour of label element is as follows:
146 // - If there is double click, two clicks will be passed to control
147 // element. Control element will *not* be focused.
148 // - If there is selection of label element by dragging, no click
149 // event is passed. Also, no focus on control element.
150 // - If there is already a selection on label element and then label
151 // is clicked, then click event is passed to control element and
152 // control element is focused.
154 bool isLabelTextSelected
= false;
156 // If the click is not simulated and the text of the label element
157 // is selected by dragging over it, then return without passing the
158 // click event to control element.
159 // Note: a click event may be not a mouse event if created by
160 // document.createEvent().
161 if (evt
->isMouseEvent() && !toMouseEvent(evt
)->isSimulated()) {
162 if (LocalFrame
* frame
= document().frame()) {
163 // Check if there is a selection and click is not on the
165 if (!nodeIsUserSelectNone(this) && frame
->selection().isRange() && !frame
->eventHandler().selectionController().mouseDownWasSingleClickInSelection())
166 isLabelTextSelected
= true;
167 // If selection is there and is single click i.e. text is
168 // selected by dragging over label text, then return.
169 // Click count >=2, meaning double click or triple click,
170 // should pass click event to control element.
171 // Only in case of drag, *neither* we pass the click event,
172 // *nor* we focus the control element.
173 if (isLabelTextSelected
&& frame
->eventHandler().clickCount() == 1)
178 m_processingClick
= true;
180 document().updateLayoutIgnorePendingStylesheets();
181 if (element
->isMouseFocusable()) {
182 // If the label is *not* selected, or if the click happened on
183 // selection of label, only then focus the control element.
184 // In case of double click or triple click, selection will be there,
185 // so do not focus the control element.
186 if (!isLabelTextSelected
)
187 element
->focus(true, WebFocusTypeMouse
);
190 // Click the corresponding control.
191 element
->dispatchSimulatedClick(evt
);
193 m_processingClick
= false;
195 evt
->setDefaultHandled();
198 HTMLElement::defaultEventHandler(evt
);
201 bool HTMLLabelElement::willRespondToMouseClickEvents()
203 if (control() && control()->willRespondToMouseClickEvents())
206 return HTMLElement::willRespondToMouseClickEvents();
209 void HTMLLabelElement::focus(bool, WebFocusType type
, InputDeviceCapabilities
* sourceCapabilities
)
211 // to match other browsers, always restore previous selection
212 if (HTMLElement
* element
= control())
213 element
->focus(true, type
, sourceCapabilities
);
215 HTMLElement::focus(true, type
, sourceCapabilities
);
218 void HTMLLabelElement::accessKeyAction(bool sendMouseEvents
)
220 if (HTMLElement
* element
= control())
221 element
->accessKeyAction(sendMouseEvents
);
223 HTMLElement::accessKeyAction(sendMouseEvents
);
226 void HTMLLabelElement::updateLabel(TreeScope
& scope
, const AtomicString
& oldForAttributeValue
, const AtomicString
& newForAttributeValue
)
231 if (oldForAttributeValue
== newForAttributeValue
)
234 if (!oldForAttributeValue
.isEmpty())
235 scope
.removeLabel(oldForAttributeValue
, this);
236 if (!newForAttributeValue
.isEmpty())
237 scope
.addLabel(newForAttributeValue
, this);
240 void HTMLLabelElement::attributeWillChange(const QualifiedName
& name
, const AtomicString
& oldValue
, const AtomicString
& newValue
)
242 if (name
== HTMLNames::forAttr
) {
243 TreeScope
& scope
= treeScope();
244 if (scope
.shouldCacheLabelsByForAttribute())
245 updateLabel(scope
, oldValue
, newValue
);
247 HTMLElement::attributeWillChange(name
, oldValue
, newValue
);
250 Node::InsertionNotificationRequest
HTMLLabelElement::insertedInto(ContainerNode
* insertionPoint
)
252 InsertionNotificationRequest result
= HTMLElement::insertedInto(insertionPoint
);
253 FormAssociatedElement::insertedInto(insertionPoint
);
254 if (insertionPoint
->isInTreeScope()) {
255 TreeScope
& scope
= insertionPoint
->treeScope();
256 if (scope
== treeScope() && scope
.shouldCacheLabelsByForAttribute())
257 updateLabel(scope
, nullAtom
, fastGetAttribute(forAttr
));
260 // Trigger for elements outside of forms.
261 if (!formOwner() && insertionPoint
->inDocument())
262 document().didAssociateFormControl(this);
267 void HTMLLabelElement::removedFrom(ContainerNode
* insertionPoint
)
269 if (insertionPoint
->isInTreeScope() && treeScope() == document()) {
270 TreeScope
& treeScope
= insertionPoint
->treeScope();
271 if (treeScope
.shouldCacheLabelsByForAttribute())
272 updateLabel(treeScope
, fastGetAttribute(forAttr
), nullAtom
);
274 HTMLElement::removedFrom(insertionPoint
);
275 FormAssociatedElement::removedFrom(insertionPoint
);
276 document().removeFormAssociation(this);
279 DEFINE_TRACE(HTMLLabelElement
)
281 HTMLElement::trace(visitor
);
282 FormAssociatedElement::trace(visitor
);
285 void HTMLLabelElement::parseAttribute(const QualifiedName
& attributeName
, const AtomicString
& attributeValue
)
287 if (attributeName
== formAttr
)
288 formAttributeChanged();
290 HTMLElement::parseAttribute(attributeName
, attributeValue
);