Commit 5ca41a08 authored by Dave Tapuska's avatar Dave Tapuska Committed by Commit Bot

Provide back/forward buttons to web content.

Allow mousedown to prevent default the back/forward actions. This feature
is controlled via the ExtendedMouseButtons feature flag which is by
default disabled. Since the button events aren't sent from the browser
when the feature is off the blink code doesn't require a feature flag
since it won't be executed.

Intent to Implement: https://groups.google.com/a/chromium.org/d/msg/blink-dev/3DjSQKQzKz8/FI9qbxHhAwAJ

BUG=680741

Change-Id: If9a59fbb085104b26d25e050f1ca7349756b102b
Reviewed-on: https://chromium-review.googlesource.com/781999
Commit-Queue: Dave Tapuska <dtapuska@chromium.org>
Reviewed-by: default avatarScott Violet <sky@chromium.org>
Reviewed-by: default avatarMustaq Ahmed <mustaq@chromium.org>
Reviewed-by: default avatarSadrul Chowdhury <sadrul@chromium.org>
Reviewed-by: default avatarAvi Drissman <avi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#521475}
parent 9ccbec24
......@@ -9,6 +9,7 @@
#include "chrome/browser/ui/views/frame/browser_view.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_features.h"
#include "ui/aura/window.h"
#include "ui/events/event.h"
......@@ -32,6 +33,10 @@ void BrowserCommandHandlerLinux::OnMouseEvent(ui::MouseEvent* event) {
if (event->type() != ui::ET_MOUSE_PRESSED)
return;
// If extended mouse buttons are supported handle them in the renderer.
if (base::FeatureList::IsEnabled(features::kExtendedMouseButtons))
return;
bool back_button_pressed =
(event->changed_button_flags() == ui::EF_BACK_MOUSE_BUTTON);
bool forward_button_pressed =
......
......@@ -18,6 +18,7 @@
#include "content/common/site_isolation_policy.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/render_widget_host.h"
#include "content/public/common/content_features.h"
#include "ui/aura/client/cursor_client.h"
#include "ui/aura/client/focus_client.h"
#include "ui/aura/client/screen_position_client.h"
......@@ -82,10 +83,11 @@ bool IsFractionalScaleFactor(float scale_factor) {
// We don't mark these as handled so that they're sent back to the
// DefWindowProc so it can generate WM_APPCOMMAND as necessary.
bool IsXButtonUpEvent(const ui::MouseEvent* event) {
bool ShouldGenerateAppCommand(const ui::MouseEvent* event) {
#if defined(OS_WIN)
switch (event->native_event().message) {
case WM_XBUTTONUP:
return !base::FeatureList::IsEnabled(features::kExtendedMouseButtons);
case WM_NCXBUTTONUP:
return true;
}
......@@ -396,7 +398,7 @@ void RenderWidgetHostViewEventHandler::OnMouseEvent(ui::MouseEvent* event) {
break;
}
if (!IsXButtonUpEvent(event))
if (!ShouldGenerateAppCommand(event))
event->SetHandled();
}
......@@ -599,6 +601,7 @@ bool RenderWidgetHostViewEventHandler::CanRendererHandleEvent(
case WM_XBUTTONDOWN:
case WM_XBUTTONUP:
case WM_XBUTTONDBLCLK:
return base::FeatureList::IsEnabled(features::kExtendedMouseButtons);
case WM_NCMOUSELEAVE:
case WM_NCMOUSEMOVE:
case WM_NCLBUTTONDOWN:
......@@ -618,18 +621,20 @@ bool RenderWidgetHostViewEventHandler::CanRendererHandleEvent(
break;
}
#elif defined(USE_X11)
// Renderer only supports standard mouse buttons, so ignore programmable
// buttons.
switch (event->type()) {
case ui::ET_MOUSE_PRESSED:
case ui::ET_MOUSE_RELEASED: {
const int kAllowedButtons = ui::EF_LEFT_MOUSE_BUTTON |
ui::EF_MIDDLE_MOUSE_BUTTON |
ui::EF_RIGHT_MOUSE_BUTTON;
return (event->flags() & kAllowedButtons) != 0;
if (!base::FeatureList::IsEnabled(features::kExtendedMouseButtons)) {
// Renderer only supports standard mouse buttons, so ignore programmable
// buttons.
switch (event->type()) {
case ui::ET_MOUSE_PRESSED:
case ui::ET_MOUSE_RELEASED: {
const int kAllowedButtons = ui::EF_LEFT_MOUSE_BUTTON |
ui::EF_MIDDLE_MOUSE_BUTTON |
ui::EF_RIGHT_MOUSE_BUTTON;
return (event->flags() & kAllowedButtons) != 0;
}
default:
break;
}
default:
break;
}
#endif
return true;
......
......@@ -92,6 +92,10 @@ const base::Feature kCompositorImageAnimation{
const base::Feature kCompositorTouchAction{"CompositorTouchAction",
base::FEATURE_DISABLED_BY_DEFAULT};
// Enables exposing back/forward mouse buttons to the renderer and the web.
const base::Feature kExtendedMouseButtons{"ExtendedMouseButtons",
base::FEATURE_DISABLED_BY_DEFAULT};
// Throttle tasks in Blink background timer queues based on CPU budgets
// for the background tab. Bug: https://crbug.com/639852.
const base::Feature kExpensiveBackgroundTimerThrottling{
......
......@@ -32,6 +32,7 @@ CONTENT_EXPORT extern const base::Feature kCompositeOpaqueFixedPosition;
CONTENT_EXPORT extern const base::Feature kCompositeOpaqueScrollers;
CONTENT_EXPORT extern const base::Feature kCompositorImageAnimation;
CONTENT_EXPORT extern const base::Feature kCompositorTouchAction;
CONTENT_EXPORT extern const base::Feature kExtendedMouseButtons;
CONTENT_EXPORT extern const base::Feature kExpensiveBackgroundTimerThrottling;
CONTENT_EXPORT extern const base::Feature kFeaturePolicy;
CONTENT_EXPORT extern const base::Feature kFetchKeepaliveTimeoutSetting;
......
......@@ -214,8 +214,17 @@ void AppendMouseEvent(const WebInputEvent& event,
if (mouse_event.GetType() == WebInputEvent::kMouseDown ||
mouse_event.GetType() == WebInputEvent::kMouseMove ||
mouse_event.GetType() == WebInputEvent::kMouseUp) {
result.mouse_button =
static_cast<PP_InputEvent_MouseButton>(mouse_event.button);
switch (mouse_event.button) {
case WebMouseEvent::Button::kNoButton:
case WebMouseEvent::Button::kLeft:
case WebMouseEvent::Button::kRight:
case WebMouseEvent::Button::kMiddle:
result.mouse_button =
static_cast<PP_InputEvent_MouseButton>(mouse_event.button);
break;
default:
return;
}
}
result.mouse_position.x = mouse_event.PositionInWidget().x;
result.mouse_position.y = mouse_event.PositionInWidget().y;
......
......@@ -79,6 +79,7 @@
#include "core/frame/EventHandlerRegistry.h"
#include "core/frame/LocalDOMWindow.h"
#include "core/frame/LocalFrame.h"
#include "core/frame/LocalFrameClient.h"
#include "core/frame/LocalFrameView.h"
#include "core/frame/UseCounter.h"
#include "core/html/HTMLDialogElement.h"
......@@ -2408,6 +2409,21 @@ void Node::DefaultEventHandler(Event* event) {
frame->GetEventHandler().StartMiddleClickAutoscroll(layout_object);
}
}
} else if (event_type == EventTypeNames::mouseup && event->IsMouseEvent()) {
MouseEvent* mouse_event = ToMouseEvent(event);
if (mouse_event->button() ==
static_cast<short>(WebPointerProperties::Button::kBack)) {
if (LocalFrame* frame = GetDocument().GetFrame()) {
if (frame->Client()->NavigateBackForward(-1))
event->SetDefaultHandled();
}
} else if (mouse_event->button() ==
static_cast<short>(WebPointerProperties::Button::kForward)) {
if (LocalFrame* frame = GetDocument().GetFrame()) {
if (frame->Client()->NavigateBackForward(1))
event->SetDefaultHandled();
}
}
}
}
......
......@@ -412,12 +412,15 @@ bool IsEnterKeyKeydownEvent(Event* event) {
}
bool IsLinkClick(Event* event) {
// Allow detail <= 1 so that synthetic clicks work. They may have detail == 0.
return (event->type() == EventTypeNames::click ||
event->type() == EventTypeNames::auxclick) &&
(!event->IsMouseEvent() ||
ToMouseEvent(event)->button() !=
static_cast<short>(WebPointerProperties::Button::kRight));
if ((event->type() != EventTypeNames::click &&
event->type() != EventTypeNames::auxclick) ||
!event->IsMouseEvent()) {
return false;
}
MouseEvent* mouse_event = ToMouseEvent(event);
short button = mouse_event->button();
return (button == static_cast<short>(WebPointerProperties::Button::kLeft) ||
button == static_cast<short>(WebPointerProperties::Button::kMiddle));
}
bool HTMLAnchorElement::WillRespondToMouseClickEvents() {
......
......@@ -887,4 +887,94 @@ TEST_F(EventHandlerLatencyTest, NeedsUnbufferedInput) {
ASSERT_FALSE(chrome_client_->ReceivedRequestForUnbufferedInput());
}
class NavigationCapturingFrameClient : public EmptyLocalFrameClient {
public:
NavigationCapturingFrameClient() {}
bool NavigateBackForward(int offset) const override {
offset_ = offset;
return true;
}
int Offset() const { return offset_; }
private:
mutable int offset_ = 0;
};
class EventHandlerNavigationTest : public EventHandlerTest {
public:
EventHandlerNavigationTest() {}
void SetUp() override {
frame_client_ = new NavigationCapturingFrameClient();
Page::PageClients clients;
FillWithEmptyClients(clients);
SetupPageWithClients(&clients, frame_client_);
}
int Offset() { return frame_client_->Offset(); }
private:
Persistent<NavigationCapturingFrameClient> frame_client_;
};
TEST_F(EventHandlerNavigationTest, MouseButtonsNavigate) {
SetHtmlInnerHTML("<div>");
EXPECT_EQ(0, Offset());
WebMouseEvent mouse_back_event(
WebInputEvent::kMouseUp, WebFloatPoint(51, 50), WebFloatPoint(51, 50),
WebPointerProperties::Button::kBack, 0, WebInputEvent::kNoModifiers,
TimeTicks::Now().InSeconds());
mouse_back_event.SetFrameScale(1);
GetDocument().GetFrame()->GetEventHandler().HandleMouseReleaseEvent(
mouse_back_event);
EXPECT_EQ(-1, Offset());
WebMouseEvent mouse_forward_event(
WebInputEvent::kMouseUp, WebFloatPoint(51, 50), WebFloatPoint(51, 50),
WebPointerProperties::Button::kForward, 0, WebInputEvent::kNoModifiers,
TimeTicks::Now().InSeconds());
mouse_forward_event.SetFrameScale(1);
GetDocument().GetFrame()->GetEventHandler().HandleMouseReleaseEvent(
mouse_forward_event);
EXPECT_EQ(1, Offset());
}
TEST_F(EventHandlerNavigationTest, MouseButtonsDontNavigate) {
SetHtmlInnerHTML("<div>");
GetDocument().GetSettings()->SetScriptEnabled(true);
Element* script = GetDocument().createElement("script");
script->SetInnerHTMLFromString(
"document.addEventListener('mouseup', event => "
"event.preventDefault());");
GetDocument().body()->AppendChild(script);
EXPECT_EQ(0, Offset());
WebMouseEvent mouse_back_event(
WebInputEvent::kMouseUp, WebFloatPoint(51, 50), WebFloatPoint(51, 50),
WebPointerProperties::Button::kBack, 0, WebInputEvent::kNoModifiers,
TimeTicks::Now().InSeconds());
mouse_back_event.SetFrameScale(1);
GetDocument().GetFrame()->GetEventHandler().HandleMouseReleaseEvent(
mouse_back_event);
EXPECT_EQ(0, Offset());
WebMouseEvent mouse_forward_event(
WebInputEvent::kMouseUp, WebFloatPoint(51, 50), WebFloatPoint(51, 50),
WebPointerProperties::Button::kForward, 0, WebInputEvent::kNoModifiers,
TimeTicks::Now().InSeconds());
mouse_forward_event.SetFrameScale(1);
GetDocument().GetFrame()->GetEventHandler().HandleMouseReleaseEvent(
mouse_forward_event);
EXPECT_EQ(0, Offset());
}
} // namespace blink
......@@ -78,6 +78,14 @@ WebMouseEvent WebMouseEventBuilder::Build(
type = WebInputEvent::kMouseDown;
button = WebMouseEvent::Button::kRight;
break;
case WM_XBUTTONDOWN:
case WM_XBUTTONDBLCLK:
type = WebInputEvent::kMouseDown;
if ((HIWORD(wparam) & XBUTTON1))
button = WebMouseEvent::Button::kBack;
else if ((HIWORD(wparam) & XBUTTON2))
button = WebMouseEvent::Button::kForward;
break;
case WM_LBUTTONUP:
type = WebInputEvent::kMouseUp;
button = WebMouseEvent::Button::kLeft;
......@@ -90,6 +98,13 @@ WebMouseEvent WebMouseEventBuilder::Build(
type = WebInputEvent::kMouseUp;
button = WebMouseEvent::Button::kRight;
break;
case WM_XBUTTONUP:
type = WebInputEvent::kMouseUp;
if ((HIWORD(wparam) & XBUTTON1))
button = WebMouseEvent::Button::kBack;
else if ((HIWORD(wparam) & XBUTTON2))
button = WebMouseEvent::Button::kForward;
break;
default:
NOTREACHED();
}
......@@ -107,6 +122,10 @@ WebMouseEvent WebMouseEventBuilder::Build(
modifiers |= WebInputEvent::kMiddleButtonDown;
if (wparam & MK_RBUTTON)
modifiers |= WebInputEvent::kRightButtonDown;
if (wparam & MK_XBUTTON1)
modifiers |= WebInputEvent::kBackButtonDown;
if (wparam & MK_XBUTTON2)
modifiers |= WebInputEvent::kForwardButtonDown;
WebMouseEvent result(type, modifiers, time_stamp);
result.pointer_type = pointer_type;
......
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