diff --git a/dom/base/nsContentUtils.cpp b/dom/base/nsContentUtils.cpp index e484abcf20..1dccbfdcb0 100644 --- a/dom/base/nsContentUtils.cpp +++ b/dom/base/nsContentUtils.cpp @@ -2377,6 +2377,56 @@ nsContentUtils::GetCommonAncestor(nsINode* aNode1, 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 parents1; + do { + parents1.AppendElement(aNode1); + if (aNode1->IsElement() && + aNode1->AsElement()->IsInteractiveHTMLContent(true)) { + break; + } + aNode1 = aNode1->GetFlattenedTreeParentNode(); + } while (aNode1); + + AutoTArray 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 */ bool nsContentUtils::PositionIsBefore(nsINode* aNode1, nsINode* aNode2) diff --git a/dom/base/nsContentUtils.h b/dom/base/nsContentUtils.h index 8597e137ef..8b8acefe72 100644 --- a/dom/base/nsContentUtils.h +++ b/dom/base/nsContentUtils.h @@ -324,6 +324,15 @@ public: static nsINode* GetCommonAncestor(nsINode* aNode1, 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 * tree. diff --git a/dom/events/Event.cpp b/dom/events/Event.cpp index 3fbb138e7c..864678dd66 100755 --- a/dom/events/Event.cpp +++ b/dom/events/Event.cpp @@ -194,6 +194,11 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Event) default: 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(mExplicitOriginalTarget) diff --git a/dom/events/EventStateManager.cpp b/dom/events/EventStateManager.cpp index c8f1acdb55..a3c3f5e0c8 100644 --- a/dom/events/EventStateManager.cpp +++ b/dom/events/EventStateManager.cpp @@ -434,11 +434,8 @@ NS_IMPL_CYCLE_COLLECTION(EventStateManager, mGestureDownContent, mGestureDownFrameOwner, mLastLeftMouseDownContent, - mLastLeftMouseDownContentParent, mLastMiddleMouseDownContent, - mLastMiddleMouseDownContentParent, mLastRightMouseDownContent, - mLastRightMouseDownContentParent, mActiveContent, mHoverContent, mURLTargetContent, @@ -3177,14 +3174,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(); @@ -4579,16 +4573,13 @@ EventStateManager::SetClickCount(WidgetMouseEvent* aEvent, nsEventStatus* aStatus) { nsCOMPtr mouseContent; - nsIContent* mouseContentParent = nullptr; if (mCurrentTarget) { mCurrentTarget->GetContentForEvent(aEvent, getter_AddRefs(mouseContent)); } - if (mouseContent) { - if (mouseContent->IsNodeOfType(nsINode::eTEXT)) { - mouseContent = mouseContent->GetParent(); - } - if (mouseContent && mouseContent->IsRootOfNativeAnonymousSubtree()) { - mouseContentParent = mouseContent->GetParent(); + if (mouseContent && mouseContent->IsNodeOfType(nsINode::eTEXT)) { + nsINode* parent = mouseContent->GetFlattenedTreeParentNode(); + if (parent && parent->IsContent()) { + mouseContent = parent->AsContent(); } } @@ -4596,54 +4587,51 @@ EventStateManager::SetClickCount(WidgetMouseEvent* aEvent, case WidgetMouseEvent::eLeftButton: if (aEvent->mMessage == eMouseDown) { mLastLeftMouseDownContent = mouseContent; - mLastLeftMouseDownContentParent = mouseContentParent; } else if (aEvent->mMessage == eMouseUp) { - if (mLastLeftMouseDownContent == mouseContent || - mLastLeftMouseDownContentParent == mouseContent || - mLastLeftMouseDownContent == mouseContentParent) { + aEvent->mClickTarget = + nsContentUtils::GetCommonAncestorUnderInteractiveContent( + mouseContent, mLastLeftMouseDownContent); + if (aEvent->mClickTarget) { aEvent->mClickCount = mLClickCount; mLClickCount = 0; } else { aEvent->mClickCount = 0; } mLastLeftMouseDownContent = nullptr; - mLastLeftMouseDownContentParent = nullptr; } break; case WidgetMouseEvent::eMiddleButton: if (aEvent->mMessage == eMouseDown) { mLastMiddleMouseDownContent = mouseContent; - mLastMiddleMouseDownContentParent = mouseContentParent; } else if (aEvent->mMessage == eMouseUp) { - if (mLastMiddleMouseDownContent == mouseContent || - mLastMiddleMouseDownContentParent == mouseContent || - mLastMiddleMouseDownContent == mouseContentParent) { + aEvent->mClickTarget = + nsContentUtils::GetCommonAncestorUnderInteractiveContent( + mouseContent, mLastMiddleMouseDownContent); + if (aEvent->mClickTarget) { aEvent->mClickCount = mMClickCount; mMClickCount = 0; } else { aEvent->mClickCount = 0; } mLastMiddleMouseDownContent = nullptr; - mLastMiddleMouseDownContentParent = nullptr; } break; case WidgetMouseEvent::eRightButton: if (aEvent->mMessage == eMouseDown) { mLastRightMouseDownContent = mouseContent; - mLastRightMouseDownContentParent = mouseContentParent; } else if (aEvent->mMessage == eMouseUp) { - if (mLastRightMouseDownContent == mouseContent || - mLastRightMouseDownContentParent == mouseContent || - mLastRightMouseDownContent == mouseContentParent) { + aEvent->mClickTarget = + nsContentUtils::GetCommonAncestorUnderInteractiveContent( + mouseContent, mLastRightMouseDownContent); + if (aEvent->mClickTarget) { aEvent->mClickCount = mRClickCount; mRClickCount = 0; } else { aEvent->mClickCount = 0; } mLastRightMouseDownContent = nullptr; - mLastRightMouseDownContentParent = nullptr; } break; } @@ -4651,89 +4639,151 @@ 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 || !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 -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); - //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()) { + 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; + + if (!aMouseUpContent->IsInComposedDoc()) { + return NS_OK; + } + + // 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 presShell = mPresContext->GetPresShell(); + if (!presShell) { + return NS_OK; + } + + nsCOMPtr 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); + if (NS_WARN_IF(NS_FAILED(ret))) { + return ret; + } + + // Fire double click event if click count is 2. + if (aMouseUpEvent->mClickCount == 2 && + aClickTarget && aClickTarget->IsInComposedDoc()) { + ret = InitAndDispatchClickEvent(aMouseUpEvent, aStatus, eMouseDoubleClick, + aPresShell, aClickTarget, currentTarget, + notDispatchToContents); + if (NS_WARN_IF(NS_FAILED(ret))) { return ret; } - //fire click - bool notDispatchToContents = - (aEvent->button == WidgetMouseEvent::eMiddleButton || - aEvent->button == WidgetMouseEvent::eRightButton); - - bool fireAuxClick = notDispatchToContents; - - nsCOMPtr presShell = mPresContext->GetPresShell(); - if (presShell) { - nsCOMPtr 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); - } - } } + + // Fire auxclick even if necessary. + if (fireAuxClick && + aClickTarget && aClickTarget->IsInComposedDoc()) { + ret = InitAndDispatchClickEvent(aMouseUpEvent, aStatus, eMouseAuxClick, + aPresShell, aClickTarget, currentTarget, + false); + NS_WARNING_ASSERTION(NS_SUCCEEDED(ret), "Failed to dispatch eMouseAuxClick"); + } + return ret; } diff --git a/dom/events/EventStateManager.h b/dom/events/EventStateManager.h index 819c93b602..e0c250f3b7 100644 --- a/dom/events/EventStateManager.h +++ b/dom/events/EventStateManager.h @@ -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); @@ -951,11 +1014,8 @@ private: uint16_t mGestureDownButtons; nsCOMPtr mLastLeftMouseDownContent; - nsCOMPtr mLastLeftMouseDownContentParent; nsCOMPtr mLastMiddleMouseDownContent; - nsCOMPtr mLastMiddleMouseDownContentParent; nsCOMPtr mLastRightMouseDownContent; - nsCOMPtr mLastRightMouseDownContentParent; nsCOMPtr mActiveContent; nsCOMPtr mHoverContent; diff --git a/dom/events/UIEvent.cpp b/dom/events/UIEvent.cpp index 09544f0b81..40015f65eb 100644 --- a/dom/events/UIEvent.cpp +++ b/dom/events/UIEvent.cpp @@ -228,26 +228,20 @@ UIEvent::GetWhich(uint32_t* aWhich) already_AddRefed 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 parent = targetFrame->GetContentOffsetsFromPoint(pt).content; - if (parent) { - if (parent->ChromeOnlyAccess() && - !nsContentUtils::CanAccessNativeAnon()) { - return nullptr; - } - return parent.forget(); - } + nsCOMPtr presShell = mPresContext->GetPresShell(); + if (NS_WARN_IF(!presShell)) { + return nullptr; } - return nullptr; + nsCOMPtr 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 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 diff --git a/dom/html/HTMLButtonElement.cpp b/dom/html/HTMLButtonElement.cpp index e493d1891d..e3fdbb4d32 100644 --- a/dom/html/HTMLButtonElement.cpp +++ b/dom/html/HTMLButtonElement.cpp @@ -221,11 +221,14 @@ HTMLButtonElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) bool outerActivateEvent = ((mouseEvent && mouseEvent->IsLeftClickEvent()) || (aVisitor.mEvent->mMessage == eLegacyDOMActivate && - !mInInternalActivate)); + !mInInternalActivate && + aVisitor.mEvent->mOriginalTarget == this)); if (outerActivateEvent) { 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; // tell the form that we are about to enter a click handler. // that means that if there are scripted submissions, the diff --git a/dom/html/HTMLInputElement.cpp b/dom/html/HTMLInputElement.cpp index 557e673e15..2f4ee84a49 100644 --- a/dom/html/HTMLInputElement.cpp +++ b/dom/html/HTMLInputElement.cpp @@ -143,11 +143,14 @@ namespace dom { #define NS_ORIGINAL_CHECKED_VALUE (1 << 10) #define NS_NO_CONTENT_DISPATCH (1 << 11) #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_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: // -1: no, 1: yes, 0: uninitialized @@ -3800,7 +3803,10 @@ HTMLInputElement::GetEventTargetParent(EventChainPreVisitor& aVisitor) case NS_FORM_INPUT_SUBMIT: 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. // that means that if there are scripted submissions, the // 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) { case NS_FORM_INPUT_SUBMIT: case NS_FORM_INPUT_IMAGE: - if (mForm) { - // tell the form that we are about to exit a click handler - // so the form knows not to defer subsequent submissions - // the pending ones that were created during the handler - // will be flushed or forgoten. - mForm->OnSubmitClickEnd(); - } + // tell the form that we are about to exit a click handler + // so the form knows not to defer subsequent submissions + // the pending ones that were created during the handler + // will be flushed or forgoten. + mForm->OnSubmitClickEnd(); break; default: break; @@ -4770,7 +4774,8 @@ HTMLInputElement::PostHandleEvent(EventChainPostVisitor& aVisitor) if (outerActivateEvent) { if (mForm && (oldType == NS_FORM_INPUT_SUBMIT || 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 // flush the stored submission if there is one (as if the submit() // was allowed to succeed) @@ -4809,7 +4814,7 @@ HTMLInputElement::PostHandleEvent(EventChainPostVisitor& aVisitor) break; } //switch } //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_IMAGE) && mForm) { diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 7e3561b7bc..fbf0a04792 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -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 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 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); diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h index 21407511af..f8724d69d3 100644 --- a/layout/base/nsLayoutUtils.h +++ b/layout/base/nsLayoutUtils.h @@ -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 diff --git a/layout/forms/nsHTMLButtonControlFrame.cpp b/layout/forms/nsHTMLButtonControlFrame.cpp index 1ec0ec6c94..d1fbfbabd7 100644 --- a/layout/forms/nsHTMLButtonControlFrame.cpp +++ b/layout/forms/nsHTMLButtonControlFrame.cpp @@ -118,8 +118,8 @@ nsHTMLButtonControlFrame::BuildDisplayList(nsDisplayListBuilder* aBuilder, nsDisplayListCollection set(aBuilder); - // Do not allow the child subtree to receive events. - if (!isForEventDelivery) { + // HTMLInputElement buttons are opaque to hit tests, HTMLButtonElement buttons are not. + if (!(isForEventDelivery && this->IsInput())) { DisplayListClipState::AutoSaveRestore clipState(aBuilder); if (ShouldClipPaintingToBorderBox()) { diff --git a/layout/style/res/forms.css b/layout/style/res/forms.css index a808822c89..fcf5874829 100644 --- a/layout/style/res/forms.css +++ b/layout/style/res/forms.css @@ -699,6 +699,16 @@ button { 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, input[type="color"]:-moz-system-metric(color-picker-available):hover, input[type="reset"]:hover, diff --git a/ldap/c-sdk/liblber/lber-int.h b/ldap/c-sdk/liblber/lber-int.h index 24b15bdc5f..e9efba345c 100644 --- a/ldap/c-sdk/liblber/lber-int.h +++ b/ldap/c-sdk/liblber/lber-int.h @@ -85,7 +85,7 @@ extern "C" { /* # include */ # include # include -#if !defined(XP_OS2) && !defined(DARWIN) +#if !defined(XP_OS2) # include #endif # endif /* defined( _WINDOWS ) */ diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index bc1d9ea089..e94b5ec7b2 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -1198,6 +1198,9 @@ pref("dom.forms.datetime.timepicker", false); // TODO: implement/fix these. pref("dom.forms.datetime.others", false); +//