Handle the mouse back/forward button in the browser directly
Ensure that the result of mouse up is passed back to the browser so
that it can be executed on the browser side.
BUG=850847,705583
Change-Id: Iebf871c43a9957af1f66a398344efcddc47d7293
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1769525
Commit-Queue: Dave Tapuska <[email protected]>
Reviewed-by: Charlie Reis <[email protected]>
Reviewed-by: Navid Zolghadr <[email protected]>
Cr-Commit-Position: refs/heads/master@{#694770}
diff --git a/content/browser/renderer_host/input/mouse_latency_browsertest.cc b/content/browser/renderer_host/input/mouse_latency_browsertest.cc
index 23caf0a..41ffc93 100644
--- a/content/browser/renderer_host/input/mouse_latency_browsertest.cc
+++ b/content/browser/renderer_host/input/mouse_latency_browsertest.cc
@@ -352,7 +352,7 @@
GetWidgetHost(), blink::WebInputEvent::kMouseUp);
StartTracing();
DoSyncClick(gfx::PointF(100, 100));
- EXPECT_EQ(INPUT_EVENT_ACK_STATE_CONSUMED,
+ EXPECT_EQ(INPUT_EVENT_ACK_STATE_NOT_CONSUMED,
filter->GetAckStateWaitIfNecessary());
const base::Value& trace_data = StopTracing();
diff --git a/content/browser/renderer_host/render_widget_host_delegate.cc b/content/browser/renderer_host/render_widget_host_delegate.cc
index 03f964a..fe8609c 100644
--- a/content/browser/renderer_host/render_widget_host_delegate.cc
+++ b/content/browser/renderer_host/render_widget_host_delegate.cc
@@ -22,6 +22,11 @@
return false;
}
+bool RenderWidgetHostDelegate::HandleMouseEvent(
+ const blink::WebMouseEvent& event) {
+ return false;
+}
+
bool RenderWidgetHostDelegate::HandleWheelEvent(
const blink::WebMouseWheelEvent& event) {
return false;
diff --git a/content/browser/renderer_host/render_widget_host_delegate.h b/content/browser/renderer_host/render_widget_host_delegate.h
index d073e85d7..7894b65 100644
--- a/content/browser/renderer_host/render_widget_host_delegate.h
+++ b/content/browser/renderer_host/render_widget_host_delegate.h
@@ -108,6 +108,11 @@
// Callback to inform the browser that the renderer did not process the
// specified events. This gives an opportunity to the browser to process the
+ // back/forward mouse buttons.
+ virtual bool HandleMouseEvent(const blink::WebMouseEvent& event);
+
+ // Callback to inform the browser that the renderer did not process the
+ // specified events. This gives an opportunity to the browser to process the
// event (used for keyboard shortcuts).
virtual bool HandleKeyboardEvent(const NativeWebKeyboardEvent& event);
diff --git a/content/browser/renderer_host/render_widget_host_impl.cc b/content/browser/renderer_host/render_widget_host_impl.cc
index d7055ce..1fb9487 100644
--- a/content/browser/renderer_host/render_widget_host_impl.cc
+++ b/content/browser/renderer_host/render_widget_host_impl.cc
@@ -1972,6 +1972,11 @@
for (auto& input_event_observer : input_event_observers_)
input_event_observer.OnInputEventAck(ack_source, ack_result,
mouse_event.event);
+
+ // Give the delegate the ability to handle a mouse event that wasn't consumed
+ // by the renderer. eg. Back/Forward mouse buttons.
+ if (delegate_ && ack_result != INPUT_EVENT_ACK_STATE_CONSUMED && !is_hidden())
+ delegate_->HandleMouseEvent(mouse_event.event);
}
bool RenderWidgetHostImpl::IsMouseLocked() const {
diff --git a/content/browser/web_contents/web_contents_impl.cc b/content/browser/web_contents/web_contents_impl.cc
index ddd8255..6ab3420 100644
--- a/content/browser/web_contents/web_contents_impl.cc
+++ b/content/browser/web_contents/web_contents_impl.cc
@@ -2372,6 +2372,25 @@
: KeyboardEventProcessingResult::NOT_HANDLED;
}
+bool WebContentsImpl::HandleMouseEvent(const blink::WebMouseEvent& event) {
+ // Handle mouse button back/forward in the browser process after the render
+ // process is done with the event. This ensures all renderer-initiated history
+ // navigations can be treated consistently.
+ if (event.GetType() == blink::WebInputEvent::Type::kMouseUp) {
+ WebContentsImpl* outermost = GetOutermostWebContents();
+ if (event.button == blink::WebPointerProperties::Button::kBack &&
+ outermost->controller_.CanGoBack()) {
+ outermost->controller_.GoBack();
+ return true;
+ } else if (event.button == blink::WebPointerProperties::Button::kForward &&
+ outermost->controller_.CanGoForward()) {
+ outermost->controller_.GoForward();
+ return true;
+ }
+ }
+ return false;
+}
+
bool WebContentsImpl::HandleKeyboardEvent(const NativeWebKeyboardEvent& event) {
if (browser_plugin_embedder_ &&
browser_plugin_embedder_->HandleKeyboardEvent(event)) {
diff --git a/content/browser/web_contents/web_contents_impl.h b/content/browser/web_contents/web_contents_impl.h
index b256fe6..3121f105 100644
--- a/content/browser/web_contents/web_contents_impl.h
+++ b/content/browser/web_contents/web_contents_impl.h
@@ -763,6 +763,7 @@
KeyboardEventProcessingResult PreHandleKeyboardEvent(
const NativeWebKeyboardEvent& event) override;
+ bool HandleMouseEvent(const blink::WebMouseEvent& event) override;
bool HandleKeyboardEvent(const NativeWebKeyboardEvent& event) override;
bool HandleWheelEvent(const blink::WebMouseWheelEvent& event) override;
bool PreHandleGestureEvent(const blink::WebGestureEvent& event) override;
diff --git a/content/browser/web_contents/web_contents_impl_browsertest.cc b/content/browser/web_contents/web_contents_impl_browsertest.cc
index 7ba29e5..5550c69 100644
--- a/content/browser/web_contents/web_contents_impl_browsertest.cc
+++ b/content/browser/web_contents/web_contents_impl_browsertest.cc
@@ -17,6 +17,7 @@
#include "base/strings/pattern.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/scoped_feature_list.h"
+#include "base/test/test_timeouts.h"
#include "base/threading/thread_restrictions.h"
#include "base/time/time.h"
#include "base/values.h"
@@ -3906,4 +3907,110 @@
EXPECT_THAT(deleted_routing_ids, testing::Contains(frame_routing_id));
}
+IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, MouseButtonsNavigate) {
+ ASSERT_TRUE(embedded_test_server()->Start());
+ WebContentsImpl* web_contents =
+ static_cast<WebContentsImpl*>(shell()->web_contents());
+
+ GURL url_a = embedded_test_server()->GetURL("a.com", "/title1.html");
+ GURL url_b = embedded_test_server()->GetURL("b.com", "/title1.html");
+
+ EXPECT_TRUE(NavigateToURL(shell(), url_a));
+ EXPECT_TRUE(NavigateToURL(shell(), url_b));
+
+ {
+ TestNavigationObserver back_observer(web_contents);
+ web_contents->GetRenderWidgetHostWithPageFocus()->ForwardMouseEvent(
+ blink::WebMouseEvent(
+ blink::WebInputEvent::kMouseUp, blink::WebFloatPoint(51, 50),
+ blink::WebFloatPoint(51, 50),
+ blink::WebPointerProperties::Button::kBack, 0,
+ blink::WebInputEvent::kNoModifiers, base::TimeTicks::Now()));
+ back_observer.Wait();
+ ASSERT_EQ(url_a, web_contents->GetLastCommittedURL());
+ }
+
+ {
+ TestNavigationObserver forward_observer(web_contents);
+ web_contents->GetRenderWidgetHostWithPageFocus()->ForwardMouseEvent(
+ blink::WebMouseEvent(
+ blink::WebInputEvent::kMouseUp, blink::WebFloatPoint(51, 50),
+ blink::WebFloatPoint(51, 50),
+ blink::WebPointerProperties::Button::kForward, 0,
+ blink::WebInputEvent::kNoModifiers, base::TimeTicks::Now()));
+ forward_observer.Wait();
+ ASSERT_EQ(url_b, web_contents->GetLastCommittedURL());
+ }
+}
+
+IN_PROC_BROWSER_TEST_F(WebContentsImplBrowserTest, MouseButtonsDontNavigate) {
+ // This test injects mouse event listeners in javascript that will
+ // preventDefault the action causing the default navigation action not to be
+ // taken.
+
+ ASSERT_TRUE(embedded_test_server()->Start());
+ WebContentsImpl* web_contents =
+ static_cast<WebContentsImpl*>(shell()->web_contents());
+
+ GURL url_a = embedded_test_server()->GetURL("a.com", "/title1.html");
+ GURL url_b = embedded_test_server()->GetURL("b.com", "/title1.html");
+
+ EXPECT_TRUE(NavigateToURL(shell(), url_a));
+ EXPECT_TRUE(NavigateToURL(shell(), url_b));
+
+ // Prevent default the action.
+ EXPECT_TRUE(content::ExecuteScript(shell(),
+ "document.addEventListener('mouseup', "
+ "event => event.preventDefault());"));
+
+ RenderWidgetHostImpl* render_widget_host =
+ web_contents->GetRenderWidgetHostWithPageFocus();
+ render_widget_host->ForwardMouseEvent(blink::WebMouseEvent(
+ blink::WebInputEvent::kMouseUp, blink::WebFloatPoint(51, 50),
+ blink::WebFloatPoint(51, 50), blink::WebPointerProperties::Button::kBack,
+ 0, blink::WebInputEvent::kNoModifiers, base::TimeTicks::Now()));
+ RunUntilInputProcessed(render_widget_host);
+
+ // Wait an action timeout and assert the URL is correct.
+ {
+ base::RunLoop run_loop;
+ base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE, run_loop.QuitClosure(), TestTimeouts::action_timeout());
+ run_loop.Run();
+ }
+ ASSERT_EQ(url_b, web_contents->GetLastCommittedURL());
+
+ // Move back so we have a forward entry in the history stack so we
+ // can test forward getting canceled.
+ {
+ TestNavigationObserver back_observer(web_contents);
+ web_contents->GetController().GoBack();
+ back_observer.Wait();
+ ASSERT_EQ(url_a, web_contents->GetLastCommittedURL());
+ }
+
+ // Now test the forward button by going back, and injecting the prevention
+ // script.
+ // Prevent default the action.
+ EXPECT_TRUE(content::ExecuteScript(shell(),
+ "document.addEventListener('mouseup', "
+ "event => event.preventDefault());"));
+
+ render_widget_host = web_contents->GetRenderWidgetHostWithPageFocus();
+ render_widget_host->ForwardMouseEvent(blink::WebMouseEvent(
+ blink::WebInputEvent::kMouseUp, blink::WebFloatPoint(51, 50),
+ blink::WebFloatPoint(51, 50),
+ blink::WebPointerProperties::Button::kForward, 0,
+ blink::WebInputEvent::kNoModifiers, base::TimeTicks::Now()));
+ RunUntilInputProcessed(render_widget_host);
+ // Wait an action timeout and assert the URL is correct.
+ {
+ base::RunLoop run_loop;
+ base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
+ FROM_HERE, run_loop.QuitClosure(), TestTimeouts::action_timeout());
+ run_loop.Run();
+ }
+ ASSERT_EQ(url_a, web_contents->GetLastCommittedURL());
+}
+
} // namespace content
diff --git a/content/renderer/input/render_widget_input_handler.cc b/content/renderer/input/render_widget_input_handler.cc
index 68a99ade..364073b 100644
--- a/content/renderer/input/render_widget_input_handler.cc
+++ b/content/renderer/input/render_widget_input_handler.cc
@@ -376,6 +376,7 @@
}
bool prevent_default = false;
+ bool show_virtual_keyboard_for_mouse = false;
if (WebInputEvent::IsMouseEventType(input_event.GetType())) {
const WebMouseEvent& mouse_event =
static_cast<const WebMouseEvent&>(input_event);
@@ -388,6 +389,11 @@
// time that the mouse enters we always set the cursor accordingly.
if (mouse_event.GetType() == WebInputEvent::kMouseLeave)
current_cursor_.reset();
+
+ if (mouse_event.button == WebPointerProperties::Button::kLeft &&
+ mouse_event.GetType() == WebInputEvent::kMouseUp) {
+ show_virtual_keyboard_for_mouse = true;
+ }
}
if (WebInputEvent::IsKeyboardEventType(input_event.GetType())) {
@@ -494,9 +500,9 @@
// Show the virtual keyboard if enabled and a user gesture triggers a focus
// change.
- if (processed != WebInputEventResult::kNotHandled &&
- (input_event.GetType() == WebInputEvent::kTouchEnd ||
- input_event.GetType() == WebInputEvent::kMouseUp)) {
+ if ((processed != WebInputEventResult::kNotHandled &&
+ input_event.GetType() == WebInputEvent::kTouchEnd) ||
+ show_virtual_keyboard_for_mouse) {
delegate_->ShowVirtualKeyboard();
}