tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

HTMLLabelElement.cpp (9041B)


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