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/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