Keep auxilliary media objects on the heap always.
[chromium-blink-merge.git] / third_party / WebKit / Source / core / html / HTMLLabelElement.cpp
blob72b328be2c5e1dc8f3b84d6f397164caea89d26b
1 /*
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.
25 #include "config.h"
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"
39 namespace blink {
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())
71 return &element;
73 return nullptr;
76 if (Element* element = treeScope().getElementById(controlId)) {
77 if (isLabelableElement(*element) && toLabelableElement(*element).supportLabels())
78 return toLabelableElement(element);
81 return nullptr;
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
117 return true;
120 bool HTMLLabelElement::isInInteractiveContent(Node* node) const
122 if (!containsIncludingShadowDOM(node))
123 return false;
124 while (node && this != node) {
125 if (node->isHTMLElement() && toHTMLElement(node)->isInteractiveContent())
126 return true;
127 node = node->parentOrShadowHostNode();
129 return false;
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())))
140 return;
142 if (evt->target() && isInInteractiveContent(evt->target()->toNode()))
143 return;
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
164 // selection.
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)
174 return;
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())
204 return true;
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);
214 if (isFocusable())
215 HTMLElement::focus(true, type, sourceCapabilities);
218 void HTMLLabelElement::accessKeyAction(bool sendMouseEvents)
220 if (HTMLElement* element = control())
221 element->accessKeyAction(sendMouseEvents);
222 else
223 HTMLElement::accessKeyAction(sendMouseEvents);
226 void HTMLLabelElement::updateLabel(TreeScope& scope, const AtomicString& oldForAttributeValue, const AtomicString& newForAttributeValue)
228 if (!inDocument())
229 return;
231 if (oldForAttributeValue == newForAttributeValue)
232 return;
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);
264 return result;
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();
289 else
290 HTMLElement::parseAttribute(attributeName, attributeValue);
293 } // namespace