Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/dom/html/HTMLLabelElement.cpp
Line
Count
Source (jump to first uncovered line)
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/. */
6
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 "nsContentUtils.h"
17
#include "nsQueryObject.h"
18
#include "mozilla/dom/ShadowRoot.h"
19
20
// construction, destruction
21
22
NS_IMPL_NS_NEW_HTML_ELEMENT(Label)
23
24
namespace mozilla {
25
namespace dom {
26
27
HTMLLabelElement::~HTMLLabelElement()
28
0
{
29
0
}
30
31
JSObject*
32
HTMLLabelElement::WrapNode(JSContext *aCx, JS::Handle<JSObject*> aGivenProto)
33
0
{
34
0
  return HTMLLabelElement_Binding::Wrap(aCx, this, aGivenProto);
35
0
}
36
37
// nsIDOMHTMLLabelElement
38
39
NS_IMPL_ELEMENT_CLONE(HTMLLabelElement)
40
41
HTMLFormElement*
42
HTMLLabelElement::GetForm() const
43
0
{
44
0
  nsGenericHTMLElement* control = GetControl();
45
0
  if (!control) {
46
0
    return nullptr;
47
0
  }
48
0
49
0
  // Not all labeled things have a form association.  Stick to the ones that do.
50
0
  nsCOMPtr<nsIFormControl> formControl = do_QueryObject(control);
51
0
  if (!formControl) {
52
0
    return nullptr;
53
0
  }
54
0
55
0
  return static_cast<HTMLFormElement*>(formControl->GetFormElement());
56
0
}
57
58
void
59
HTMLLabelElement::Focus(ErrorResult& aError)
60
0
{
61
0
  // retarget the focus method at the for content
62
0
  nsIFocusManager* fm = nsFocusManager::GetFocusManager();
63
0
  if (fm) {
64
0
    RefPtr<Element> elem = GetLabeledElement();
65
0
    if (elem) {
66
0
      fm->SetFocus(elem, 0);
67
0
    }
68
0
  }
69
0
}
70
71
static bool
72
InInteractiveHTMLContent(nsIContent* aContent, nsIContent* aStop)
73
0
{
74
0
  nsIContent* content = aContent;
75
0
  while (content && content != aStop) {
76
0
    if (content->IsElement() &&
77
0
        content->AsElement()->IsInteractiveHTMLContent(true)) {
78
0
      return true;
79
0
    }
80
0
    content = content->GetParent();
81
0
  }
82
0
  return false;
83
0
}
84
85
nsresult
86
HTMLLabelElement::PostHandleEvent(EventChainPostVisitor& aVisitor)
87
0
{
88
0
  WidgetMouseEvent* mouseEvent = aVisitor.mEvent->AsMouseEvent();
89
0
  if (mHandlingEvent ||
90
0
      (!(mouseEvent && mouseEvent->IsLeftClickEvent()) &&
91
0
       aVisitor.mEvent->mMessage != eMouseDown) ||
92
0
      aVisitor.mEventStatus == nsEventStatus_eConsumeNoDefault ||
93
0
      !aVisitor.mPresContext ||
94
0
      // Don't handle the event if it's already been handled by another label
95
0
      aVisitor.mEvent->mFlags.mMultipleActionsPrevented) {
96
0
    return NS_OK;
97
0
  }
98
0
99
0
  nsCOMPtr<nsIContent> target = do_QueryInterface(aVisitor.mEvent->mTarget);
100
0
  if (InInteractiveHTMLContent(target, this)) {
101
0
    return NS_OK;
102
0
  }
103
0
104
0
  // Strong ref because event dispatch is going to happen.
105
0
  RefPtr<Element> content = GetLabeledElement();
106
0
107
0
  if (content) {
108
0
    mHandlingEvent = true;
109
0
    switch (aVisitor.mEvent->mMessage) {
110
0
      case eMouseDown:
111
0
        if (mouseEvent->button == WidgetMouseEvent::eLeftButton) {
112
0
          // We reset the mouse-down point on every event because there is
113
0
          // no guarantee we will reach the eMouseClick code below.
114
0
          LayoutDeviceIntPoint* curPoint =
115
0
            new LayoutDeviceIntPoint(mouseEvent->mRefPoint);
116
0
          SetProperty(nsGkAtoms::labelMouseDownPtProperty,
117
0
                      static_cast<void*>(curPoint),
118
0
                      nsINode::DeleteProperty<LayoutDeviceIntPoint>);
119
0
        }
120
0
        break;
121
0
122
0
      case eMouseClick:
123
0
        if (mouseEvent->IsLeftClickEvent()) {
124
0
          LayoutDeviceIntPoint* mouseDownPoint =
125
0
            static_cast<LayoutDeviceIntPoint*>(
126
0
              GetProperty(nsGkAtoms::labelMouseDownPtProperty));
127
0
128
0
          bool dragSelect = false;
129
0
          if (mouseDownPoint) {
130
0
            LayoutDeviceIntPoint dragDistance = *mouseDownPoint;
131
0
            DeleteProperty(nsGkAtoms::labelMouseDownPtProperty);
132
0
133
0
            dragDistance -= mouseEvent->mRefPoint;
134
0
            const int CLICK_DISTANCE = 2;
135
0
            dragSelect = dragDistance.x > CLICK_DISTANCE ||
136
0
                         dragDistance.x < -CLICK_DISTANCE ||
137
0
                         dragDistance.y > CLICK_DISTANCE ||
138
0
                         dragDistance.y < -CLICK_DISTANCE;
139
0
          }
140
0
          // Don't click the for-content if we did drag-select text or if we
141
0
          // have a kbd modifier (which adjusts a selection).
142
0
          if (dragSelect || mouseEvent->IsShift() || mouseEvent->IsControl() ||
143
0
              mouseEvent->IsAlt() || mouseEvent->IsMeta()) {
144
0
            break;
145
0
          }
146
0
          // Only set focus on the first click of multiple clicks to prevent
147
0
          // to prevent immediate de-focus.
148
0
          if (mouseEvent->mClickCount <= 1) {
149
0
            nsIFocusManager* fm = nsFocusManager::GetFocusManager();
150
0
            if (fm) {
151
0
              // Use FLAG_BYMOVEFOCUS here so that the label is scrolled to.
152
0
              // Also, within HTMLInputElement::PostHandleEvent, inputs will
153
0
              // be selected only when focused via a key or when the navigation
154
0
              // flag is used and we want to select the text on label clicks as
155
0
              // well.
156
0
              // If the label has been clicked by the user, we also want to
157
0
              // pass FLAG_BYMOUSE so that we get correct focus ring behavior,
158
0
              // but we don't want to pass FLAG_BYMOUSE if this click event was
159
0
              // caused by the user pressing an accesskey.
160
0
              bool byMouse = (mouseEvent->inputSource != MouseEvent_Binding::MOZ_SOURCE_KEYBOARD);
161
0
              bool byTouch = (mouseEvent->inputSource == MouseEvent_Binding::MOZ_SOURCE_TOUCH);
162
0
              fm->SetFocus(content,
163
0
                           nsIFocusManager::FLAG_BYMOVEFOCUS |
164
0
                           (byMouse ? nsIFocusManager::FLAG_BYMOUSE : 0) |
165
0
                           (byTouch ? nsIFocusManager::FLAG_BYTOUCH : 0));
166
0
            }
167
0
          }
168
0
          // Dispatch a new click event to |content|
169
0
          //    (For compatibility with IE, we do only left click.  If
170
0
          //    we wanted to interpret the HTML spec very narrowly, we
171
0
          //    would do nothing.  If we wanted to do something
172
0
          //    sensible, we might send more events through like
173
0
          //    this.)  See bug 7554, bug 49897, and bug 96813.
174
0
          nsEventStatus status = aVisitor.mEventStatus;
175
0
          // Ok to use aVisitor.mEvent as parameter because DispatchClickEvent
176
0
          // will actually create a new event.
177
0
          EventFlags eventFlags;
178
0
          eventFlags.mMultipleActionsPrevented = true;
179
0
          DispatchClickEvent(aVisitor.mPresContext, mouseEvent,
180
0
                             content, false, &eventFlags, &status);
181
0
          // Do we care about the status this returned?  I don't think we do...
182
0
          // Don't run another <label> off of this click
183
0
          mouseEvent->mFlags.mMultipleActionsPrevented = true;
184
0
        }
185
0
        break;
186
0
187
0
      default:
188
0
        break;
189
0
    }
190
0
    mHandlingEvent = false;
191
0
  }
192
0
  return NS_OK;
193
0
}
194
195
bool
196
HTMLLabelElement::PerformAccesskey(bool aKeyCausesActivation,
197
                                   bool aIsTrustedEvent)
198
0
{
199
0
  if (!aKeyCausesActivation) {
200
0
    RefPtr<Element> element = GetLabeledElement();
201
0
    if (element) {
202
0
      return element->PerformAccesskey(aKeyCausesActivation, aIsTrustedEvent);
203
0
    }
204
0
  } else {
205
0
    nsPresContext *presContext = GetPresContext(eForUncomposedDoc);
206
0
    if (!presContext) {
207
0
      return false;
208
0
    }
209
0
210
0
    // Click on it if the users prefs indicate to do so.
211
0
    WidgetMouseEvent event(aIsTrustedEvent, eMouseClick,
212
0
                           nullptr, WidgetMouseEvent::eReal);
213
0
    event.inputSource = MouseEvent_Binding::MOZ_SOURCE_KEYBOARD;
214
0
215
0
    nsAutoPopupStatePusher popupStatePusher(aIsTrustedEvent ?
216
0
                                            openAllowed : openAbused);
217
0
218
0
    EventDispatcher::Dispatch(static_cast<nsIContent*>(this), presContext,
219
0
                              &event);
220
0
  }
221
0
222
0
  return aKeyCausesActivation;
223
0
}
224
225
nsGenericHTMLElement*
226
HTMLLabelElement::GetLabeledElement() const
227
0
{
228
0
  nsAutoString elementId;
229
0
230
0
  if (!GetAttr(kNameSpaceID_None, nsGkAtoms::_for, elementId)) {
231
0
    // No @for, so we are a label for our first form control element.
232
0
    // Do a depth-first traversal to look for the first form control element.
233
0
    return GetFirstLabelableDescendant();
234
0
  }
235
0
236
0
  // We have a @for. The id has to be linked to an element in the same tree
237
0
  // and this element should be a labelable form control.
238
0
  Element* element = nullptr;
239
0
240
0
  if (ShadowRoot* shadowRoot = GetContainingShadow()) {
241
0
    element = shadowRoot->GetElementById(elementId);
242
0
  } else if (nsIDocument* doc = GetUncomposedDoc()) {
243
0
    element = doc->GetElementById(elementId);
244
0
  } else {
245
0
    element = nsContentUtils::MatchElementId(SubtreeRoot()->AsContent(), elementId);
246
0
  }
247
0
248
0
  if (element && element->IsLabelable()) {
249
0
    return static_cast<nsGenericHTMLElement*>(element);
250
0
  }
251
0
252
0
  return nullptr;
253
0
}
254
255
nsGenericHTMLElement*
256
HTMLLabelElement::GetFirstLabelableDescendant() const
257
0
{
258
0
  for (nsIContent* cur = nsINode::GetFirstChild(); cur;
259
0
       cur = cur->GetNextNode(this)) {
260
0
    Element* element = Element::FromNode(cur);
261
0
    if (element && element->IsLabelable()) {
262
0
      return static_cast<nsGenericHTMLElement*>(element);
263
0
    }
264
0
  }
265
0
266
0
  return nullptr;
267
0
}
268
269
} // namespace dom
270
} // namespace mozilla