Backed out changeset 2450366cf7ca (bug 1891629) for causing win msix mochitest failures
[gecko.git] / dom / html / HTMLLabelElement.cpp
blob32acbd06bed9c5cf2420d26145a7b6175bc1b140
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 /**
8 * Implementation of HTML <label> elements.
9 */
10 #include "HTMLLabelElement.h"
11 #include "mozilla/EventDispatcher.h"
12 #include "mozilla/MouseEvents.h"
13 #include "mozilla/dom/HTMLLabelElementBinding.h"
14 #include "mozilla/dom/MouseEventBinding.h"
15 #include "nsFocusManager.h"
16 #include "nsIFrame.h"
17 #include "nsContentUtils.h"
18 #include "nsQueryObject.h"
19 #include "mozilla/dom/ShadowRoot.h"
21 // construction, destruction
23 NS_IMPL_NS_NEW_HTML_ELEMENT(Label)
25 namespace mozilla::dom {
27 HTMLLabelElement::~HTMLLabelElement() = default;
29 JSObject* HTMLLabelElement::WrapNode(JSContext* aCx,
30 JS::Handle<JSObject*> aGivenProto) {
31 return HTMLLabelElement_Binding::Wrap(aCx, this, aGivenProto);
34 // nsIDOMHTMLLabelElement
36 NS_IMPL_ELEMENT_CLONE(HTMLLabelElement)
38 HTMLFormElement* HTMLLabelElement::GetForm() const {
39 nsGenericHTMLElement* control = GetControl();
40 if (!control) {
41 return nullptr;
44 // Not all labeled things have a form association. Stick to the ones that do.
45 nsCOMPtr<nsIFormControl> formControl = do_QueryObject(control);
46 if (!formControl) {
47 return nullptr;
50 return formControl->GetForm();
53 void HTMLLabelElement::Focus(const FocusOptions& aOptions,
54 const CallerType aCallerType,
55 ErrorResult& aError) {
57 nsIFrame* frame = GetPrimaryFrame(FlushType::Frames);
58 if (frame && frame->IsFocusable()) {
59 return nsGenericHTMLElement::Focus(aOptions, aCallerType, aError);
63 if (RefPtr<Element> elem = GetLabeledElement()) {
64 return elem->Focus(aOptions, aCallerType, aError);
68 nsresult HTMLLabelElement::PostHandleEvent(EventChainPostVisitor& aVisitor) {
69 WidgetMouseEvent* mouseEvent = aVisitor.mEvent->AsMouseEvent();
70 if (mHandlingEvent ||
71 (!(mouseEvent && mouseEvent->IsLeftClickEvent()) &&
72 aVisitor.mEvent->mMessage != eMouseDown) ||
73 aVisitor.mEventStatus == nsEventStatus_eConsumeNoDefault ||
74 !aVisitor.mPresContext ||
75 // Don't handle the event if it's already been handled by another label
76 aVisitor.mEvent->mFlags.mMultipleActionsPrevented) {
77 return NS_OK;
80 nsCOMPtr<Element> target =
81 do_QueryInterface(aVisitor.mEvent->GetOriginalDOMEventTarget());
82 if (nsContentUtils::IsInInteractiveHTMLContent(target, this)) {
83 return NS_OK;
86 // Strong ref because event dispatch is going to happen.
87 RefPtr<Element> content = GetLabeledElement();
89 if (!content || content->IsDisabled()) {
90 return NS_OK;
93 mHandlingEvent = true;
94 switch (aVisitor.mEvent->mMessage) {
95 case eMouseDown:
96 if (mouseEvent->mButton == MouseButton::ePrimary) {
97 // We reset the mouse-down point on every event because there is
98 // no guarantee we will reach the eMouseClick code below.
99 LayoutDeviceIntPoint* curPoint =
100 new LayoutDeviceIntPoint(mouseEvent->mRefPoint);
101 SetProperty(nsGkAtoms::labelMouseDownPtProperty,
102 static_cast<void*>(curPoint),
103 nsINode::DeleteProperty<LayoutDeviceIntPoint>);
105 break;
107 case eMouseClick:
108 if (mouseEvent->IsLeftClickEvent()) {
109 LayoutDeviceIntPoint* mouseDownPoint =
110 static_cast<LayoutDeviceIntPoint*>(
111 GetProperty(nsGkAtoms::labelMouseDownPtProperty));
113 bool dragSelect = false;
114 if (mouseDownPoint) {
115 LayoutDeviceIntPoint dragDistance = *mouseDownPoint;
116 RemoveProperty(nsGkAtoms::labelMouseDownPtProperty);
118 dragDistance -= mouseEvent->mRefPoint;
119 const int CLICK_DISTANCE = 2;
120 dragSelect = dragDistance.x > CLICK_DISTANCE ||
121 dragDistance.x < -CLICK_DISTANCE ||
122 dragDistance.y > CLICK_DISTANCE ||
123 dragDistance.y < -CLICK_DISTANCE;
125 // Don't click the for-content if we did drag-select text or if we
126 // have a kbd modifier (which adjusts a selection).
127 if (dragSelect || mouseEvent->IsShift() || mouseEvent->IsControl() ||
128 mouseEvent->IsAlt() || mouseEvent->IsMeta()) {
129 break;
131 // Only set focus on the first click of multiple clicks to prevent
132 // to prevent immediate de-focus.
133 if (mouseEvent->mClickCount <= 1) {
134 if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
135 // Use FLAG_BYMOVEFOCUS here so that the label is scrolled to.
136 // Also, within HTMLInputElement::PostHandleEvent, inputs will
137 // be selected only when focused via a key or when the navigation
138 // flag is used and we want to select the text on label clicks as
139 // well.
140 // If the label has been clicked by the user, we also want to
141 // pass FLAG_BYMOUSE so that we get correct focus ring behavior,
142 // but we don't want to pass FLAG_BYMOUSE if this click event was
143 // caused by the user pressing an accesskey.
144 bool byMouse = (mouseEvent->mInputSource !=
145 MouseEvent_Binding::MOZ_SOURCE_KEYBOARD);
146 bool byTouch = (mouseEvent->mInputSource ==
147 MouseEvent_Binding::MOZ_SOURCE_TOUCH);
148 fm->SetFocus(content,
149 nsIFocusManager::FLAG_BYMOVEFOCUS |
150 (byMouse ? nsIFocusManager::FLAG_BYMOUSE : 0) |
151 (byTouch ? nsIFocusManager::FLAG_BYTOUCH : 0));
154 // Dispatch a new click event to |content|
155 // (For compatibility with IE, we do only left click. If
156 // we wanted to interpret the HTML spec very narrowly, we
157 // would do nothing. If we wanted to do something
158 // sensible, we might send more events through like
159 // this.) See bug 7554, bug 49897, and bug 96813.
160 nsEventStatus status = aVisitor.mEventStatus;
161 // Ok to use aVisitor.mEvent as parameter because DispatchClickEvent
162 // will actually create a new event.
163 EventFlags eventFlags;
164 eventFlags.mMultipleActionsPrevented = true;
165 DispatchClickEvent(aVisitor.mPresContext, mouseEvent, content, false,
166 &eventFlags, &status);
167 // Do we care about the status this returned? I don't think we do...
168 // Don't run another <label> off of this click
169 mouseEvent->mFlags.mMultipleActionsPrevented = true;
171 break;
173 default:
174 break;
176 mHandlingEvent = false;
177 return NS_OK;
180 Result<bool, nsresult> HTMLLabelElement::PerformAccesskey(
181 bool aKeyCausesActivation, bool aIsTrustedEvent) {
182 if (!aKeyCausesActivation) {
183 RefPtr<Element> element = GetLabeledElement();
184 if (element) {
185 return element->PerformAccesskey(aKeyCausesActivation, aIsTrustedEvent);
187 return Err(NS_ERROR_ABORT);
190 RefPtr<nsPresContext> presContext = GetPresContext(eForUncomposedDoc);
191 if (!presContext) {
192 return Err(NS_ERROR_UNEXPECTED);
195 // Click on it if the users prefs indicate to do so.
196 AutoHandlingUserInputStatePusher userInputStatePusher(aIsTrustedEvent);
197 AutoPopupStatePusher popupStatePusher(
198 aIsTrustedEvent ? PopupBlocker::openAllowed : PopupBlocker::openAbused);
199 DispatchSimulatedClick(this, aIsTrustedEvent, presContext);
201 // XXXedgar, do we need to check whether the focus is really changed?
202 return true;
205 nsGenericHTMLElement* HTMLLabelElement::GetLabeledElement() const {
206 nsAutoString elementId;
208 if (!GetAttr(nsGkAtoms::_for, elementId)) {
209 // No @for, so we are a label for our first form control element.
210 // Do a depth-first traversal to look for the first form control element.
211 return GetFirstLabelableDescendant();
214 // We have a @for. The id has to be linked to an element in the same tree
215 // and this element should be a labelable form control.
216 Element* element = nullptr;
218 if (ShadowRoot* shadowRoot = GetContainingShadow()) {
219 element = shadowRoot->GetElementById(elementId);
220 } else if (Document* doc = GetUncomposedDoc()) {
221 element = doc->GetElementById(elementId);
222 } else {
223 element =
224 nsContentUtils::MatchElementId(SubtreeRoot()->AsContent(), elementId);
227 if (element && element->IsLabelable()) {
228 return static_cast<nsGenericHTMLElement*>(element);
231 return nullptr;
234 nsGenericHTMLElement* HTMLLabelElement::GetFirstLabelableDescendant() const {
235 for (nsIContent* cur = nsINode::GetFirstChild(); cur;
236 cur = cur->GetNextNode(this)) {
237 Element* element = Element::FromNode(cur);
238 if (element && element->IsLabelable()) {
239 return static_cast<nsGenericHTMLElement*>(element);
243 return nullptr;
246 } // namespace mozilla::dom