// Copyright 2018 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "ui/base/win/direct_manipulation.h" #include #include "base/macros.h" #include "base/test/scoped_feature_list.h" #include "base/win/windows_version.h" #include "content/browser/renderer_host/legacy_render_widget_host_win.h" #include "content/browser/renderer_host/render_widget_host_view_aura.h" #include "content/browser/web_contents/web_contents_impl.h" #include "content/public/test/content_browser_test.h" #include "content/public/test/content_browser_test_utils.h" #include "content/shell/browser/shell.h" #include "ui/aura/window.h" #include "ui/aura/window_tree_host.h" #include "ui/base/ui_base_features.h" #include "ui/base/win/window_event_target.h" #include "ui/events/event_rewriter.h" #include "ui/events/event_source.h" #include "url/gurl.h" namespace content { class DirectManipulationBrowserTest : public ContentBrowserTest, public testing::WithParamInterface { public: DirectManipulationBrowserTest() { if (GetParam()) { scoped_feature_list_.InitWithFeatures( {features::kPrecisionTouchpad, features::kPrecisionTouchpadScrollPhase}, {}); } else { scoped_feature_list_.InitWithFeatures( {features::kPrecisionTouchpad}, {features::kPrecisionTouchpadScrollPhase}); } } ~DirectManipulationBrowserTest() override {} LegacyRenderWidgetHostHWND* GetLegacyRenderWidgetHostHWND() { RenderWidgetHostViewAura* rwhva = static_cast( shell()->web_contents()->GetRenderWidgetHostView()); return rwhva->legacy_render_widget_host_HWND_; } HWND GetSubWindowHWND() { LegacyRenderWidgetHostHWND* lrwhh = GetLegacyRenderWidgetHostHWND(); return lrwhh->hwnd(); } ui::WindowEventTarget* GetWindowEventTarget() { LegacyRenderWidgetHostHWND* lrwhh = GetLegacyRenderWidgetHostHWND(); return lrwhh->GetWindowEventTarget(lrwhh->GetParent()); } void SimulatePointerHitTest() { LegacyRenderWidgetHostHWND* lrwhh = GetLegacyRenderWidgetHostHWND(); lrwhh->direct_manipulation_helper_->need_poll_events_ = true; lrwhh->CreateAnimationObserver(); } void UpdateParent(HWND hwnd) { LegacyRenderWidgetHostHWND* lrwhh = GetLegacyRenderWidgetHostHWND(); lrwhh->UpdateParent(hwnd); } bool HasCompositorAnimationObserver(LegacyRenderWidgetHostHWND* lrwhh) { return lrwhh->compositor_animation_observer_ != nullptr; } private: base::test::ScopedFeatureList scoped_feature_list_; DISALLOW_COPY_AND_ASSIGN(DirectManipulationBrowserTest); }; INSTANTIATE_TEST_CASE_P(WithScrollEventPhase, DirectManipulationBrowserTest, testing::Bool()); // Ensure the AnimationObserver destroy when hwnd reparent to other hwnd. IN_PROC_BROWSER_TEST_P(DirectManipulationBrowserTest, HWNDReparent) { if (base::win::GetVersion() < base::win::VERSION_WIN10) return; NavigateToURL(shell(), GURL(url::kAboutBlankURL)); LegacyRenderWidgetHostHWND* lrwhh = GetLegacyRenderWidgetHostHWND(); ASSERT_TRUE(lrwhh); // The observer should not create before it needed. ASSERT_TRUE(!HasCompositorAnimationObserver(lrwhh)); // Add AnimationObserver to tab to simulate direct manipulation start. SimulatePointerHitTest(); ASSERT_TRUE(HasCompositorAnimationObserver(lrwhh)); // Create another browser. Shell* shell2 = CreateBrowser(); NavigateToURL(shell2, GURL(url::kAboutBlankURL)); // Move to the tab to browser2. UpdateParent( shell2->window()->GetRootWindow()->GetHost()->GetAcceleratedWidget()); // The animation observer should be removed. EXPECT_FALSE(HasCompositorAnimationObserver(lrwhh)); shell2->Close(); } // EventLogger is to obserser the events sent from WindowEventTarget (the root // window). class EventLogger : public ui::EventRewriter { public: EventLogger() {} ~EventLogger() override {} std::unique_ptr ReleaseLastEvent() { return std::move(last_event_); } private: // ui::EventRewriter ui::EventRewriteStatus RewriteEvent( const ui::Event& event, std::unique_ptr* new_event) override { DCHECK(!last_event_); last_event_ = ui::Event::Clone(event); return ui::EVENT_REWRITE_CONTINUE; } // ui::EventRewriter ui::EventRewriteStatus NextDispatchEvent( const ui::Event& last_event, std::unique_ptr* new_event) override { return ui::EVENT_REWRITE_CONTINUE; } std::unique_ptr last_event_ = nullptr; DISALLOW_COPY_AND_ASSIGN(EventLogger); }; // Check DirectManipulation events convert to ui::event correctly. IN_PROC_BROWSER_TEST_P(DirectManipulationBrowserTest, EventConvert) { if (base::win::GetVersion() < base::win::VERSION_WIN10) return; NavigateToURL(shell(), GURL(url::kAboutBlankURL)); LegacyRenderWidgetHostHWND* lrwhh = GetLegacyRenderWidgetHostHWND(); ASSERT_TRUE(lrwhh); HWND hwnd = shell()->window()->GetRootWindow()->GetHost()->GetAcceleratedWidget(); ui::EventSource* dwthw = static_cast( aura::WindowTreeHost::GetForAcceleratedWidget(hwnd)); EventLogger event_logger; dwthw->AddEventRewriter(&event_logger); ui::WindowEventTarget* target = GetWindowEventTarget(); { target->ApplyPanGestureScroll(1, 2); std::unique_ptr event = event_logger.ReleaseLastEvent(); ASSERT_TRUE(event); if (GetParam()) { EXPECT_EQ(ui::ET_SCROLL, event->type()); ui::ScrollEvent* scroll_event = event->AsScrollEvent(); EXPECT_EQ(1, scroll_event->x_offset()); EXPECT_EQ(2, scroll_event->y_offset()); EXPECT_EQ(ui::EventMomentumPhase::NONE, scroll_event->momentum_phase()); EXPECT_EQ(ui::ScrollEventPhase::kUpdate, scroll_event->scroll_event_phase()); } else { EXPECT_EQ(ui::ET_MOUSEWHEEL, event->type()); ui::MouseWheelEvent* wheel_event = event->AsMouseWheelEvent(); EXPECT_EQ(1, wheel_event->x_offset()); EXPECT_EQ(2, wheel_event->y_offset()); EXPECT_TRUE(wheel_event->flags() & ui::EF_PRECISION_SCROLLING_DELTA); } } { target->ApplyPanGestureFling(1, 2); std::unique_ptr event = event_logger.ReleaseLastEvent(); ASSERT_TRUE(event); if (GetParam()) { EXPECT_EQ(ui::ET_SCROLL, event->type()); ui::ScrollEvent* scroll_event = event->AsScrollEvent(); EXPECT_EQ(1, scroll_event->x_offset()); EXPECT_EQ(2, scroll_event->y_offset()); EXPECT_EQ(ui::EventMomentumPhase::INERTIAL_UPDATE, scroll_event->momentum_phase()); EXPECT_EQ(ui::ScrollEventPhase::kNone, scroll_event->scroll_event_phase()); } else { EXPECT_EQ(ui::ET_MOUSEWHEEL, event->type()); ui::MouseWheelEvent* wheel_event = event->AsMouseWheelEvent(); EXPECT_EQ(1, wheel_event->x_offset()); EXPECT_EQ(2, wheel_event->y_offset()); EXPECT_TRUE(wheel_event->flags() & ui::EF_PRECISION_SCROLLING_DELTA); } } { target->ApplyPanGestureScrollBegin(1, 2); std::unique_ptr event = event_logger.ReleaseLastEvent(); if (GetParam()) { ASSERT_TRUE(event); EXPECT_EQ(ui::ET_SCROLL, event->type()); ui::ScrollEvent* scroll_event = event->AsScrollEvent(); EXPECT_EQ(1, scroll_event->x_offset()); EXPECT_EQ(2, scroll_event->y_offset()); EXPECT_EQ(ui::EventMomentumPhase::NONE, scroll_event->momentum_phase()); EXPECT_EQ(ui::ScrollEventPhase::kBegan, scroll_event->scroll_event_phase()); } else { EXPECT_EQ(ui::ET_MOUSEWHEEL, event->type()); ui::MouseWheelEvent* wheel_event = event->AsMouseWheelEvent(); EXPECT_EQ(1, wheel_event->x_offset()); EXPECT_EQ(2, wheel_event->y_offset()); EXPECT_TRUE(wheel_event->flags() & ui::EF_PRECISION_SCROLLING_DELTA); } } { target->ApplyPanGestureScrollEnd(); std::unique_ptr event = event_logger.ReleaseLastEvent(); if (GetParam()) { ASSERT_TRUE(event); EXPECT_EQ(ui::ET_SCROLL, event->type()); ui::ScrollEvent* scroll_event = event->AsScrollEvent(); EXPECT_EQ(0, scroll_event->x_offset()); EXPECT_EQ(0, scroll_event->y_offset()); EXPECT_EQ(ui::EventMomentumPhase::NONE, scroll_event->momentum_phase()); EXPECT_EQ(ui::ScrollEventPhase::kEnd, scroll_event->scroll_event_phase()); } else { ASSERT_FALSE(event); } } { target->ApplyPanGestureFlingBegin(); std::unique_ptr event = event_logger.ReleaseLastEvent(); if (GetParam()) { ASSERT_TRUE(event); EXPECT_EQ(ui::ET_SCROLL, event->type()); ui::ScrollEvent* scroll_event = event->AsScrollEvent(); EXPECT_EQ(0, scroll_event->x_offset()); EXPECT_EQ(0, scroll_event->y_offset()); EXPECT_EQ(ui::EventMomentumPhase::BEGAN, scroll_event->momentum_phase()); EXPECT_EQ(ui::ScrollEventPhase::kNone, scroll_event->scroll_event_phase()); } else { ASSERT_FALSE(event); } } { target->ApplyPanGestureFlingEnd(); std::unique_ptr event = event_logger.ReleaseLastEvent(); if (GetParam()) { ASSERT_TRUE(event); EXPECT_EQ(ui::ET_SCROLL, event->type()); ui::ScrollEvent* scroll_event = event->AsScrollEvent(); EXPECT_EQ(0, scroll_event->x_offset()); EXPECT_EQ(0, scroll_event->y_offset()); EXPECT_EQ(ui::EventMomentumPhase::END, scroll_event->momentum_phase()); EXPECT_EQ(ui::ScrollEventPhase::kNone, scroll_event->scroll_event_phase()); } else { ASSERT_FALSE(event); } } { target->ApplyPinchZoomBegin(); std::unique_ptr event = event_logger.ReleaseLastEvent(); ASSERT_TRUE(event); EXPECT_EQ(ui::ET_GESTURE_PINCH_BEGIN, event->type()); ui::GestureEvent* gesture_event = event->AsGestureEvent(); EXPECT_EQ(ui::GestureDeviceType::DEVICE_TOUCHPAD, gesture_event->details().device_type()); } { target->ApplyPinchZoomScale(1.1f); std::unique_ptr event = event_logger.ReleaseLastEvent(); ASSERT_TRUE(event); EXPECT_EQ(ui::ET_GESTURE_PINCH_UPDATE, event->type()); ui::GestureEvent* gesture_event = event->AsGestureEvent(); EXPECT_EQ(ui::GestureDeviceType::DEVICE_TOUCHPAD, gesture_event->details().device_type()); EXPECT_EQ(1.1f, gesture_event->details().scale()); } { target->ApplyPinchZoomEnd(); std::unique_ptr event = event_logger.ReleaseLastEvent(); ASSERT_TRUE(event); EXPECT_EQ(ui::ET_GESTURE_PINCH_END, event->type()); ui::GestureEvent* gesture_event = event->AsGestureEvent(); EXPECT_EQ(ui::GestureDeviceType::DEVICE_TOUCHPAD, gesture_event->details().device_type()); } dwthw->RemoveEventRewriter(&event_logger); } } // namespace content