Issue #2030 - (chore) refactor event dispatch functions

Based on selected bits of M-C 1461708:
- EventStateManager::CheckForAndDispatchClick() to early-return style
- split EventStateManager::CheckForAndDispatchClick() into:
  EventCausesClickEvents, PostHandleMouseUp, DispatchClickEvents
- Move implementation of UIEvent::GetRangeParent() and UIEvent::RangeOffset() to nsLayoutUtils
This commit is contained in:
Martok 2022-11-27 16:35:25 +01:00 committed by roytam1
commit 4d310562d9
5 changed files with 292 additions and 97 deletions

View file

@ -3177,14 +3177,11 @@ EventStateManager::PostHandleEvent(nsPresContext* aPresContext,
case eMouseUp:
{
ClearGlobalActiveContent(this);
WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
if (mouseEvent && mouseEvent->IsReal()) {
if (!mCurrentTarget) {
GetEventTarget();
}
WidgetMouseEvent* mouseUpEvent = aEvent->AsMouseEvent();
if (mouseUpEvent && EventCausesClickEvents(*mouseUpEvent)) {
// Make sure to dispatch the click even if there is no frame for
// the current target element. This is required for Web compatibility.
ret = CheckForAndDispatchClick(mouseEvent, aStatus);
ret = PostHandleMouseUp(mouseUpEvent, aStatus);
}
nsIPresShell *shell = presContext->GetPresShell();
@ -4651,89 +4648,160 @@ EventStateManager::SetClickCount(WidgetMouseEvent* aEvent,
return NS_OK;
}
nsresult
EventStateManager::InitAndDispatchClickEvent(WidgetMouseEvent* aEvent,
nsEventStatus* aStatus,
EventMessage aMessage,
nsIPresShell* aPresShell,
nsIContent* aMouseTarget,
nsWeakFrame aCurrentTarget,
bool aNoContentDispatch)
// static
bool
EventStateManager::EventCausesClickEvents(const WidgetMouseEvent& aMouseEvent)
{
WidgetMouseEvent event(aEvent->IsTrusted(), aMessage,
aEvent->mWidget, WidgetMouseEvent::eReal);
event.mRefPoint = aEvent->mRefPoint;
event.mClickCount = aEvent->mClickCount;
event.mModifiers = aEvent->mModifiers;
event.buttons = aEvent->buttons;
event.mTime = aEvent->mTime;
event.mTimeStamp = aEvent->mTimeStamp;
event.mFlags.mNoContentDispatch = aNoContentDispatch;
event.button = aEvent->button;
event.inputSource = aEvent->inputSource;
return aPresShell->HandleEventWithTarget(&event, aCurrentTarget,
aMouseTarget, aStatus);
if (NS_WARN_IF(aMouseEvent.mMessage != eMouseUp)) {
return false;
}
// If the mouseup event is synthesized event, we don't need to dispatch
// click events.
if (!aMouseEvent.IsReal()) {
return false;
}
// If mouse is still over same element, clickcount will be > 1.
// If it has moved it will be zero, so no click.
if (!aMouseEvent.mClickCount) {
return false;
}
// Check that the window isn't disabled before firing a click
// (see bug 366544).
return !(aMouseEvent.mWidget && !aMouseEvent.mWidget->IsEnabled());
}
nsresult
EventStateManager::CheckForAndDispatchClick(WidgetMouseEvent* aEvent,
nsEventStatus* aStatus)
EventStateManager::InitAndDispatchClickEvent(WidgetMouseEvent* aMouseUpEvent,
nsEventStatus* aStatus,
EventMessage aMessage,
nsIPresShell* aPresShell,
nsIContent* aMouseUpContent,
nsWeakFrame aCurrentTarget,
bool aNoContentDispatch)
{
nsresult ret = NS_OK;
MOZ_ASSERT(aMouseUpEvent);
MOZ_ASSERT(EventCausesClickEvents(*aMouseUpEvent));
MOZ_ASSERT(aMouseUpContent || aCurrentTarget || aOverrideClickTarget);
//If mouse is still over same element, clickcount will be > 1.
//If it has moved it will be zero, so no click.
if (aEvent->mClickCount) {
//Check that the window isn't disabled before firing a click
//(see bug 366544).
if (aEvent->mWidget && !aEvent->mWidget->IsEnabled()) {
return ret;
}
//fire click
bool notDispatchToContents =
(aEvent->button == WidgetMouseEvent::eMiddleButton ||
aEvent->button == WidgetMouseEvent::eRightButton);
WidgetMouseEvent event(aMouseUpEvent->IsTrusted(), aMessage,
aMouseUpEvent->mWidget, WidgetMouseEvent::eReal);
event.mRefPoint = aMouseUpEvent->mRefPoint;
event.mClickCount = aMouseUpEvent->mClickCount;
event.mModifiers = aMouseUpEvent->mModifiers;
event.buttons = aMouseUpEvent->buttons;
event.mTime = aMouseUpEvent->mTime;
event.mTimeStamp = aMouseUpEvent->mTimeStamp;
event.mFlags.mNoContentDispatch = aNoContentDispatch;
event.button = aMouseUpEvent->button;
event.inputSource = aMouseUpEvent->inputSource;
bool fireAuxClick = notDispatchToContents;
nsCOMPtr<nsIPresShell> presShell = mPresContext->GetPresShell();
if (presShell) {
nsCOMPtr<nsIContent> mouseContent = GetEventTargetContent(aEvent);
// Click events apply to *elements* not nodes. At this point the target
// content may have been reset to some non-element content, and so we need
// to walk up the closest ancestor element, just like we do in
// nsPresShell::HandlePositionedEvent.
while (mouseContent && !mouseContent->IsElement()) {
mouseContent = mouseContent->GetParent();
}
if (!mouseContent && !mCurrentTarget) {
return NS_OK;
}
// HandleEvent clears out mCurrentTarget which we might need again
nsWeakFrame currentTarget = mCurrentTarget;
ret = InitAndDispatchClickEvent(aEvent, aStatus, eMouseClick,
presShell, mouseContent, currentTarget,
notDispatchToContents);
if (NS_SUCCEEDED(ret) && aEvent->mClickCount == 2 &&
mouseContent && mouseContent->IsInComposedDoc()) {
//fire double click
ret = InitAndDispatchClickEvent(aEvent, aStatus, eMouseDoubleClick,
presShell, mouseContent, currentTarget,
notDispatchToContents);
}
if (NS_SUCCEEDED(ret) && mouseContent && fireAuxClick &&
mouseContent->IsInComposedDoc()) {
ret = InitAndDispatchClickEvent(aEvent, aStatus, eMouseAuxClick,
presShell, mouseContent, currentTarget,
false);
}
}
// Use local event status for each click event dispatching since it'll be
// cleared by EventStateManager::PreHandleEvent(). Therefore, dispatching
// an event means that previous event status will be ignored.
nsEventStatus status = nsEventStatus_eIgnore;
nsresult rv = aPresShell->HandleEventWithTarget(&event, aCurrentTarget,
aMouseUpContent, &status);
// If current status is nsEventStatus_eConsumeNoDefault, we don't need to
// overwrite it.
if (*aStatus == nsEventStatus_eConsumeNoDefault) {
return rv;
}
// If new status is nsEventStatus_eConsumeNoDefault or
// nsEventStatus_eConsumeDoDefault, use it.
if (status == nsEventStatus_eConsumeNoDefault ||
status == nsEventStatus_eConsumeDoDefault) {
*aStatus = status;
return rv;
}
// Otherwise, keep the original status.
return rv;
}
nsresult
EventStateManager::PostHandleMouseUp(WidgetMouseEvent* aMouseUpEvent,
nsEventStatus* aStatus)
{
MOZ_ASSERT(aMouseUpEvent);
MOZ_ASSERT(EventCausesClickEvents(*aMouseUpEvent));
MOZ_ASSERT(aStatus);
nsCOMPtr<nsIPresShell> presShell = mPresContext->GetPresShell();
if (!presShell) {
return NS_OK;
}
nsCOMPtr<nsIContent> mouseUpContent = GetEventTargetContent(aMouseUpEvent);
// Click events apply to *elements* not nodes. At this point the target
// content may have been reset to some non-element content, and so we need
// to walk up the closest ancestor element, just like we do in
// nsPresShell::HandlePositionedEvent.
while (mouseUpContent && !mouseUpContent->IsElement()) {
mouseUpContent = mouseUpContent->GetFlattenedTreeParent();
}
if (!mouseUpContent && !mCurrentTarget) {
return NS_OK;
}
// Fire click events if the event target is still available.
nsresult rv = DispatchClickEvents(presShell, aMouseUpEvent, aStatus,
mouseUpContent);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
nsresult
EventStateManager::DispatchClickEvents(nsIPresShell* aPresShell,
WidgetMouseEvent* aMouseUpEvent,
nsEventStatus* aStatus,
nsIContent* aMouseUpContent)
{
MOZ_ASSERT(aPresShell);
MOZ_ASSERT(aMouseUpEvent);
MOZ_ASSERT(EventCausesClickEvents(*aMouseUpEvent));
MOZ_ASSERT(aStatus);
MOZ_ASSERT(aMouseUpContent || mCurrentTarget || aOverrideClickTarget);
bool notDispatchToContents =
(aMouseUpEvent->button == WidgetMouseEvent::eMiddleButton ||
aMouseUpEvent->button == WidgetMouseEvent::eRightButton);
bool fireAuxClick = notDispatchToContents;
// HandleEvent clears out mCurrentTarget which we might need again
nsWeakFrame currentTarget = mCurrentTarget;
nsresult ret =
InitAndDispatchClickEvent(aMouseUpEvent, aStatus, eMouseClick,
aPresShell, aMouseUpContent, currentTarget,
notDispatchToContents);
if (NS_WARN_IF(NS_FAILED(ret))) {
return ret;
}
// Fire double click event if click count is 2.
if (aMouseUpEvent->mClickCount == 2 &&
aMouseUpContent && aMouseUpContent->IsInComposedDoc()) {
ret = InitAndDispatchClickEvent(aMouseUpEvent, aStatus, eMouseDoubleClick,
aPresShell, aMouseUpContent, currentTarget,
notDispatchToContents);
if (NS_WARN_IF(NS_FAILED(ret))) {
return ret;
}
}
// Fire auxclick even if necessary.
if (fireAuxClick &&
aMouseUpContent && aMouseUpContent->IsInComposedDoc()) {
ret = InitAndDispatchClickEvent(aMouseUpEvent, aStatus, eMouseAuxClick,
aPresShell, aMouseUpContent, currentTarget,
false);
NS_WARNING_ASSERTION(NS_SUCCEEDED(ret), "Failed to dispatch eMouseAuxClick");
}
return ret;
}

View file

@ -414,16 +414,79 @@ protected:
*/
void UpdateDragDataTransfer(WidgetDragEvent* dragEvent);
static nsresult InitAndDispatchClickEvent(WidgetMouseEvent* aEvent,
/**
* InitAndDispatchClickEvent() dispatches a click event.
*
* @param aMouseUpEvent eMouseUp event which causes the click event.
* EventCausesClickEvents() must return true
* if this event is set to it.
* @param aStatus Returns the result of click event.
* If the status indicates consumed, the
* value won't be overwritten with
* nsEventStatus_eIgnore.
* @param aMessage Should be eMouseClick, eMouseDoubleClick or
* eMouseAuxClick.
* @param aPresShell The PresShell.
* @param aMouseUpContent The event target of aMouseUpEvent.
* @param aCurrentTarget Current target of the caller.
* @param aNoContentDispatch true if the event shouldn't be exposed to
* web contents (although will be fired on
* document and window).
* @param aOverrideClickTarget Preferred click event target. If this is
* not nullptr, aMouseUpContent and
* aCurrentTarget are ignored.
*/
static nsresult InitAndDispatchClickEvent(WidgetMouseEvent* aMouseUpEvent,
nsEventStatus* aStatus,
EventMessage aMessage,
nsIPresShell* aPresShell,
nsIContent* aMouseTarget,
nsIContent* aMouseUpContent,
nsWeakFrame aCurrentTarget,
bool aNoContentDispatch);
nsresult SetClickCount(WidgetMouseEvent* aEvent, nsEventStatus* aStatus);
nsresult CheckForAndDispatchClick(WidgetMouseEvent* aEvent,
nsEventStatus* aStatus);
/**
* EventCausesClickEvents() returns true when aMouseEvent is an eMouseUp
* event and it should cause eMouseClick, eMouseDoubleClick and/or
* eMouseAuxClick events. Note that this method assumes that
* aMouseEvent.mClickCount has already been initialized with SetClickCount().
*/
static bool EventCausesClickEvents(const WidgetMouseEvent& aMouseEvent);
/**
* PostHandleMouseUp() handles default actions of eMouseUp event.
*
* @param aMouseUpEvent eMouseUp event which causes the click event.
* EventCausesClickEvents() must return true
* if this event is set to it.
* @param aStatus Returns the result of event status.
* If one of dispatching event is consumed or
* this does something as default action,
* returns nsEventStatus_eConsumeNoDefault.
*/
nsresult PostHandleMouseUp(WidgetMouseEvent* aMouseUpEvent,
nsEventStatus* aStatus);
/**
* DispatchClickEvents() dispatches eMouseClick, eMouseDoubleClick and
* eMouseAuxClick events for aMouseUpEvent. aMouseUpEvent should cause
* click event.
*
* @param aPresShell The PresShell.
* @param aMouseUpEvent eMouseUp event which causes the click event.
* EventCausesClickEvents() must return true
* if this event is set to it.
* @param aStatus Returns the result of event status.
* If one of dispatching click event is
* consumed, returns
* nsEventStatus_eConsumeNoDefault.
* @param aMouseUpContent The event target of aMouseUpEvent.
*/
nsresult DispatchClickEvents(nsIPresShell* aPresShell,
WidgetMouseEvent* aMouseUpEvent,
nsEventStatus* aStatus,
nsIContent* aMouseUpContent);
void EnsureDocument(nsPresContext* aPresContext);
void FlushPendingEvents(nsPresContext* aPresContext);

View file

@ -228,26 +228,20 @@ UIEvent::GetWhich(uint32_t* aWhich)
already_AddRefed<nsINode>
UIEvent::GetRangeParent()
{
nsIFrame* targetFrame = nullptr;
if (mPresContext) {
targetFrame = mPresContext->EventStateManager()->GetEventTarget();
if (NS_WARN_IF(!mPresContext)) {
return nullptr;
}
if (targetFrame) {
nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(mEvent,
targetFrame);
nsCOMPtr<nsIContent> parent = targetFrame->GetContentOffsetsFromPoint(pt).content;
if (parent) {
if (parent->ChromeOnlyAccess() &&
!nsContentUtils::CanAccessNativeAnon()) {
return nullptr;
}
return parent.forget();
}
nsCOMPtr<nsIPresShell> presShell = mPresContext->GetPresShell();
if (NS_WARN_IF(!presShell)) {
return nullptr;
}
return nullptr;
nsCOMPtr<nsIContent> container;
nsLayoutUtils::GetContainerAndOffsetAtEvent(presShell, mEvent,
getter_AddRefs(container),
nullptr);
return container.forget();
}
NS_IMETHODIMP
@ -273,18 +267,19 @@ UIEvent::GetRangeOffset(int32_t* aRangeOffset)
int32_t
UIEvent::RangeOffset() const
{
if (!mPresContext) {
if (NS_WARN_IF(!mPresContext)) {
return 0;
}
nsIFrame* targetFrame = mPresContext->EventStateManager()->GetEventTarget();
if (!targetFrame) {
nsCOMPtr<nsIPresShell> presShell = mPresContext->GetPresShell();
if (NS_WARN_IF(!presShell)) {
return 0;
}
nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(mEvent,
targetFrame);
return targetFrame->GetContentOffsetsFromPoint(pt).offset;
int32_t offset = 0;
nsLayoutUtils::GetContainerAndOffsetAtEvent(presShell, mEvent,
nullptr, &offset);
return offset;
}
nsIntPoint

View file

@ -11,6 +11,7 @@
#include "mozilla/EffectCompositor.h"
#include "mozilla/EffectSet.h"
#include "mozilla/EventDispatcher.h"
#include "mozilla/EventStateManager.h"
#include "mozilla/FloatingPoint.h"
#include "mozilla/gfx/gfxVars.h"
#include "mozilla/gfx/PathHelpers.h"
@ -2255,6 +2256,59 @@ nsLayoutUtils::GetPopupFrameForEventCoordinates(nsPresContext* aPresContext,
return nullptr;
}
void
nsLayoutUtils::GetContainerAndOffsetAtEvent(nsIPresShell* aPresShell,
const WidgetEvent* aEvent,
nsIContent** aContainer,
int32_t* aOffset)
{
MOZ_ASSERT(aContainer || aOffset);
if (aContainer) {
*aContainer = nullptr;
}
if (aOffset) {
*aOffset = 0;
}
if (!aPresShell) {
return;
}
aPresShell->FlushPendingNotifications(Flush_Layout);
RefPtr<nsPresContext> presContext = aPresShell->GetPresContext();
if (!presContext) {
return;
}
nsIFrame* targetFrame = presContext->EventStateManager()->GetEventTarget();
if (!targetFrame) {
return;
}
nsPoint point =
nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, targetFrame);
if (aContainer) {
// TODO: This result may be useful to change to Selection. However, this
// may return improper node (e.g., native anonymous node) for the
// Selection. Perhaps, this should take Selection optionally and
// if it's specified, needs to check if it's proper for the
// Selection.
nsCOMPtr<nsIContent> container =
targetFrame->GetContentOffsetsFromPoint(point).content;
if (container &&
(!container->ChromeOnlyAccess() ||
nsContentUtils::CanAccessNativeAnon())) {
container.forget(aContainer);
}
}
if (aOffset) {
*aOffset = targetFrame->GetContentOffsetsFromPoint(point).offset;
}
}
static void ConstrainToCoordValues(float& aStart, float& aSize)
{
MOZ_ASSERT(aSize >= 0);

View file

@ -728,6 +728,21 @@ public:
nsPresContext* aPresContext,
const mozilla::WidgetEvent* aEvent);
/**
* Get container and offset if aEvent collapses Selection.
* @param aPresShell The PresShell handling aEvent.
* @param aEvent The event having coordinates where you want to
* collapse Selection.
* @param aContainer Returns the container node at the point.
* Set nullptr if you don't need this.
* @param aOffset Returns offset in the container node at the point.
* Set nullptr if you don't need this.
*/
static void GetContainerAndOffsetAtEvent(nsIPresShell* aPresShell,
const mozilla::WidgetEvent* aEvent,
nsIContent** aContainer,
int32_t* aOffset);
/**
* Translate from widget coordinates to the view's coordinates
* @param aPresContext the PresContext for the view