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,
return (GetModifiers() & kIsCompatibilityEventForTouch) != 0;
}
int ClickCount() const { return click_count; }
WebMenuSourceType GetMenuSourceType() const { return menu_source_type; }
WebMouseEvent(Type type_param,
const WebGestureEvent&,
Button button_param,
......
......@@ -734,55 +734,60 @@ void SelectionController::SelectClosestMisspellingFromHitTestResult(
.Build());
}
template <typename MouseEventObject>
bool SelectionController::SelectClosestWordFromMouseEvent(
const MouseEventWithHitTestResults& result) {
const MouseEventObject* mouse_event,
const HitTestResult& result) {
if (!mouse_down_may_start_select_)
return false;
AppendTrailingWhitespace append_trailing_whitespace =
(result.Event().click_count == 2 &&
(mouse_event->ClickCount() == 2 &&
frame_->GetEditor().IsSelectTrailingWhitespaceEnabled())
? AppendTrailingWhitespace::kShouldAppend
: AppendTrailingWhitespace::kDontAppend;
DCHECK(!frame_->GetDocument()->NeedsLayoutTreeUpdate());
return SelectClosestWordFromHitTestResult(
result.GetHitTestResult(), append_trailing_whitespace,
result.Event().FromTouch() ? SelectInputEventType::kTouch
return SelectClosestWordFromHitTestResult(result, append_trailing_whitespace,
mouse_event->FromTouch()
? SelectInputEventType::kTouch
: SelectInputEventType::kMouse);
}
template <typename MouseEventObject>
void SelectionController::SelectClosestMisspellingFromMouseEvent(
const MouseEventWithHitTestResults& result) {
const MouseEventObject* mouse_event,
const HitTestResult& hit_test_result) {
if (!mouse_down_may_start_select_)
return;
SelectClosestMisspellingFromHitTestResult(
result.GetHitTestResult(),
(result.Event().click_count == 2 &&
hit_test_result, (mouse_event->ClickCount() == 2 &&
frame_->GetEditor().IsSelectTrailingWhitespaceEnabled())
? AppendTrailingWhitespace::kShouldAppend
: AppendTrailingWhitespace::kDontAppend);
}
template <typename MouseEventObject>
void SelectionController::SelectClosestWordOrLinkFromMouseEvent(
const MouseEventWithHitTestResults& result) {
if (!result.GetHitTestResult().IsLiveLink()) {
SelectClosestWordFromMouseEvent(result);
const MouseEventObject* mouse_event,
const HitTestResult& hit_test_result) {
if (!hit_test_result.IsLiveLink()) {
SelectClosestWordFromMouseEvent(mouse_event, hit_test_result);
return;
}
Node* const inner_node = result.InnerNode();
Node* const inner_node = hit_test_result.InnerNode();
if (!inner_node || !inner_node->GetLayoutObject() ||
!mouse_down_may_start_select_)
return;
Element* url_element = result.GetHitTestResult().URLElement();
Element* url_element = hit_test_result.URLElement();
const PositionInFlatTreeWithAffinity pos =
CreateVisiblePosition(
PositionWithAffinityOfHitTestResult(result.GetHitTestResult()))
PositionWithAffinityOfHitTestResult(hit_test_result))
.ToPositionWithAffinity();
const SelectionInFlatTree& new_selection =
pos.IsNotNull() && pos.AnchorNode()->IsDescendantOf(url_element)
......@@ -925,7 +930,8 @@ bool SelectionController::HandleDoubleClick(
selection_state_ = SelectionState::kExtendedSelection;
return true;
}
if (!SelectClosestWordFromMouseEvent(event))
if (!SelectClosestWordFromMouseEvent(&event.Event(),
event.GetHitTestResult()))
return true;
if (!Selection().IsHandleVisible())
return true;
......@@ -1205,12 +1211,14 @@ static bool HitTestResultIsMisspelled(const HitTestResult& result) {
ToPositionInFlatTree(marker_position));
}
template <typename MouseEventObject>
void SelectionController::UpdateSelectionForContextMenuEvent(
const MouseEventWithHitTestResults& mev,
const MouseEventObject* mouse_event,
const HitTestResult& hit_test_result,
const PhysicalOffset& position) {
if (!Selection().IsAvailable())
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
// that isn't underneath the mouse.
// If the selection is non-editable, we do word selection to make it
......@@ -1219,21 +1227,23 @@ void SelectionController::UpdateSelectionForContextMenuEvent(
!(Selection()
.ComputeVisibleSelectionInDOMTreeDeprecated()
.IsContentEditable() ||
(mev.InnerNode() && mev.InnerNode()->IsTextNode())))
(hit_test_result.InnerNode() &&
hit_test_result.InnerNode()->IsTextNode())))
return;
// Context menu events are always allowed to perform a selection.
base::AutoReset<bool> mouse_down_may_start_select_change(
&mouse_down_may_start_select_, true);
if (mev.Event().menu_source_type != kMenuSourceTouchHandle &&
HitTestResultIsMisspelled(mev.GetHitTestResult()))
return SelectClosestMisspellingFromMouseEvent(mev);
if (mouse_event->GetMenuSourceType() != kMenuSourceTouchHandle &&
HitTestResultIsMisspelled(hit_test_result)) {
return SelectClosestMisspellingFromMouseEvent(mouse_event, hit_test_result);
}
if (!frame_->GetEditor().Behavior().ShouldSelectOnContextualMenuClick())
return;
SelectClosestWordOrLinkFromMouseEvent(mev);
SelectClosestWordOrLinkFromMouseEvent(mouse_event, hit_test_result);
}
void SelectionController::PassMousePressEventToSubframe(
......@@ -1335,4 +1345,7 @@ bool IsExtendingSelection(const MouseEventWithHitTestResults& event) {
!is_mouse_down_on_link_or_image && !IsUserNodeDraggable(event);
}
template void SelectionController::UpdateSelectionForContextMenuEvent<
MouseEvent>(const MouseEvent*, const HitTestResult&, const PhysicalOffset&);
} // namespace blink
......@@ -65,8 +65,10 @@ class CORE_EXPORT SelectionController final
void UpdateSelectionForMouseDrag(const HitTestResult&,
const PhysicalOffset&,
const PhysicalOffset&);
void UpdateSelectionForContextMenuEvent(const MouseEventWithHitTestResults&,
const PhysicalOffset&);
template <typename MouseEventObject>
void UpdateSelectionForContextMenuEvent(const MouseEventObject* mouse_event,
const HitTestResult& hit_test_result,
const PhysicalOffset& position);
void PassMousePressEventToSubframe(const MouseEventWithHitTestResults&);
void InitializeSelectionState();
......@@ -97,11 +99,17 @@ class CORE_EXPORT SelectionController final
void SelectClosestMisspellingFromHitTestResult(const HitTestResult&,
AppendTrailingWhitespace);
// 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(
const MouseEventWithHitTestResults&);
const MouseEventObject* mouse_event,
const HitTestResult& hit_test_result);
template <typename MouseEventObject>
void SelectClosestWordOrLinkFromMouseEvent(
const MouseEventWithHitTestResults&);
const MouseEventObject* mouse_event,
const HitTestResult& hit_test_result);
void SetNonDirectionalSelectionIfNeeded(const SelectionInFlatTree&,
const SetSelectionOptions&,
EndPointsAdjustmentMode);
......
......@@ -131,7 +131,7 @@ class CORE_EXPORT MouseEvent : public UIEventWithKeyState {
bool IsMouseEvent() const override;
unsigned which() const override;
int ClickCount() { return detail(); }
int ClickCount() const { return detail(); }
enum class PositionType {
kPosition,
......
......@@ -2046,9 +2046,6 @@ WebInputEventResult EventHandler::SendContextMenuEvent(
frame_->GetDocument()->UpdateStyleAndLayout(
DocumentUpdateReason::kContextMenu);
GetSelectionController().UpdateSelectionForContextMenuEvent(
mev, position_in_contents);
Element* target_element =
override_target_element ? override_target_element : mev.InnerElement();
return mouse_event_manager_->DispatchMouseEvent(
......
......@@ -43,6 +43,7 @@
#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/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/events/mouse_event.h"
#include "third_party/blink/renderer/core/exported/web_plugin_container_impl.h"
......@@ -102,7 +103,9 @@ void ContextMenuController::HandleContextMenuEvent(MouseEvent* mouse_event) {
LocalFrame* frame = mouse_event->target()->ToNode()->GetDocument().GetFrame();
PhysicalOffset location = PhysicalOffset::FromFloatPointRound(
FloatPoint(mouse_event->AbsoluteLocation()));
if (ShowContextMenu(frame, location, mouse_event->GetMenuSourceType()))
if (ShowContextMenu(frame, location, mouse_event->GetMenuSourceType(),
mouse_event))
mouse_event->SetDefaultHandled();
}
......@@ -210,7 +213,8 @@ bool ContextMenuController::ShouldShowContextMenuFromTouch(
bool ContextMenuController::ShowContextMenu(LocalFrame* frame,
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
// have context, i.e. whether this is being invoked via a script or in
// response to user input (Mouse event WM_RBUTTONDOWN,
......@@ -233,6 +237,14 @@ bool ContextMenuController::ShowContextMenu(LocalFrame* frame,
result.SetToShadowHostIfInRestrictedShadowRoot();
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;
data.mouse_position = selected_frame->View()->FrameToViewport(
......
......@@ -68,7 +68,10 @@ class CORE_EXPORT ContextMenuController final
friend class ContextMenuControllerTest;
// 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&);
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