nsIFrame*
FindFrameTargetedByInputEvent(const WidgetGUIEvent* aEvent,
                              nsIFrame* aRootFrame,
                              const nsPoint& aPointRelativeToRootFrame,
                              uint32_t aFlags)
{
  uint32_t flags = (aFlags & INPUT_IGNORE_ROOT_SCROLL_FRAME) ?
     nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME : 0;
  nsIFrame* target =
    nsLayoutUtils::GetFrameForPoint(aRootFrame, aPointRelativeToRootFrame, flags);

  const EventRadiusPrefs* prefs = GetPrefsFor(aEvent->eventStructType);
  if (!prefs || !prefs->mEnabled || (target && IsElementClickable(target, nsGkAtoms::body))) {
    return target;
  }

  // Do not modify targeting for actual mouse hardware; only for mouse
  // events generated by touch-screen hardware.
  if (aEvent->eventStructType == NS_MOUSE_EVENT &&
      prefs->mTouchOnly &&
      aEvent->AsMouseEvent()->inputSource !=
        nsIDOMMouseEvent::MOZ_SOURCE_TOUCH) {
    return target;
  }

  // If the exact target is non-null, only consider candidate targets in the same
  // document as the exact target. Otherwise, if an ancestor document has
  // a mouse event handler for example, targets that are !IsElementClickable can
  // never be targeted --- something nsSubDocumentFrame in an ancestor document
  // would be targeted instead.
  nsIFrame* restrictToDescendants = target ?
    target->PresContext()->PresShell()->GetRootFrame() : aRootFrame;

  nsRect targetRect = GetTargetRect(aRootFrame, aPointRelativeToRootFrame,
                                    restrictToDescendants, prefs);
  nsAutoTArray<nsIFrame*,8> candidates;
  nsresult rv = nsLayoutUtils::GetFramesForArea(aRootFrame, targetRect, candidates, flags);
  if (NS_FAILED(rv)) {
    return target;
  }

  nsIFrame* closestClickable =
    GetClosest(aRootFrame, aPointRelativeToRootFrame, targetRect, prefs,
               restrictToDescendants, candidates);
  return closestClickable ? closestClickable : target;
}
nsIFrame*
FindFrameTargetedByInputEvent(WidgetGUIEvent* aEvent,
                              nsIFrame* aRootFrame,
                              const nsPoint& aPointRelativeToRootFrame,
                              uint32_t aFlags)
{
  uint32_t flags = (aFlags & INPUT_IGNORE_ROOT_SCROLL_FRAME) ?
     nsLayoutUtils::IGNORE_ROOT_SCROLL_FRAME : 0;
  nsIFrame* target =
    nsLayoutUtils::GetFrameForPoint(aRootFrame, aPointRelativeToRootFrame, flags);
  PET_LOG("Found initial target %p for event class %s point %s relative to root frame %p\n",
    target, (aEvent->mClass == eMouseEventClass ? "mouse" :
             (aEvent->mClass == eTouchEventClass ? "touch" : "other")),
    mozilla::layers::Stringify(aPointRelativeToRootFrame).c_str(), aRootFrame);

  const EventRadiusPrefs* prefs = GetPrefsFor(aEvent->mClass);
  if (!prefs || !prefs->mEnabled) {
    PET_LOG("Retargeting disabled\n");
    return target;
  }
  nsIContent* clickableAncestor = nullptr;
  if (target) {
    clickableAncestor = GetClickableAncestor(target, nsGkAtoms::body);
    if (clickableAncestor) {
      if (!IsElementClickableAndReadable(target, aEvent, prefs)) {
        aEvent->AsMouseEventBase()->hitCluster = true;
      }
      PET_LOG("Target %p is clickable\n", target);
      // If the target that was directly hit has a clickable ancestor, that
      // means it too is clickable. And since it is the same as or a descendant
      // of clickableAncestor, it should become the root for the GetClosest
      // search.
      clickableAncestor = target->GetContent();
    }
  }

  // Do not modify targeting for actual mouse hardware; only for mouse
  // events generated by touch-screen hardware.
  if (aEvent->mClass == eMouseEventClass &&
      prefs->mTouchOnly &&
      aEvent->AsMouseEvent()->inputSource !=
        nsIDOMMouseEvent::MOZ_SOURCE_TOUCH) {
    PET_LOG("Mouse input event is not from a touch source\n");
    return target;
  }

  // If the exact target is non-null, only consider candidate targets in the same
  // document as the exact target. Otherwise, if an ancestor document has
  // a mouse event handler for example, targets that are !GetClickableAncestor can
  // never be targeted --- something nsSubDocumentFrame in an ancestor document
  // would be targeted instead.
  nsIFrame* restrictToDescendants = target ?
    target->PresContext()->PresShell()->GetRootFrame() : aRootFrame;

  nsRect targetRect = GetTargetRect(aRootFrame, aPointRelativeToRootFrame,
                                    restrictToDescendants, prefs, aFlags);
  PET_LOG("Expanded point to target rect %s\n",
    mozilla::layers::Stringify(targetRect).c_str());
  AutoTArray<nsIFrame*,8> candidates;
  nsresult rv = nsLayoutUtils::GetFramesForArea(aRootFrame, targetRect, candidates, flags);
  if (NS_FAILED(rv)) {
    return target;
  }

  int32_t elementsInCluster = 0;

  nsIFrame* closestClickable =
    GetClosest(aRootFrame, aPointRelativeToRootFrame, targetRect, prefs,
               restrictToDescendants, clickableAncestor, candidates,
               &elementsInCluster);
  if (closestClickable) {
    if ((prefs->mTouchClusterDetectionEnabled && elementsInCluster > 1) ||
        (!IsElementClickableAndReadable(closestClickable, aEvent, prefs))) {
      if (aEvent->mClass == eMouseEventClass) {
        WidgetMouseEventBase* mouseEventBase = aEvent->AsMouseEventBase();
        mouseEventBase->hitCluster = true;
      }
    }
    target = closestClickable;
  }
  PET_LOG("Final target is %p\n", target);

  // Uncomment this to dump the frame tree to help with debugging.
  // Note that dumping the frame tree at the top of the function may flood
  // logcat on Android devices and cause the PET_LOGs to get dropped.
  // aRootFrame->DumpFrameTree();

  if (!target || !prefs->mRepositionEventCoords) {
    // No repositioning required for this event
    return target;
  }

  // Take the point relative to the root frame, make it relative to the target,
  // clamp it to the bounds, and then make it relative to the root frame again.
  nsPoint point = aPointRelativeToRootFrame;
  if (nsLayoutUtils::TRANSFORM_SUCCEEDED != nsLayoutUtils::TransformPoint(aRootFrame, target, point)) {
    return target;
  }
  point = target->GetRectRelativeToSelf().ClampPoint(point);
  if (nsLayoutUtils::TRANSFORM_SUCCEEDED != nsLayoutUtils::TransformPoint(target, aRootFrame, point)) {
    return target;
  }
  // Now we basically undo the operations in GetEventCoordinatesRelativeTo, to
  // get back the (now-clamped) coordinates in the event's widget's space.
  nsView* view = aRootFrame->GetView();
  if (!view) {
    return target;
  }
  LayoutDeviceIntPoint widgetPoint = nsLayoutUtils::TranslateViewToWidget(
        aRootFrame->PresContext(), view, point, aEvent->mWidget);
  if (widgetPoint.x != NS_UNCONSTRAINEDSIZE) {
    // If that succeeded, we update the point in the event
    aEvent->refPoint = widgetPoint;
  }
  return target;
}