 Chromium Code Reviews
 Chromium Code Reviews Issue 2902303002:
  phase based wheel scroll latching for mac  (Closed)
    
  
    Issue 2902303002:
  phase based wheel scroll latching for mac  (Closed) 
  | Index: content/browser/renderer_host/render_widget_host_view_mac_unittest.mm | 
| diff --git a/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm b/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm | 
| index e482c755c8966ead410ed6881e340c63afa52a83..17e4afa5ece25b4ee5716817fc0d72eb78b878c8 100644 | 
| --- a/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm | 
| +++ b/content/browser/renderer_host/render_widget_host_view_mac_unittest.mm | 
| @@ -16,7 +16,9 @@ | 
| #include "base/run_loop.h" | 
| #include "base/strings/utf_string_conversions.h" | 
| #include "base/test/histogram_tester.h" | 
| +#include "base/test/scoped_feature_list.h" | 
| #include "base/test/simple_test_tick_clock.h" | 
| +#include "base/threading/thread_task_runner_handle.h" | 
| #include "content/browser/browser_thread_impl.h" | 
| #include "content/browser/compositor/test/no_transport_image_transport_factory.h" | 
| #include "content/browser/frame_host/render_widget_host_view_guest.h" | 
| @@ -28,6 +30,7 @@ | 
| #include "content/common/view_messages.h" | 
| #include "content/public/browser/notification_types.h" | 
| #include "content/public/browser/render_widget_host_view_mac_delegate.h" | 
| +#include "content/public/common/content_features.h" | 
| #include "content/public/common/content_switches.h" | 
| #include "content/public/test/mock_render_process_host.h" | 
| #include "content/public/test/test_browser_context.h" | 
| @@ -52,6 +55,7 @@ | 
| @interface MockPhaseMethods : NSObject { | 
| } | 
| +- (NSEventPhase)phaseNone; | 
| - (NSEventPhase)phaseBegan; | 
| - (NSEventPhase)phaseChanged; | 
| - (NSEventPhase)phaseEnded; | 
| @@ -59,6 +63,9 @@ | 
| @implementation MockPhaseMethods | 
| +- (NSEventPhase)phaseNone { | 
| + return NSEventPhaseNone; | 
| +} | 
| - (NSEventPhase)phaseBegan { | 
| return NSEventPhaseBegan; | 
| } | 
| @@ -297,6 +304,26 @@ NSEvent* MockScrollWheelEventWithPhase(SEL mockPhaseSelector, int32_t delta) { | 
| return event; | 
| } | 
| +NSEvent* MockScrollWheelEventWithMomentumPhase(SEL mockPhaseSelector, | 
| + int32_t delta) { | 
| + // Create a dum event with phaseNone. This is for resetting the phase info of | 
| 
tdresser
2017/05/25 15:18:37
dum -> dummy?
 
sahel
2017/05/25 16:00:04
Done.
 | 
| + // CGEventRef. | 
| + MockScrollWheelEventWithPhase(@selector(phaseNone), 0); | 
| + CGEventRef cg_event1 = CGEventCreateScrollWheelEvent( | 
| 
tdresser
2017/05/25 15:18:36
Why event1 instead of just event? (and below with
 
sahel
2017/05/25 16:00:04
Done.
 | 
| + nullptr, kCGScrollEventUnitLine, 1, delta, 0); | 
| + CGEventTimestamp timestamp = 0; | 
| + CGEventSetTimestamp(cg_event1, timestamp); | 
| + // CGEventSetIntegerValueField(cg_event1, kCGScrollWheelEventMomentumPhase, | 
| + // cg_momentum_phase); | 
| 
tdresser
2017/05/25 15:18:36
Should these lines be removed?
 
sahel
2017/05/25 16:00:04
Done, I am sorry for the sloppy unittest file, I d
 | 
| + // CGEventSetFlags(cg_event1, static_cast<CGEventFlags>(0)); | 
| + NSEvent* event1 = [NSEvent eventWithCGEvent:cg_event1]; | 
| + CFRelease(cg_event1); | 
| + method_setImplementation( | 
| + class_getInstanceMethod([NSEvent class], @selector(momentumPhase)), | 
| + [MockPhaseMethods instanceMethodForSelector:mockPhaseSelector]); | 
| + return event1; | 
| +} | 
| + | 
| } // namespace | 
| class RenderWidgetHostViewMacTest : public RenderViewHostImplTestHarness { | 
| @@ -1308,6 +1335,177 @@ TEST_F(RenderWidgetHostViewMacTest, Background) { | 
| host->ShutdownAndDestroyWidget(true); | 
| } | 
| +class RenderWidgetHostViewMacWithWheelScrollLatchingEnabledTest | 
| + : public RenderWidgetHostViewMacTest { | 
| + public: | 
| + RenderWidgetHostViewMacWithWheelScrollLatchingEnabledTest() { | 
| + feature_list_.InitFromCommandLine( | 
| + features::kTouchpadAndWheelScrollLatching.name, ""); | 
| + } | 
| + | 
| + private: | 
| + base::test::ScopedFeatureList feature_list_; | 
| +}; | 
| + | 
| +TEST_F(RenderWidgetHostViewMacWithWheelScrollLatchingEnabledTest, | 
| + WheelWithPhaseEndedIsNotForwardedImmediately) { | 
| 
tdresser
2017/05/25 15:18:36
Can you add a comment briefly explaining why it sh
 
sahel
2017/05/25 16:00:04
Done.
 | 
| + // Initialize the view associated with a MockRenderWidgetHostImpl, rather than | 
| + // the MockRenderProcessHost that is set up by the test harness which mocks | 
| + // out |OnMessageReceived()|. | 
| + TestBrowserContext browser_context; | 
| + MockRenderProcessHost* process_host = | 
| + new MockRenderProcessHost(&browser_context); | 
| + process_host->Init(); | 
| + MockRenderWidgetHostDelegate delegate; | 
| + int32_t routing_id = process_host->GetNextRoutingID(); | 
| + MockRenderWidgetHostImpl* host = | 
| + new MockRenderWidgetHostImpl(&delegate, process_host, routing_id); | 
| + RenderWidgetHostViewMac* view = new RenderWidgetHostViewMac(host, false); | 
| + process_host->sink().ClearMessages(); | 
| + | 
| + // Send an initial wheel event for scrolling by 3 lines. | 
| + NSEvent* wheelEvent1 = | 
| + MockScrollWheelEventWithPhase(@selector(phaseBegan), 3); | 
| + [view->cocoa_view() scrollWheel:wheelEvent1]; | 
| + ASSERT_EQ(1U, process_host->sink().message_count()); | 
| + process_host->sink().ClearMessages(); | 
| + | 
| + // Indicate that the wheel event was unhandled. | 
| + InputEventAck unhandled_ack(InputEventAckSource::COMPOSITOR_THREAD, | 
| + blink::WebInputEvent::kMouseWheel, | 
| + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); | 
| + std::unique_ptr<IPC::Message> response1( | 
| + new InputHostMsg_HandleInputEvent_ACK(0, unhandled_ack)); | 
| + host->OnMessageReceived(*response1); | 
| + ASSERT_EQ(2U, process_host->sink().message_count()); | 
| + process_host->sink().ClearMessages(); | 
| + | 
| + // Send a wheel event with phaseEnded. When wheel scroll latching is enabled | 
| + // the evnet will be dropped and the mouse_wheel_end_dispatch_timer_ will | 
| 
tdresser
2017/05/25 15:18:37
evnet -> event
 
sahel
2017/05/25 16:00:04
Done.
 | 
| + // start. | 
| + NSEvent* wheelEvent2 = | 
| + MockScrollWheelEventWithPhase(@selector(phaseEnded), 0); | 
| + [view->cocoa_view() scrollWheel:wheelEvent2]; | 
| + ASSERT_EQ(0U, process_host->sink().message_count()); | 
| + DCHECK(view->mouse_wheel_end_dispatch_timer_.IsRunning()); | 
| + process_host->sink().ClearMessages(); | 
| + | 
| + host->ShutdownAndDestroyWidget(true); | 
| +} | 
| + | 
| +TEST_F(RenderWidgetHostViewMacWithWheelScrollLatchingEnabledTest, | 
| + WheelWithMomentumPhaseBeganStopsTheWheelEndDispatchTimer) { | 
| + // Initialize the view associated with a MockRenderWidgetHostImpl, rather than | 
| + // the MockRenderProcessHost that is set up by the test harness which mocks | 
| + // out |OnMessageReceived()|. | 
| + TestBrowserContext browser_context; | 
| + MockRenderProcessHost* process_host = | 
| + new MockRenderProcessHost(&browser_context); | 
| + process_host->Init(); | 
| + MockRenderWidgetHostDelegate delegate; | 
| + int32_t routing_id = process_host->GetNextRoutingID(); | 
| + MockRenderWidgetHostImpl* host = | 
| + new MockRenderWidgetHostImpl(&delegate, process_host, routing_id); | 
| + RenderWidgetHostViewMac* view = new RenderWidgetHostViewMac(host, false); | 
| + process_host->sink().ClearMessages(); | 
| + | 
| + // Send an initial wheel event for scrolling by 3 lines. | 
| + NSEvent* wheelEvent1 = | 
| + MockScrollWheelEventWithPhase(@selector(phaseBegan), 3); | 
| + [view->cocoa_view() scrollWheel:wheelEvent1]; | 
| + ASSERT_EQ(1U, process_host->sink().message_count()); | 
| + process_host->sink().ClearMessages(); | 
| + | 
| + // Indicate that the wheel event was unhandled. | 
| + InputEventAck unhandled_ack(InputEventAckSource::COMPOSITOR_THREAD, | 
| + blink::WebInputEvent::kMouseWheel, | 
| + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); | 
| + std::unique_ptr<IPC::Message> response1( | 
| + new InputHostMsg_HandleInputEvent_ACK(0, unhandled_ack)); | 
| + host->OnMessageReceived(*response1); | 
| + ASSERT_EQ(2U, process_host->sink().message_count()); | 
| + process_host->sink().ClearMessages(); | 
| + | 
| + // Send a wheel event with phaseEnded. When wheel scroll latching is enabled | 
| + // the evnet will be dropped and the mouse_wheel_end_dispatch_timer_ will | 
| 
tdresser
2017/05/25 15:18:37
evnet -> event
 
sahel
2017/05/25 16:00:04
Done.
 | 
| + // start. | 
| + NSEvent* wheelEvent2 = | 
| + MockScrollWheelEventWithPhase(@selector(phaseEnded), 0); | 
| + [view->cocoa_view() scrollWheel:wheelEvent2]; | 
| + ASSERT_EQ(0U, process_host->sink().message_count()); | 
| + DCHECK(view->mouse_wheel_end_dispatch_timer_.IsRunning()); | 
| + process_host->sink().ClearMessages(); | 
| + | 
| + // Send a wheel event with momentum phase started, this should stop the wheel | 
| + // end dispatch timer. | 
| + NSEvent* wheelEvent3 = | 
| + MockScrollWheelEventWithMomentumPhase(@selector(phaseBegan), 3); | 
| + ASSERT_TRUE(wheelEvent3); | 
| + [view->cocoa_view() scrollWheel:wheelEvent3]; | 
| + ASSERT_EQ(1U, process_host->sink().message_count()); | 
| + DCHECK(!view->mouse_wheel_end_dispatch_timer_.IsRunning()); | 
| + process_host->sink().ClearMessages(); | 
| + | 
| + host->ShutdownAndDestroyWidget(true); | 
| +} | 
| + | 
| +TEST_F(RenderWidgetHostViewMacWithWheelScrollLatchingEnabledTest, | 
| + WheelWithPhaseBeganDispatchesThePendingWheelEnd) { | 
| + // Initialize the view associated with a MockRenderWidgetHostImpl, rather than | 
| + // the MockRenderProcessHost that is set up by the test harness which mocks | 
| + // out |OnMessageReceived()|. | 
| + TestBrowserContext browser_context; | 
| + MockRenderProcessHost* process_host = | 
| + new MockRenderProcessHost(&browser_context); | 
| + process_host->Init(); | 
| + MockRenderWidgetHostDelegate delegate; | 
| + int32_t routing_id = process_host->GetNextRoutingID(); | 
| + MockRenderWidgetHostImpl* host = | 
| + new MockRenderWidgetHostImpl(&delegate, process_host, routing_id); | 
| + RenderWidgetHostViewMac* view = new RenderWidgetHostViewMac(host, false); | 
| + process_host->sink().ClearMessages(); | 
| + | 
| + // Send an initial wheel event for scrolling by 3 lines. | 
| + NSEvent* wheelEvent1 = | 
| + MockScrollWheelEventWithPhase(@selector(phaseBegan), 3); | 
| + [view->cocoa_view() scrollWheel:wheelEvent1]; | 
| + ASSERT_EQ(1U, process_host->sink().message_count()); | 
| + process_host->sink().ClearMessages(); | 
| + | 
| + // Indicate that the wheel event was unhandled. | 
| + InputEventAck unhandled_ack(InputEventAckSource::COMPOSITOR_THREAD, | 
| + blink::WebInputEvent::kMouseWheel, | 
| + INPUT_EVENT_ACK_STATE_NOT_CONSUMED); | 
| + std::unique_ptr<IPC::Message> response1( | 
| + new InputHostMsg_HandleInputEvent_ACK(0, unhandled_ack)); | 
| + host->OnMessageReceived(*response1); | 
| + ASSERT_EQ(2U, process_host->sink().message_count()); | 
| + process_host->sink().ClearMessages(); | 
| + | 
| + // Send a wheel event with phaseEnded. When wheel scroll latching is enabled | 
| + // the evnet will be dropped and the mouse_wheel_end_dispatch_timer_ will | 
| 
tdresser
2017/05/25 15:18:36
evnet -> event
 
sahel
2017/05/25 16:00:04
Done.
 | 
| + // start. | 
| + NSEvent* wheelEvent2 = | 
| + MockScrollWheelEventWithPhase(@selector(phaseEnded), 0); | 
| + [view->cocoa_view() scrollWheel:wheelEvent2]; | 
| + ASSERT_EQ(0U, process_host->sink().message_count()); | 
| + DCHECK(view->mouse_wheel_end_dispatch_timer_.IsRunning()); | 
| + process_host->sink().ClearMessages(); | 
| + | 
| + // Send a wheel event with phase started, this should stop the wheel end | 
| + // dispatch timer and dispatch the pending wheel end event for the previous | 
| + // scroll sequence. | 
| + NSEvent* wheelEvent3 = | 
| + MockScrollWheelEventWithPhase(@selector(phaseBegan), 3); | 
| + ASSERT_TRUE(wheelEvent3); | 
| + [view->cocoa_view() scrollWheel:wheelEvent3]; | 
| + ASSERT_EQ(2U, process_host->sink().message_count()); | 
| + DCHECK(!view->mouse_wheel_end_dispatch_timer_.IsRunning()); | 
| + process_host->sink().ClearMessages(); | 
| + | 
| + host->ShutdownAndDestroyWidget(true); | 
| +} | 
| + | 
| class RenderWidgetHostViewMacPinchTest : public RenderWidgetHostViewMacTest { | 
| public: | 
| RenderWidgetHostViewMacPinchTest() : process_host_(nullptr) {} |