Backed out 2 changesets (bug 1528289) for breaking navigation with clicks after user scrolled by clicking with middle mouse button. a=backout
Backed out changeset 33c7b633ada2 (bug 1528289) Backed out changeset be5cf87707f9 (bug 1528289)
This commit is contained in:
@@ -3232,30 +3232,6 @@ nsresult EventStateManager::PostHandleEvent(nsPresContext* aPresContext,
|
||||
}
|
||||
}
|
||||
|
||||
// If MouseEvent::PreventClickEvent() was called by chrome script,
|
||||
// we need to forget the clicking content and click count for the
|
||||
// following eMouseUp event.
|
||||
if (mouseEvent->mClickEventPrevented) {
|
||||
RefPtr<EventStateManager> esm =
|
||||
ESMFromContentOrThis(aOverrideClickTarget);
|
||||
switch (mouseEvent->mButton) {
|
||||
case MouseButton::ePrimary:
|
||||
esm->mLastLeftMouseDownContent = nullptr;
|
||||
esm->mLClickCount = 0;
|
||||
break;
|
||||
case MouseButton::eSecondary:
|
||||
esm->mLastMiddleMouseDownContent = nullptr;
|
||||
esm->mMClickCount = 0;
|
||||
break;
|
||||
case MouseButton::eMiddle:
|
||||
esm->mLastRightMouseDownContent = nullptr;
|
||||
esm->mRClickCount = 0;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIContent> activeContent;
|
||||
// When content calls PreventDefault on pointerdown, we also call
|
||||
// PreventDefault on the subsequent mouse events to suppress default
|
||||
@@ -4941,14 +4917,11 @@ nsresult EventStateManager::SetClickCount(WidgetMouseEvent* aEvent,
|
||||
switch (aEvent->mButton) {
|
||||
case MouseButton::ePrimary:
|
||||
if (aEvent->mMessage == eMouseDown) {
|
||||
mLastLeftMouseDownContent =
|
||||
!aEvent->mClickEventPrevented ? mouseContent : nullptr;
|
||||
mLastLeftMouseDownContent = mouseContent;
|
||||
} else if (aEvent->mMessage == eMouseUp) {
|
||||
aEvent->mClickTarget =
|
||||
!aEvent->mClickEventPrevented
|
||||
? nsContentUtils::GetCommonAncestorUnderInteractiveContent(
|
||||
mouseContent, mLastLeftMouseDownContent)
|
||||
: nullptr;
|
||||
nsContentUtils::GetCommonAncestorUnderInteractiveContent(
|
||||
mouseContent, mLastLeftMouseDownContent);
|
||||
if (aEvent->mClickTarget) {
|
||||
aEvent->mClickCount = mLClickCount;
|
||||
mLClickCount = 0;
|
||||
@@ -4961,14 +4934,11 @@ nsresult EventStateManager::SetClickCount(WidgetMouseEvent* aEvent,
|
||||
|
||||
case MouseButton::eMiddle:
|
||||
if (aEvent->mMessage == eMouseDown) {
|
||||
mLastMiddleMouseDownContent =
|
||||
!aEvent->mClickEventPrevented ? mouseContent : nullptr;
|
||||
mLastMiddleMouseDownContent = mouseContent;
|
||||
} else if (aEvent->mMessage == eMouseUp) {
|
||||
aEvent->mClickTarget =
|
||||
!aEvent->mClickEventPrevented
|
||||
? nsContentUtils::GetCommonAncestorUnderInteractiveContent(
|
||||
mouseContent, mLastMiddleMouseDownContent)
|
||||
: nullptr;
|
||||
nsContentUtils::GetCommonAncestorUnderInteractiveContent(
|
||||
mouseContent, mLastMiddleMouseDownContent);
|
||||
if (aEvent->mClickTarget) {
|
||||
aEvent->mClickCount = mMClickCount;
|
||||
mMClickCount = 0;
|
||||
@@ -4981,14 +4951,11 @@ nsresult EventStateManager::SetClickCount(WidgetMouseEvent* aEvent,
|
||||
|
||||
case MouseButton::eSecondary:
|
||||
if (aEvent->mMessage == eMouseDown) {
|
||||
mLastRightMouseDownContent =
|
||||
!aEvent->mClickEventPrevented ? mouseContent : nullptr;
|
||||
mLastRightMouseDownContent = mouseContent;
|
||||
} else if (aEvent->mMessage == eMouseUp) {
|
||||
aEvent->mClickTarget =
|
||||
!aEvent->mClickEventPrevented
|
||||
? nsContentUtils::GetCommonAncestorUnderInteractiveContent(
|
||||
mouseContent, mLastRightMouseDownContent)
|
||||
: nullptr;
|
||||
nsContentUtils::GetCommonAncestorUnderInteractiveContent(
|
||||
mouseContent, mLastRightMouseDownContent);
|
||||
if (aEvent->mClickTarget) {
|
||||
aEvent->mClickCount = mRClickCount;
|
||||
mRClickCount = 0;
|
||||
@@ -5019,10 +4986,6 @@ bool EventStateManager::EventCausesClickEvents(
|
||||
if (!aMouseEvent.mClickCount || !aMouseEvent.mClickTarget) {
|
||||
return false;
|
||||
}
|
||||
// If click event was explicitly prevented, we shouldn't dispatch it.
|
||||
if (aMouseEvent.mClickEventPrevented) {
|
||||
return false;
|
||||
}
|
||||
// Check that the window isn't disabled before firing a click
|
||||
// (see bug 366544).
|
||||
return !(aMouseEvent.mWidget && !aMouseEvent.mWidget->IsEnabled());
|
||||
@@ -5235,8 +5198,20 @@ nsresult EventStateManager::HandleMiddleClickPaste(
|
||||
}
|
||||
}
|
||||
|
||||
// Don't modify selection here because we've already set caret to the point
|
||||
// at "mousedown" event.
|
||||
// Move selection to the clicked point.
|
||||
nsCOMPtr<nsIContent> container;
|
||||
int32_t offset;
|
||||
nsLayoutUtils::GetContainerAndOffsetAtEvent(
|
||||
aPresShell, aMouseEvent, getter_AddRefs(container), &offset);
|
||||
if (container) {
|
||||
// XXX If readonly or disabled <input> or <textarea> in contenteditable
|
||||
// designMode editor is clicked, the point is in the editor.
|
||||
// However, outer HTMLEditor and Selection should handle it.
|
||||
// So, in such case, Selection::Collapse() will fail.
|
||||
DebugOnly<nsresult> rv = selection->CollapseInLimiter(container, offset);
|
||||
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
|
||||
"Failed to collapse Selection at middle clicked");
|
||||
}
|
||||
|
||||
int32_t clipboardType = nsIClipboard::kGlobalClipboard;
|
||||
nsresult rv = NS_OK;
|
||||
|
||||
@@ -154,12 +154,6 @@ void MouseEvent::InitNSMouseEvent(const nsAString& aType, bool aCanBubble,
|
||||
mouseEventBase->mInputSource = aInputSource;
|
||||
}
|
||||
|
||||
void MouseEvent::PreventClickEvent() {
|
||||
if (WidgetMouseEvent* mouseEvent = mEvent->AsMouseEvent()) {
|
||||
mouseEvent->mClickEventPrevented = true;
|
||||
}
|
||||
}
|
||||
|
||||
int16_t MouseEvent::Button() {
|
||||
switch (mEvent->mClass) {
|
||||
case eMouseEventClass:
|
||||
|
||||
@@ -76,7 +76,6 @@ class MouseEvent : public UIEvent {
|
||||
bool aAltKey, bool aShiftKey, bool aMetaKey,
|
||||
uint16_t aButton, EventTarget* aRelatedTarget,
|
||||
float aPressure, uint16_t aInputSource);
|
||||
void PreventClickEvent();
|
||||
|
||||
protected:
|
||||
~MouseEvent() = default;
|
||||
|
||||
@@ -116,11 +116,5 @@ partial interface MouseEvent
|
||||
optional float pressure = 0,
|
||||
optional unsigned short inputSourceArg = 0);
|
||||
|
||||
/**
|
||||
* preventClickEvent() prevents the following "click", "auxclick" and
|
||||
* "dblclick" events of "mousedown" and "mouseup" events.
|
||||
*/
|
||||
[ChromeOnly]
|
||||
void preventClickEvent();
|
||||
};
|
||||
|
||||
|
||||
@@ -4341,23 +4341,7 @@ nsresult nsIFrame::HandleEvent(nsPresContext* aPresContext,
|
||||
} else if (aEvent->mMessage == eMouseUp || aEvent->mMessage == eTouchEnd) {
|
||||
HandleRelease(aPresContext, aEvent, aEventStatus);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// When middle button is down, we need to just move selection and focus at
|
||||
// the clicked point. Note that even if middle click paste is not enabled,
|
||||
// Chrome moves selection at middle mouse button down. So, we should follow
|
||||
// the behavior for the compatibility.
|
||||
if (aEvent->mMessage == eMouseDown) {
|
||||
WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
|
||||
if (mouseEvent && mouseEvent->mButton == MouseButton::eMiddle) {
|
||||
if (*aEventStatus == nsEventStatus_eConsumeNoDefault) {
|
||||
return NS_OK;
|
||||
}
|
||||
return MoveCaretToEventPoint(aPresContext, mouseEvent, aEventStatus);
|
||||
}
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@@ -4562,6 +4546,14 @@ nsIFrame::HandlePress(nsPresContext* aPresContext, WidgetGUIEvent* aEvent,
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// if we are in Navigator and the click is in a draggable node, we don't want
|
||||
// to start selection because we don't want to interfere with a potential
|
||||
// drag of said node and steal all its glory.
|
||||
int16_t isEditor = presShell->GetSelectionFlags();
|
||||
// weaaak. only the editor can display frame selection not just text and
|
||||
// images
|
||||
isEditor = isEditor == nsISelectionDisplay::DISPLAY_ALL;
|
||||
|
||||
WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
|
||||
|
||||
if (!mouseEvent->IsAlt()) {
|
||||
@@ -4579,35 +4571,6 @@ nsIFrame::HandlePress(nsPresContext* aPresContext, WidgetGUIEvent* aEvent,
|
||||
}
|
||||
}
|
||||
|
||||
return MoveCaretToEventPoint(aPresContext, mouseEvent, aEventStatus);
|
||||
}
|
||||
|
||||
nsresult nsIFrame::MoveCaretToEventPoint(nsPresContext* aPresContext,
|
||||
WidgetMouseEvent* aMouseEvent,
|
||||
nsEventStatus* aEventStatus) {
|
||||
MOZ_ASSERT(aPresContext);
|
||||
MOZ_ASSERT(aMouseEvent);
|
||||
MOZ_ASSERT(aMouseEvent->mMessage == eMouseDown);
|
||||
MOZ_ASSERT(aMouseEvent->mButton == MouseButton::ePrimary ||
|
||||
aMouseEvent->mButton == MouseButton::eMiddle);
|
||||
MOZ_ASSERT(aEventStatus);
|
||||
|
||||
mozilla::PresShell* presShell = aPresContext->GetPresShell();
|
||||
if (!presShell) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// if we are in Navigator and the click is in a draggable node, we don't want
|
||||
// to start selection because we don't want to interfere with a potential
|
||||
// drag of said node and steal all its glory.
|
||||
int16_t isEditor = presShell->GetSelectionFlags();
|
||||
// weaaak. only the editor can display frame selection not just text and
|
||||
// images
|
||||
isEditor = isEditor == nsISelectionDisplay::DISPLAY_ALL;
|
||||
|
||||
// Don't do something if it's moddle button down event.
|
||||
bool isPrimaryButtonDown = aMouseEvent->mButton == MouseButton::ePrimary;
|
||||
|
||||
// check whether style allows selection
|
||||
// if not, don't tell selection the mouse event even occurred.
|
||||
StyleUserSelect selectStyle;
|
||||
@@ -4616,155 +4579,141 @@ nsresult nsIFrame::MoveCaretToEventPoint(nsPresContext* aPresContext,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (isPrimaryButtonDown) {
|
||||
// If the mouse is dragged outside the nearest enclosing scrollable area
|
||||
// while making a selection, the area will be scrolled. To do this, capture
|
||||
// the mouse on the nearest scrollable frame. If there isn't a scrollable
|
||||
// frame, or something else is already capturing the mouse, there's no
|
||||
// reason to capture.
|
||||
if (!PresShell::GetCapturingContent()) {
|
||||
nsIScrollableFrame* scrollFrame =
|
||||
nsLayoutUtils::GetNearestScrollableFrame(
|
||||
this, nsLayoutUtils::SCROLLABLE_SAME_DOC |
|
||||
nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
|
||||
if (scrollFrame) {
|
||||
nsIFrame* capturingFrame = do_QueryFrame(scrollFrame);
|
||||
PresShell::SetCapturingContent(capturingFrame->GetContent(),
|
||||
CaptureFlags::IgnoreAllowedState);
|
||||
}
|
||||
bool useFrameSelection = (selectStyle == StyleUserSelect::Text);
|
||||
|
||||
// If the mouse is dragged outside the nearest enclosing scrollable area
|
||||
// while making a selection, the area will be scrolled. To do this, capture
|
||||
// the mouse on the nearest scrollable frame. If there isn't a scrollable
|
||||
// frame, or something else is already capturing the mouse, there's no
|
||||
// reason to capture.
|
||||
if (!PresShell::GetCapturingContent()) {
|
||||
nsIScrollableFrame* scrollFrame = nsLayoutUtils::GetNearestScrollableFrame(
|
||||
this, nsLayoutUtils::SCROLLABLE_SAME_DOC |
|
||||
nsLayoutUtils::SCROLLABLE_INCLUDE_HIDDEN);
|
||||
if (scrollFrame) {
|
||||
nsIFrame* capturingFrame = do_QueryFrame(scrollFrame);
|
||||
PresShell::SetCapturingContent(capturingFrame->GetContent(),
|
||||
CaptureFlags::IgnoreAllowedState);
|
||||
}
|
||||
}
|
||||
|
||||
// XXX This is screwy; it really should use the selection frame, not the
|
||||
// event frame
|
||||
const nsFrameSelection* frameselection =
|
||||
selectStyle == StyleUserSelect::Text ? GetConstFrameSelection()
|
||||
: presShell->ConstFrameSelection();
|
||||
const nsFrameSelection* frameselection = nullptr;
|
||||
if (useFrameSelection)
|
||||
frameselection = GetConstFrameSelection();
|
||||
else
|
||||
frameselection = presShell->ConstFrameSelection();
|
||||
|
||||
if (!frameselection || frameselection->GetDisplaySelection() ==
|
||||
nsISelectionController::SELECTION_OFF) {
|
||||
nsISelectionController::SELECTION_OFF)
|
||||
return NS_OK; // nothing to do we cannot affect selection from here
|
||||
}
|
||||
|
||||
#ifdef XP_MACOSX
|
||||
// If Control key is pressed on macOS, it should be treated as right click.
|
||||
// So, don't change selection.
|
||||
if (aMouseEvent->IsControl()) {
|
||||
return NS_OK;
|
||||
}
|
||||
bool control = aMouseEvent->IsMeta();
|
||||
if (mouseEvent->IsControl())
|
||||
return NS_OK; // short circuit. hard coded for mac due to time restraints.
|
||||
bool control = mouseEvent->IsMeta();
|
||||
#else
|
||||
bool control = aMouseEvent->IsControl();
|
||||
bool control = mouseEvent->IsControl();
|
||||
#endif
|
||||
|
||||
RefPtr<nsFrameSelection> fc = const_cast<nsFrameSelection*>(frameselection);
|
||||
if (isPrimaryButtonDown && aMouseEvent->mClickCount > 1) {
|
||||
if (mouseEvent->mClickCount > 1) {
|
||||
// These methods aren't const but can't actually delete anything,
|
||||
// so no need for AutoWeakFrame.
|
||||
fc->SetDragState(true);
|
||||
return HandleMultiplePress(aPresContext, aMouseEvent, aEventStatus,
|
||||
control);
|
||||
return HandleMultiplePress(aPresContext, mouseEvent, aEventStatus, control);
|
||||
}
|
||||
|
||||
nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(aMouseEvent,
|
||||
nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(mouseEvent,
|
||||
RelativeTo{this});
|
||||
ContentOffsets offsets = GetContentOffsetsFromPoint(pt, SKIP_HIDDEN);
|
||||
|
||||
if (!offsets.content) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
if (!offsets.content) return NS_ERROR_FAILURE;
|
||||
|
||||
if (isPrimaryButtonDown) {
|
||||
// Let Ctrl/Cmd + left mouse down do table selection instead of drag
|
||||
// initiation.
|
||||
nsCOMPtr<nsIContent> parentContent;
|
||||
int32_t contentOffset;
|
||||
TableSelectionMode target;
|
||||
nsresult rv = GetDataForTableSelection(
|
||||
frameselection, presShell, aMouseEvent, getter_AddRefs(parentContent),
|
||||
&contentOffset, &target);
|
||||
if (NS_SUCCEEDED(rv) && parentContent) {
|
||||
fc->SetDragState(true);
|
||||
return fc->HandleTableSelection(parentContent, contentOffset, target,
|
||||
aMouseEvent);
|
||||
}
|
||||
// Let Ctrl/Cmd+mouse down do table selection instead of drag initiation
|
||||
nsCOMPtr<nsIContent> parentContent;
|
||||
int32_t contentOffset;
|
||||
TableSelectionMode target;
|
||||
nsresult rv;
|
||||
rv = GetDataForTableSelection(frameselection, presShell, mouseEvent,
|
||||
getter_AddRefs(parentContent), &contentOffset,
|
||||
&target);
|
||||
if (NS_SUCCEEDED(rv) && parentContent) {
|
||||
fc->SetDragState(true);
|
||||
return fc->HandleTableSelection(parentContent, contentOffset, target,
|
||||
mouseEvent);
|
||||
}
|
||||
|
||||
fc->SetDelayedCaretData(0);
|
||||
|
||||
if (isPrimaryButtonDown) {
|
||||
// Check if any part of this frame is selected, and if the user clicked
|
||||
// inside the selected region, and if it's the left button. If so, we delay
|
||||
// starting a new selection since the user may be trying to drag the
|
||||
// selected region to some other app.
|
||||
// Check if any part of this frame is selected, and if the
|
||||
// user clicked inside the selected region. If so, we delay
|
||||
// starting a new selection since the user may be trying to
|
||||
// drag the selected region to some other app.
|
||||
|
||||
if (GetContent() && GetContent()->IsMaybeSelected()) {
|
||||
bool inSelection = false;
|
||||
UniquePtr<SelectionDetails> details = frameselection->LookUpSelection(
|
||||
offsets.content, 0, offsets.EndOffset(), false);
|
||||
if (GetContent() && GetContent()->IsMaybeSelected()) {
|
||||
bool inSelection = false;
|
||||
UniquePtr<SelectionDetails> details = frameselection->LookUpSelection(
|
||||
offsets.content, 0, offsets.EndOffset(), false);
|
||||
|
||||
//
|
||||
// If there are any details, check to see if the user clicked
|
||||
// within any selected region of the frame.
|
||||
//
|
||||
|
||||
for (SelectionDetails* curDetail = details.get(); curDetail;
|
||||
curDetail = curDetail->mNext.get()) {
|
||||
//
|
||||
// If there are any details, check to see if the user clicked
|
||||
// within any selected region of the frame.
|
||||
// If the user clicked inside a selection, then just
|
||||
// return without doing anything. We will handle placing
|
||||
// the caret later on when the mouse is released. We ignore
|
||||
// the spellcheck, find and url formatting selections.
|
||||
//
|
||||
|
||||
for (SelectionDetails* curDetail = details.get(); curDetail;
|
||||
curDetail = curDetail->mNext.get()) {
|
||||
//
|
||||
// If the user clicked inside a selection, then just
|
||||
// return without doing anything. We will handle placing
|
||||
// the caret later on when the mouse is released. We ignore
|
||||
// the spellcheck, find and url formatting selections.
|
||||
//
|
||||
if (curDetail->mSelectionType != SelectionType::eSpellCheck &&
|
||||
curDetail->mSelectionType != SelectionType::eFind &&
|
||||
curDetail->mSelectionType != SelectionType::eURLSecondary &&
|
||||
curDetail->mSelectionType != SelectionType::eURLStrikeout &&
|
||||
curDetail->mStart <= offsets.StartOffset() &&
|
||||
offsets.EndOffset() <= curDetail->mEnd) {
|
||||
inSelection = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (inSelection) {
|
||||
fc->SetDragState(false);
|
||||
fc->SetDelayedCaretData(aMouseEvent);
|
||||
return NS_OK;
|
||||
if (curDetail->mSelectionType != SelectionType::eSpellCheck &&
|
||||
curDetail->mSelectionType != SelectionType::eFind &&
|
||||
curDetail->mSelectionType != SelectionType::eURLSecondary &&
|
||||
curDetail->mSelectionType != SelectionType::eURLStrikeout &&
|
||||
curDetail->mStart <= offsets.StartOffset() &&
|
||||
offsets.EndOffset() <= curDetail->mEnd) {
|
||||
inSelection = true;
|
||||
}
|
||||
}
|
||||
|
||||
fc->SetDragState(true);
|
||||
if (inSelection) {
|
||||
fc->SetDragState(false);
|
||||
fc->SetDelayedCaretData(mouseEvent);
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
fc->SetDragState(true);
|
||||
|
||||
// Do not touch any nsFrame members after this point without adding
|
||||
// weakFrame checks.
|
||||
const nsFrameSelection::FocusMode focusMode = [&]() {
|
||||
// If "Shift" and "Ctrl" are both pressed, "Shift" is given precedence. This
|
||||
// mimics the old behaviour.
|
||||
if (aMouseEvent->IsShift()) {
|
||||
if (mouseEvent->IsShift()) {
|
||||
return nsFrameSelection::FocusMode::kExtendSelection;
|
||||
}
|
||||
|
||||
if (isPrimaryButtonDown && control) {
|
||||
if (control) {
|
||||
return nsFrameSelection::FocusMode::kMultiRangeSelection;
|
||||
}
|
||||
|
||||
return nsFrameSelection::FocusMode::kCollapseToNewPoint;
|
||||
}();
|
||||
|
||||
nsresult rv = fc->HandleClick(
|
||||
MOZ_KnownLive(offsets.content) /* bug 1636889 */, offsets.StartOffset(),
|
||||
offsets.EndOffset(), focusMode, offsets.associate);
|
||||
if (NS_FAILED(rv)) {
|
||||
return rv;
|
||||
}
|
||||
rv = fc->HandleClick(MOZ_KnownLive(offsets.content) /* bug 1636889 */,
|
||||
offsets.StartOffset(), offsets.EndOffset(), focusMode,
|
||||
offsets.associate);
|
||||
|
||||
// We don't handle mouse button up if it's middle button.
|
||||
if (isPrimaryButtonDown && offsets.offset != offsets.secondaryOffset) {
|
||||
fc->MaintainSelection();
|
||||
}
|
||||
if (NS_FAILED(rv)) return rv;
|
||||
|
||||
if (isPrimaryButtonDown && isEditor && !aMouseEvent->IsShift() &&
|
||||
if (offsets.offset != offsets.secondaryOffset) fc->MaintainSelection();
|
||||
|
||||
if (isEditor && !mouseEvent->IsShift() &&
|
||||
(offsets.EndOffset() - offsets.StartOffset()) == 1) {
|
||||
// A single node is selected and we aren't extending an existing
|
||||
// selection, which means the user clicked directly on an object (either
|
||||
@@ -4774,7 +4723,7 @@ nsresult nsIFrame::MoveCaretToEventPoint(nsPresContext* aPresContext,
|
||||
fc->SetDragState(false);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult nsIFrame::SelectByTypeAtPoint(nsPresContext* aPresContext,
|
||||
|
||||
@@ -2175,20 +2175,6 @@ class nsIFrame : public nsQueryFrame {
|
||||
HandlePress(nsPresContext* aPresContext, mozilla::WidgetGUIEvent* aEvent,
|
||||
nsEventStatus* aEventStatus);
|
||||
|
||||
/**
|
||||
* MoveCaretToEventPoint() moves caret at the point of aMouseEvent.
|
||||
*
|
||||
* @param aPresContext Must not be nullptr.
|
||||
* @param aMouseEvent Must not be nullptr, the message must be
|
||||
* eMouseDown and its button must be primary or
|
||||
* middle button.
|
||||
* @param aEventStatus [out] Must not be nullptr. This method ignores
|
||||
* its initial value, but callees may refer it.
|
||||
*/
|
||||
MOZ_CAN_RUN_SCRIPT nsresult MoveCaretToEventPoint(
|
||||
nsPresContext* aPresContext, mozilla::WidgetMouseEvent* aMouseEvent,
|
||||
nsEventStatus* aEventStatus);
|
||||
|
||||
MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD HandleMultiplePress(
|
||||
nsPresContext* aPresContext, mozilla::WidgetGUIEvent* aEvent,
|
||||
nsEventStatus* aEventStatus, bool aControlHeld);
|
||||
|
||||
@@ -119,7 +119,6 @@ skip-if = (toolkit == "gtk") || (os == "win")
|
||||
support-files = page_scroll_with_fixed_pos_window.html
|
||||
[test_scroll_behavior.html]
|
||||
[test_scrollframe_abspos_interrupt.html]
|
||||
[test_selection_changes_with_middle_mouse_button.html]
|
||||
[test_selection_doubleclick.html]
|
||||
[test_selection_expanding.html]
|
||||
[test_selection_preventDefault.html]
|
||||
|
||||
@@ -1,211 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test for selection changes with middle mouse button</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
<style>
|
||||
span, td {
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none;">
|
||||
|
||||
</div>
|
||||
|
||||
<textarea id="textarea" cols="1" rows="1">copied to clipboard</textarea>
|
||||
|
||||
<div id="container">
|
||||
<span id="span1">first span.</span>
|
||||
<span id="span2">second span.</span>
|
||||
<table>
|
||||
<tr><td id="td1">first td.</td></tr>
|
||||
<tr><td id="td2">second td.</td></tr>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<pre id="test">
|
||||
|
||||
<script class="testbody" type="application/javascript">
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
const kIsMac = navigator.platform.includes("Mac");
|
||||
const selection = getSelection();
|
||||
|
||||
async function doTests(aEnableMiddlePaste, aEditable, aDescription) {
|
||||
await SpecialPowers.pushPrefEnv({"set": [["middlemouse.paste", aEnableMiddlePaste],
|
||||
["middlemouse.contentLoadURL", false],
|
||||
["general.autoScroll", false]]});
|
||||
|
||||
if (aEditable) {
|
||||
document.getElementById("container").setAttribute("contenteditable", "true");
|
||||
} else {
|
||||
document.getElementById("container").removeAttribute("contenteditable");
|
||||
}
|
||||
|
||||
let pasteEvents = [];
|
||||
function pasteEventHandler(event) {
|
||||
pasteEvents.push(event);
|
||||
event.preventDefault();
|
||||
}
|
||||
document.getElementById("container").addEventListener("paste", pasteEventHandler, true);
|
||||
|
||||
// We need to behave as same as Chromium as far as possible.
|
||||
// - middle mouse button down should be collapse selection at the point.
|
||||
// - middle mouse button down can expand only with mouse button down with Shift key.
|
||||
// - middle mouse button shouldn't select table cells.
|
||||
|
||||
function doTest(aMouseDown, aMouseUp,
|
||||
aExpectedSelectionAnchor, aExpectedSelectionFocus, aExpectedPastEventTarget,
|
||||
aAdditionalDescription) {
|
||||
pasteEvents = [];
|
||||
synthesizeMouseAtCenter(aMouseDown.target,
|
||||
{
|
||||
button: 1,
|
||||
type: "mousedown",
|
||||
shiftKey: aMouseDown.shiftKey,
|
||||
ctrlKey: aMouseDown.ctrlKey && !kIsMac,
|
||||
metaKey: aMouseDown.ctrlKey && kIsMac,
|
||||
});
|
||||
if (aExpectedSelectionAnchor === aExpectedSelectionFocus) {
|
||||
ok(selection.isCollapsed,
|
||||
aDescription + aAdditionalDescription + "Selection should be collapsed at mousedown");
|
||||
is(selection.focusNode, aExpectedSelectionFocus,
|
||||
aDescription + aAdditionalDescription + "Selection should be collapsed in the node at mousedown");
|
||||
} else {
|
||||
is(selection.anchorNode, aExpectedSelectionAnchor,
|
||||
aDescription + aAdditionalDescription + "Anchor node of Selection should be previous anchor node");
|
||||
is(selection.focusNode, aExpectedSelectionFocus,
|
||||
aDescription + aAdditionalDescription + "Focus node of Selection should be the node at mousedown");
|
||||
}
|
||||
is(pasteEvents.length, 0,
|
||||
aDescription + aAdditionalDescription + "paste event shouldn't be fired when middle mouse button down");
|
||||
|
||||
if (aMouseDown.target != aMouseUp.target) {
|
||||
synthesizeMouseAtCenter(aMouseUp.target, {type: "mousemove"});
|
||||
}
|
||||
synthesizeMouseAtCenter(aMouseUp.target,
|
||||
{
|
||||
button: 1,
|
||||
type: "mouseup",
|
||||
shiftKey: aMouseUp.shiftKey,
|
||||
ctrlKey: aMouseUp.ctrlKey && !kIsMac,
|
||||
metaKey: aMouseUp.ctrlKey && kIsMac,
|
||||
});
|
||||
is(selection.anchorNode, aExpectedSelectionAnchor,
|
||||
aDescription + aAdditionalDescription + "Anchor node of Selection shouldn't be modified at mouseup");
|
||||
is(selection.focusNode, aExpectedSelectionFocus,
|
||||
aDescription + aAdditionalDescription + "Focus node of Selection shouldn't be modified at mouseup");
|
||||
if (aEnableMiddlePaste) {
|
||||
if (aExpectedPastEventTarget === null) {
|
||||
is(pasteEvents.length, 0,
|
||||
aDescription + aAdditionalDescription + "paste event shouldn't be fired even when middle mouse button up");
|
||||
} else {
|
||||
is(pasteEvents.length, 1,
|
||||
aDescription + aAdditionalDescription + "paste event should be fired only once at mouse up");
|
||||
is(pasteEvents[0].target, aExpectedPastEventTarget,
|
||||
aDescription + aAdditionalDescription + "paste event should be fired on start of selection");
|
||||
}
|
||||
} else {
|
||||
is(pasteEvents.length, 0,
|
||||
aDescription + aAdditionalDescription + "paste event shouldn't be fired when middle mouse button up when middle mouse paste is disabled");
|
||||
}
|
||||
}
|
||||
|
||||
let span1 = document.getElementById("span1");
|
||||
let span2 = document.getElementById("span2");
|
||||
|
||||
selection.removeAllRanges();
|
||||
doTest({target: span1}, {target: span1},
|
||||
span1.firstChild, span1.firstChild, span1,
|
||||
"Clicking span1 when there is no selection: ");
|
||||
doTest({target: span2}, {target: span2},
|
||||
span2.firstChild, span2.firstChild, span2,
|
||||
"Clicking span2 when selection is collapsed in span1: ");
|
||||
doTest({target: span1}, {target: span2},
|
||||
span1.firstChild, span1.firstChild, span1,
|
||||
"Dragging from span1 to span2: ");
|
||||
doTest({target: span2}, {target: span1},
|
||||
span2.firstChild, span2.firstChild, span2,
|
||||
"Dragging from span2 to span1: ");
|
||||
doTest({target: span1, shiftKey: true}, {target: span1, shiftKey: true},
|
||||
span2.firstChild, span1.firstChild, span1,
|
||||
"Expanding selection with Shift key from span2 to span1: ");
|
||||
// "paste" event should be fired in the "start" of selection.
|
||||
selection.collapse(span1.firstChild, 3);
|
||||
doTest({target: span2, shiftKey: true}, {target: span2, shiftKey: true},
|
||||
span1.firstChild, span2.firstChild, span1,
|
||||
"Expanding selection with Shift key from span1 to span2: ");
|
||||
// XXX This case is different from Chrome for Linux.
|
||||
// In this case, Chrome does not collapse Selection at mousedown,
|
||||
// but collapse at click. So, if mouseup occurs different element,
|
||||
// Selection isn't modified.
|
||||
selection.selectAllChildren(span1);
|
||||
doTest({target: span1}, {target: span1},
|
||||
span1.firstChild, span1.firstChild, span1,
|
||||
"Clicking span1 when span1 is selected: ");
|
||||
|
||||
let td1 = document.getElementById("td1");
|
||||
let td2 = document.getElementById("td2");
|
||||
|
||||
selection.removeAllRanges();
|
||||
doTest({target: td1}, {target: td1},
|
||||
td1.firstChild, td1.firstChild, td1,
|
||||
"Clicking td1 when there is no selection: ");
|
||||
if (aEditable) {
|
||||
// XXX In this case, we don't allow to expand selection with Shift key
|
||||
// click across table cell boundary.
|
||||
doTest({target: td2, shiftKey: true}, {target: td2, shiftKey: true},
|
||||
td1.firstChild, td1.firstChild, td1,
|
||||
"Expanding selection with Shift key from td1 to td2: ");
|
||||
} else {
|
||||
doTest({target: td2, shiftKey: true}, {target: td2, shiftKey: true},
|
||||
td1.firstChild, td2.firstChild, td1,
|
||||
"Expanding selection with Shift key from td1 to td2: ");
|
||||
}
|
||||
// Shouldn't select per table cell when the button is middle mouse button.
|
||||
doTest({target: td1, ctrlKey: true}, {target: td1, ctrlKey: true},
|
||||
td1.firstChild, td1.firstChild, td1,
|
||||
"Click td1 with Control key: ");
|
||||
|
||||
document.getElementById("container").removeEventListener("paste", pasteEventHandler, true);
|
||||
}
|
||||
|
||||
async function runAllTests() {
|
||||
let textarea = document.getElementById("textarea");
|
||||
textarea.focus();
|
||||
await new Promise((resolve, reject) => {
|
||||
SimpleTest.waitForClipboard(textarea.value,
|
||||
() => {
|
||||
synthesizeKey("a", {accelKey: true});
|
||||
synthesizeKey("c", {accelKey: true});
|
||||
},
|
||||
() => {
|
||||
ok(true, `Succeeded to copy "${textarea.value}" to clipboard`);
|
||||
textarea.style.display = "none";
|
||||
resolve();
|
||||
},
|
||||
() => {
|
||||
ok(false, `Failed to copy "${textarea.value}" to clipboard`);
|
||||
reject();
|
||||
});
|
||||
});
|
||||
|
||||
await doTests(true, false, "Testing with the middle paste enabled: ");
|
||||
await doTests(false, false, "Testing with the middle paste disabled: ");
|
||||
|
||||
await doTests(true, true, "Testing in editable content with the middle paste enabled: ");
|
||||
await doTests(false, true, "Testing in editable content with the middle paste disabled: ");
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForFocus(runAllTests);
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,4 +0,0 @@
|
||||
[modifying-selection-with-middle-mouse-button.tentative.html]
|
||||
[Middle click shouldn't move caret in an editable element if the default of pointerdown event is prevented]
|
||||
expected: FAIL
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
[modifying-selection-with-primary-mouse-button.tentative.html]
|
||||
[Primary click should move caret in an editable element]
|
||||
expected: FAIL
|
||||
|
||||
@@ -1,192 +0,0 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Testing default action of `mousedown` of middle button and `mouseup` of middle button</title>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/resources/testdriver.js"></script>
|
||||
<script src="/resources/testdriver-vendor.js"></script>
|
||||
<script src="/resources/testdriver-actions.js"></script>
|
||||
<style>
|
||||
span {
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div contenteditable></div>
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
var editor = document.querySelector("div[contenteditable]");
|
||||
var span1, span2;
|
||||
var selection = getSelection();
|
||||
|
||||
function preventDefault(event) {
|
||||
event.preventDefault();
|
||||
}
|
||||
editor.addEventListener("paste", preventDefault, {capture: true});
|
||||
|
||||
function resetEditor() {
|
||||
editor.innerHTML =
|
||||
'<span id="span1">first span.</span><br><span id="span2">second span.</span>';
|
||||
span1 = document.getElementById("span1");
|
||||
span2 = document.getElementById("span2");
|
||||
}
|
||||
|
||||
promise_test(async () => {
|
||||
resetEditor();
|
||||
editor.blur();
|
||||
let actions = new test_driver.Actions();
|
||||
await actions
|
||||
.pointerMove(0, 0)
|
||||
.pointerMove(0, 0, {origin: span1})
|
||||
.pointerDown({button: actions.ButtonType.MIDDLE})
|
||||
.pointerUp({button: actions.ButtonType.MIDDLE})
|
||||
.send();
|
||||
|
||||
assert_equals(document.activeElement, editor,
|
||||
"The clicked editor should get focus");
|
||||
assert_true(selection.isCollapsed,
|
||||
"Selection should be collapsed after middle button click");
|
||||
assert_equals(selection.focusNode, span1.firstChild,
|
||||
"Selection should be collapsed in the first <span> element which was clicked by middle button");
|
||||
}, "Middle click should set focus to clicked editable element and collapse selection around the clicked point");
|
||||
|
||||
promise_test(async () => {
|
||||
resetEditor();
|
||||
editor.focus();
|
||||
selection.collapse(span1.firstChild, 2);
|
||||
let actions = new test_driver.Actions();
|
||||
await actions
|
||||
.pointerMove(0, 0)
|
||||
.pointerMove(0, 0, {origin: span2})
|
||||
.pointerDown({button: actions.ButtonType.MIDDLE})
|
||||
.pointerUp({button: actions.ButtonType.MIDDLE})
|
||||
.send();
|
||||
|
||||
assert_equals(document.activeElement, editor,
|
||||
"The clicked editor should keep having focus");
|
||||
assert_true(selection.isCollapsed,
|
||||
"Selection should be collapsed after middle button click");
|
||||
assert_equals(selection.focusNode, span2.firstChild,
|
||||
"Selection should be collapsed in the second <span> element which was clicked by middle button");
|
||||
}, "Middle click should move caret in an editable element");
|
||||
|
||||
promise_test(async () => {
|
||||
resetEditor();
|
||||
editor.focus();
|
||||
selection.collapse(span1.firstChild, 2);
|
||||
addEventListener("mousedown", preventDefault);
|
||||
let actions = new test_driver.Actions();
|
||||
await actions
|
||||
.pointerMove(0, 0)
|
||||
.pointerMove(0, 0, {origin: span2})
|
||||
.pointerDown({button: actions.ButtonType.MIDDLE})
|
||||
.pointerUp({button: actions.ButtonType.MIDDLE})
|
||||
.send();
|
||||
removeEventListener("mousedown", preventDefault);
|
||||
|
||||
assert_equals(selection.focusNode, span1.firstChild,
|
||||
"Selection should keep collapsed selection in the first <span> element");
|
||||
assert_equals(selection.focusOffset, 2,
|
||||
"Selection should keep collapsed selection at 2 of the first <span> element");
|
||||
}, "Middle click shouldn't move caret in an editable element if the default of mousedown event is prevented");
|
||||
|
||||
promise_test(async () => {
|
||||
resetEditor();
|
||||
editor.focus();
|
||||
selection.collapse(span1.firstChild, 2);
|
||||
addEventListener("pointerdown", preventDefault);
|
||||
let actions = new test_driver.Actions();
|
||||
await actions
|
||||
.pointerMove(0, 0)
|
||||
.pointerMove(0, 0, {origin: span2})
|
||||
.pointerDown({button: actions.ButtonType.MIDDLE})
|
||||
.pointerUp({button: actions.ButtonType.MIDDLE})
|
||||
.send();
|
||||
removeEventListener("pointerdown", preventDefault);
|
||||
|
||||
assert_equals(selection.focusNode, span1.firstChild,
|
||||
"Selection should keep collapsed selection in the first <span> element");
|
||||
assert_equals(selection.focusOffset, 2,
|
||||
"Selection should keep collapsed selection at 2 of the first <span> element");
|
||||
}, "Middle click shouldn't move caret in an editable element if the default of pointerdown event is prevented");
|
||||
|
||||
promise_test(async () => {
|
||||
resetEditor();
|
||||
editor.focus();
|
||||
selection.collapse(span1.firstChild, 2);
|
||||
let actions = new test_driver.Actions();
|
||||
await actions
|
||||
.pointerMove(0, 0)
|
||||
.pointerMove(0, 0, {origin: span2})
|
||||
.keyDown("\uE008")
|
||||
.pointerDown({button: actions.ButtonType.MIDDLE})
|
||||
.pointerUp({button: actions.ButtonType.MIDDLE})
|
||||
.keyUp("\uE008")
|
||||
.send();
|
||||
|
||||
assert_equals(selection.anchorNode, span1.firstChild,
|
||||
"Selection#anchorNode should keep in the first <span> element");
|
||||
assert_equals(selection.anchorOffset, 2,
|
||||
"Selection#anchorNode should keep at 2 of the first <span> element");
|
||||
assert_equals(selection.focusNode, span2.firstChild,
|
||||
"Selection#focusNode should be in the second <span> element which was clicked by middle button");
|
||||
}, "Shift + Middle click should extend the selection");
|
||||
|
||||
promise_test(async () => {
|
||||
resetEditor();
|
||||
editor.focus();
|
||||
selection.collapse(span1.firstChild, 2);
|
||||
editor.addEventListener("pointerdown", () => {
|
||||
assert_true(selection.isCollapsed,
|
||||
"Selection shouldn't be modified before pointerdown event");
|
||||
assert_equals(selection.focusNode, span1.firstChild,
|
||||
"Selection should stay in the first <span> element when pointerdown event is fired (focusNode)");
|
||||
assert_equals(selection.focusOffset, 2,
|
||||
"Selection should stay in the first <span> element when pointerdown event is fired (focusOffset)");
|
||||
}, {once: true});
|
||||
editor.addEventListener("mousedown", () => {
|
||||
assert_true(selection.isCollapsed,
|
||||
"Selection shouldn't be modified before mousedown event");
|
||||
assert_equals(selection.focusNode, span1.firstChild,
|
||||
"Selection should stay in the first <span> element when mousedown event is fired (focusNode)");
|
||||
assert_equals(selection.focusOffset, 2,
|
||||
"Selection should stay in the first <span> element when mousedown event is fired (focusOffset)");
|
||||
}, {once: true});
|
||||
editor.addEventListener("pointerup", () => {
|
||||
assert_true(selection.isCollapsed,
|
||||
"Selection should be collapsed before pointerup event");
|
||||
assert_equals(selection.focusNode, span2.firstChild,
|
||||
"Selection should be collapsed in the second <span> element which was clicked by middle button before pointerup event ");
|
||||
}, {once: true});
|
||||
let focusOffsetAtMouseUp;
|
||||
editor.addEventListener("mouseup", () => {
|
||||
assert_true(selection.isCollapsed,
|
||||
"Selection should be collapsed before mouseup event");
|
||||
assert_equals(selection.focusNode, span2.firstChild,
|
||||
"Selection should be collapsed in the second <span> element which was clicked by middle button before mouseup event ");
|
||||
focusOffsetAtMouseUp = selection.focusOffset;
|
||||
}, {once: true});
|
||||
let actions = new test_driver.Actions();
|
||||
await actions
|
||||
.pointerMove(0, 0)
|
||||
.pointerMove(0, 0, {origin: span2})
|
||||
.pointerDown({button: actions.ButtonType.MIDDLE})
|
||||
.pointerMove(0, 0, {origin: span1})
|
||||
.pointerUp({button: actions.ButtonType.MIDDLE})
|
||||
.send();
|
||||
|
||||
assert_true(selection.isCollapsed,
|
||||
"Selection shouldn't be extended by pointer moves during pressing middle button");
|
||||
assert_equals(selection.focusNode, span2.firstChild,
|
||||
"Selection#focusNode should stay in the second <span> element");
|
||||
assert_equals(selection.focusOffset, focusOffsetAtMouseUp,
|
||||
"Selection#focusOffset should stay in the second <span> element");
|
||||
}, "Middle mouse button down should move caret, but middle mouse button up shouldn't move caret");
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,191 +0,0 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Testing default action of `mousedown` of primary button and `mouseup` of primary button</title>
|
||||
<script src="/resources/testharness.js"></script>
|
||||
<script src="/resources/testharnessreport.js"></script>
|
||||
<script src="/resources/testdriver.js"></script>
|
||||
<script src="/resources/testdriver-vendor.js"></script>
|
||||
<script src="/resources/testdriver-actions.js"></script>
|
||||
<style>
|
||||
span {
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div contenteditable></div>
|
||||
<script>
|
||||
"use strict";
|
||||
|
||||
var editor = document.querySelector("div[contenteditable]");
|
||||
var span1, span2;
|
||||
var selection = getSelection();
|
||||
|
||||
function preventDefault(event) {
|
||||
event.preventDefault();
|
||||
}
|
||||
editor.addEventListener("paste", preventDefault, {capture: true});
|
||||
|
||||
function resetEditor() {
|
||||
editor.innerHTML =
|
||||
'<span id="span1">first span.</span><br><span id="span2">second span.</span>';
|
||||
span1 = document.getElementById("span1");
|
||||
span2 = document.getElementById("span2");
|
||||
}
|
||||
|
||||
promise_test(async () => {
|
||||
resetEditor();
|
||||
editor.blur();
|
||||
let actions = new test_driver.Actions();
|
||||
await actions
|
||||
.pointerMove(0, 0)
|
||||
.pointerMove(0, 0, {origin: span1})
|
||||
.pointerDown({button: actions.ButtonType.LEFT})
|
||||
.pointerUp({button: actions.ButtonType.LEFT})
|
||||
.send();
|
||||
|
||||
assert_equals(document.activeElement, editor,
|
||||
"The clicked editor should get focus");
|
||||
assert_true(selection.isCollapsed,
|
||||
"Selection should be collapsed after primary button click");
|
||||
assert_equals(selection.focusNode, span1.firstChild,
|
||||
"Selection should be collapsed in the first <span> element which was clicked by primary button");
|
||||
}, "Primary click should set focus to clicked editable element and collapse selection around the clicked point");
|
||||
|
||||
promise_test(async () => {
|
||||
resetEditor();
|
||||
editor.focus();
|
||||
selection.collapse(span1.firstChild, 2);
|
||||
let actions = new test_driver.Actions();
|
||||
await actions
|
||||
.pointerMove(0, 0)
|
||||
.pointerMove(0, 0, {origin: span2})
|
||||
.pointerDown({button: actions.ButtonType.LEFT})
|
||||
.pointerUp({button: actions.ButtonType.LEFT})
|
||||
.send();
|
||||
|
||||
assert_equals(document.activeElement, editor,
|
||||
"The clicked editor should keep having focus");
|
||||
assert_true(selection.isCollapsed,
|
||||
"Selection should be collapsed after primary button click");
|
||||
assert_equals(selection.focusNode, span2.firstChild,
|
||||
"Selection should be collapsed in the second <span> element which was clicked by primary button");
|
||||
}, "Primary click should move caret in an editable element");
|
||||
|
||||
promise_test(async () => {
|
||||
resetEditor();
|
||||
editor.focus();
|
||||
selection.collapse(span1.firstChild, 2);
|
||||
addEventListener("mousedown", preventDefault);
|
||||
let actions = new test_driver.Actions();
|
||||
await actions
|
||||
.pointerMove(0, 0)
|
||||
.pointerMove(0, 0, {origin: span2})
|
||||
.pointerDown({button: actions.ButtonType.LEFT})
|
||||
.pointerUp({button: actions.ButtonType.LEFT})
|
||||
.send();
|
||||
removeEventListener("mousedown", preventDefault);
|
||||
|
||||
assert_equals(selection.focusNode, span1.firstChild,
|
||||
"Selection should keep collapsed selection in the first <span> element");
|
||||
assert_equals(selection.focusOffset, 2,
|
||||
"Selection should keep collapsed selection at 2 of the first <span> element");
|
||||
}, "Primary click shouldn't move caret in an editable element if the default of mousedown event is prevented");
|
||||
|
||||
promise_test(async () => {
|
||||
resetEditor();
|
||||
editor.focus();
|
||||
selection.collapse(span1.firstChild, 2);
|
||||
addEventListener("pointerdown", preventDefault);
|
||||
let actions = new test_driver.Actions();
|
||||
await actions
|
||||
.pointerMove(0, 0)
|
||||
.pointerMove(0, 0, {origin: span2})
|
||||
.pointerDown({button: actions.ButtonType.LEFT})
|
||||
.pointerUp({button: actions.ButtonType.LEFT})
|
||||
.send();
|
||||
removeEventListener("pointerdown", preventDefault);
|
||||
|
||||
assert_equals(selection.focusNode, span1.firstChild,
|
||||
"Selection should keep collapsed selection in the first <span> element");
|
||||
assert_equals(selection.focusOffset, 2,
|
||||
"Selection should keep collapsed selection at 2 of the first <span> element");
|
||||
}, "Primary click shouldn't move caret in an editable element if the default of pointerdown event is prevented");
|
||||
|
||||
promise_test(async () => {
|
||||
resetEditor();
|
||||
editor.focus();
|
||||
selection.collapse(span1.firstChild, 2);
|
||||
let actions = new test_driver.Actions();
|
||||
await actions
|
||||
.pointerMove(0, 0)
|
||||
.pointerMove(0, 0, {origin: span2})
|
||||
.keyDown("\uE008")
|
||||
.pointerDown({button: actions.ButtonType.LEFT})
|
||||
.pointerUp({button: actions.ButtonType.LEFT})
|
||||
.keyUp("\uE008")
|
||||
.send();
|
||||
|
||||
assert_equals(selection.anchorNode, span1.firstChild,
|
||||
"Selection#anchorNode should keep in the first <span> element");
|
||||
assert_equals(selection.anchorOffset, 2,
|
||||
"Selection#anchorNode should keep at 2 of the first <span> element");
|
||||
assert_equals(selection.focusNode, span2.firstChild,
|
||||
"Selection#focusNode should be in the second <span> element which was clicked by primary button");
|
||||
}, "Shift + Primary click should extend the selection");
|
||||
|
||||
promise_test(async () => {
|
||||
resetEditor();
|
||||
editor.focus();
|
||||
selection.collapse(span1.firstChild, 2);
|
||||
editor.addEventListener("pointerdown", () => {
|
||||
assert_true(selection.isCollapsed,
|
||||
"Selection shouldn't be modified before pointerdown event");
|
||||
assert_equals(selection.focusNode, span1.firstChild,
|
||||
"Selection should stay in the first <span> element when pointerdown event is fired (focusNode)");
|
||||
assert_equals(selection.focusOffset, 2,
|
||||
"Selection should stay in the first <span> element when pointerdown event is fired (focusOffset)");
|
||||
}, {once: true});
|
||||
editor.addEventListener("mousedown", () => {
|
||||
assert_true(selection.isCollapsed,
|
||||
"Selection shouldn't be modified before mousedown event");
|
||||
assert_equals(selection.focusNode, span1.firstChild,
|
||||
"Selection should stay in the first <span> element when mousedown event is fired (focusNode)");
|
||||
assert_equals(selection.focusOffset, 2,
|
||||
"Selection should stay in the first <span> element when mousedown event is fired (focusOffset)");
|
||||
}, {once: true});
|
||||
editor.addEventListener("pointerup", () => {
|
||||
assert_true(!selection.isCollapsed,
|
||||
"Selection should be modified before pointerup event");
|
||||
assert_equals(selection.focusNode, span1.firstChild,
|
||||
"Selection should be modified to extend the range before pointerup event ");
|
||||
}, {once: true});
|
||||
let anchorOffsetAtMouseUp;
|
||||
editor.addEventListener("mouseup", () => {
|
||||
assert_true(!selection.isCollapsed,
|
||||
"Selection should be modified before mouseup event");
|
||||
assert_equals(selection.focusNode, span1.firstChild,
|
||||
"Selection should be modified to extend the range before mouseup event ");
|
||||
anchorOffsetAtMouseUp = selection.anchorOffset;
|
||||
}, {once: true});
|
||||
let actions = new test_driver.Actions();
|
||||
await actions
|
||||
.pointerMove(0, 0)
|
||||
.pointerMove(0, 0, {origin: span2})
|
||||
.pointerDown({button: actions.ButtonType.LEFT})
|
||||
.pointerMove(0, 0, {origin: span1})
|
||||
.pointerUp({button: actions.ButtonType.LEFT})
|
||||
.send();
|
||||
|
||||
assert_equals(selection.anchorNode, span2.firstChild,
|
||||
"Selection#anchorNode should stay in the second <span> element which mousedown occurred on");
|
||||
assert_equals(selection.anchorOffset, anchorOffsetAtMouseUp,
|
||||
"Selection#anchorOffset should stay in the second <span> element which mousedown occurred on");
|
||||
assert_equals(selection.focusNode, span1.firstChild,
|
||||
"Selection#focusNode should be in the first <span> element which mouseup occurred on");
|
||||
}, "Primary mouse button down should move caret, and primary mouse button up should extend the selection");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -204,7 +204,6 @@ class AutoScrollChild extends JSWindowActorChild {
|
||||
}
|
||||
|
||||
Services.els.addSystemEventListener(this.document, "mousemove", this, true);
|
||||
Services.els.addSystemEventListener(this.document, "mouseup", this, true);
|
||||
this.document.addEventListener("pagehide", this, true);
|
||||
|
||||
this._ignoreMouseEvents = true;
|
||||
@@ -254,12 +253,6 @@ class AutoScrollChild extends JSWindowActorChild {
|
||||
this,
|
||||
true
|
||||
);
|
||||
Services.els.removeSystemEventListener(
|
||||
this.document,
|
||||
"mouseup",
|
||||
this,
|
||||
true
|
||||
);
|
||||
this.document.removeEventListener("pagehide", this, true);
|
||||
if (this._autoscrollHandledByApz) {
|
||||
Services.obs.removeObserver(
|
||||
@@ -331,38 +324,26 @@ class AutoScrollChild extends JSWindowActorChild {
|
||||
}
|
||||
|
||||
handleEvent(event) {
|
||||
switch (event.type) {
|
||||
case "mousemove":
|
||||
this._screenX = event.screenX;
|
||||
this._screenY = event.screenY;
|
||||
break;
|
||||
case "mousedown":
|
||||
if (
|
||||
event.isTrusted &&
|
||||
!event.defaultPrevented &&
|
||||
event.button === 1 &&
|
||||
!this._scrollable &&
|
||||
!this.isAutoscrollBlocker(event.originalTarget)
|
||||
) {
|
||||
this.startScroll(event);
|
||||
if (event.type == "mousemove") {
|
||||
this._screenX = event.screenX;
|
||||
this._screenY = event.screenY;
|
||||
} else if (event.type == "mousedown") {
|
||||
if (
|
||||
event.isTrusted & !event.defaultPrevented &&
|
||||
event.button == 1 &&
|
||||
!this._scrollable &&
|
||||
!this.isAutoscrollBlocker(event.originalTarget)
|
||||
) {
|
||||
this.startScroll(event);
|
||||
}
|
||||
} else if (event.type == "pagehide") {
|
||||
if (this._scrollable) {
|
||||
var doc = this._scrollable.ownerDocument || this._scrollable.document;
|
||||
if (doc == event.target) {
|
||||
this.sendAsyncMessage("Autoscroll:Cancel");
|
||||
this.stopScroll();
|
||||
}
|
||||
// fallthrough
|
||||
case "mouseup":
|
||||
if (this._ignoreMouseEvents) {
|
||||
// Middle mouse click event shouldn't be fired in web content for
|
||||
// compatibility with Chrome.
|
||||
event.preventClickEvent();
|
||||
}
|
||||
break;
|
||||
case "pagehide":
|
||||
if (this._scrollable) {
|
||||
var doc = this._scrollable.ownerDocument || this._scrollable.document;
|
||||
if (doc == event.target) {
|
||||
this.sendAsyncMessage("Autoscroll:Cancel");
|
||||
this.stopScroll();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -61,8 +61,6 @@ skip-if = true # bug 1399845 tracks re-enabling this test.
|
||||
[browser_bug594509.js]
|
||||
[browser_bug982298.js]
|
||||
[browser_charsetMenu_swapBrowsers.js]
|
||||
[browser_click_event_during_autoscrolling.js]
|
||||
skip-if = !e10s
|
||||
[browser_content_url_annotation.js]
|
||||
skip-if = !e10s || !crashreporter
|
||||
[browser_contentTitle.js]
|
||||
|
||||
@@ -1,438 +0,0 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
add_task(async function() {
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [
|
||||
["general.autoScroll", true],
|
||||
["middlemouse.contentLoadURL", false],
|
||||
["test.events.async.enabled", false],
|
||||
],
|
||||
});
|
||||
|
||||
await BrowserTestUtils.withNewTab(
|
||||
"https://example.com/browser/toolkit/content/tests/browser/file_empty.html",
|
||||
async function(browser) {
|
||||
ok(browser.isRemoteBrowser, "This test passes only in e10s mode");
|
||||
await SpecialPowers.spawn(browser, [], () => {
|
||||
content.document.body.innerHTML =
|
||||
'<div style="height: 10000px;"></div>';
|
||||
content.document.documentElement.scrollTop = 500;
|
||||
content.document.documentElement.scrollTop; // Flush layout.
|
||||
// Prevent to open context menu when testing the secondary button click.
|
||||
content.window.addEventListener(
|
||||
"contextmenu",
|
||||
event => event.preventDefault(),
|
||||
{ capture: true }
|
||||
);
|
||||
});
|
||||
|
||||
function promiseFlushLayoutInContent() {
|
||||
return SpecialPowers.spawn(browser, [], () => {
|
||||
content.document.documentElement.scrollTop; // Flush layout in the remote content.
|
||||
});
|
||||
}
|
||||
|
||||
let autoScroller;
|
||||
function promiseWaitForAutoScrollerOpen() {
|
||||
if (autoScroller?.state == "open") {
|
||||
info("The autoscroller has already been open");
|
||||
return Promise.resolve();
|
||||
}
|
||||
return BrowserTestUtils.waitForEvent(
|
||||
window,
|
||||
"popupshown",
|
||||
{ capture: true },
|
||||
event => {
|
||||
if (event.originalTarget.id != "autoscroller") {
|
||||
return false;
|
||||
}
|
||||
autoScroller = event.originalTarget;
|
||||
info('"popupshown" event is fired');
|
||||
autoScroller.getBoundingClientRect(); // Flush layout of the autoscroller
|
||||
return true;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function promiseWaitForAutoScrollerClosed() {
|
||||
if (!autoScroller || autoScroller.state == "closed") {
|
||||
info("The autoscroller has already been closed");
|
||||
return Promise.resolve();
|
||||
}
|
||||
return BrowserTestUtils.waitForEvent(
|
||||
autoScroller,
|
||||
"popuphidden",
|
||||
{ capture: true },
|
||||
() => {
|
||||
info('"popuphidden" event is fired');
|
||||
return true;
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Unfortunately, we cannot use synthesized mouse events for starting and
|
||||
// stopping autoscrolling because they may run different path from user
|
||||
// operation especially when there is a popup.
|
||||
|
||||
/**
|
||||
* Instead of using `waitForContentEvent`, we use `addContentEventListener`
|
||||
* for checking which events are fired because `waitForContentEvent` cannot
|
||||
* detect redundant event since it's removed automatically at first event
|
||||
* or timeout if the expected count is 0.
|
||||
*/
|
||||
class ContentEventCounter {
|
||||
constructor(aBrowser, aEventTypes) {
|
||||
this.eventData = new Map();
|
||||
for (let eventType of aEventTypes) {
|
||||
const removeEventListener = BrowserTestUtils.addContentEventListener(
|
||||
aBrowser,
|
||||
eventType,
|
||||
() => {
|
||||
let eventData = this.eventData.get(eventType);
|
||||
eventData.count++;
|
||||
},
|
||||
{ capture: true }
|
||||
);
|
||||
this.eventData.set(eventType, {
|
||||
count: 0, // how many times the event fired.
|
||||
removeEventListener, // function to remove the event listener.
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
getCountAndRemoveEventListener(aEventType) {
|
||||
let eventData = this.eventData.get(aEventType);
|
||||
if (eventData.removeEventListener) {
|
||||
eventData.removeEventListener();
|
||||
eventData.removeEventListener = null;
|
||||
}
|
||||
return eventData.count;
|
||||
}
|
||||
|
||||
promiseMouseEvents(aEventTypes, aMessage) {
|
||||
let needsToWait = [];
|
||||
for (const eventType of aEventTypes) {
|
||||
let eventData = this.eventData.get(eventType);
|
||||
if (eventData.count > 0) {
|
||||
info(`${aMessage}: Waiting "${eventType}" event in content...`);
|
||||
needsToWait.push(
|
||||
// Let's use `waitForCondition` here. "timeout" is not worthwhile
|
||||
// to debug this test. We want clearer failure log.
|
||||
TestUtils.waitForCondition(
|
||||
() => eventData.count > 0,
|
||||
`${aMessage}: "${eventType}" should be fired, but timed-out`
|
||||
)
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Promise.all(needsToWait);
|
||||
}
|
||||
}
|
||||
|
||||
await (async function testMouseEventsAtStartingAutoScrolling() {
|
||||
info(
|
||||
"Waiting autoscroller popup for testing mouse events at starting autoscrolling"
|
||||
);
|
||||
await promiseFlushLayoutInContent();
|
||||
let eventsInContent = new ContentEventCounter(browser, [
|
||||
"click",
|
||||
"auxclick",
|
||||
"mousedown",
|
||||
"mouseup",
|
||||
"paste",
|
||||
]);
|
||||
// Ensure that the event listeners added in the content with accessing
|
||||
// the remote content.
|
||||
await promiseFlushLayoutInContent();
|
||||
await EventUtils.promiseNativeMouseEvent({
|
||||
type: "mousemove",
|
||||
target: browser,
|
||||
atCenter: true,
|
||||
});
|
||||
const waitForOpenAutoScroll = promiseWaitForAutoScrollerOpen();
|
||||
await EventUtils.promiseNativeMouseEvent({
|
||||
type: "mousedown",
|
||||
target: browser,
|
||||
atCenter: true,
|
||||
button: 1, // middle button
|
||||
});
|
||||
await waitForOpenAutoScroll;
|
||||
// In the wild, native "mouseup" event occurs after the popup is open.
|
||||
await EventUtils.promiseNativeMouseEvent({
|
||||
type: "mouseup",
|
||||
target: browser,
|
||||
atCenter: true,
|
||||
button: 1, // middle button
|
||||
});
|
||||
await promiseFlushLayoutInContent();
|
||||
await eventsInContent.promiseMouseEvents(
|
||||
["mouseup"],
|
||||
"At starting autoscrolling"
|
||||
);
|
||||
for (let eventType of ["click", "auxclick", "paste"]) {
|
||||
is(
|
||||
eventsInContent.getCountAndRemoveEventListener(eventType),
|
||||
0,
|
||||
`"${eventType}" event shouldn't be fired in the content when a middle click starts autoscrolling`
|
||||
);
|
||||
}
|
||||
for (let eventType of ["mousedown", "mouseup"]) {
|
||||
is(
|
||||
eventsInContent.getCountAndRemoveEventListener(eventType),
|
||||
1,
|
||||
`"${eventType}" event should be fired in the content when a middle click starts autoscrolling`
|
||||
);
|
||||
}
|
||||
info("Waiting autoscroller close for preparing the following tests");
|
||||
let waitForAutoScrollEnd = promiseWaitForAutoScrollerClosed();
|
||||
EventUtils.synthesizeKey("KEY_Escape");
|
||||
await waitForAutoScrollEnd;
|
||||
})();
|
||||
|
||||
async function doTestMouseEventsAtStoppingAutoScrolling({
|
||||
aButton = 0,
|
||||
aClickOutsideAutoScroller = false,
|
||||
aDescription = "Unspecified",
|
||||
}) {
|
||||
info(
|
||||
`Starting autoscrolling for testing to stop autoscrolling with ${aDescription}`
|
||||
);
|
||||
await promiseFlushLayoutInContent();
|
||||
await EventUtils.promiseNativeMouseEvent({
|
||||
type: "mousemove",
|
||||
target: browser,
|
||||
atCenter: true,
|
||||
});
|
||||
const waitForOpenAutoScroll = promiseWaitForAutoScrollerOpen();
|
||||
await EventUtils.promiseNativeMouseEvent({
|
||||
type: "mousedown",
|
||||
target: browser,
|
||||
atCenter: true,
|
||||
button: 1, // middle button
|
||||
});
|
||||
// In the wild, native "mouseup" event occurs after the popup is open.
|
||||
await waitForOpenAutoScroll;
|
||||
await EventUtils.promiseNativeMouseEvent({
|
||||
type: "mouseup",
|
||||
target: browser,
|
||||
atCenter: true,
|
||||
button: 1, // middle button
|
||||
});
|
||||
await promiseFlushLayoutInContent();
|
||||
// Just to be sure, wait for a tick for wait APZ stable.
|
||||
await TestUtils.waitForTick();
|
||||
|
||||
let eventsInContent = new ContentEventCounter(browser, [
|
||||
"click",
|
||||
"auxclick",
|
||||
"mousedown",
|
||||
"mouseup",
|
||||
"paste",
|
||||
"contextmenu",
|
||||
]);
|
||||
// Ensure that the event listeners added in the content with accessing
|
||||
// the remote content.
|
||||
await promiseFlushLayoutInContent();
|
||||
|
||||
aDescription = `Stop autoscrolling with ${aDescription}`;
|
||||
info(
|
||||
`${aDescription}: Synthesizing primary mouse button event on the autoscroller`
|
||||
);
|
||||
const autoScrollerRect = autoScroller.getOuterScreenRect();
|
||||
info(
|
||||
`${aDescription}: autoScroller: { left: ${autoScrollerRect.left}, top: ${autoScrollerRect.top}, width: ${autoScrollerRect.width}, height: ${autoScrollerRect.height} }`
|
||||
);
|
||||
const waitForCloseAutoScroller = promiseWaitForAutoScrollerClosed();
|
||||
if (aClickOutsideAutoScroller) {
|
||||
info(
|
||||
`${aDescription}: Synthesizing mousemove move cursor outside the autoscroller...`
|
||||
);
|
||||
await EventUtils.promiseNativeMouseEvent({
|
||||
type: "mousemove",
|
||||
target: autoScroller,
|
||||
offsetX: -10,
|
||||
offsetY: -10,
|
||||
elementOnWidget: browser, // use widget for the parent window of the autoscroller
|
||||
});
|
||||
info(
|
||||
`${aDescription}: Synthesizing mousedown to stop autoscrolling...`
|
||||
);
|
||||
await EventUtils.promiseNativeMouseEvent({
|
||||
type: "mousedown",
|
||||
target: autoScroller,
|
||||
offsetX: -10,
|
||||
offsetY: -10,
|
||||
button: aButton,
|
||||
elementOnWidget: browser, // use widget for the parent window of the autoscroller
|
||||
});
|
||||
} else {
|
||||
info(
|
||||
`${aDescription}: Synthesizing mousemove move cursor onto the autoscroller...`
|
||||
);
|
||||
await EventUtils.promiseNativeMouseEvent({
|
||||
type: "mousemove",
|
||||
target: autoScroller,
|
||||
atCenter: true,
|
||||
elementOnWidget: browser, // use widget for the parent window of the autoscroller
|
||||
});
|
||||
info(
|
||||
`${aDescription}: Synthesizing mousedown to stop autoscrolling...`
|
||||
);
|
||||
await EventUtils.promiseNativeMouseEvent({
|
||||
type: "mousedown",
|
||||
target: autoScroller,
|
||||
atCenter: true,
|
||||
button: aButton,
|
||||
elementOnWidget: browser, // use widget for the parent window of the autoscroller
|
||||
});
|
||||
}
|
||||
// In the wild, native "mouseup" event occurs after the popup is closed.
|
||||
await waitForCloseAutoScroller;
|
||||
info(
|
||||
`${aDescription}: Synthesizing mouseup event for preceding mousedown which is for stopping autoscrolling`
|
||||
);
|
||||
await EventUtils.promiseNativeMouseEvent({
|
||||
type: "mouseup",
|
||||
target: browser,
|
||||
atCenter: true,
|
||||
button: aButton,
|
||||
});
|
||||
await promiseFlushLayoutInContent();
|
||||
await eventsInContent.promiseMouseEvents(
|
||||
aButton != 2 ? ["mouseup"] : ["mouseup", "contextmenu"],
|
||||
aDescription
|
||||
);
|
||||
is(
|
||||
autoScroller.state,
|
||||
"closed",
|
||||
`${aDescription}: The autoscroller should've been closed`
|
||||
);
|
||||
// - On macOS, when clicking outside autoscroller, nsChildView
|
||||
// intentionally blocks both "mousedown" and "mouseup" events in the
|
||||
// case of the primary button click, and only "mousedown" for the
|
||||
// middle button when the "mousedown". I'm not sure how it should work
|
||||
// on macOS for conforming to the platform manner. Note that autoscroll
|
||||
// isn't available on the other browsers on macOS. So, there is no
|
||||
// reference, but for consistency between platforms, it may be better
|
||||
// to ignore the platform manner.
|
||||
// - On Windows, when clicking outside autoscroller, nsWindow
|
||||
// intentionally blocks only "mousedown" events for the primary button
|
||||
// and the middle button. But this behavior is different from Chrome
|
||||
// so that we need to fix this in the future.
|
||||
// - On Linux, when clicking outside autoscroller, nsWindow
|
||||
// intentionally blocks only "mousedown" events for any buttons. But
|
||||
// on Linux, autoscroll isn't available by the default settings. So,
|
||||
// not so urgent, but should be fixed in the future for consistency
|
||||
// between platforms and compatibility with Chrome on Windows.
|
||||
const rollingUpPopupConsumeMouseDown =
|
||||
aClickOutsideAutoScroller &&
|
||||
(aButton != 2 || navigator.platform.includes("Linux"));
|
||||
const rollingUpPopupConsumeMouseUp =
|
||||
aClickOutsideAutoScroller &&
|
||||
aButton == 0 &&
|
||||
navigator.platform.includes("Mac");
|
||||
const checkFuncForClick =
|
||||
aClickOutsideAutoScroller &&
|
||||
aButton == 2 &&
|
||||
!navigator.platform.includes("Linux")
|
||||
? todo_is
|
||||
: is;
|
||||
for (let eventType of ["click", "auxclick"]) {
|
||||
checkFuncForClick(
|
||||
eventsInContent.getCountAndRemoveEventListener(eventType),
|
||||
0,
|
||||
`${aDescription}: "${eventType}" event shouldn't be fired in the remote content`
|
||||
);
|
||||
}
|
||||
is(
|
||||
eventsInContent.getCountAndRemoveEventListener("paste"),
|
||||
0,
|
||||
`${aDescription}: "paste" event shouldn't be fired in the remote content`
|
||||
);
|
||||
const checkFuncForMouseDown = rollingUpPopupConsumeMouseDown
|
||||
? todo_is
|
||||
: is;
|
||||
checkFuncForMouseDown(
|
||||
eventsInContent.getCountAndRemoveEventListener("mousedown"),
|
||||
1,
|
||||
`${aDescription}: "mousedown" event should be fired in the remote content`
|
||||
);
|
||||
const checkFuncForMouseUp = rollingUpPopupConsumeMouseUp ? todo_is : is;
|
||||
checkFuncForMouseUp(
|
||||
eventsInContent.getCountAndRemoveEventListener("mouseup"),
|
||||
1,
|
||||
`${aDescription}: "mouseup" event should be fired in the remote content`
|
||||
);
|
||||
const checkFuncForContextMenu =
|
||||
aButton == 2 &&
|
||||
aClickOutsideAutoScroller &&
|
||||
navigator.platform.includes("Linux")
|
||||
? todo_is
|
||||
: is;
|
||||
checkFuncForContextMenu(
|
||||
eventsInContent.getCountAndRemoveEventListener("contextmenu"),
|
||||
aButton == 2 ? 1 : 0,
|
||||
`${aDescription}: "contextmenu" event should${
|
||||
aButton != 2 ? " not" : ""
|
||||
} be fired in the remote content`
|
||||
);
|
||||
}
|
||||
|
||||
// Clicking the primary button to stop autoscrolling.
|
||||
await doTestMouseEventsAtStoppingAutoScrolling({
|
||||
aButton: 0,
|
||||
aClickOutsideAutoScroller: false,
|
||||
aDescription: "a primary button click on autoscroller",
|
||||
});
|
||||
await doTestMouseEventsAtStoppingAutoScrolling({
|
||||
aButton: 0,
|
||||
aClickOutsideAutoScroller: true,
|
||||
aDescription: "a primary button click outside autoscroller",
|
||||
});
|
||||
|
||||
// Clicking the secondary button to stop autoscrolling.
|
||||
await doTestMouseEventsAtStoppingAutoScrolling({
|
||||
aButton: 2,
|
||||
aClickOutsideAutoScroller: false,
|
||||
aDescription: "a secondary button click on autoscroller",
|
||||
});
|
||||
await doTestMouseEventsAtStoppingAutoScrolling({
|
||||
aButton: 2,
|
||||
aClickOutsideAutoScroller: true,
|
||||
aDescription: "a secondary button click outside autoscroller",
|
||||
});
|
||||
|
||||
// Clicking the middle button to stop autoscrolling.
|
||||
await SpecialPowers.pushPrefEnv({ set: [["middlemouse.paste", true]] });
|
||||
await doTestMouseEventsAtStoppingAutoScrolling({
|
||||
aButton: 1,
|
||||
aClickOutsideAutoScroller: false,
|
||||
aDescription:
|
||||
"a middle button click on autoscroller (middle click paste enabled)",
|
||||
});
|
||||
await doTestMouseEventsAtStoppingAutoScrolling({
|
||||
aButton: 1,
|
||||
aClickOutsideAutoScroller: true,
|
||||
aDescription:
|
||||
"a middle button click outside autoscroller (middle click paste enabled)",
|
||||
});
|
||||
await SpecialPowers.pushPrefEnv({ set: [["middlemouse.paste", false]] });
|
||||
await doTestMouseEventsAtStoppingAutoScrolling({
|
||||
aButton: 1,
|
||||
aClickOutsideAutoScroller: false,
|
||||
aDescription:
|
||||
"a middle button click on autoscroller (middle click paste disabled)",
|
||||
});
|
||||
await doTestMouseEventsAtStoppingAutoScrolling({
|
||||
aButton: 1,
|
||||
aClickOutsideAutoScroller: true,
|
||||
aDescription:
|
||||
"a middle button click outside autoscroller (middle click paste disabled)",
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
@@ -1446,10 +1446,6 @@
|
||||
}
|
||||
case "mouseup":
|
||||
case "mousedown":
|
||||
// The following mouse click/auxclick event on the autoscroller
|
||||
// shouldn't be fired in web content for compatibility with Chrome.
|
||||
aEvent.preventClickEvent();
|
||||
// fallthrough
|
||||
case "contextmenu": {
|
||||
if (!this._ignoreMouseEvents) {
|
||||
// Use a timeout to prevent the mousedown from opening the popup again.
|
||||
@@ -1466,9 +1462,6 @@
|
||||
break;
|
||||
}
|
||||
case "popuphidden": {
|
||||
// TODO: When the autoscroller is closed by clicking outside of it,
|
||||
// we need to prevent following click event for compatibility
|
||||
// with Chrome. However, there is no way to do that for now.
|
||||
this._autoScrollPopup.removeEventListener(
|
||||
"popuphidden",
|
||||
this,
|
||||
|
||||
@@ -202,20 +202,18 @@ class WidgetMouseEvent : public WidgetMouseEventBase,
|
||||
WidgetMouseEvent()
|
||||
: mReason(eReal),
|
||||
mContextMenuTrigger(eNormal),
|
||||
mClickCount(0),
|
||||
mIgnoreRootScrollFrame(false),
|
||||
mUseLegacyNonPrimaryDispatch(false),
|
||||
mClickEventPrevented(false) {}
|
||||
mClickCount(0),
|
||||
mUseLegacyNonPrimaryDispatch(false) {}
|
||||
|
||||
WidgetMouseEvent(bool aIsTrusted, EventMessage aMessage, nsIWidget* aWidget,
|
||||
EventClassID aEventClassID, Reason aReason)
|
||||
: WidgetMouseEventBase(aIsTrusted, aMessage, aWidget, aEventClassID),
|
||||
mReason(aReason),
|
||||
mContextMenuTrigger(eNormal),
|
||||
mClickCount(0),
|
||||
mIgnoreRootScrollFrame(false),
|
||||
mUseLegacyNonPrimaryDispatch(false),
|
||||
mClickEventPrevented(false) {}
|
||||
mClickCount(0),
|
||||
mUseLegacyNonPrimaryDispatch(false) {}
|
||||
|
||||
public:
|
||||
virtual WidgetMouseEvent* AsMouseEvent() override { return this; }
|
||||
@@ -226,10 +224,9 @@ class WidgetMouseEvent : public WidgetMouseEventBase,
|
||||
: WidgetMouseEventBase(aIsTrusted, aMessage, aWidget, eMouseEventClass),
|
||||
mReason(aReason),
|
||||
mContextMenuTrigger(aContextMenuTrigger),
|
||||
mClickCount(0),
|
||||
mIgnoreRootScrollFrame(false),
|
||||
mUseLegacyNonPrimaryDispatch(false),
|
||||
mClickEventPrevented(false) {
|
||||
mClickCount(0),
|
||||
mUseLegacyNonPrimaryDispatch(false) {
|
||||
if (aMessage == eContextMenu) {
|
||||
mButton = (mContextMenuTrigger == eNormal) ? MouseButton::eSecondary
|
||||
: MouseButton::ePrimary;
|
||||
@@ -279,30 +276,26 @@ class WidgetMouseEvent : public WidgetMouseEventBase,
|
||||
// a child widget or a puppet widget.
|
||||
Maybe<ExitFrom> mExitFrom;
|
||||
|
||||
// Whether the event should ignore scroll frame bounds during dispatch.
|
||||
bool mIgnoreRootScrollFrame;
|
||||
|
||||
// mClickCount may be non-zero value when mMessage is eMouseDown, eMouseUp,
|
||||
// eMouseClick or eMouseDoubleClick. The number is count of mouse clicks.
|
||||
// Otherwise, this must be 0.
|
||||
uint32_t mClickCount;
|
||||
|
||||
// Whether the event should ignore scroll frame bounds during dispatch.
|
||||
bool mIgnoreRootScrollFrame;
|
||||
|
||||
// Indicates whether the event should dispatch click events for non-primary
|
||||
// mouse buttons on window and document.
|
||||
bool mUseLegacyNonPrimaryDispatch;
|
||||
|
||||
// Whether the event shouldn't cause click event.
|
||||
bool mClickEventPrevented;
|
||||
|
||||
void AssignMouseEventData(const WidgetMouseEvent& aEvent, bool aCopyTargets) {
|
||||
AssignMouseEventBaseData(aEvent, aCopyTargets);
|
||||
AssignPointerHelperData(aEvent, /* aCopyCoalescedEvents */ true);
|
||||
|
||||
mExitFrom = aEvent.mExitFrom;
|
||||
mClickCount = aEvent.mClickCount;
|
||||
mIgnoreRootScrollFrame = aEvent.mIgnoreRootScrollFrame;
|
||||
mClickCount = aEvent.mClickCount;
|
||||
mUseLegacyNonPrimaryDispatch = aEvent.mUseLegacyNonPrimaryDispatch;
|
||||
mClickEventPrevented = aEvent.mClickEventPrevented;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -243,7 +243,6 @@ struct ParamTraits<mozilla::WidgetMouseEvent> {
|
||||
WriteParam(aMsg, static_cast<const mozilla::WidgetMouseEventBase&>(aParam));
|
||||
WriteParam(aMsg, static_cast<const mozilla::WidgetPointerHelper&>(aParam));
|
||||
WriteParam(aMsg, aParam.mIgnoreRootScrollFrame);
|
||||
WriteParam(aMsg, aParam.mClickEventPrevented);
|
||||
WriteParam(aMsg, static_cast<paramType::ReasonType>(aParam.mReason));
|
||||
WriteParam(aMsg, static_cast<paramType::ContextMenuTriggerType>(
|
||||
aParam.mContextMenuTrigger));
|
||||
@@ -266,7 +265,6 @@ struct ParamTraits<mozilla::WidgetMouseEvent> {
|
||||
ReadParam(aMsg, aIter,
|
||||
static_cast<mozilla::WidgetPointerHelper*>(aResult)) &&
|
||||
ReadParam(aMsg, aIter, &aResult->mIgnoreRootScrollFrame) &&
|
||||
ReadParam(aMsg, aIter, &aResult->mClickEventPrevented) &&
|
||||
ReadParam(aMsg, aIter, &reason) &&
|
||||
ReadParam(aMsg, aIter, &contextMenuTrigger);
|
||||
aResult->mReason = static_cast<paramType::Reason>(reason);
|
||||
|
||||
Reference in New Issue
Block a user