Merge remote-tracking branch 'origin/tracking' into custom

This commit is contained in:
roytam1 2022-12-02 23:57:49 +08:00
commit 74a139ee22
19 changed files with 477 additions and 204 deletions

View file

@ -2377,6 +2377,56 @@ nsContentUtils::GetCommonAncestor(nsINode* aNode1,
return parent; return parent;
} }
// static
nsINode*
nsContentUtils::GetCommonAncestorUnderInteractiveContent(nsINode* aNode1,
nsINode* aNode2)
{
if (!aNode1 || !aNode2) {
return nullptr;
}
if (aNode1 == aNode2) {
return aNode1;
}
// Build the chain of parents
AutoTArray<nsINode*, 30> parents1;
do {
parents1.AppendElement(aNode1);
if (aNode1->IsElement() &&
aNode1->AsElement()->IsInteractiveHTMLContent(true)) {
break;
}
aNode1 = aNode1->GetFlattenedTreeParentNode();
} while (aNode1);
AutoTArray<nsINode*, 30> parents2;
do {
parents2.AppendElement(aNode2);
if (aNode2->IsElement() &&
aNode2->AsElement()->IsInteractiveHTMLContent(true)) {
break;
}
aNode2 = aNode2->GetFlattenedTreeParentNode();
} while (aNode2);
// Find where the parent chain differs
uint32_t pos1 = parents1.Length();
uint32_t pos2 = parents2.Length();
nsINode* parent = nullptr;
for (uint32_t len = std::min(pos1, pos2); len > 0; --len) {
nsINode* child1 = parents1.ElementAt(--pos1);
nsINode* child2 = parents2.ElementAt(--pos2);
if (child1 != child2) {
break;
}
parent = child1;
}
return parent;
}
/* static */ /* static */
bool bool
nsContentUtils::PositionIsBefore(nsINode* aNode1, nsINode* aNode2) nsContentUtils::PositionIsBefore(nsINode* aNode1, nsINode* aNode2)

View file

@ -324,6 +324,15 @@ public:
static nsINode* GetCommonAncestor(nsINode* aNode1, static nsINode* GetCommonAncestor(nsINode* aNode1,
nsINode* aNode2); nsINode* aNode2);
/**
* Returns the common ancestor under interactive content, if any.
* If neither one has interactive content as ancestor, common ancestor will be
* returned. If only one has interactive content as ancestor, null will be
* returned. If the nodes are the same, that node is returned.
*/
static nsINode* GetCommonAncestorUnderInteractiveContent(nsINode* aNode1,
nsINode* aNode2);
/** /**
* Returns true if aNode1 is before aNode2 in the same connected * Returns true if aNode1 is before aNode2 in the same connected
* tree. * tree.

View file

@ -194,6 +194,11 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Event)
default: default:
break; break;
} }
if (WidgetMouseEvent* mouseEvent = tmp->mEvent->AsMouseEvent()) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mEvent->mClickTarget");
cb.NoteXPCOMChild(mouseEvent->mClickTarget);
}
} }
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPresContext) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPresContext)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExplicitOriginalTarget) NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mExplicitOriginalTarget)

View file

@ -434,11 +434,8 @@ NS_IMPL_CYCLE_COLLECTION(EventStateManager,
mGestureDownContent, mGestureDownContent,
mGestureDownFrameOwner, mGestureDownFrameOwner,
mLastLeftMouseDownContent, mLastLeftMouseDownContent,
mLastLeftMouseDownContentParent,
mLastMiddleMouseDownContent, mLastMiddleMouseDownContent,
mLastMiddleMouseDownContentParent,
mLastRightMouseDownContent, mLastRightMouseDownContent,
mLastRightMouseDownContentParent,
mActiveContent, mActiveContent,
mHoverContent, mHoverContent,
mURLTargetContent, mURLTargetContent,
@ -3177,14 +3174,11 @@ EventStateManager::PostHandleEvent(nsPresContext* aPresContext,
case eMouseUp: case eMouseUp:
{ {
ClearGlobalActiveContent(this); ClearGlobalActiveContent(this);
WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent(); WidgetMouseEvent* mouseUpEvent = aEvent->AsMouseEvent();
if (mouseEvent && mouseEvent->IsReal()) { if (mouseUpEvent && EventCausesClickEvents(*mouseUpEvent)) {
if (!mCurrentTarget) {
GetEventTarget();
}
// Make sure to dispatch the click even if there is no frame for // Make sure to dispatch the click even if there is no frame for
// the current target element. This is required for Web compatibility. // the current target element. This is required for Web compatibility.
ret = CheckForAndDispatchClick(mouseEvent, aStatus); ret = PostHandleMouseUp(mouseUpEvent, aStatus);
} }
nsIPresShell *shell = presContext->GetPresShell(); nsIPresShell *shell = presContext->GetPresShell();
@ -4579,16 +4573,13 @@ EventStateManager::SetClickCount(WidgetMouseEvent* aEvent,
nsEventStatus* aStatus) nsEventStatus* aStatus)
{ {
nsCOMPtr<nsIContent> mouseContent; nsCOMPtr<nsIContent> mouseContent;
nsIContent* mouseContentParent = nullptr;
if (mCurrentTarget) { if (mCurrentTarget) {
mCurrentTarget->GetContentForEvent(aEvent, getter_AddRefs(mouseContent)); mCurrentTarget->GetContentForEvent(aEvent, getter_AddRefs(mouseContent));
} }
if (mouseContent) { if (mouseContent && mouseContent->IsNodeOfType(nsINode::eTEXT)) {
if (mouseContent->IsNodeOfType(nsINode::eTEXT)) { nsINode* parent = mouseContent->GetFlattenedTreeParentNode();
mouseContent = mouseContent->GetParent(); if (parent && parent->IsContent()) {
} mouseContent = parent->AsContent();
if (mouseContent && mouseContent->IsRootOfNativeAnonymousSubtree()) {
mouseContentParent = mouseContent->GetParent();
} }
} }
@ -4596,54 +4587,51 @@ EventStateManager::SetClickCount(WidgetMouseEvent* aEvent,
case WidgetMouseEvent::eLeftButton: case WidgetMouseEvent::eLeftButton:
if (aEvent->mMessage == eMouseDown) { if (aEvent->mMessage == eMouseDown) {
mLastLeftMouseDownContent = mouseContent; mLastLeftMouseDownContent = mouseContent;
mLastLeftMouseDownContentParent = mouseContentParent;
} else if (aEvent->mMessage == eMouseUp) { } else if (aEvent->mMessage == eMouseUp) {
if (mLastLeftMouseDownContent == mouseContent || aEvent->mClickTarget =
mLastLeftMouseDownContentParent == mouseContent || nsContentUtils::GetCommonAncestorUnderInteractiveContent(
mLastLeftMouseDownContent == mouseContentParent) { mouseContent, mLastLeftMouseDownContent);
if (aEvent->mClickTarget) {
aEvent->mClickCount = mLClickCount; aEvent->mClickCount = mLClickCount;
mLClickCount = 0; mLClickCount = 0;
} else { } else {
aEvent->mClickCount = 0; aEvent->mClickCount = 0;
} }
mLastLeftMouseDownContent = nullptr; mLastLeftMouseDownContent = nullptr;
mLastLeftMouseDownContentParent = nullptr;
} }
break; break;
case WidgetMouseEvent::eMiddleButton: case WidgetMouseEvent::eMiddleButton:
if (aEvent->mMessage == eMouseDown) { if (aEvent->mMessage == eMouseDown) {
mLastMiddleMouseDownContent = mouseContent; mLastMiddleMouseDownContent = mouseContent;
mLastMiddleMouseDownContentParent = mouseContentParent;
} else if (aEvent->mMessage == eMouseUp) { } else if (aEvent->mMessage == eMouseUp) {
if (mLastMiddleMouseDownContent == mouseContent || aEvent->mClickTarget =
mLastMiddleMouseDownContentParent == mouseContent || nsContentUtils::GetCommonAncestorUnderInteractiveContent(
mLastMiddleMouseDownContent == mouseContentParent) { mouseContent, mLastMiddleMouseDownContent);
if (aEvent->mClickTarget) {
aEvent->mClickCount = mMClickCount; aEvent->mClickCount = mMClickCount;
mMClickCount = 0; mMClickCount = 0;
} else { } else {
aEvent->mClickCount = 0; aEvent->mClickCount = 0;
} }
mLastMiddleMouseDownContent = nullptr; mLastMiddleMouseDownContent = nullptr;
mLastMiddleMouseDownContentParent = nullptr;
} }
break; break;
case WidgetMouseEvent::eRightButton: case WidgetMouseEvent::eRightButton:
if (aEvent->mMessage == eMouseDown) { if (aEvent->mMessage == eMouseDown) {
mLastRightMouseDownContent = mouseContent; mLastRightMouseDownContent = mouseContent;
mLastRightMouseDownContentParent = mouseContentParent;
} else if (aEvent->mMessage == eMouseUp) { } else if (aEvent->mMessage == eMouseUp) {
if (mLastRightMouseDownContent == mouseContent || aEvent->mClickTarget =
mLastRightMouseDownContentParent == mouseContent || nsContentUtils::GetCommonAncestorUnderInteractiveContent(
mLastRightMouseDownContent == mouseContentParent) { mouseContent, mLastRightMouseDownContent);
if (aEvent->mClickTarget) {
aEvent->mClickCount = mRClickCount; aEvent->mClickCount = mRClickCount;
mRClickCount = 0; mRClickCount = 0;
} else { } else {
aEvent->mClickCount = 0; aEvent->mClickCount = 0;
} }
mLastRightMouseDownContent = nullptr; mLastRightMouseDownContent = nullptr;
mLastRightMouseDownContentParent = nullptr;
} }
break; break;
} }
@ -4651,89 +4639,151 @@ EventStateManager::SetClickCount(WidgetMouseEvent* aEvent,
return NS_OK; return NS_OK;
} }
// static
bool
EventStateManager::EventCausesClickEvents(const WidgetMouseEvent& aMouseEvent)
{
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 || !aMouseEvent.mClickTarget) {
return false;
}
// Check that the window isn't disabled before firing a click
// (see bug 366544).
return !(aMouseEvent.mWidget && !aMouseEvent.mWidget->IsEnabled());
}
nsresult nsresult
EventStateManager::InitAndDispatchClickEvent(WidgetMouseEvent* aEvent, EventStateManager::InitAndDispatchClickEvent(WidgetMouseEvent* aMouseUpEvent,
nsEventStatus* aStatus, nsEventStatus* aStatus,
EventMessage aMessage, EventMessage aMessage,
nsIPresShell* aPresShell, nsIPresShell* aPresShell,
nsIContent* aMouseTarget, nsIContent* aMouseUpContent,
nsWeakFrame aCurrentTarget, nsWeakFrame aCurrentTarget,
bool aNoContentDispatch) bool aNoContentDispatch)
{ {
WidgetMouseEvent event(aEvent->IsTrusted(), aMessage, MOZ_ASSERT(aMouseUpEvent);
aEvent->mWidget, WidgetMouseEvent::eReal); MOZ_ASSERT(EventCausesClickEvents(*aMouseUpEvent));
MOZ_ASSERT(aMouseUpContent || aCurrentTarget);
event.mRefPoint = aEvent->mRefPoint; WidgetMouseEvent event(aMouseUpEvent->IsTrusted(), aMessage,
event.mClickCount = aEvent->mClickCount; aMouseUpEvent->mWidget, WidgetMouseEvent::eReal);
event.mModifiers = aEvent->mModifiers; event.mRefPoint = aMouseUpEvent->mRefPoint;
event.buttons = aEvent->buttons; event.mClickCount = aMouseUpEvent->mClickCount;
event.mTime = aEvent->mTime; event.mModifiers = aMouseUpEvent->mModifiers;
event.mTimeStamp = aEvent->mTimeStamp; event.buttons = aMouseUpEvent->buttons;
event.mTime = aMouseUpEvent->mTime;
event.mTimeStamp = aMouseUpEvent->mTimeStamp;
event.mFlags.mNoContentDispatch = aNoContentDispatch; event.mFlags.mNoContentDispatch = aNoContentDispatch;
event.button = aEvent->button; event.button = aMouseUpEvent->button;
event.inputSource = aEvent->inputSource; event.inputSource = aMouseUpEvent->inputSource;
return aPresShell->HandleEventWithTarget(&event, aCurrentTarget, if (!aMouseUpContent->IsInComposedDoc()) {
aMouseTarget, aStatus);
}
nsresult
EventStateManager::CheckForAndDispatchClick(WidgetMouseEvent* aEvent,
nsEventStatus* aStatus)
{
nsresult ret = NS_OK;
//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);
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; return NS_OK;
} }
// HandleEvent clears out mCurrentTarget which we might need again // Use local event status for each click event dispatching since it'll be
nsWeakFrame currentTarget = mCurrentTarget; // cleared by EventStateManager::PreHandleEvent(). Therefore, dispatching
ret = InitAndDispatchClickEvent(aEvent, aStatus, eMouseClick, // an event means that previous event status will be ignored.
presShell, mouseContent, currentTarget, nsEventStatus status = nsEventStatus_eIgnore;
notDispatchToContents); 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;
}
if (NS_SUCCEEDED(ret) && aEvent->mClickCount == 2 && nsresult
mouseContent && mouseContent->IsInComposedDoc()) { EventStateManager::PostHandleMouseUp(WidgetMouseEvent* aMouseUpEvent,
//fire double click nsEventStatus* aStatus)
ret = InitAndDispatchClickEvent(aEvent, aStatus, eMouseDoubleClick, {
presShell, mouseContent, currentTarget, MOZ_ASSERT(aMouseUpEvent);
MOZ_ASSERT(EventCausesClickEvents(*aMouseUpEvent));
MOZ_ASSERT(aStatus);
nsCOMPtr<nsIPresShell> presShell = mPresContext->GetPresShell();
if (!presShell) {
return NS_OK;
}
nsCOMPtr<nsIContent> clickTarget = do_QueryInterface(aMouseUpEvent->mClickTarget);
NS_ENSURE_STATE(clickTarget);
// Fire click events if the event target is still available.
nsresult rv = DispatchClickEvents(presShell, aMouseUpEvent, aStatus,
clickTarget);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
nsresult
EventStateManager::DispatchClickEvents(nsIPresShell* aPresShell,
WidgetMouseEvent* aMouseUpEvent,
nsEventStatus* aStatus,
nsIContent* aClickTarget)
{
MOZ_ASSERT(aPresShell);
MOZ_ASSERT(aMouseUpEvent);
MOZ_ASSERT(EventCausesClickEvents(*aMouseUpEvent));
MOZ_ASSERT(aStatus);
MOZ_ASSERT(aClickTarget);
bool notDispatchToContents =
(aMouseUpEvent->button == WidgetMouseEvent::eMiddleButton ||
aMouseUpEvent->button == WidgetMouseEvent::eRightButton);
bool fireAuxClick = notDispatchToContents;
nsWeakFrame currentTarget = aClickTarget->GetPrimaryFrame();
nsresult ret =
InitAndDispatchClickEvent(aMouseUpEvent, aStatus, eMouseClick,
aPresShell, aClickTarget, currentTarget,
notDispatchToContents); notDispatchToContents);
if (NS_WARN_IF(NS_FAILED(ret))) {
return ret;
} }
if (NS_SUCCEEDED(ret) && mouseContent && fireAuxClick &&
mouseContent->IsInComposedDoc()) { // Fire double click event if click count is 2.
ret = InitAndDispatchClickEvent(aEvent, aStatus, eMouseAuxClick, if (aMouseUpEvent->mClickCount == 2 &&
presShell, mouseContent, currentTarget, aClickTarget && aClickTarget->IsInComposedDoc()) {
ret = InitAndDispatchClickEvent(aMouseUpEvent, aStatus, eMouseDoubleClick,
aPresShell, aClickTarget, currentTarget,
notDispatchToContents);
if (NS_WARN_IF(NS_FAILED(ret))) {
return ret;
}
}
// Fire auxclick even if necessary.
if (fireAuxClick &&
aClickTarget && aClickTarget->IsInComposedDoc()) {
ret = InitAndDispatchClickEvent(aMouseUpEvent, aStatus, eMouseAuxClick,
aPresShell, aClickTarget, currentTarget,
false); false);
NS_WARNING_ASSERTION(NS_SUCCEEDED(ret), "Failed to dispatch eMouseAuxClick");
} }
}
}
return ret; return ret;
} }

View file

@ -414,16 +414,79 @@ protected:
*/ */
void UpdateDragDataTransfer(WidgetDragEvent* dragEvent); 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, nsEventStatus* aStatus,
EventMessage aMessage, EventMessage aMessage,
nsIPresShell* aPresShell, nsIPresShell* aPresShell,
nsIContent* aMouseTarget, nsIContent* aMouseUpContent,
nsWeakFrame aCurrentTarget, nsWeakFrame aCurrentTarget,
bool aNoContentDispatch); bool aNoContentDispatch);
nsresult SetClickCount(WidgetMouseEvent* aEvent, nsEventStatus* aStatus); nsresult SetClickCount(WidgetMouseEvent* aEvent, nsEventStatus* aStatus);
nsresult CheckForAndDispatchClick(WidgetMouseEvent* aEvent,
/**
* 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); 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 EnsureDocument(nsPresContext* aPresContext);
void FlushPendingEvents(nsPresContext* aPresContext); void FlushPendingEvents(nsPresContext* aPresContext);
@ -951,11 +1014,8 @@ private:
uint16_t mGestureDownButtons; uint16_t mGestureDownButtons;
nsCOMPtr<nsIContent> mLastLeftMouseDownContent; nsCOMPtr<nsIContent> mLastLeftMouseDownContent;
nsCOMPtr<nsIContent> mLastLeftMouseDownContentParent;
nsCOMPtr<nsIContent> mLastMiddleMouseDownContent; nsCOMPtr<nsIContent> mLastMiddleMouseDownContent;
nsCOMPtr<nsIContent> mLastMiddleMouseDownContentParent;
nsCOMPtr<nsIContent> mLastRightMouseDownContent; nsCOMPtr<nsIContent> mLastRightMouseDownContent;
nsCOMPtr<nsIContent> mLastRightMouseDownContentParent;
nsCOMPtr<nsIContent> mActiveContent; nsCOMPtr<nsIContent> mActiveContent;
nsCOMPtr<nsIContent> mHoverContent; nsCOMPtr<nsIContent> mHoverContent;

View file

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

View file

@ -221,11 +221,14 @@ HTMLButtonElement::GetEventTargetParent(EventChainPreVisitor& aVisitor)
bool outerActivateEvent = bool outerActivateEvent =
((mouseEvent && mouseEvent->IsLeftClickEvent()) || ((mouseEvent && mouseEvent->IsLeftClickEvent()) ||
(aVisitor.mEvent->mMessage == eLegacyDOMActivate && (aVisitor.mEvent->mMessage == eLegacyDOMActivate &&
!mInInternalActivate)); !mInInternalActivate &&
aVisitor.mEvent->mOriginalTarget == this));
if (outerActivateEvent) { if (outerActivateEvent) {
aVisitor.mItemFlags |= NS_OUTER_ACTIVATE_EVENT; aVisitor.mItemFlags |= NS_OUTER_ACTIVATE_EVENT;
if (mType == NS_FORM_BUTTON_SUBMIT && mForm) { if (mType == NS_FORM_BUTTON_SUBMIT && mForm &&
!aVisitor.mEvent->mFlags.mMultiplePreActionsPrevented) {
aVisitor.mEvent->mFlags.mMultiplePreActionsPrevented = true;
aVisitor.mItemFlags |= NS_IN_SUBMIT_CLICK; aVisitor.mItemFlags |= NS_IN_SUBMIT_CLICK;
// tell the form that we are about to enter a click handler. // tell the form that we are about to enter a click handler.
// that means that if there are scripted submissions, the // that means that if there are scripted submissions, the

View file

@ -143,11 +143,14 @@ namespace dom {
#define NS_ORIGINAL_CHECKED_VALUE (1 << 10) #define NS_ORIGINAL_CHECKED_VALUE (1 << 10)
#define NS_NO_CONTENT_DISPATCH (1 << 11) #define NS_NO_CONTENT_DISPATCH (1 << 11)
#define NS_ORIGINAL_INDETERMINATE_VALUE (1 << 12) #define NS_ORIGINAL_INDETERMINATE_VALUE (1 << 12)
#define NS_CONTROL_TYPE(bits) ((bits) & ~( \
NS_OUTER_ACTIVATE_EVENT | NS_ORIGINAL_CHECKED_VALUE | NS_NO_CONTENT_DISPATCH | \
NS_ORIGINAL_INDETERMINATE_VALUE))
#define NS_PRE_HANDLE_BLUR_EVENT (1 << 13) #define NS_PRE_HANDLE_BLUR_EVENT (1 << 13)
#define NS_PRE_HANDLE_INPUT_EVENT (1 << 14) #define NS_PRE_HANDLE_INPUT_EVENT (1 << 14)
#define NS_IN_SUBMIT_CLICK (1 << 15)
#define NS_CONTROL_TYPE(bits) \
((bits) & ~(NS_OUTER_ACTIVATE_EVENT | NS_ORIGINAL_CHECKED_VALUE | \
NS_NO_CONTENT_DISPATCH | NS_ORIGINAL_INDETERMINATE_VALUE | \
NS_PRE_HANDLE_BLUR_EVENT | NS_PRE_HANDLE_INPUT_EVENT | \
NS_IN_SUBMIT_CLICK))
// whether textfields should be selected once focused: // whether textfields should be selected once focused:
// -1: no, 1: yes, 0: uninitialized // -1: no, 1: yes, 0: uninitialized
@ -3800,7 +3803,10 @@ HTMLInputElement::GetEventTargetParent(EventChainPreVisitor& aVisitor)
case NS_FORM_INPUT_SUBMIT: case NS_FORM_INPUT_SUBMIT:
case NS_FORM_INPUT_IMAGE: case NS_FORM_INPUT_IMAGE:
if (mForm) { if (mForm && !aVisitor.mEvent->mFlags.mMultiplePreActionsPrevented) {
// Make sure other submit elements don't try to trigger submission.
aVisitor.mEvent->mFlags.mMultiplePreActionsPrevented = true;
aVisitor.mItemFlags |= NS_IN_SUBMIT_CLICK;
// tell the form that we are about to enter a click handler. // tell the form that we are about to enter a click handler.
// that means that if there are scripted submissions, the // that means that if there are scripted submissions, the
// latest one will be deferred until after the exit point of the handler. // latest one will be deferred until after the exit point of the handler.
@ -4394,17 +4400,15 @@ HTMLInputElement::PostHandleEvent(EventChainPostVisitor& aVisitor)
} }
} }
if (outerActivateEvent) { if ((aVisitor.mItemFlags & NS_IN_SUBMIT_CLICK) && mForm) {
switch(oldType) { switch(oldType) {
case NS_FORM_INPUT_SUBMIT: case NS_FORM_INPUT_SUBMIT:
case NS_FORM_INPUT_IMAGE: case NS_FORM_INPUT_IMAGE:
if (mForm) {
// tell the form that we are about to exit a click handler // tell the form that we are about to exit a click handler
// so the form knows not to defer subsequent submissions // so the form knows not to defer subsequent submissions
// the pending ones that were created during the handler // the pending ones that were created during the handler
// will be flushed or forgoten. // will be flushed or forgoten.
mForm->OnSubmitClickEnd(); mForm->OnSubmitClickEnd();
}
break; break;
default: default:
break; break;
@ -4770,7 +4774,8 @@ HTMLInputElement::PostHandleEvent(EventChainPostVisitor& aVisitor)
if (outerActivateEvent) { if (outerActivateEvent) {
if (mForm && (oldType == NS_FORM_INPUT_SUBMIT || if (mForm && (oldType == NS_FORM_INPUT_SUBMIT ||
oldType == NS_FORM_INPUT_IMAGE)) { oldType == NS_FORM_INPUT_IMAGE)) {
if (mType != NS_FORM_INPUT_SUBMIT && mType != NS_FORM_INPUT_IMAGE) { if (mType != NS_FORM_INPUT_SUBMIT && mType != NS_FORM_INPUT_IMAGE &&
aVisitor.mItemFlags & NS_IN_SUBMIT_CLICK) {
// If the type has changed to a non-submit type, then we want to // If the type has changed to a non-submit type, then we want to
// flush the stored submission if there is one (as if the submit() // flush the stored submission if there is one (as if the submit()
// was allowed to succeed) // was allowed to succeed)
@ -4809,7 +4814,7 @@ HTMLInputElement::PostHandleEvent(EventChainPostVisitor& aVisitor)
break; break;
} //switch } //switch
} //click or outer activate event } //click or outer activate event
} else if (outerActivateEvent && } else if ((aVisitor.mItemFlags & NS_IN_SUBMIT_CLICK) &&
(oldType == NS_FORM_INPUT_SUBMIT || (oldType == NS_FORM_INPUT_SUBMIT ||
oldType == NS_FORM_INPUT_IMAGE) && oldType == NS_FORM_INPUT_IMAGE) &&
mForm) { mForm) {

View file

@ -11,6 +11,7 @@
#include "mozilla/EffectCompositor.h" #include "mozilla/EffectCompositor.h"
#include "mozilla/EffectSet.h" #include "mozilla/EffectSet.h"
#include "mozilla/EventDispatcher.h" #include "mozilla/EventDispatcher.h"
#include "mozilla/EventStateManager.h"
#include "mozilla/FloatingPoint.h" #include "mozilla/FloatingPoint.h"
#include "mozilla/gfx/gfxVars.h" #include "mozilla/gfx/gfxVars.h"
#include "mozilla/gfx/PathHelpers.h" #include "mozilla/gfx/PathHelpers.h"
@ -2255,6 +2256,59 @@ nsLayoutUtils::GetPopupFrameForEventCoordinates(nsPresContext* aPresContext,
return nullptr; 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) static void ConstrainToCoordValues(float& aStart, float& aSize)
{ {
MOZ_ASSERT(aSize >= 0); MOZ_ASSERT(aSize >= 0);

View file

@ -728,6 +728,21 @@ public:
nsPresContext* aPresContext, nsPresContext* aPresContext,
const mozilla::WidgetEvent* aEvent); 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 * Translate from widget coordinates to the view's coordinates
* @param aPresContext the PresContext for the view * @param aPresContext the PresContext for the view

View file

@ -118,8 +118,8 @@ nsHTMLButtonControlFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder,
nsDisplayListCollection set(aBuilder); nsDisplayListCollection set(aBuilder);
// Do not allow the child subtree to receive events. // HTMLInputElement buttons are opaque to hit tests, HTMLButtonElement buttons are not.
if (!isForEventDelivery) { if (!(isForEventDelivery && this->IsInput())) {
DisplayListClipState::AutoSaveRestore clipState(aBuilder); DisplayListClipState::AutoSaveRestore clipState(aBuilder);
if (ShouldClipPaintingToBorderBox()) { if (ShouldClipPaintingToBorderBox()) {

View file

@ -699,6 +699,16 @@ button {
justify-items: inherit; justify-items: inherit;
} }
@supports -moz-bool-pref("dom.forms.button.standards_compliant") {
/*
* Button content does historically not receive pointer events.
* This pref is enabled by default and can disabled to get Webkit behavior.
*/
button * {
pointer-events: none !important;
}
}
button:hover, button:hover,
input[type="color"]:-moz-system-metric(color-picker-available):hover, input[type="color"]:-moz-system-metric(color-picker-available):hover,
input[type="reset"]:hover, input[type="reset"]:hover,

View file

@ -85,7 +85,7 @@ extern "C" {
/* # include <sys/varargs.h> */ /* # include <sys/varargs.h> */
# include <sys/socket.h> # include <sys/socket.h>
# include <netinet/in.h> # include <netinet/in.h>
#if !defined(XP_OS2) && !defined(DARWIN) #if !defined(XP_OS2)
# include <unistd.h> # include <unistd.h>
#endif #endif
# endif /* defined( _WINDOWS ) */ # endif /* defined( _WINDOWS ) */

View file

@ -1198,6 +1198,9 @@ pref("dom.forms.datetime.timepicker", false);
// TODO: implement/fix these. // TODO: implement/fix these.
pref("dom.forms.datetime.others", false); pref("dom.forms.datetime.others", false);
// <button> elements are opaque to pointer events.
pref("dom.forms.button.standards_compliant", true);
// Support for new @autocomplete values // Support for new @autocomplete values
pref("dom.forms.autocomplete.experimental", false); pref("dom.forms.autocomplete.experimental", false);

View file

@ -80,6 +80,8 @@ public:
// the first <label> element is clicked, that one may set this true. // the first <label> element is clicked, that one may set this true.
// Then, the second <label> element won't handle the event. // Then, the second <label> element won't handle the event.
bool mMultipleActionsPrevented : 1; bool mMultipleActionsPrevented : 1;
// Similar to above but expected to be used during PreHandleEvent phase.
bool mMultiplePreActionsPrevented : 1;
// If mIsBeingDispatched is true, the DOM event created from the event is // If mIsBeingDispatched is true, the DOM event created from the event is
// dispatching into the DOM tree and not completed. // dispatching into the DOM tree and not completed.
bool mIsBeingDispatched : 1; bool mIsBeingDispatched : 1;

View file

@ -275,6 +275,10 @@ public:
return result; return result;
} }
// If during mouseup handling we detect that click event might need to be
// dispatched, this is setup to be the target of the click event.
nsCOMPtr<dom::EventTarget> mClickTarget;
// mReason indicates the reason why the event is fired: // mReason indicates the reason why the event is fired:
// - Representing mouse operation. // - Representing mouse operation.
// - Synthesized for emulating mousemove event when the content under the // - Synthesized for emulating mousemove event when the content under the

View file

@ -2905,7 +2905,7 @@ static NSMutableSet *gSwizzledFrameViewClasses = nil;
[super initWithContentRect:aContentRect styleMask:aStyle backing:aBufferingType defer:aFlag]; [super initWithContentRect:aContentRect styleMask:aStyle backing:aBufferingType defer:aFlag];
// MacOS 13 Ventura, doesn't seem to create the contentView... so create it ourselves // MacOS 13 Ventura, doesn't seem to create the contentView... so create it ourselves
if(![super contentView]) { if(![super contentView]) {
[super setContentView:[[NSView alloc] initWithFrame:aContentRect]]; [super setContentView:[[[NSView alloc] initWithFrame:aContentRect] autorelease]];
} }
mState = nil; mState = nil;
mActiveTitlebarColor = nil; mActiveTitlebarColor = nil;
@ -3213,64 +3213,6 @@ static const NSString* kStateCollectionBehavior = @"collectionBehavior";
} }
} }
// Override methods that translate between content rect and frame rect.
- (NSRect)contentRectForFrameRect:(NSRect)aRect
{
if ([self drawsContentsIntoWindowFrame]) {
return aRect;
}
return [super contentRectForFrameRect:aRect];
}
- (NSRect)contentRectForFrameRect:(NSRect)aRect styleMask:(NSUInteger)aMask
{
if ([self drawsContentsIntoWindowFrame]) {
return aRect;
}
if ([super respondsToSelector:@selector(contentRectForFrameRect:styleMask:)]) {
return [super contentRectForFrameRect:aRect styleMask:aMask];
} else {
return [NSWindow contentRectForFrameRect:aRect styleMask:aMask];
}
}
- (NSRect)frameRectForContentRect:(NSRect)aRect
{
if ([self drawsContentsIntoWindowFrame]) {
return aRect;
}
return [super frameRectForContentRect:aRect];
}
- (NSRect)frameRectForContentRect:(NSRect)aRect styleMask:(NSUInteger)aMask
{
if ([self drawsContentsIntoWindowFrame]) {
return aRect;
}
if ([super respondsToSelector:@selector(frameRectForContentRect:styleMask:)]) {
return [super frameRectForContentRect:aRect styleMask:aMask];
} else {
return [NSWindow frameRectForContentRect:aRect styleMask:aMask];
}
}
- (void)setContentView:(NSView*)aView
{
[super setContentView:aView];
// Now move the contentView to the bottommost layer so that it's guaranteed
// to be under the window buttons.
NSView* frameView = [aView superview];
[aView removeFromSuperview];
if ([frameView respondsToSelector:@selector(_addKnownSubview:positioned:relativeTo:)]) {
// 10.10 prints a warning when we call addSubview on the frame view, so we
// silence the warning by calling a private method instead.
[frameView _addKnownSubview:aView positioned:NSWindowBelow relativeTo:nil];
} else {
[frameView addSubview:aView positioned:NSWindowBelow relativeTo:nil];
}
}
- (NSArray*)titlebarControls - (NSArray*)titlebarControls
{ {
// Return all subviews of the frameView which are not the content view. // Return all subviews of the frameView which are not the content view.
@ -3504,6 +3446,64 @@ static const NSString* kStateCollectionBehavior = @"collectionBehavior";
return NSMaxY(frameRect) - NSMaxY(originalContentRect); return NSMaxY(frameRect) - NSMaxY(originalContentRect);
} }
// Override methods that translate between content rect and frame rect.
- (NSRect)contentRectForFrameRect:(NSRect)aRect
{
if ([self drawsContentsIntoWindowFrame]) {
return aRect;
}
return [super contentRectForFrameRect:aRect];
}
- (NSRect)contentRectForFrameRect:(NSRect)aRect styleMask:(NSUInteger)aMask
{
if ([self drawsContentsIntoWindowFrame]) {
return aRect;
}
if ([super respondsToSelector:@selector(contentRectForFrameRect:styleMask:)]) {
return [super contentRectForFrameRect:aRect styleMask:aMask];
} else {
return [NSWindow contentRectForFrameRect:aRect styleMask:aMask];
}
}
- (NSRect)frameRectForContentRect:(NSRect)aRect
{
if ([self drawsContentsIntoWindowFrame]) {
return aRect;
}
return [super frameRectForContentRect:aRect];
}
- (NSRect)frameRectForContentRect:(NSRect)aRect styleMask:(NSUInteger)aMask
{
if ([self drawsContentsIntoWindowFrame]) {
return aRect;
}
if ([super respondsToSelector:@selector(frameRectForContentRect:styleMask:)]) {
return [super frameRectForContentRect:aRect styleMask:aMask];
} else {
return [NSWindow frameRectForContentRect:aRect styleMask:aMask];
}
}
- (void)setContentView:(NSView*)aView
{
[super setContentView:aView];
// Now move the contentView to the bottommost layer so that it's guaranteed
// to be under the window buttons.
NSView* frameView = [aView superview];
[aView removeFromSuperview];
if ([frameView respondsToSelector:@selector(_addKnownSubview:positioned:relativeTo:)]) {
// 10.10 prints a warning when we call addSubview on the frame view, so we
// silence the warning by calling a private method instead.
[frameView _addKnownSubview:aView positioned:NSWindowBelow relativeTo:nil];
} else {
[frameView addSubview:aView positioned:NSWindowBelow relativeTo:nil];
}
}
// Stores the complete height of titlebar + toolbar. // Stores the complete height of titlebar + toolbar.
- (void)setUnifiedToolbarHeight:(CGFloat)aHeight - (void)setUnifiedToolbarHeight:(CGFloat)aHeight
{ {

View file

@ -478,6 +478,7 @@ STUB(gtk_widget_unparent)
STUB(gtk_window_deiconify) STUB(gtk_window_deiconify)
STUB(gtk_window_fullscreen) STUB(gtk_window_fullscreen)
STUB(gtk_window_get_group) STUB(gtk_window_get_group)
STUB(gtk_window_get_modal)
STUB(gtk_window_get_transient_for) STUB(gtk_window_get_transient_for)
STUB(gtk_window_get_type) STUB(gtk_window_get_type)
STUB(gtk_window_get_type_hint) STUB(gtk_window_get_type_hint)

View file

@ -100,7 +100,11 @@ NS_IMETHODIMP nsColorPicker::Open(nsIColorPickerShownCallback *aColorPickerShown
GtkWidget* color_chooser = gtk_color_chooser_dialog_new(title, parent_window); GtkWidget* color_chooser = gtk_color_chooser_dialog_new(title, parent_window);
if (parent_window) { if (parent_window) {
gtk_window_set_destroy_with_parent(GTK_WINDOW(color_chooser), TRUE); GtkWindow *window = GTK_WINDOW(color_chooser);
gtk_window_set_destroy_with_parent(window, TRUE);
if (gtk_window_get_modal(parent_window)) {
gtk_window_set_modal(window, TRUE);
}
} }
gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(color_chooser), FALSE); gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(color_chooser), FALSE);
@ -117,6 +121,9 @@ NS_IMETHODIMP nsColorPicker::Open(nsIColorPickerShownCallback *aColorPickerShown
GtkWindow *window = GTK_WINDOW(color_chooser); GtkWindow *window = GTK_WINDOW(color_chooser);
gtk_window_set_transient_for(window, parent_window); gtk_window_set_transient_for(window, parent_window);
gtk_window_set_destroy_with_parent(window, TRUE); gtk_window_set_destroy_with_parent(window, TRUE);
if (gtk_window_get_modal(parent_window)) {
gtk_window_set_modal(window, TRUE);
}
} }
GdkColor color_gdk = convertToGdkColor(color); GdkColor color_gdk = convertToGdkColor(color);