blob: 2b73a8175cea39427645617a371a2cf9f19c1f9b [file] [log] [blame]
// Copyright 2016 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/aura/window_tree_host.h"
#include "base/containers/contains.h"
#include "base/test/bind.h"
#include "base/test/scoped_feature_list.h"
#include "build/build_config.h"
#include "ui/aura/native_window_occlusion_tracker.h"
#include "ui/aura/test/aura_test_base.h"
#include "ui/aura/test/aura_test_utils.h"
#include "ui/aura/test/test_screen.h"
#include "ui/aura/test/window_event_dispatcher_test_api.h"
#include "ui/aura/window.h"
#include "ui/aura/window_tree_host_platform.h"
#include "ui/base/ime/input_method.h"
#include "ui/base/ui_base_features.h"
#include "ui/compositor/compositor.h"
#include "ui/compositor/layer.h"
#include "ui/compositor/test/draw_waiter_for_test.h"
#include "ui/events/event_rewriter.h"
#include "ui/events/keycodes/dom/dom_code.h"
#include "ui/events/test/test_event_rewriter.h"
#include "ui/platform_window/stub/stub_window.h"
namespace aura {
namespace {
// A convenient wrapper that makes it easy to invoke this method inside an
// EXPECT_EQ statement.
gfx::Point ConvertDIPToPixels(const WindowTreeHost* host, gfx::Point point) {
host->ConvertDIPToPixels(&point);
return point;
}
// A convenient wrapper that makes it easy to invoke this method inside an
// EXPECT_EQ statement.
gfx::PointF ConvertDIPToPixels(const WindowTreeHost* host, gfx::PointF point) {
host->ConvertDIPToPixels(&point);
return point;
}
// A convenient wrapper that makes it easy to invoke this method inside an
// EXPECT_EQ statement.
gfx::Point ConvertPixelsToDIP(const WindowTreeHost* host, gfx::Point point) {
host->ConvertPixelsToDIP(&point);
return point;
}
// A convenient wrapper that makes it easy to invoke this method inside an
// EXPECT_EQ statement.
gfx::PointF ConvertPixelsToDIP(const WindowTreeHost* host, gfx::PointF point) {
host->ConvertPixelsToDIP(&point);
return point;
}
} // namespace
using WindowTreeHostTest = test::AuraTestBase;
TEST_F(WindowTreeHostTest, DPIWindowSize) {
constexpr gfx::Rect starting_bounds(
aura::test::AuraTestHelper::kDefaultHostSize);
EXPECT_EQ(starting_bounds.size(), host()->compositor()->size());
EXPECT_EQ(starting_bounds, host()->GetBoundsInPixels());
EXPECT_EQ(starting_bounds, root_window()->bounds());
test_screen()->SetDeviceScaleFactor(1.5f);
EXPECT_EQ(starting_bounds, host()->GetBoundsInPixels());
// Size should be rounded up after scaling.
EXPECT_EQ(gfx::Rect(0, 0, 534, 400), root_window()->bounds());
gfx::Transform transform;
transform.Translate(0, -1.1f);
host()->SetRootTransform(transform);
EXPECT_EQ(gfx::Rect(0, 1, 534, 401), root_window()->bounds());
EXPECT_EQ(starting_bounds, host()->GetBoundsInPixels());
EXPECT_EQ(gfx::Rect(0, 1, 534, 401), root_window()->bounds());
}
TEST_F(WindowTreeHostTest,
ShouldHaveExactRootWindowBoundsWithDisplayRotation1xScale) {
test_screen()->SetDeviceScaleFactor(1.f);
host()->SetBoundsInPixels(gfx::Rect(0, 0, 400, 300));
test_screen()->SetDisplayRotation(display::Display::ROTATE_0);
EXPECT_EQ(host()->GetBoundsInPixels(), gfx::Rect(0, 0, 400, 300));
EXPECT_EQ(display::Screen::Get()->GetPrimaryDisplay().rotation(),
display::Display::ROTATE_0);
EXPECT_EQ(display::Screen::Get()->GetPrimaryDisplay().GetSizeInPixel(),
gfx::Size(400, 300));
EXPECT_EQ(display::Screen::Get()->GetPrimaryDisplay().bounds(),
gfx::Rect(0, 0, 400, 300));
EXPECT_EQ(gfx::Rect(400, 300), host()->window()->bounds());
host()->SetBoundsInPixels(gfx::Rect(0, 0, 400, 300));
test_screen()->SetDisplayRotation(display::Display::ROTATE_90);
EXPECT_EQ(host()->GetBoundsInPixels(), gfx::Rect(0, 0, 400, 300));
EXPECT_EQ(display::Screen::Get()->GetPrimaryDisplay().rotation(),
display::Display::ROTATE_90);
EXPECT_EQ(display::Screen::Get()->GetPrimaryDisplay().GetSizeInPixel(),
gfx::Size(300, 400));
EXPECT_EQ(display::Screen::Get()->GetPrimaryDisplay().bounds(),
gfx::Rect(0, 0, 300, 400));
EXPECT_EQ(gfx::Rect(300, 400), host()->window()->bounds());
host()->SetBoundsInPixels(gfx::Rect(0, 0, 400, 300));
test_screen()->SetDisplayRotation(display::Display::ROTATE_180);
EXPECT_EQ(host()->GetBoundsInPixels(), gfx::Rect(0, 0, 400, 300));
EXPECT_EQ(display::Screen::Get()->GetPrimaryDisplay().rotation(),
display::Display::ROTATE_180);
EXPECT_EQ(display::Screen::Get()->GetPrimaryDisplay().GetSizeInPixel(),
gfx::Size(400, 300));
EXPECT_EQ(display::Screen::Get()->GetPrimaryDisplay().bounds(),
gfx::Rect(0, 0, 400, 300));
EXPECT_EQ(gfx::Rect(400, 300), host()->window()->bounds());
host()->SetBoundsInPixels(gfx::Rect(0, 0, 400, 300));
test_screen()->SetDisplayRotation(display::Display::ROTATE_270);
EXPECT_EQ(host()->GetBoundsInPixels(), gfx::Rect(0, 0, 400, 300));
EXPECT_EQ(display::Screen::Get()->GetPrimaryDisplay().rotation(),
display::Display::ROTATE_270);
EXPECT_EQ(display::Screen::Get()->GetPrimaryDisplay().GetSizeInPixel(),
gfx::Size(300, 400));
EXPECT_EQ(display::Screen::Get()->GetPrimaryDisplay().bounds(),
gfx::Rect(0, 0, 300, 400));
EXPECT_EQ(gfx::Rect(300, 400), host()->window()->bounds());
}
#if BUILDFLAG(IS_CHROMEOS)
TEST_F(WindowTreeHostTest, HoldPointerMovesOnChildResizing) {
aura::WindowEventDispatcher* dispatcher = host()->dispatcher();
aura::test::WindowEventDispatcherTestApi dispatcher_api(dispatcher);
EXPECT_FALSE(dispatcher_api.HoldingPointerMoves());
// Signal to the ui::Compositor that a child is resizing. This will
// immediately trigger input throttling.
host()->compositor()->OnChildResizing();
// Pointer moves should be throttled until the next commit. This has the
// effect of prioritizing the resize event above other operations in aura.
EXPECT_TRUE(dispatcher_api.HoldingPointerMoves());
// Wait for a CompositorFrame to be activated.
ui::DrawWaiterForTest::WaitForCompositingEnded(host()->compositor());
// Pointer moves should still be blocked until the child surface has
// activated.
EXPECT_TRUE(dispatcher_api.HoldingPointerMoves());
// Signal that the child has had its resized surface activated.
host()->compositor()->OnChildResizeActivated();
// Pointer moves should be routed normally after activation.
EXPECT_FALSE(dispatcher_api.HoldingPointerMoves());
}
#endif
#if !BUILDFLAG(IS_CHROMEOS)
// Tests if scale factor changes take effect. Previously a scale factor change
// wouldn't take effect without a bounds change. For context see
// https://crbug.com/1087626
TEST_F(WindowTreeHostTest, ShouldHandleTextScale) {
constexpr gfx::Rect starting_bounds(
aura::test::AuraTestHelper::kDefaultHostSize);
auto asserter = [&](float test_scale_factor) {
test_screen()->SetDeviceScaleFactor(test_scale_factor, false);
EXPECT_EQ(starting_bounds, host()->GetBoundsInPixels());
// Size should be rounded up after scaling.
EXPECT_EQ(
gfx::ScaleToEnclosingRect(starting_bounds, 1.0f / test_scale_factor),
root_window()->bounds());
EXPECT_EQ(test_scale_factor, host()->device_scale_factor());
};
asserter(1.0f);
asserter(1.05f);
asserter(1.5f);
}
#endif
TEST_F(WindowTreeHostTest, NoRewritesPostIME) {
ui::test::TestEventRewriter event_rewriter;
host()->AddEventRewriter(&event_rewriter);
ui::KeyEvent key_event =
ui::KeyEvent::FromCharacter('A', ui::VKEY_A, ui::DomCode::NONE, 0);
ui::EventDispatchDetails details =
host()->GetInputMethod()->DispatchKeyEvent(&key_event);
ASSERT_TRUE(!details.dispatcher_destroyed && !details.target_destroyed);
EXPECT_EQ(0, event_rewriter.events_seen());
host()->RemoveEventRewriter(&event_rewriter);
}
TEST_F(WindowTreeHostTest, ConvertDIPToPixelsShouldRespectScaleFactor) {
const int width_in_pixels = 400;
const int height_in_pixels = 300;
const int width_in_dip = 200;
const int height_in_dip = 150;
host()->SetBoundsInPixels(gfx::Rect(0, 0, width_in_pixels, height_in_pixels));
test_screen()->SetDisplayRotation(display::Display::ROTATE_0);
test_screen()->SetDeviceScaleFactor(2.f);
EXPECT_EQ(ConvertDIPToPixels(host(), gfx::Point(0, 0)), gfx::Point(0, 0));
EXPECT_EQ(ConvertDIPToPixels(host(), gfx::Point(width_in_dip, 0)),
gfx::Point(width_in_pixels, 0));
EXPECT_EQ(ConvertDIPToPixels(host(), gfx::Point(0, height_in_dip)),
gfx::Point(0, height_in_pixels));
}
TEST_F(WindowTreeHostTest, ConvertDIPToPixelsShouldRespectRotation) {
const int width_in_pixels = 400;
const int height_in_pixels = 300;
const int width_in_dip = 300;
const int height_in_dip = 400;
host()->SetBoundsInPixels(gfx::Rect(0, 0, width_in_pixels, height_in_pixels));
test_screen()->SetDeviceScaleFactor(1.f);
test_screen()->SetDisplayRotation(display::Display::ROTATE_90);
EXPECT_EQ(ConvertDIPToPixels(host(), gfx::Point(0, 0)),
gfx::Point(width_in_pixels, 0));
EXPECT_EQ(ConvertDIPToPixels(host(), gfx::Point(width_in_dip, 0)),
gfx::Point(width_in_pixels, height_in_pixels));
EXPECT_EQ(ConvertDIPToPixels(host(), gfx::Point(width_in_dip, height_in_dip)),
gfx::Point(0, height_in_pixels));
EXPECT_EQ(ConvertDIPToPixels(host(), gfx::Point(0, height_in_dip)),
gfx::Point(0, 0));
}
TEST_F(WindowTreeHostTest, ConvertDIPToPixelsShouldWorkWithPointF) {
host()->SetBoundsInPixels(gfx::Rect(0, 0, 400, 400));
test_screen()->SetDisplayRotation(display::Display::ROTATE_0);
test_screen()->SetDeviceScaleFactor(2.f);
EXPECT_EQ(ConvertDIPToPixels(host(), gfx::PointF(5.3f, 0)),
gfx::PointF(10.6f, 0));
}
TEST_F(WindowTreeHostTest, ConvertPixelsToDIPShouldRespectScaleFactor) {
const int width_in_pixels = 400;
const int height_in_pixels = 300;
const int width_in_dip = 200;
const int height_in_dip = 150;
host()->SetBoundsInPixels(gfx::Rect(0, 0, width_in_pixels, height_in_pixels));
test_screen()->SetDisplayRotation(display::Display::ROTATE_0);
test_screen()->SetDeviceScaleFactor(2.f);
EXPECT_EQ(ConvertPixelsToDIP(host(), gfx::Point(0, 0)), gfx::Point(0, 0));
EXPECT_EQ(ConvertPixelsToDIP(host(), gfx::Point(width_in_pixels, 0)),
gfx::Point(width_in_dip, 0));
EXPECT_EQ(ConvertPixelsToDIP(host(), gfx::Point(0, height_in_pixels)),
gfx::Point(0, height_in_dip));
}
TEST_F(WindowTreeHostTest, ConvertPixelsToDIPShouldRespectRotation) {
const int width_in_pixels = 400;
const int height_in_pixels = 300;
const int width_in_dip = 300;
const int height_in_dip = 400;
host()->SetBoundsInPixels(gfx::Rect(0, 0, width_in_pixels, height_in_pixels));
test_screen()->SetDeviceScaleFactor(1.f);
test_screen()->SetDisplayRotation(display::Display::ROTATE_90);
EXPECT_EQ(ConvertPixelsToDIP(host(), gfx::Point(0, 0)),
gfx::Point(0, height_in_dip));
EXPECT_EQ(ConvertPixelsToDIP(host(), gfx::Point(width_in_pixels, 0)),
gfx::Point(0, 0));
EXPECT_EQ(
ConvertPixelsToDIP(host(), gfx::Point(width_in_pixels, height_in_pixels)),
gfx::Point(width_in_dip, 0));
EXPECT_EQ(ConvertPixelsToDIP(host(), gfx::Point(0, height_in_pixels)),
gfx::Point(width_in_dip, height_in_dip));
}
TEST_F(WindowTreeHostTest, ConvertPixelsToDIPShouldWorkWithPointF) {
host()->SetBoundsInPixels(gfx::Rect(0, 0, 400, 400));
test_screen()->SetDisplayRotation(display::Display::ROTATE_0);
test_screen()->SetDeviceScaleFactor(2.f);
EXPECT_EQ(ConvertPixelsToDIP(host(), gfx::PointF(10.6f, 0)),
gfx::PointF(5.3f, 0));
}
class TestWindow : public ui::StubWindow {
public:
explicit TestWindow(ui::PlatformWindowDelegate* delegate)
: StubWindow(delegate, false, gfx::Rect(400, 600)) {}
TestWindow(const TestWindow&) = delete;
TestWindow& operator=(const TestWindow&) = delete;
~TestWindow() override {}
private:
// ui::StubWindow
void Close() override {
// It is possible for the window to receive capture-change messages during
// destruction, for example on Windows (see crbug.com/770670).
delegate()->OnLostCapture();
}
};
class TestWindowTreeHost : public WindowTreeHostPlatform {
public:
TestWindowTreeHost() {
SetPlatformWindow(std::make_unique<TestWindow>(this));
CreateCompositor();
}
TestWindowTreeHost(const TestWindowTreeHost&) = delete;
TestWindowTreeHost& operator=(const TestWindowTreeHost&) = delete;
};
TEST_F(WindowTreeHostTest, LostCaptureDuringTearDown) {
#if BUILDFLAG(IS_WIN)
base::test::ScopedFeatureList scoped_feature_list;
scoped_feature_list.InitAndDisableFeature(
features::kApplyNativeOcclusionToCompositor);
#endif
TestWindowTreeHost host;
}
#if BUILDFLAG(IS_WIN)
class WindowTreeHostWithReleaseTest : public test::AuraTestBase {
public:
// AuraTestBase:
void SetUp() override {
scoped_feature_list_.InitWithFeaturesAndParameters(
{{features::kCalculateNativeWinOcclusion, {}},
{features::kApplyNativeOcclusionToCompositor,
{{features::kApplyNativeOcclusionToCompositorType.name,
features::kApplyNativeOcclusionToCompositorTypeRelease}}},
// Disable the headless check as the bots run with CHROME_HEADLESS
// set.
{features::kAlwaysTrackNativeWindowOcclusionForTest, {}}},
{});
AuraTestBase::SetUp();
}
void TearDown() override { test::AuraTestBase::TearDown(); }
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
TEST_F(WindowTreeHostWithReleaseTest, ToggleOccluded) {
host()->Show();
// This test needs to drive native occlusion. If native occlusion is
// used, it'll conflict with this test.
NativeWindowOcclusionTracker::DisableNativeWindowOcclusionTracking(host());
ASSERT_TRUE(NativeWindowOcclusionTracker::
IsNativeWindowOcclusionTrackingAlwaysEnabled(host()));
host()->SetNativeWindowOcclusionState(Window::OcclusionState::OCCLUDED, {});
EXPECT_FALSE(host()->compositor()->IsVisible());
host()->SetNativeWindowOcclusionState(Window::OcclusionState::VISIBLE, {});
EXPECT_TRUE(host()->compositor()->IsVisible());
}
TEST_F(WindowTreeHostWithReleaseTest, ToggleHidden) {
host()->Show();
// This test needs to drive native occlusion. If native occlusion is
// used, it'll conflict with this test.
NativeWindowOcclusionTracker::DisableNativeWindowOcclusionTracking(host());
ASSERT_TRUE(NativeWindowOcclusionTracker::
IsNativeWindowOcclusionTrackingAlwaysEnabled(host()));
host()->SetNativeWindowOcclusionState(Window::OcclusionState::HIDDEN, {});
EXPECT_FALSE(host()->compositor()->IsVisible());
host()->SetNativeWindowOcclusionState(Window::OcclusionState::VISIBLE, {});
EXPECT_TRUE(host()->compositor()->IsVisible());
}
TEST_F(WindowTreeHostWithReleaseTest, VideoCaptureLockForcesVisible) {
ASSERT_TRUE(NativeWindowOcclusionTracker::
IsNativeWindowOcclusionTrackingAlwaysEnabled(host()));
// This test needs to drive native occlusion. If native occlusion is
// used, it'll conflict with this test.
NativeWindowOcclusionTracker::DisableNativeWindowOcclusionTracking(host());
host()->Show();
host()->SetNativeWindowOcclusionState(Window::OcclusionState::OCCLUDED, {});
EXPECT_FALSE(host()->compositor()->IsVisible());
std::unique_ptr<WindowTreeHost::VideoCaptureLock> lock =
host()->CreateVideoCaptureLock();
EXPECT_TRUE(host()->compositor()->IsVisible());
host()->SetNativeWindowOcclusionState(Window::OcclusionState::VISIBLE, {});
EXPECT_TRUE(host()->compositor()->IsVisible());
host()->SetNativeWindowOcclusionState(Window::OcclusionState::OCCLUDED, {});
EXPECT_TRUE(host()->compositor()->IsVisible());
lock.reset();
EXPECT_FALSE(host()->compositor()->IsVisible());
host()->SetNativeWindowOcclusionState(Window::OcclusionState::VISIBLE, {});
EXPECT_TRUE(host()->compositor()->IsVisible());
}
TEST_F(WindowTreeHostWithReleaseTest, VideoCaptureLockAffectsOcclusionState) {
ASSERT_TRUE(NativeWindowOcclusionTracker::
IsNativeWindowOcclusionTrackingAlwaysEnabled(host()));
// This test needs to drive native occlusion. If native occlusion is
// used, it'll conflict with this test.
NativeWindowOcclusionTracker::DisableNativeWindowOcclusionTracking(host());
host()->Show();
host()->SetNativeWindowOcclusionState(Window::OcclusionState::OCCLUDED, {});
EXPECT_EQ(Window::OcclusionState::OCCLUDED,
host()->GetNativeWindowOcclusionState());
EXPECT_FALSE(host()->compositor()->IsVisible());
std::unique_ptr<WindowTreeHost::VideoCaptureLock> lock =
host()->CreateVideoCaptureLock();
// VideoCaptureLock should force this to visible.
EXPECT_EQ(Window::OcclusionState::VISIBLE,
host()->GetNativeWindowOcclusionState());
EXPECT_TRUE(host()->compositor()->IsVisible());
lock.reset();
// It should return to occluded after.
EXPECT_EQ(Window::OcclusionState::OCCLUDED,
host()->GetNativeWindowOcclusionState());
EXPECT_FALSE(host()->compositor()->IsVisible());
lock = host()->CreateVideoCaptureLock();
host()->SetNativeWindowOcclusionState(Window::OcclusionState::VISIBLE, {});
EXPECT_EQ(Window::OcclusionState::VISIBLE,
host()->GetNativeWindowOcclusionState());
EXPECT_TRUE(host()->compositor()->IsVisible());
lock.reset();
// We set the native occlusion state to visible, this should persist after
// destroying the capture lock.
EXPECT_EQ(Window::OcclusionState::VISIBLE,
host()->GetNativeWindowOcclusionState());
EXPECT_TRUE(host()->compositor()->IsVisible());
// Now try the same thing for the hidden occlusion state:
host()->SetNativeWindowOcclusionState(Window::OcclusionState::HIDDEN, {});
EXPECT_EQ(Window::OcclusionState::HIDDEN,
host()->GetNativeWindowOcclusionState());
EXPECT_FALSE(host()->compositor()->IsVisible());
lock = host()->CreateVideoCaptureLock();
// VideoCaptureLock should force this to visible.
EXPECT_EQ(Window::OcclusionState::VISIBLE,
host()->GetNativeWindowOcclusionState());
EXPECT_TRUE(host()->compositor()->IsVisible());
lock.reset();
// It should return to hidden after.
EXPECT_EQ(Window::OcclusionState::HIDDEN,
host()->GetNativeWindowOcclusionState());
EXPECT_FALSE(host()->compositor()->IsVisible());
lock = host()->CreateVideoCaptureLock();
host()->SetNativeWindowOcclusionState(Window::OcclusionState::VISIBLE, {});
EXPECT_EQ(Window::OcclusionState::VISIBLE,
host()->GetNativeWindowOcclusionState());
EXPECT_TRUE(host()->compositor()->IsVisible());
lock.reset();
// We set the native occlusion state to visible, this should persist after
// destroying the capture lock.
EXPECT_EQ(Window::OcclusionState::VISIBLE,
host()->GetNativeWindowOcclusionState());
EXPECT_TRUE(host()->compositor()->IsVisible());
}
class WindowTreeHostWithThrottleTest : public test::AuraTestBase {
public:
// AuraTestBase:
void SetUp() override {
// Disable the headless check as the bots run with CHROME_HEADLESS set.
scoped_feature_list_.InitWithFeaturesAndParameters(
{
{features::kCalculateNativeWinOcclusion, {}},
{features::kApplyNativeOcclusionToCompositor,
{{features::kApplyNativeOcclusionToCompositorType.name,
features::kApplyNativeOcclusionToCompositorTypeThrottle}}},
// Disable the headless check as the bots run with CHROME_HEADLESS
// set.
{features::kAlwaysTrackNativeWindowOcclusionForTest, {}},
},
{});
AuraTestBase::SetUp();
}
void TearDown() override { test::AuraTestBase::TearDown(); }
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
TEST_F(WindowTreeHostWithThrottleTest, Basic) {
ASSERT_TRUE(NativeWindowOcclusionTracker::
IsNativeWindowOcclusionTrackingAlwaysEnabled(host()));
// This test needs to drive native occlusion. If native occlusion is
// used, it'll conflict with this test.
NativeWindowOcclusionTracker::DisableNativeWindowOcclusionTracking(host());
host()->Show();
EXPECT_TRUE(host()->compositor()->IsVisible());
EXPECT_TRUE(test::GetThrottledHosts().empty());
host()->SetNativeWindowOcclusionState(Window::OcclusionState::OCCLUDED, {});
EXPECT_TRUE(host()->compositor()->IsVisible());
EXPECT_TRUE(base::Contains(test::GetThrottledHosts(), host()));
host()->SetNativeWindowOcclusionState(Window::OcclusionState::VISIBLE, {});
EXPECT_TRUE(test::GetThrottledHosts().empty());
EXPECT_TRUE(host()->compositor()->IsVisible());
}
TEST_F(WindowTreeHostWithThrottleTest, CallHideDirectly) {
ASSERT_TRUE(NativeWindowOcclusionTracker::
IsNativeWindowOcclusionTrackingAlwaysEnabled(host()));
// This test needs to drive native occlusion. If native occlusion is
// used, it'll conflict with this test.
NativeWindowOcclusionTracker::DisableNativeWindowOcclusionTracking(host());
host()->Show();
EXPECT_TRUE(host()->compositor()->IsVisible());
EXPECT_TRUE(test::GetThrottledHosts().empty());
host()->SetNativeWindowOcclusionState(Window::OcclusionState::OCCLUDED, {});
EXPECT_TRUE(host()->compositor()->IsVisible());
EXPECT_TRUE(base::Contains(test::GetThrottledHosts(), host()));
host()->Hide();
EXPECT_TRUE(test::GetThrottledHosts().empty());
EXPECT_FALSE(host()->compositor()->IsVisible());
}
TEST_F(WindowTreeHostWithThrottleTest, VideoCaptureLockAffectsOcclusionState) {
ASSERT_TRUE(NativeWindowOcclusionTracker::
IsNativeWindowOcclusionTrackingAlwaysEnabled(host()));
// This test needs to drive native occlusion. If native occlusion is
// used, it'll conflict with this test.
NativeWindowOcclusionTracker::DisableNativeWindowOcclusionTracking(host());
host()->Show();
host()->SetNativeWindowOcclusionState(Window::OcclusionState::OCCLUDED, {});
EXPECT_EQ(Window::OcclusionState::OCCLUDED,
host()->GetNativeWindowOcclusionState());
std::unique_ptr<WindowTreeHost::VideoCaptureLock> lock =
host()->CreateVideoCaptureLock();
// VideoCaptureLock should force this to visible.
EXPECT_EQ(Window::OcclusionState::VISIBLE,
host()->GetNativeWindowOcclusionState());
lock.reset();
// It should return to occluded after.
EXPECT_EQ(Window::OcclusionState::OCCLUDED,
host()->GetNativeWindowOcclusionState());
lock = host()->CreateVideoCaptureLock();
host()->SetNativeWindowOcclusionState(Window::OcclusionState::VISIBLE, {});
EXPECT_EQ(Window::OcclusionState::VISIBLE,
host()->GetNativeWindowOcclusionState());
lock.reset();
// We set the native occlusion state to visible, this should persist after
// destroying the capture lock.
EXPECT_EQ(Window::OcclusionState::VISIBLE,
host()->GetNativeWindowOcclusionState());
// Now try the same thing for the hidden occlusion state:
host()->SetNativeWindowOcclusionState(Window::OcclusionState::HIDDEN, {});
EXPECT_EQ(Window::OcclusionState::HIDDEN,
host()->GetNativeWindowOcclusionState());
lock = host()->CreateVideoCaptureLock();
// VideoCaptureLock should force this to visible.
EXPECT_EQ(Window::OcclusionState::VISIBLE,
host()->GetNativeWindowOcclusionState());
lock.reset();
// It should return to hidden after.
EXPECT_EQ(Window::OcclusionState::HIDDEN,
host()->GetNativeWindowOcclusionState());
lock = host()->CreateVideoCaptureLock();
host()->SetNativeWindowOcclusionState(Window::OcclusionState::VISIBLE, {});
EXPECT_EQ(Window::OcclusionState::VISIBLE,
host()->GetNativeWindowOcclusionState());
lock.reset();
// We set the native occlusion state to visible, this should persist after
// destroying the capture lock.
EXPECT_EQ(Window::OcclusionState::VISIBLE,
host()->GetNativeWindowOcclusionState());
}
class WindowTreeHostWithThrottleAndReleaseTest : public test::AuraTestBase {
public:
// AuraTestBase:
void SetUp() override {
scoped_feature_list_.InitWithFeaturesAndParameters(
{
{features::kCalculateNativeWinOcclusion, {}},
{features::kApplyNativeOcclusionToCompositor,
{{features::kApplyNativeOcclusionToCompositorType.name,
features::
kApplyNativeOcclusionToCompositorTypeThrottleAndRelease}}},
// Disable the headless check as the bots run with CHROME_HEADLESS
// set.
{features::kAlwaysTrackNativeWindowOcclusionForTest, {}},
},
{});
AuraTestBase::SetUp();
}
void TearDown() override { test::AuraTestBase::TearDown(); }
private:
base::test::ScopedFeatureList scoped_feature_list_;
};
TEST_F(WindowTreeHostWithThrottleAndReleaseTest, ToggleOccluded) {
host()->Show();
// This test needs to drive native occlusion. If native occlusion is
// used, it'll conflict with this test.
NativeWindowOcclusionTracker::DisableNativeWindowOcclusionTracking(host());
ASSERT_TRUE(NativeWindowOcclusionTracker::
IsNativeWindowOcclusionTrackingAlwaysEnabled(host()));
EXPECT_TRUE(test::GetThrottledHosts().empty());
host()->SetNativeWindowOcclusionState(Window::OcclusionState::OCCLUDED, {});
EXPECT_FALSE(host()->compositor()->IsVisible());
EXPECT_TRUE(base::Contains(test::GetThrottledHosts(), host()));
host()->SetNativeWindowOcclusionState(Window::OcclusionState::VISIBLE, {});
EXPECT_TRUE(host()->compositor()->IsVisible());
EXPECT_TRUE(test::GetThrottledHosts().empty());
}
TEST_F(WindowTreeHostWithThrottleAndReleaseTest, ToggleHidden) {
host()->Show();
// This test needs to drive native occlusion. If native occlusion is
// used, it'll conflict with this test.
NativeWindowOcclusionTracker::DisableNativeWindowOcclusionTracking(host());
ASSERT_TRUE(NativeWindowOcclusionTracker::
IsNativeWindowOcclusionTrackingAlwaysEnabled(host()));
EXPECT_TRUE(test::GetThrottledHosts().empty());
host()->SetNativeWindowOcclusionState(Window::OcclusionState::HIDDEN, {});
EXPECT_FALSE(host()->compositor()->IsVisible());
EXPECT_TRUE(test::GetThrottledHosts().empty());
host()->SetNativeWindowOcclusionState(Window::OcclusionState::VISIBLE, {});
EXPECT_TRUE(host()->compositor()->IsVisible());
EXPECT_TRUE(test::GetThrottledHosts().empty());
}
TEST_F(WindowTreeHostWithThrottleAndReleaseTest, DestroyWhileThrottled) {
host()->Show();
// This test needs to drive native occlusion. If native occlusion is
// used, it'll conflict with this test.
NativeWindowOcclusionTracker::DisableNativeWindowOcclusionTracking(host());
ASSERT_TRUE(NativeWindowOcclusionTracker::
IsNativeWindowOcclusionTrackingAlwaysEnabled(host()));
EXPECT_TRUE(test::GetThrottledHosts().empty());
host()->SetNativeWindowOcclusionState(Window::OcclusionState::OCCLUDED, {});
EXPECT_FALSE(host()->compositor()->IsVisible());
EXPECT_TRUE(base::Contains(test::GetThrottledHosts(), host()));
// Expect not to crash after destroying WindowTreeHost after this.
}
TEST_F(WindowTreeHostWithThrottleAndReleaseTest,
VideoCaptureLockForcesVisible) {
ASSERT_TRUE(NativeWindowOcclusionTracker::
IsNativeWindowOcclusionTrackingAlwaysEnabled(host()));
// This test needs to drive native occlusion. If native occlusion is
// used, it'll conflict with this test.
NativeWindowOcclusionTracker::DisableNativeWindowOcclusionTracking(host());
host()->Show();
EXPECT_TRUE(test::GetThrottledHosts().empty());
host()->SetNativeWindowOcclusionState(Window::OcclusionState::OCCLUDED, {});
EXPECT_FALSE(host()->compositor()->IsVisible());
EXPECT_TRUE(base::Contains(test::GetThrottledHosts(), host()));
std::unique_ptr<WindowTreeHost::VideoCaptureLock> lock =
host()->CreateVideoCaptureLock();
EXPECT_TRUE(host()->compositor()->IsVisible());
EXPECT_TRUE(test::GetThrottledHosts().empty());
host()->SetNativeWindowOcclusionState(Window::OcclusionState::VISIBLE, {});
EXPECT_TRUE(host()->compositor()->IsVisible());
EXPECT_TRUE(test::GetThrottledHosts().empty());
host()->SetNativeWindowOcclusionState(Window::OcclusionState::OCCLUDED, {});
EXPECT_TRUE(host()->compositor()->IsVisible());
EXPECT_TRUE(test::GetThrottledHosts().empty());
lock.reset();
EXPECT_FALSE(host()->compositor()->IsVisible());
EXPECT_TRUE(base::Contains(test::GetThrottledHosts(), host()));
host()->SetNativeWindowOcclusionState(Window::OcclusionState::VISIBLE, {});
EXPECT_TRUE(host()->compositor()->IsVisible());
EXPECT_TRUE(test::GetThrottledHosts().empty());
}
TEST_F(WindowTreeHostWithThrottleAndReleaseTest,
VideoCaptureLockAffectsOcclusionState) {
ASSERT_TRUE(NativeWindowOcclusionTracker::
IsNativeWindowOcclusionTrackingAlwaysEnabled(host()));
// This test needs to drive native occlusion. If native occlusion is
// used, it'll conflict with this test.
NativeWindowOcclusionTracker::DisableNativeWindowOcclusionTracking(host());
host()->Show();
host()->SetNativeWindowOcclusionState(Window::OcclusionState::OCCLUDED, {});
EXPECT_EQ(Window::OcclusionState::OCCLUDED,
host()->GetNativeWindowOcclusionState());
EXPECT_FALSE(host()->compositor()->IsVisible());
std::unique_ptr<WindowTreeHost::VideoCaptureLock> lock =
host()->CreateVideoCaptureLock();
// VideoCaptureLock should force this to visible.
EXPECT_EQ(Window::OcclusionState::VISIBLE,
host()->GetNativeWindowOcclusionState());
EXPECT_TRUE(host()->compositor()->IsVisible());
lock.reset();
// It should return to occluded after.
EXPECT_EQ(Window::OcclusionState::OCCLUDED,
host()->GetNativeWindowOcclusionState());
EXPECT_FALSE(host()->compositor()->IsVisible());
lock = host()->CreateVideoCaptureLock();
host()->SetNativeWindowOcclusionState(Window::OcclusionState::VISIBLE, {});
EXPECT_EQ(Window::OcclusionState::VISIBLE,
host()->GetNativeWindowOcclusionState());
EXPECT_TRUE(host()->compositor()->IsVisible());
lock.reset();
// We set the native occlusion state to visible, this should persist after
// destroying the capture lock.
EXPECT_EQ(Window::OcclusionState::VISIBLE,
host()->GetNativeWindowOcclusionState());
EXPECT_TRUE(host()->compositor()->IsVisible());
// Now try the same thing for the hidden occlusion state:
host()->SetNativeWindowOcclusionState(Window::OcclusionState::HIDDEN, {});
EXPECT_EQ(Window::OcclusionState::HIDDEN,
host()->GetNativeWindowOcclusionState());
EXPECT_FALSE(host()->compositor()->IsVisible());
lock = host()->CreateVideoCaptureLock();
// VideoCaptureLock should force this to visible.
EXPECT_EQ(Window::OcclusionState::VISIBLE,
host()->GetNativeWindowOcclusionState());
EXPECT_TRUE(host()->compositor()->IsVisible());
lock.reset();
// It should return to hidden after.
EXPECT_EQ(Window::OcclusionState::HIDDEN,
host()->GetNativeWindowOcclusionState());
EXPECT_FALSE(host()->compositor()->IsVisible());
lock = host()->CreateVideoCaptureLock();
host()->SetNativeWindowOcclusionState(Window::OcclusionState::VISIBLE, {});
EXPECT_EQ(Window::OcclusionState::VISIBLE,
host()->GetNativeWindowOcclusionState());
EXPECT_TRUE(host()->compositor()->IsVisible());
lock.reset();
// We set the native occlusion state to visible, this should persist after
// destroying the capture lock.
EXPECT_EQ(Window::OcclusionState::VISIBLE,
host()->GetNativeWindowOcclusionState());
EXPECT_TRUE(host()->compositor()->IsVisible());
}
#endif // BUILDFLAG(IS_WIN)
} // namespace aura