| // Copyright (c) 2015 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 "content/browser/media/capture/cursor_renderer_aura.h" |
| |
| #include <stdint.h> |
| |
| #include <memory> |
| |
| #include "base/files/file_path.h" |
| #include "base/path_service.h" |
| #include "base/time/time.h" |
| #include "media/base/video_frame.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/aura/env.h" |
| #include "ui/aura/test/aura_test_base.h" |
| #include "ui/aura/test/test_windows.h" |
| #include "ui/aura/window.h" |
| #include "ui/base/resource/resource_bundle.h" |
| #include "ui/events/event.h" |
| #include "ui/events/event_utils.h" |
| #include "ui/wm/core/default_activation_client.h" |
| #include "ui/wm/core/default_screen_position_client.h" |
| #include "ui/wm/core/window_util.h" |
| |
| namespace content { |
| |
| using aura::test::AuraTestBase; |
| |
| class CursorRendererAuraTest : public AuraTestBase { |
| public: |
| CursorRendererAuraTest() {} |
| ~CursorRendererAuraTest() override {} |
| |
| void SetUp() override { |
| AuraTestBase::SetUp(); |
| // This is needed to avoid duplicate initialization across tests that leads |
| // to a failure. |
| if (!ui::ResourceBundle::HasSharedInstance()) { |
| // Initialize the shared global resource bundle that has bitmap |
| // resources needed by CursorRenderer |
| base::FilePath pak_file; |
| bool r = PathService::Get(base::DIR_MODULE, &pak_file); |
| DCHECK(r); |
| pak_file = pak_file.Append(FILE_PATH_LITERAL("content_shell.pak")); |
| ui::ResourceBundle::InitSharedInstanceWithPakPath(pak_file); |
| } |
| |
| window_.reset(aura::test::CreateTestWindowWithBounds( |
| gfx::Rect(0, 0, 800, 600), root_window())); |
| cursor_renderer_.reset(new CursorRendererAura( |
| CursorRenderer::CURSOR_DISPLAYED_ON_MOUSE_MOVEMENT)); |
| cursor_renderer_->SetTargetView(window_.get()); |
| new wm::DefaultActivationClient(root_window()); |
| aura::client::SetScreenPositionClient(root_window(), |
| &screen_position_client_); |
| wm::ActivateWindow(window_.get()); |
| } |
| |
| void TearDown() override { |
| aura::client::SetScreenPositionClient(root_window(), nullptr); |
| cursor_renderer_.reset(); |
| window_.reset(); |
| AuraTestBase::TearDown(); |
| } |
| |
| base::TimeTicks Now() { return base::TimeTicks::Now(); } |
| |
| bool CursorDisplayed() { |
| // Request rendering into a dummy video frame. If RenderCursorOnVideoFrame() |
| // returns true, then the cursor is being displayed. |
| if (!dummy_frame_) { |
| constexpr gfx::Size dummy_frame_size = gfx::Size(320, 200); |
| dummy_frame_ = media::VideoFrame::CreateZeroInitializedFrame( |
| media::PIXEL_FORMAT_I420, dummy_frame_size, |
| gfx::Rect(dummy_frame_size), dummy_frame_size, base::TimeDelta()); |
| } |
| return RenderCursorOnVideoFrame(dummy_frame_.get(), nullptr); |
| } |
| |
| bool RenderCursorOnVideoFrame(media::VideoFrame* frame, |
| CursorRendererUndoer* undoer) { |
| return cursor_renderer_->RenderOnVideoFrame(frame, frame->visible_rect(), |
| undoer); |
| } |
| |
| bool IsUserInteractingWithView() { |
| return cursor_renderer_->IsUserInteractingWithView(); |
| } |
| |
| void MoveMouseCursorWithinWindow() { |
| gfx::Point point1(20, 20); |
| ui::MouseEvent event1(ui::ET_MOUSE_MOVED, point1, point1, Now(), 0, 0); |
| aura::Env::GetInstance()->set_last_mouse_location(point1); |
| cursor_renderer_->OnMouseEvent(&event1); |
| gfx::Point point2(60, 60); |
| ui::MouseEvent event2(ui::ET_MOUSE_MOVED, point2, point2, Now(), 0, 0); |
| aura::Env::GetInstance()->set_last_mouse_location(point2); |
| cursor_renderer_->OnMouseEvent(&event2); |
| } |
| |
| void MoveMouseCursorOutsideWindow() { |
| gfx::Point point(1000, 1000); |
| ui::MouseEvent event1(ui::ET_MOUSE_MOVED, point, point, Now(), 0, 0); |
| aura::Env::GetInstance()->set_last_mouse_location(point); |
| cursor_renderer_->OnMouseEvent(&event1); |
| } |
| |
| void SimulateMouseWentIdle() { |
| EXPECT_TRUE(cursor_renderer_->mouse_activity_ended_timer_.IsRunning()); |
| cursor_renderer_->mouse_activity_ended_timer_.Stop(); |
| cursor_renderer_->OnMouseHasGoneIdle(); |
| } |
| |
| // A very simple test of whether there are any non-zero pixels |
| // in the region |rect| within |frame|. |
| bool NonZeroPixelsInRegion(scoped_refptr<media::VideoFrame> frame, |
| gfx::Rect rect) { |
| bool y_found = false, u_found = false, v_found = false; |
| for (int y = rect.y(); y < rect.bottom(); ++y) { |
| uint8_t* yplane = frame->visible_data(media::VideoFrame::kYPlane) + |
| y * frame->stride(media::VideoFrame::kYPlane); |
| uint8_t* uplane = frame->visible_data(media::VideoFrame::kUPlane) + |
| (y / 2) * frame->stride(media::VideoFrame::kUPlane); |
| uint8_t* vplane = frame->visible_data(media::VideoFrame::kVPlane) + |
| (y / 2) * frame->stride(media::VideoFrame::kVPlane); |
| for (int x = rect.x(); x < rect.right(); ++x) { |
| if (yplane[x] != 0) |
| y_found = true; |
| if (uplane[x / 2]) |
| u_found = true; |
| if (vplane[x / 2]) |
| v_found = true; |
| } |
| } |
| return (y_found && u_found && v_found); |
| } |
| |
| protected: |
| wm::DefaultScreenPositionClient screen_position_client_; |
| std::unique_ptr<aura::Window> window_; |
| std::unique_ptr<CursorRendererAura> cursor_renderer_; |
| |
| scoped_refptr<media::VideoFrame> dummy_frame_; |
| }; |
| |
| TEST_F(CursorRendererAuraTest, CursorAlwaysDisplayed) { |
| // Set up cursor renderer to always display cursor. |
| cursor_renderer_.reset( |
| new CursorRendererAura(CursorRenderer::CURSOR_DISPLAYED_ALWAYS)); |
| cursor_renderer_->SetTargetView(window_.get()); |
| |
| // Cursor displayed at start. |
| EXPECT_TRUE(CursorDisplayed()); |
| EXPECT_FALSE(IsUserInteractingWithView()); |
| |
| // Cursor displayed after mouse movement. |
| MoveMouseCursorWithinWindow(); |
| EXPECT_TRUE(CursorDisplayed()); |
| EXPECT_TRUE(IsUserInteractingWithView()); |
| |
| // Cursor displayed after idle period. |
| SimulateMouseWentIdle(); |
| EXPECT_TRUE(CursorDisplayed()); |
| EXPECT_FALSE(IsUserInteractingWithView()); |
| |
| // Cursor not displayed with mouse outside the window. |
| MoveMouseCursorOutsideWindow(); |
| EXPECT_FALSE(CursorDisplayed()); |
| } |
| |
| TEST_F(CursorRendererAuraTest, CursorDuringMouseMovement) { |
| // Cursor not displayed at start. |
| EXPECT_FALSE(CursorDisplayed()); |
| EXPECT_FALSE(IsUserInteractingWithView()); |
| |
| // Cursor displayed after mouse movement. |
| MoveMouseCursorWithinWindow(); |
| EXPECT_TRUE(CursorDisplayed()); |
| EXPECT_TRUE(IsUserInteractingWithView()); |
| |
| // Cursor not displayed after idle period. |
| SimulateMouseWentIdle(); |
| EXPECT_FALSE(CursorDisplayed()); |
| EXPECT_FALSE(IsUserInteractingWithView()); |
| |
| // Cursor displayed with mouse movement following idle period. |
| MoveMouseCursorWithinWindow(); |
| EXPECT_TRUE(CursorDisplayed()); |
| EXPECT_TRUE(IsUserInteractingWithView()); |
| |
| // Cursor not displayed if mouse outside the window |
| MoveMouseCursorOutsideWindow(); |
| EXPECT_FALSE(CursorDisplayed()); |
| } |
| |
| TEST_F(CursorRendererAuraTest, CursorOnActiveWindow) { |
| // Cursor not displayed at start. |
| EXPECT_FALSE(CursorDisplayed()); |
| EXPECT_FALSE(IsUserInteractingWithView()); |
| |
| // Cursor displayed after mouse movement. |
| MoveMouseCursorWithinWindow(); |
| EXPECT_TRUE(CursorDisplayed()); |
| EXPECT_TRUE(IsUserInteractingWithView()); |
| |
| // Cursor not be displayed if a second window is activated. |
| std::unique_ptr<aura::Window> window2(aura::test::CreateTestWindowWithBounds( |
| gfx::Rect(0, 0, 800, 600), root_window())); |
| wm::ActivateWindow(window2.get()); |
| MoveMouseCursorWithinWindow(); |
| EXPECT_FALSE(CursorDisplayed()); |
| EXPECT_TRUE(IsUserInteractingWithView()); |
| |
| // Cursor displayed if window activated again. |
| wm::ActivateWindow(window_.get()); |
| MoveMouseCursorWithinWindow(); |
| EXPECT_TRUE(CursorDisplayed()); |
| EXPECT_TRUE(IsUserInteractingWithView()); |
| } |
| |
| TEST_F(CursorRendererAuraTest, CursorRenderedOnFrame) { |
| // Cursor not displayed at start. |
| EXPECT_FALSE(CursorDisplayed()); |
| |
| gfx::Size size(800, 600); |
| scoped_refptr<media::VideoFrame> frame = |
| media::VideoFrame::CreateZeroInitializedFrame(media::PIXEL_FORMAT_I420, |
| size, gfx::Rect(size), size, |
| base::TimeDelta()); |
| |
| MoveMouseCursorWithinWindow(); |
| EXPECT_TRUE(CursorDisplayed()); |
| |
| EXPECT_FALSE(NonZeroPixelsInRegion(frame, frame->visible_rect())); |
| CursorRendererUndoer undoer; |
| EXPECT_TRUE(RenderCursorOnVideoFrame(frame.get(), &undoer)); |
| EXPECT_TRUE(NonZeroPixelsInRegion(frame, gfx::Rect(50, 50, 70, 70))); |
| undoer.Undo(frame.get()); |
| EXPECT_FALSE(NonZeroPixelsInRegion(frame, frame->visible_rect())); |
| } |
| |
| TEST_F(CursorRendererAuraTest, CursorRenderedOnRootWindow) { |
| cursor_renderer_->SetTargetView(root_window()); |
| |
| // Cursor not displayed at start. |
| EXPECT_FALSE(CursorDisplayed()); |
| |
| // Cursor displayed after mouse movement. |
| MoveMouseCursorWithinWindow(); |
| EXPECT_TRUE(CursorDisplayed()); |
| |
| // Cursor being displayed even if another window is activated. |
| std::unique_ptr<aura::Window> window2(aura::test::CreateTestWindowWithBounds( |
| gfx::Rect(0, 0, 800, 600), root_window())); |
| wm::ActivateWindow(window2.get()); |
| EXPECT_TRUE(CursorDisplayed()); |
| } |
| |
| } // namespace content |