Commit b681e1bf authored by Liviu Tinta's avatar Liviu Tinta Committed by Commit Bot

On contextmenu word underlined by spellcheck is highlighted

Because word is highlighted the cursor position is lost.
The fix moves highlighting after code knows if the event was
'preventdefaulted'.

Bug: 1076078
Change-Id: I08ebece22beee7cc71dd6e8241cb42f7be2e0331
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2183174Reviewed-by: default avatarDave Tapuska <dtapuska@chromium.org>
Reviewed-by: default avatarYoshifumi Inoue <yosin@chromium.org>
Commit-Queue: Liviu Tinta <liviutinta@chromium.org>
Cr-Commit-Position: refs/heads/master@{#791911}
parent e5d0119e
...@@ -59,6 +59,10 @@ class BLINK_COMMON_EXPORT WebMouseEvent : public WebInputEvent, ...@@ -59,6 +59,10 @@ class BLINK_COMMON_EXPORT WebMouseEvent : public WebInputEvent,
return (GetModifiers() & kIsCompatibilityEventForTouch) != 0; return (GetModifiers() & kIsCompatibilityEventForTouch) != 0;
} }
int ClickCount() const { return click_count; }
WebMenuSourceType GetMenuSourceType() const { return menu_source_type; }
WebMouseEvent(Type type_param, WebMouseEvent(Type type_param,
const WebGestureEvent&, const WebGestureEvent&,
Button button_param, Button button_param,
......
...@@ -734,55 +734,60 @@ void SelectionController::SelectClosestMisspellingFromHitTestResult( ...@@ -734,55 +734,60 @@ void SelectionController::SelectClosestMisspellingFromHitTestResult(
.Build()); .Build());
} }
template <typename MouseEventObject>
bool SelectionController::SelectClosestWordFromMouseEvent( bool SelectionController::SelectClosestWordFromMouseEvent(
const MouseEventWithHitTestResults& result) { const MouseEventObject* mouse_event,
const HitTestResult& result) {
if (!mouse_down_may_start_select_) if (!mouse_down_may_start_select_)
return false; return false;
AppendTrailingWhitespace append_trailing_whitespace = AppendTrailingWhitespace append_trailing_whitespace =
(result.Event().click_count == 2 && (mouse_event->ClickCount() == 2 &&
frame_->GetEditor().IsSelectTrailingWhitespaceEnabled()) frame_->GetEditor().IsSelectTrailingWhitespaceEnabled())
? AppendTrailingWhitespace::kShouldAppend ? AppendTrailingWhitespace::kShouldAppend
: AppendTrailingWhitespace::kDontAppend; : AppendTrailingWhitespace::kDontAppend;
DCHECK(!frame_->GetDocument()->NeedsLayoutTreeUpdate()); DCHECK(!frame_->GetDocument()->NeedsLayoutTreeUpdate());
return SelectClosestWordFromHitTestResult( return SelectClosestWordFromHitTestResult(result, append_trailing_whitespace,
result.GetHitTestResult(), append_trailing_whitespace, mouse_event->FromTouch()
result.Event().FromTouch() ? SelectInputEventType::kTouch ? SelectInputEventType::kTouch
: SelectInputEventType::kMouse); : SelectInputEventType::kMouse);
} }
template <typename MouseEventObject>
void SelectionController::SelectClosestMisspellingFromMouseEvent( void SelectionController::SelectClosestMisspellingFromMouseEvent(
const MouseEventWithHitTestResults& result) { const MouseEventObject* mouse_event,
const HitTestResult& hit_test_result) {
if (!mouse_down_may_start_select_) if (!mouse_down_may_start_select_)
return; return;
SelectClosestMisspellingFromHitTestResult( SelectClosestMisspellingFromHitTestResult(
result.GetHitTestResult(), hit_test_result, (mouse_event->ClickCount() == 2 &&
(result.Event().click_count == 2 && frame_->GetEditor().IsSelectTrailingWhitespaceEnabled())
frame_->GetEditor().IsSelectTrailingWhitespaceEnabled()) ? AppendTrailingWhitespace::kShouldAppend
? AppendTrailingWhitespace::kShouldAppend : AppendTrailingWhitespace::kDontAppend);
: AppendTrailingWhitespace::kDontAppend);
} }
template <typename MouseEventObject>
void SelectionController::SelectClosestWordOrLinkFromMouseEvent( void SelectionController::SelectClosestWordOrLinkFromMouseEvent(
const MouseEventWithHitTestResults& result) { const MouseEventObject* mouse_event,
if (!result.GetHitTestResult().IsLiveLink()) { const HitTestResult& hit_test_result) {
SelectClosestWordFromMouseEvent(result); if (!hit_test_result.IsLiveLink()) {
SelectClosestWordFromMouseEvent(mouse_event, hit_test_result);
return; return;
} }
Node* const inner_node = result.InnerNode(); Node* const inner_node = hit_test_result.InnerNode();
if (!inner_node || !inner_node->GetLayoutObject() || if (!inner_node || !inner_node->GetLayoutObject() ||
!mouse_down_may_start_select_) !mouse_down_may_start_select_)
return; return;
Element* url_element = result.GetHitTestResult().URLElement(); Element* url_element = hit_test_result.URLElement();
const PositionInFlatTreeWithAffinity pos = const PositionInFlatTreeWithAffinity pos =
CreateVisiblePosition( CreateVisiblePosition(
PositionWithAffinityOfHitTestResult(result.GetHitTestResult())) PositionWithAffinityOfHitTestResult(hit_test_result))
.ToPositionWithAffinity(); .ToPositionWithAffinity();
const SelectionInFlatTree& new_selection = const SelectionInFlatTree& new_selection =
pos.IsNotNull() && pos.AnchorNode()->IsDescendantOf(url_element) pos.IsNotNull() && pos.AnchorNode()->IsDescendantOf(url_element)
...@@ -925,7 +930,8 @@ bool SelectionController::HandleDoubleClick( ...@@ -925,7 +930,8 @@ bool SelectionController::HandleDoubleClick(
selection_state_ = SelectionState::kExtendedSelection; selection_state_ = SelectionState::kExtendedSelection;
return true; return true;
} }
if (!SelectClosestWordFromMouseEvent(event)) if (!SelectClosestWordFromMouseEvent(&event.Event(),
event.GetHitTestResult()))
return true; return true;
if (!Selection().IsHandleVisible()) if (!Selection().IsHandleVisible())
return true; return true;
...@@ -1205,12 +1211,14 @@ static bool HitTestResultIsMisspelled(const HitTestResult& result) { ...@@ -1205,12 +1211,14 @@ static bool HitTestResultIsMisspelled(const HitTestResult& result) {
ToPositionInFlatTree(marker_position)); ToPositionInFlatTree(marker_position));
} }
template <typename MouseEventObject>
void SelectionController::UpdateSelectionForContextMenuEvent( void SelectionController::UpdateSelectionForContextMenuEvent(
const MouseEventWithHitTestResults& mev, const MouseEventObject* mouse_event,
const HitTestResult& hit_test_result,
const PhysicalOffset& position) { const PhysicalOffset& position) {
if (!Selection().IsAvailable()) if (!Selection().IsAvailable())
return; return;
if (Selection().Contains(position) || mev.GetScrollbar() || if (Selection().Contains(position) || hit_test_result.GetScrollbar() ||
// FIXME: In the editable case, word selection sometimes selects content // FIXME: In the editable case, word selection sometimes selects content
// that isn't underneath the mouse. // that isn't underneath the mouse.
// If the selection is non-editable, we do word selection to make it // If the selection is non-editable, we do word selection to make it
...@@ -1219,21 +1227,23 @@ void SelectionController::UpdateSelectionForContextMenuEvent( ...@@ -1219,21 +1227,23 @@ void SelectionController::UpdateSelectionForContextMenuEvent(
!(Selection() !(Selection()
.ComputeVisibleSelectionInDOMTreeDeprecated() .ComputeVisibleSelectionInDOMTreeDeprecated()
.IsContentEditable() || .IsContentEditable() ||
(mev.InnerNode() && mev.InnerNode()->IsTextNode()))) (hit_test_result.InnerNode() &&
hit_test_result.InnerNode()->IsTextNode())))
return; return;
// Context menu events are always allowed to perform a selection. // Context menu events are always allowed to perform a selection.
base::AutoReset<bool> mouse_down_may_start_select_change( base::AutoReset<bool> mouse_down_may_start_select_change(
&mouse_down_may_start_select_, true); &mouse_down_may_start_select_, true);
if (mev.Event().menu_source_type != kMenuSourceTouchHandle && if (mouse_event->GetMenuSourceType() != kMenuSourceTouchHandle &&
HitTestResultIsMisspelled(mev.GetHitTestResult())) HitTestResultIsMisspelled(hit_test_result)) {
return SelectClosestMisspellingFromMouseEvent(mev); return SelectClosestMisspellingFromMouseEvent(mouse_event, hit_test_result);
}
if (!frame_->GetEditor().Behavior().ShouldSelectOnContextualMenuClick()) if (!frame_->GetEditor().Behavior().ShouldSelectOnContextualMenuClick())
return; return;
SelectClosestWordOrLinkFromMouseEvent(mev); SelectClosestWordOrLinkFromMouseEvent(mouse_event, hit_test_result);
} }
void SelectionController::PassMousePressEventToSubframe( void SelectionController::PassMousePressEventToSubframe(
...@@ -1335,4 +1345,7 @@ bool IsExtendingSelection(const MouseEventWithHitTestResults& event) { ...@@ -1335,4 +1345,7 @@ bool IsExtendingSelection(const MouseEventWithHitTestResults& event) {
!is_mouse_down_on_link_or_image && !IsUserNodeDraggable(event); !is_mouse_down_on_link_or_image && !IsUserNodeDraggable(event);
} }
template void SelectionController::UpdateSelectionForContextMenuEvent<
MouseEvent>(const MouseEvent*, const HitTestResult&, const PhysicalOffset&);
} // namespace blink } // namespace blink
...@@ -65,8 +65,10 @@ class CORE_EXPORT SelectionController final ...@@ -65,8 +65,10 @@ class CORE_EXPORT SelectionController final
void UpdateSelectionForMouseDrag(const HitTestResult&, void UpdateSelectionForMouseDrag(const HitTestResult&,
const PhysicalOffset&, const PhysicalOffset&,
const PhysicalOffset&); const PhysicalOffset&);
void UpdateSelectionForContextMenuEvent(const MouseEventWithHitTestResults&, template <typename MouseEventObject>
const PhysicalOffset&); void UpdateSelectionForContextMenuEvent(const MouseEventObject* mouse_event,
const HitTestResult& hit_test_result,
const PhysicalOffset& position);
void PassMousePressEventToSubframe(const MouseEventWithHitTestResults&); void PassMousePressEventToSubframe(const MouseEventWithHitTestResults&);
void InitializeSelectionState(); void InitializeSelectionState();
...@@ -97,11 +99,17 @@ class CORE_EXPORT SelectionController final ...@@ -97,11 +99,17 @@ class CORE_EXPORT SelectionController final
void SelectClosestMisspellingFromHitTestResult(const HitTestResult&, void SelectClosestMisspellingFromHitTestResult(const HitTestResult&,
AppendTrailingWhitespace); AppendTrailingWhitespace);
// Returns |true| if a word was selected. // Returns |true| if a word was selected.
bool SelectClosestWordFromMouseEvent(const MouseEventWithHitTestResults&); template <typename MouseEventObject>
bool SelectClosestWordFromMouseEvent(const MouseEventObject* mouse_event,
const HitTestResult& result);
template <typename MouseEventObject>
void SelectClosestMisspellingFromMouseEvent( void SelectClosestMisspellingFromMouseEvent(
const MouseEventWithHitTestResults&); const MouseEventObject* mouse_event,
const HitTestResult& hit_test_result);
template <typename MouseEventObject>
void SelectClosestWordOrLinkFromMouseEvent( void SelectClosestWordOrLinkFromMouseEvent(
const MouseEventWithHitTestResults&); const MouseEventObject* mouse_event,
const HitTestResult& hit_test_result);
void SetNonDirectionalSelectionIfNeeded(const SelectionInFlatTree&, void SetNonDirectionalSelectionIfNeeded(const SelectionInFlatTree&,
const SetSelectionOptions&, const SetSelectionOptions&,
EndPointsAdjustmentMode); EndPointsAdjustmentMode);
......
...@@ -131,7 +131,7 @@ class CORE_EXPORT MouseEvent : public UIEventWithKeyState { ...@@ -131,7 +131,7 @@ class CORE_EXPORT MouseEvent : public UIEventWithKeyState {
bool IsMouseEvent() const override; bool IsMouseEvent() const override;
unsigned which() const override; unsigned which() const override;
int ClickCount() { return detail(); } int ClickCount() const { return detail(); }
enum class PositionType { enum class PositionType {
kPosition, kPosition,
......
...@@ -2046,9 +2046,6 @@ WebInputEventResult EventHandler::SendContextMenuEvent( ...@@ -2046,9 +2046,6 @@ WebInputEventResult EventHandler::SendContextMenuEvent(
frame_->GetDocument()->UpdateStyleAndLayout( frame_->GetDocument()->UpdateStyleAndLayout(
DocumentUpdateReason::kContextMenu); DocumentUpdateReason::kContextMenu);
GetSelectionController().UpdateSelectionForContextMenuEvent(
mev, position_in_contents);
Element* target_element = Element* target_element =
override_target_element ? override_target_element : mev.InnerElement(); override_target_element ? override_target_element : mev.InnerElement();
return mouse_event_manager_->DispatchMouseEvent( return mouse_event_manager_->DispatchMouseEvent(
......
...@@ -43,6 +43,7 @@ ...@@ -43,6 +43,7 @@
#include "third_party/blink/renderer/core/editing/editor.h" #include "third_party/blink/renderer/core/editing/editor.h"
#include "third_party/blink/renderer/core/editing/frame_selection.h" #include "third_party/blink/renderer/core/editing/frame_selection.h"
#include "third_party/blink/renderer/core/editing/ime/input_method_controller.h" #include "third_party/blink/renderer/core/editing/ime/input_method_controller.h"
#include "third_party/blink/renderer/core/editing/selection_controller.h"
#include "third_party/blink/renderer/core/editing/spellcheck/spell_checker.h" #include "third_party/blink/renderer/core/editing/spellcheck/spell_checker.h"
#include "third_party/blink/renderer/core/events/mouse_event.h" #include "third_party/blink/renderer/core/events/mouse_event.h"
#include "third_party/blink/renderer/core/exported/web_plugin_container_impl.h" #include "third_party/blink/renderer/core/exported/web_plugin_container_impl.h"
...@@ -102,7 +103,9 @@ void ContextMenuController::HandleContextMenuEvent(MouseEvent* mouse_event) { ...@@ -102,7 +103,9 @@ void ContextMenuController::HandleContextMenuEvent(MouseEvent* mouse_event) {
LocalFrame* frame = mouse_event->target()->ToNode()->GetDocument().GetFrame(); LocalFrame* frame = mouse_event->target()->ToNode()->GetDocument().GetFrame();
PhysicalOffset location = PhysicalOffset::FromFloatPointRound( PhysicalOffset location = PhysicalOffset::FromFloatPointRound(
FloatPoint(mouse_event->AbsoluteLocation())); FloatPoint(mouse_event->AbsoluteLocation()));
if (ShowContextMenu(frame, location, mouse_event->GetMenuSourceType()))
if (ShowContextMenu(frame, location, mouse_event->GetMenuSourceType(),
mouse_event))
mouse_event->SetDefaultHandled(); mouse_event->SetDefaultHandled();
} }
...@@ -210,7 +213,8 @@ bool ContextMenuController::ShouldShowContextMenuFromTouch( ...@@ -210,7 +213,8 @@ bool ContextMenuController::ShouldShowContextMenuFromTouch(
bool ContextMenuController::ShowContextMenu(LocalFrame* frame, bool ContextMenuController::ShowContextMenu(LocalFrame* frame,
const PhysicalOffset& point, const PhysicalOffset& point,
WebMenuSourceType source_type) { WebMenuSourceType source_type,
const MouseEvent* mouse_event) {
// Displaying the context menu in this function is a big hack as we don't // Displaying the context menu in this function is a big hack as we don't
// have context, i.e. whether this is being invoked via a script or in // have context, i.e. whether this is being invoked via a script or in
// response to user input (Mouse event WM_RBUTTONDOWN, // response to user input (Mouse event WM_RBUTTONDOWN,
...@@ -233,6 +237,14 @@ bool ContextMenuController::ShowContextMenu(LocalFrame* frame, ...@@ -233,6 +237,14 @@ bool ContextMenuController::ShowContextMenu(LocalFrame* frame,
result.SetToShadowHostIfInRestrictedShadowRoot(); result.SetToShadowHostIfInRestrictedShadowRoot();
LocalFrame* selected_frame = result.InnerNodeFrame(); LocalFrame* selected_frame = result.InnerNodeFrame();
// Tests that do not require selection pass mouse_event = nullptr
if (mouse_event) {
selected_frame->GetEventHandler()
.GetSelectionController()
.UpdateSelectionForContextMenuEvent(
mouse_event, hit_test_result_,
PhysicalOffset(FlooredIntPoint(point)));
}
WebContextMenuData data; WebContextMenuData data;
data.mouse_position = selected_frame->View()->FrameToViewport( data.mouse_position = selected_frame->View()->FrameToViewport(
......
...@@ -68,7 +68,10 @@ class CORE_EXPORT ContextMenuController final ...@@ -68,7 +68,10 @@ class CORE_EXPORT ContextMenuController final
friend class ContextMenuControllerTest; friend class ContextMenuControllerTest;
// Returns whether a Context Menu was actually shown. // Returns whether a Context Menu was actually shown.
bool ShowContextMenu(LocalFrame*, const PhysicalOffset&, WebMenuSourceType); bool ShowContextMenu(LocalFrame*,
const PhysicalOffset&,
WebMenuSourceType,
const MouseEvent* mouse_event = nullptr);
bool ShouldShowContextMenuFromTouch(const WebContextMenuData&); bool ShouldShowContextMenuFromTouch(const WebContextMenuData&);
Member<Page> page_; Member<Page> page_;
......
<!doctype html>
<script src="../../resources/testharness.js"></script>
<script src="../../resources/testharnessreport.js"></script>
<script src="../../resources/gesture-util.js"></script>
<body onload="onLoad()">
<h2>Default action on Context Menu for misspelled word prevented</h2>
<p>
If there is
a contextmenu handler that prevents the default action then when mouse is
positioned on a misspelled word and right click is pressed, the misspelled word
should not be selected.
</p>
<div id="text" contenteditable>
Wellcome
</div>
<script>
function onLoad(){
let textDiv = document.getElementById("text");
let finishedContextMenuEvent = false;
textDiv.addEventListener("contextmenu", (event)=>{
event.preventDefault();
finishedContextMenuEvent = true;
});
function inject_actions(x, y){
return new Promise((resolve, reject)=>{
if(chrome.gpuBenchmarking){
chrome.gpuBenchmarking.pointerActionSequence( [
{source: 'mouse',
actions: [
{ name: 'pointerDown', x: x, y: y, button: 2 /*RIGHT button*/},
{ name: 'pointerUp', x: x, y: y, button: 2 /*RIGHT button*/}
]
}], resolve);
}
else
reject();
});
}
promise_test((test) => new Promise(async (resolve, reject)=>{
let bounding_rect = textDiv.getBoundingClientRect();
let x = bounding_rect.x;
let y = bounding_rect.y + 10;
// right click on misspelled word "Wellcome"
await inject_actions(x, y);
await waitFor(() => finishedContextMenuEvent, "ContextMenu event not fired");
test.step(() => {
let selection = getSelection();
assert_equals(selection.type, "Caret", "The cursor should be on text");
assert_true(selection.isCollapsed, "The selection should be empty");
});
resolve();
}), "No word selection if right click on misspelled word and contextmenu action prevented");
}
</script>
</body>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment