blob: b9017fa7d5c7e4274d5d97eacf829ec6ffebb15e [file] [log] [blame]
// 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