static bool
IsElementClickable(nsIFrame* aFrame, nsIAtom* stopAt = nullptr)
{
  // Input events propagate up the content tree so we'll follow the content
  // ancestors to look for elements accepting the click.
  for (nsIContent* content = aFrame->GetContent(); content;
       content = content->GetFlattenedTreeParent()) {
    nsIAtom* tag = content->Tag();
    if (content->IsHTML() && stopAt && tag == stopAt) {
      break;
    }
    if (HasMouseListener(content)) {
      return true;
    }
    if (content->IsHTML()) {
      if (tag == nsGkAtoms::button ||
          tag == nsGkAtoms::input ||
          tag == nsGkAtoms::select ||
          tag == nsGkAtoms::textarea ||
          tag == nsGkAtoms::label) {
        return true;
      }
    } else if (content->IsXUL()) {
      nsIAtom* tag = content->Tag();
      // See nsCSSFrameConstructor::FindXULTagData. This code is not
      // really intended to be used with XUL, though.
      if (tag == nsGkAtoms::button ||
          tag == nsGkAtoms::checkbox ||
          tag == nsGkAtoms::radio ||
          tag == nsGkAtoms::autorepeatbutton ||
          tag == nsGkAtoms::menu ||
          tag == nsGkAtoms::menubutton ||
          tag == nsGkAtoms::menuitem ||
          tag == nsGkAtoms::menulist ||
          tag == nsGkAtoms::scrollbarbutton ||
          tag == nsGkAtoms::resizer) {
        return true;
      }
    }
    if (content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::role,
                             nsGkAtoms::button, eIgnoreCase)) {
      return true;
    }
    if (content->IsEditable()) {
      return true;
    }
    nsCOMPtr<nsIURI> linkURI;
    if (content->IsLink(getter_AddRefs(linkURI))) {
      return true;
    }
  }
  return false;
}
static nsIContent*
GetClickableAncestor(nsIFrame* aFrame, nsIAtom* stopAt = nullptr, nsAutoString* aLabelTargetId = nullptr)
{
  // Input events propagate up the content tree so we'll follow the content
  // ancestors to look for elements accepting the click.
  for (nsIContent* content = aFrame->GetContent(); content;
       content = content->GetFlattenedTreeParent()) {
    if (stopAt && content->IsHTMLElement(stopAt)) {
      break;
    }
    if (HasTouchListener(content) || HasMouseListener(content)) {
      return content;
    }
    if (content->IsAnyOfHTMLElements(nsGkAtoms::button,
                                     nsGkAtoms::input,
                                     nsGkAtoms::select,
                                     nsGkAtoms::textarea)) {
      return content;
    }
    if (content->IsHTMLElement(nsGkAtoms::label)) {
      if (aLabelTargetId) {
        content->GetAttr(kNameSpaceID_None, nsGkAtoms::_for, *aLabelTargetId);
      }
      return content;
    }

    // Bug 921928: we don't have access to the content of remote iframe.
    // So fluffing won't go there. We do an optimistic assumption here:
    // that the content of the remote iframe needs to be a target.
    if (content->IsHTMLElement(nsGkAtoms::iframe) &&
        content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::mozbrowser,
                             nsGkAtoms::_true, eIgnoreCase) &&
        content->AttrValueIs(kNameSpaceID_None, nsGkAtoms::Remote,
                             nsGkAtoms::_true, eIgnoreCase)) {
      return content;
    }

    // See nsCSSFrameConstructor::FindXULTagData. This code is not
    // really intended to be used with XUL, though.
    if (content->IsAnyOfXULElements(nsGkAtoms::button,
                                    nsGkAtoms::checkbox,
                                    nsGkAtoms::radio,
                                    nsGkAtoms::autorepeatbutton,
                                    nsGkAtoms::menu,
                                    nsGkAtoms::menubutton,
                                    nsGkAtoms::menuitem,
                                    nsGkAtoms::menulist,
                                    nsGkAtoms::scrollbarbutton,
                                    nsGkAtoms::resizer)) {
      return content;
    }

    static nsIContent::AttrValuesArray clickableRoles[] =
      { &nsGkAtoms::button, &nsGkAtoms::key, nullptr };
    if (content->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::role,
                                 clickableRoles, eIgnoreCase) >= 0) {
      return content;
    }
    if (content->IsEditable()) {
      return content;
    }
    nsCOMPtr<nsIURI> linkURI;
    if (content->IsLink(getter_AddRefs(linkURI))) {
      return content;
    }
  }
  return nullptr;
}