native_window_occlusion_tracker_win_interactive_test.cc 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697
  1. // Copyright 2018 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "base/memory/raw_ptr.h"
  5. #include "ui/aura/native_window_occlusion_tracker_win.h"
  6. #include <winuser.h>
  7. #include "base/at_exit.h"
  8. #include "base/command_line.h"
  9. #include "base/feature_list.h"
  10. #include "base/run_loop.h"
  11. #include "base/task/current_thread.h"
  12. #include "base/task/thread_pool/thread_pool_instance.h"
  13. #include "base/test/bind.h"
  14. #include "base/test/scoped_feature_list.h"
  15. #include "base/win/scoped_gdi_object.h"
  16. #include "base/win/windows_version.h"
  17. #include "mojo/core/embedder/embedder.h"
  18. #include "testing/gtest/include/gtest/gtest.h"
  19. #include "ui/aura/env.h"
  20. #include "ui/aura/test/aura_test_base.h"
  21. #include "ui/aura/test/test_focus_client.h"
  22. #include "ui/aura/test/test_screen.h"
  23. #include "ui/aura/test/test_window_delegate.h"
  24. #include "ui/aura/test/test_window_parenting_client.h"
  25. #include "ui/aura/window.h"
  26. #include "ui/aura/window_observer.h"
  27. #include "ui/aura/window_occlusion_tracker.h"
  28. #include "ui/aura/window_tree_host.h"
  29. #include "ui/aura/window_tree_host_platform.h"
  30. #include "ui/base/test/ui_controls.h"
  31. #include "ui/base/ui_base_features.h"
  32. #include "ui/display/display.h"
  33. #include "ui/display/win/dpi.h"
  34. #include "ui/gfx/geometry/rect.h"
  35. #include "ui/gfx/win/singleton_hwnd.h"
  36. #include "ui/gfx/win/window_impl.h"
  37. #include "ui/gl/test/gl_surface_test_support.h"
  38. namespace aura {
  39. // This class is used to verify expectations about occlusion state changes by
  40. // adding instances of it as an observer of aura:Windows the tests create and
  41. // checking that they get the expected call(s) to OnOcclusionStateChanged.
  42. // The tests verify that the current state, when idle, is the expected state,
  43. // because the state can be VISIBLE before it reaches the expected state.
  44. class MockWindowTreeHostObserver : public WindowTreeHostObserver {
  45. public:
  46. explicit MockWindowTreeHostObserver(base::OnceClosure quit_closure)
  47. : quit_closure_(std::move(quit_closure)) {}
  48. MockWindowTreeHostObserver(const MockWindowTreeHostObserver&) = delete;
  49. MockWindowTreeHostObserver& operator=(const MockWindowTreeHostObserver&) =
  50. delete;
  51. ~MockWindowTreeHostObserver() override { EXPECT_FALSE(is_expecting_call()); }
  52. // WindowTreeHostObserver:
  53. void OnOcclusionStateChanged(WindowTreeHost* host,
  54. Window::OcclusionState new_state,
  55. const SkRegion& occluded_region) override {
  56. // Should only get notified when the occlusion state changes.
  57. EXPECT_NE(new_state, cur_state_);
  58. cur_state_ = new_state;
  59. if (expectation_ != Window::OcclusionState::UNKNOWN &&
  60. cur_state_ == expectation_) {
  61. EXPECT_FALSE(quit_closure_.is_null());
  62. std::move(quit_closure_).Run();
  63. }
  64. }
  65. void set_quit_closure(base::OnceClosure quit_closure) {
  66. quit_closure_ = std::move(quit_closure);
  67. }
  68. void set_expectation(Window::OcclusionState expectation) {
  69. expectation_ = expectation;
  70. }
  71. bool is_expecting_call() const { return expectation_ != cur_state_; }
  72. private:
  73. Window::OcclusionState expectation_ = Window::OcclusionState::UNKNOWN;
  74. Window::OcclusionState cur_state_ = Window::OcclusionState::UNKNOWN;
  75. base::OnceClosure quit_closure_;
  76. };
  77. class MockWindowObserver : public WindowObserver {
  78. public:
  79. explicit MockWindowObserver(Window* window) : window_(window) {
  80. window_->AddObserver(this);
  81. }
  82. ~MockWindowObserver() override {
  83. if (window_)
  84. window_->RemoveObserver(this);
  85. }
  86. void set_quit_closure(base::OnceClosure quit_closure) {
  87. quit_closure_ = std::move(quit_closure);
  88. }
  89. void set_expectation(Window::OcclusionState expectation) {
  90. expectation_ = expectation;
  91. }
  92. // WindowObserver:
  93. void OnWindowOcclusionChanged(Window* window) override {
  94. if (expectation_ == window->GetOcclusionState()) {
  95. ASSERT_FALSE(quit_closure_.is_null());
  96. std::move(quit_closure_).Run();
  97. }
  98. }
  99. void OnWindowDestroyed(Window* window) override {
  100. window_->RemoveObserver(this);
  101. window_ = nullptr;
  102. }
  103. private:
  104. raw_ptr<Window> window_;
  105. Window::OcclusionState expectation_ = Window::OcclusionState::UNKNOWN;
  106. base::OnceClosure quit_closure_;
  107. };
  108. // Test wrapper around native window HWND.
  109. class TestNativeWindow : public gfx::WindowImpl {
  110. public:
  111. TestNativeWindow() {}
  112. TestNativeWindow(const TestNativeWindow&) = delete;
  113. TestNativeWindow& operator=(const TestNativeWindow&) = delete;
  114. ~TestNativeWindow() override;
  115. private:
  116. // Overridden from gfx::WindowImpl:
  117. BOOL ProcessWindowMessage(HWND window,
  118. UINT message,
  119. WPARAM w_param,
  120. LPARAM l_param,
  121. LRESULT& result,
  122. DWORD msg_map_id) override {
  123. return FALSE; // Results in DefWindowProc().
  124. }
  125. };
  126. TestNativeWindow::~TestNativeWindow() {
  127. if (hwnd())
  128. DestroyWindow(hwnd());
  129. }
  130. class NativeWindowOcclusionTrackerTest : public test::AuraTestBase {
  131. public:
  132. NativeWindowOcclusionTrackerTest() {
  133. // These interactive_ui_tests are not based on browser tests which would
  134. // normally handle initializing mojo. We can safely initialize mojo at the
  135. // start of the test here since a new process is launched for each test.
  136. mojo::core::Init();
  137. }
  138. NativeWindowOcclusionTrackerTest(const NativeWindowOcclusionTrackerTest&) =
  139. delete;
  140. NativeWindowOcclusionTrackerTest& operator=(
  141. const NativeWindowOcclusionTrackerTest&) = delete;
  142. void SetUp() override {
  143. if (gl::GetGLImplementation() == gl::kGLImplementationNone)
  144. gl::GLSurfaceTestSupport::InitializeOneOff();
  145. scoped_feature_list_.InitWithFeatures(
  146. {features::kCalculateNativeWinOcclusion,
  147. features::kApplyNativeOccludedRegionToWindowTracker},
  148. {});
  149. AuraTestBase::SetUp();
  150. }
  151. void SetNativeWindowBounds(HWND hwnd, const gfx::Rect& bounds) {
  152. RECT wr = bounds.ToRECT();
  153. AdjustWindowRectEx(&wr, GetWindowLong(hwnd, GWL_STYLE), FALSE,
  154. GetWindowLong(hwnd, GWL_EXSTYLE));
  155. // Make sure to keep the window onscreen, as AdjustWindowRectEx() may have
  156. // moved part of it offscreen. But, if the original requested bounds are
  157. // offscreen, don't adjust the position.
  158. gfx::Rect window_bounds(wr);
  159. if (bounds.x() >= 0)
  160. window_bounds.set_x(std::max(0, window_bounds.x()));
  161. if (bounds.y() >= 0)
  162. window_bounds.set_y(std::max(0, window_bounds.y()));
  163. SetWindowPos(hwnd, HWND_TOP, window_bounds.x(), window_bounds.y(),
  164. window_bounds.width(), window_bounds.height(),
  165. SWP_NOREPOSITION);
  166. EXPECT_TRUE(UpdateWindow(hwnd));
  167. }
  168. HWND CreateNativeWindowWithBounds(const gfx::Rect& bounds) {
  169. std::unique_ptr<TestNativeWindow> native_win =
  170. std::make_unique<TestNativeWindow>();
  171. native_win->set_window_style(WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN);
  172. native_win->Init(nullptr, bounds);
  173. HWND hwnd = native_win->hwnd();
  174. SetNativeWindowBounds(hwnd, bounds);
  175. base::win::ScopedRegion region(CreateRectRgn(0, 0, 0, 0));
  176. if (GetWindowRgn(hwnd, region.get()) == COMPLEXREGION) {
  177. // On Windows 7, the newly created window has a complex region, which
  178. // means it will be ignored during the occlusion calculation. So, force
  179. // it to have a simple region so that we get test coverage on win 7.
  180. RECT bounding_rect;
  181. GetWindowRect(hwnd, &bounding_rect);
  182. base::win::ScopedRegion rectangular_region(
  183. CreateRectRgnIndirect(&bounding_rect));
  184. SetWindowRgn(hwnd, rectangular_region.get(), TRUE);
  185. }
  186. ShowWindow(hwnd, SW_SHOWNORMAL);
  187. EXPECT_TRUE(UpdateWindow(hwnd));
  188. native_wins_.push_back(std::move(native_win));
  189. return hwnd;
  190. }
  191. Window* CreateTrackedAuraWindowWithBounds(
  192. MockWindowTreeHostObserver* observer,
  193. const gfx::Rect& bounds) {
  194. host()->Show();
  195. host()->SetBoundsInPixels(bounds);
  196. if (observer)
  197. host()->AddObserver(observer);
  198. Window* window = CreateNormalWindow(1, host()->window(), nullptr);
  199. window->SetBounds(gfx::Rect(bounds.size()));
  200. Env::GetInstance()->GetWindowOcclusionTracker()->Track(window);
  201. return window;
  202. }
  203. int GetNumVisibleRootWindows() {
  204. return NativeWindowOcclusionTrackerWin::GetOrCreateInstance()
  205. ->num_visible_root_windows_;
  206. }
  207. void MakeFullscreen(HWND hwnd) {
  208. DWORD style = GetWindowLong(hwnd, GWL_STYLE);
  209. DWORD ex_style = GetWindowLong(hwnd, GWL_STYLE);
  210. SetWindowLong(hwnd, GWL_STYLE, style & ~(WS_CAPTION | WS_THICKFRAME));
  211. SetWindowLong(hwnd, GWL_EXSTYLE,
  212. ex_style & ~(WS_EX_DLGMODALFRAME | WS_EX_WINDOWEDGE |
  213. WS_EX_CLIENTEDGE | WS_EX_STATICEDGE));
  214. MONITORINFO monitor_info;
  215. monitor_info.cbSize = sizeof(monitor_info);
  216. GetMonitorInfo(MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST),
  217. &monitor_info);
  218. gfx::Rect window_rect(monitor_info.rcMonitor);
  219. SetWindowPos(hwnd, nullptr, window_rect.x(), window_rect.y(),
  220. window_rect.width(), window_rect.height(),
  221. SWP_FRAMECHANGED | SWP_ASYNCWINDOWPOS);
  222. }
  223. private:
  224. base::test::ScopedFeatureList scoped_feature_list_;
  225. std::vector<std::unique_ptr<TestNativeWindow>> native_wins_;
  226. };
  227. // Simple test completely covering an aura window with a native window.
  228. TEST_F(NativeWindowOcclusionTrackerTest, SimpleOcclusion) {
  229. base::RunLoop run_loop;
  230. MockWindowTreeHostObserver observer(run_loop.QuitClosure());
  231. CreateTrackedAuraWindowWithBounds(&observer, gfx::Rect(0, 0, 100, 100));
  232. observer.set_expectation(Window::OcclusionState::OCCLUDED);
  233. CreateNativeWindowWithBounds(gfx::Rect(0, 0, 100, 100));
  234. run_loop.Run();
  235. EXPECT_FALSE(observer.is_expecting_call());
  236. host()->RemoveObserver(&observer);
  237. }
  238. // Simple test partially covering an aura window with a native window.
  239. TEST_F(NativeWindowOcclusionTrackerTest, PartialOcclusion) {
  240. base::RunLoop run_loop;
  241. MockWindowTreeHostObserver observer(run_loop.QuitClosure());
  242. CreateTrackedAuraWindowWithBounds(&observer, gfx::Rect(0, 0, 100, 100));
  243. observer.set_expectation(Window::OcclusionState::VISIBLE);
  244. CreateNativeWindowWithBounds(gfx::Rect(0, 0, 50, 50));
  245. run_loop.Run();
  246. EXPECT_FALSE(observer.is_expecting_call());
  247. host()->RemoveObserver(&observer);
  248. }
  249. // Simple test that a partly off screen aura window, with the on screen part
  250. // occluded by a native window, is considered occluded.
  251. TEST_F(NativeWindowOcclusionTrackerTest, OffscreenOcclusion) {
  252. base::RunLoop run_loop;
  253. MockWindowTreeHostObserver observer(run_loop.QuitClosure());
  254. CreateTrackedAuraWindowWithBounds(&observer, gfx::Rect(0, 0, 100, 100));
  255. // Move the tracked window 50 pixels offscreen to the left.
  256. int screen_left = GetSystemMetrics(SM_XVIRTUALSCREEN);
  257. SetWindowPos(host()->GetAcceleratedWidget(), HWND_TOP, screen_left - 50, 0,
  258. 100, 100, SWP_NOZORDER | SWP_NOSIZE);
  259. // Create a native window that covers the onscreen part of the tracked window.
  260. CreateNativeWindowWithBounds(gfx::Rect(screen_left, 0, 50, 100));
  261. observer.set_expectation(Window::OcclusionState::OCCLUDED);
  262. run_loop.Run();
  263. EXPECT_FALSE(observer.is_expecting_call());
  264. host()->RemoveObserver(&observer);
  265. }
  266. // Simple test with an aura window and native window that do not overlap.
  267. TEST_F(NativeWindowOcclusionTrackerTest, SimpleVisible) {
  268. base::RunLoop run_loop;
  269. MockWindowTreeHostObserver observer(run_loop.QuitClosure());
  270. CreateTrackedAuraWindowWithBounds(&observer, gfx::Rect(0, 0, 100, 100));
  271. observer.set_expectation(Window::OcclusionState::VISIBLE);
  272. CreateNativeWindowWithBounds(gfx::Rect(200, 0, 100, 100));
  273. run_loop.Run();
  274. EXPECT_FALSE(observer.is_expecting_call());
  275. host()->RemoveObserver(&observer);
  276. }
  277. // Simple test with a minimized aura window and native window.
  278. TEST_F(NativeWindowOcclusionTrackerTest, SimpleHidden) {
  279. base::RunLoop run_loop;
  280. MockWindowTreeHostObserver observer(run_loop.QuitClosure());
  281. CreateTrackedAuraWindowWithBounds(&observer, gfx::Rect(0, 0, 100, 100));
  282. CreateNativeWindowWithBounds(gfx::Rect(200, 0, 100, 100));
  283. // Iconify the tracked aura window and check that its occlusion state
  284. // is HIDDEN.
  285. CloseWindow(host()->GetAcceleratedWidget());
  286. observer.set_expectation(Window::OcclusionState::HIDDEN);
  287. run_loop.Run();
  288. EXPECT_FALSE(observer.is_expecting_call());
  289. host()->RemoveObserver(&observer);
  290. }
  291. // Test that minimizing and restoring an app window results in the occlusion
  292. // tracker re-registering for win events and detecting that a native window
  293. // occludes the app window.
  294. TEST_F(NativeWindowOcclusionTrackerTest, OcclusionAfterVisibilityToggle) {
  295. base::RunLoop run_loop;
  296. MockWindowTreeHostObserver observer(run_loop.QuitClosure());
  297. CreateTrackedAuraWindowWithBounds(&observer, gfx::Rect(0, 0, 100, 100));
  298. observer.set_expectation(Window::OcclusionState::VISIBLE);
  299. run_loop.Run();
  300. base::RunLoop run_loop2;
  301. observer.set_expectation(Window::OcclusionState::HIDDEN);
  302. observer.set_quit_closure(run_loop2.QuitClosure());
  303. // host()->window()->Hide() is needed to generate OnWindowVisibilityChanged
  304. // notifications.
  305. host()->window()->Hide();
  306. // This makes the window iconic.
  307. ::CloseWindow(host()->GetAcceleratedWidget());
  308. run_loop2.Run();
  309. // HIDDEN state is set synchronously by OnWindowVsiblityChanged notification,
  310. // before occlusion is calculated, so the above expectation will be met w/o an
  311. // occlusion calculation.
  312. // Loop until an occlusion calculation has run with no non-hidden app windows.
  313. do {
  314. // Need to pump events in order for UpdateOcclusionState to get called, and
  315. // update the number of non hidden root windows. When that number is 0,
  316. // occlusion has been calculated with no visible root windows.
  317. base::RunLoop().RunUntilIdle();
  318. } while (GetNumVisibleRootWindows() != 0);
  319. base::RunLoop run_loop3;
  320. observer.set_expectation(Window::OcclusionState::VISIBLE);
  321. observer.set_quit_closure(run_loop3.QuitClosure());
  322. host()->window()->Show();
  323. // This opens the window made iconic above.
  324. OpenIcon(host()->GetAcceleratedWidget());
  325. run_loop3.Run();
  326. // Open a native window that occludes the visible app window.
  327. base::RunLoop run_loop4;
  328. observer.set_expectation(Window::OcclusionState::OCCLUDED);
  329. observer.set_quit_closure(run_loop4.QuitClosure());
  330. CreateNativeWindowWithBounds(gfx::Rect(0, 0, 100, 100));
  331. run_loop4.Run();
  332. EXPECT_FALSE(observer.is_expecting_call());
  333. host()->RemoveObserver(&observer);
  334. }
  335. // Test that locking the screen causes visible windows to become occluded.
  336. TEST_F(NativeWindowOcclusionTrackerTest, LockScreenVisibleOcclusion) {
  337. base::RunLoop run_loop;
  338. MockWindowTreeHostObserver observer(run_loop.QuitClosure());
  339. CreateTrackedAuraWindowWithBounds(&observer, gfx::Rect(0, 0, 100, 100));
  340. observer.set_expectation(Window::OcclusionState::VISIBLE);
  341. run_loop.Run();
  342. EXPECT_FALSE(observer.is_expecting_call());
  343. observer.set_expectation(Window::OcclusionState::OCCLUDED);
  344. base::RunLoop run_loop2;
  345. observer.set_quit_closure(run_loop2.QuitClosure());
  346. // Unfortunately, this relies on knowing that NativeWindowOcclusionTracker
  347. // uses SessionChangeObserver to listen for WM_WTSSESSION_CHANGE messages, but
  348. // actually locking the screen isn't feasible.
  349. DWORD current_session_id = 0;
  350. ProcessIdToSessionId(::GetCurrentProcessId(), &current_session_id);
  351. PostMessage(gfx::SingletonHwnd::GetInstance()->hwnd(), WM_WTSSESSION_CHANGE,
  352. WTS_SESSION_LOCK, current_session_id);
  353. run_loop2.Run();
  354. EXPECT_FALSE(observer.is_expecting_call());
  355. host()->RemoveObserver(&observer);
  356. }
  357. // Test that locking the screen leaves hidden windows as hidden.
  358. TEST_F(NativeWindowOcclusionTrackerTest, LockScreenHiddenOcclusion) {
  359. base::RunLoop run_loop;
  360. MockWindowTreeHostObserver observer(run_loop.QuitClosure());
  361. CreateTrackedAuraWindowWithBounds(&observer, gfx::Rect(0, 0, 100, 100));
  362. // Iconify the tracked aura window and check that its occlusion state
  363. // is HIDDEN.
  364. CloseWindow(host()->GetAcceleratedWidget());
  365. observer.set_expectation(Window::OcclusionState::HIDDEN);
  366. run_loop.Run();
  367. EXPECT_FALSE(observer.is_expecting_call());
  368. // Observer only gets notified on occlusion state changes, so force the
  369. // state to VISIBLE so that setting the state to hidden will trigger
  370. // a notification.
  371. host()->SetNativeWindowOcclusionState(Window::OcclusionState::VISIBLE, {});
  372. observer.set_expectation(Window::OcclusionState::HIDDEN);
  373. base::RunLoop run_loop2;
  374. observer.set_quit_closure(run_loop2.QuitClosure());
  375. // Unfortunately, this relies on knowing that NativeWindowOcclusionTracker
  376. // uses SessionChangeObserver to listen for WM_WTSSESSION_CHANGE messages, but
  377. // actually locking the screen isn't feasible.
  378. DWORD current_session_id = 0;
  379. ProcessIdToSessionId(::GetCurrentProcessId(), &current_session_id);
  380. PostMessage(gfx::SingletonHwnd::GetInstance()->hwnd(), WM_WTSSESSION_CHANGE,
  381. WTS_SESSION_LOCK, current_session_id);
  382. run_loop2.Run();
  383. EXPECT_FALSE(observer.is_expecting_call());
  384. host()->RemoveObserver(&observer);
  385. }
  386. // Test that locking the screen from a different session doesn't mark window
  387. // as occluded.
  388. TEST_F(NativeWindowOcclusionTrackerTest, LockScreenDifferentSession) {
  389. base::RunLoop run_loop;
  390. MockWindowTreeHostObserver observer(run_loop.QuitClosure());
  391. CreateTrackedAuraWindowWithBounds(&observer, gfx::Rect(0, 0, 100, 100));
  392. observer.set_expectation(Window::OcclusionState::VISIBLE);
  393. run_loop.Run();
  394. EXPECT_FALSE(observer.is_expecting_call());
  395. // Observer only gets notified on occlusion state changes, so force the
  396. // state to OCCLUDED so that setting the state to VISIBLE will trigger
  397. // a notification.
  398. host()->SetNativeWindowOcclusionState(Window::OcclusionState::OCCLUDED, {});
  399. // Generate a session change lock screen with a session id that's not
  400. // |current_session_id|.
  401. DWORD current_session_id = 0;
  402. ProcessIdToSessionId(::GetCurrentProcessId(), &current_session_id);
  403. PostMessage(gfx::SingletonHwnd::GetInstance()->hwnd(), WM_WTSSESSION_CHANGE,
  404. WTS_SESSION_LOCK, current_session_id + 1);
  405. observer.set_expectation(Window::OcclusionState::VISIBLE);
  406. base::RunLoop run_loop2;
  407. observer.set_quit_closure(run_loop2.QuitClosure());
  408. // Create a native window to trigger occlusion calculation.
  409. CreateNativeWindowWithBounds(gfx::Rect(0, 0, 50, 50));
  410. run_loop2.Run();
  411. EXPECT_FALSE(observer.is_expecting_call());
  412. host()->RemoveObserver(&observer);
  413. }
  414. // Test that display off & on power state notification causes visible windows to
  415. // become occluded, then visible.
  416. TEST_F(NativeWindowOcclusionTrackerTest, DisplayOnOffHandling) {
  417. base::RunLoop run_loop;
  418. MockWindowTreeHostObserver observer(run_loop.QuitClosure());
  419. CreateTrackedAuraWindowWithBounds(&observer, gfx::Rect(0, 0, 100, 100));
  420. observer.set_expectation(Window::OcclusionState::VISIBLE);
  421. run_loop.Run();
  422. EXPECT_FALSE(observer.is_expecting_call());
  423. NativeWindowOcclusionTrackerWin* occlusion_tracker =
  424. NativeWindowOcclusionTrackerWin::GetOrCreateInstance();
  425. observer.set_expectation(Window::OcclusionState::OCCLUDED);
  426. base::RunLoop run_loop2;
  427. observer.set_quit_closure(run_loop2.QuitClosure());
  428. // Turning display off and on isn't feasible, so send a notification.
  429. occlusion_tracker->OnDisplayStateChanged(/*display_on=*/false);
  430. run_loop2.Run();
  431. EXPECT_FALSE(observer.is_expecting_call());
  432. observer.set_expectation(Window::OcclusionState::VISIBLE);
  433. base::RunLoop run_loop3;
  434. observer.set_quit_closure(run_loop3.QuitClosure());
  435. occlusion_tracker->OnDisplayStateChanged(/*display_on=*/true);
  436. run_loop3.Run();
  437. EXPECT_FALSE(observer.is_expecting_call());
  438. host()->RemoveObserver(&observer);
  439. }
  440. // Verifies that a window is not occluded if the only window occluding it is
  441. // being moved/dragged.
  442. //
  443. // TODO(crbug.com/1266124): Flaky on Windows.
  444. TEST_F(NativeWindowOcclusionTrackerTest,
  445. DISABLED_MovingWindowNotConsideredInCalculations) {
  446. // Needed as this test triggers a native nested message loop.
  447. base::CurrentThread::ScopedAllowApplicationTasksInNativeNestedLoop
  448. allow_nesting;
  449. // Create the initial window.
  450. base::RunLoop run_loop;
  451. MockWindowTreeHostObserver observer(run_loop.QuitClosure());
  452. CreateTrackedAuraWindowWithBounds(&observer, gfx::Rect(40, 40, 100, 100));
  453. observer.set_expectation(Window::OcclusionState::VISIBLE);
  454. run_loop.Run();
  455. EXPECT_FALSE(observer.is_expecting_call());
  456. // Creates a new window that obscures the initial window.
  457. CreateNativeWindowWithBounds(gfx::Rect(0, 0, 200, 200));
  458. observer.set_expectation(Window::OcclusionState::OCCLUDED);
  459. base::RunLoop run_loop2;
  460. observer.set_quit_closure(run_loop2.QuitClosure());
  461. run_loop2.Run();
  462. EXPECT_FALSE(observer.is_expecting_call());
  463. // Start a window move loop. As windows being moved/dragged are not considered
  464. // during occlusion calculation, the initial window should become visible.
  465. base::RunLoop run_loop3(base::RunLoop::Type::kNestableTasksAllowed);
  466. observer.set_expectation(Window::OcclusionState::VISIBLE);
  467. observer.set_quit_closure(base::BindLambdaForTesting([&] {
  468. // Release the mouse, which should make the initial window occluded.
  469. observer.set_expectation(Window::OcclusionState::OCCLUDED);
  470. observer.set_quit_closure(run_loop3.QuitClosure());
  471. ASSERT_TRUE(
  472. ui_controls::SendMouseEvents(ui_controls::LEFT, ui_controls::UP));
  473. }));
  474. ASSERT_TRUE(ui_controls::SendMouseMove(40, 8));
  475. ASSERT_TRUE(
  476. ui_controls::SendMouseEvents(ui_controls::LEFT, ui_controls::DOWN));
  477. run_loop3.Run();
  478. EXPECT_FALSE(observer.is_expecting_call());
  479. host()->RemoveObserver(&observer);
  480. }
  481. // Test that a maximized aura window that is covered by a fullscreen window
  482. // is marked as occluded.
  483. TEST_F(NativeWindowOcclusionTrackerTest, MaximizedOccludedByFullscreenWindow) {
  484. // Win7 has non rectangular windows and odd padding; this breaks fullscreen
  485. // window occlusion of maximized windows, which makes this test fail on Win7.
  486. // Win7 support is going away soon and shouldn't get in the way of this test
  487. // coverage.
  488. if (base::win::GetVersion() <= base::win::Version::WIN7)
  489. return;
  490. // Create an aura window that is maximized.
  491. base::RunLoop run_loop1;
  492. MockWindowTreeHostObserver observer(run_loop1.QuitClosure());
  493. HWND hwnd_aura_window_maximized =
  494. CreateTrackedAuraWindowWithBounds(&observer, gfx::Rect(0, 0, 100, 100))
  495. ->GetHost()
  496. ->GetAcceleratedWidget();
  497. ShowWindow(hwnd_aura_window_maximized, SW_SHOWMAXIMIZED);
  498. observer.set_expectation(Window::OcclusionState::VISIBLE);
  499. run_loop1.Run();
  500. EXPECT_FALSE(observer.is_expecting_call());
  501. // Create a fullscreen native window that occludes the aura window.
  502. base::RunLoop run_loop2;
  503. observer.set_quit_closure(run_loop2.QuitClosure());
  504. observer.set_expectation(Window::OcclusionState::OCCLUDED);
  505. HWND hwnd_native_window =
  506. CreateNativeWindowWithBounds(gfx::Rect(0, 0, 100, 100));
  507. MakeFullscreen(hwnd_native_window);
  508. run_loop2.Run();
  509. EXPECT_FALSE(observer.is_expecting_call());
  510. host()->RemoveObserver(&observer);
  511. }
  512. TEST_F(NativeWindowOcclusionTrackerTest, OccludedRegionSimple) {
  513. Window* tracked_aura_window =
  514. CreateTrackedAuraWindowWithBounds(nullptr, gfx::Rect(20, 20, 200, 200));
  515. tracked_aura_window->SetBounds(gfx::Rect(0, 0, 60, 60));
  516. MockWindowObserver observer(tracked_aura_window);
  517. base::RunLoop run_loop;
  518. observer.set_expectation(Window::OcclusionState::OCCLUDED);
  519. observer.set_quit_closure(run_loop.QuitClosure());
  520. HWND obscuring_hwnd =
  521. CreateNativeWindowWithBounds(gfx::Rect(20, 20, 110, 110));
  522. run_loop.Run();
  523. EXPECT_EQ(Window::OcclusionState::OCCLUDED,
  524. tracked_aura_window->GetOcclusionState());
  525. base::RunLoop run_loop2;
  526. observer.set_expectation(Window::OcclusionState::VISIBLE);
  527. observer.set_quit_closure(run_loop2.QuitClosure());
  528. tracked_aura_window->SetBounds(gfx::Rect(160, 160, 20, 20));
  529. run_loop2.Run();
  530. EXPECT_EQ(Window::OcclusionState::VISIBLE,
  531. tracked_aura_window->GetOcclusionState());
  532. base::RunLoop run_loop3;
  533. observer.set_expectation(Window::OcclusionState::OCCLUDED);
  534. observer.set_quit_closure(run_loop3.QuitClosure());
  535. SetNativeWindowBounds(obscuring_hwnd, gfx::Rect(140, 140, 110, 110));
  536. run_loop3.Run();
  537. EXPECT_EQ(Window::OcclusionState::OCCLUDED,
  538. tracked_aura_window->GetOcclusionState());
  539. }
  540. TEST_F(NativeWindowOcclusionTrackerTest, OccludedRegionComplex) {
  541. Window* tracked_aura_window =
  542. CreateTrackedAuraWindowWithBounds(nullptr, gfx::Rect(20, 20, 200, 200));
  543. tracked_aura_window->SetBounds(gfx::Rect(0, 0, 60, 60));
  544. MockWindowObserver observer(tracked_aura_window);
  545. base::RunLoop run_loop;
  546. observer.set_expectation(Window::OcclusionState::OCCLUDED);
  547. observer.set_quit_closure(run_loop.QuitClosure());
  548. CreateNativeWindowWithBounds(gfx::Rect(20, 20, 110, 110));
  549. run_loop.Run();
  550. EXPECT_EQ(Window::OcclusionState::OCCLUDED,
  551. tracked_aura_window->GetOcclusionState());
  552. base::RunLoop run_loop2;
  553. observer.set_expectation(Window::OcclusionState::VISIBLE);
  554. observer.set_quit_closure(run_loop2.QuitClosure());
  555. tracked_aura_window->SetBounds(gfx::Rect(160, 160, 20, 20));
  556. run_loop2.Run();
  557. EXPECT_EQ(Window::OcclusionState::VISIBLE,
  558. tracked_aura_window->GetOcclusionState());
  559. base::RunLoop run_loop3;
  560. observer.set_expectation(Window::OcclusionState::OCCLUDED);
  561. observer.set_quit_closure(run_loop3.QuitClosure());
  562. CreateNativeWindowWithBounds(gfx::Rect(140, 140, 110, 110));
  563. run_loop3.Run();
  564. EXPECT_EQ(Window::OcclusionState::OCCLUDED,
  565. tracked_aura_window->GetOcclusionState());
  566. }
  567. class NativeWindowOcclusionTrackerTestWithDpi2
  568. : public NativeWindowOcclusionTrackerTest {
  569. public:
  570. // NativeWindowOcclusionTrackerTest:
  571. void SetUp() override {
  572. display::Display::SetForceDeviceScaleFactor(2.0);
  573. NativeWindowOcclusionTrackerTest::SetUp();
  574. }
  575. };
  576. TEST_F(NativeWindowOcclusionTrackerTestWithDpi2, OccludedRegionSimple) {
  577. Window* tracked_aura_window =
  578. CreateTrackedAuraWindowWithBounds(nullptr, gfx::Rect(20, 20, 200, 200));
  579. tracked_aura_window->SetBounds(gfx::Rect(0, 0, 30, 30));
  580. MockWindowObserver observer(tracked_aura_window);
  581. base::RunLoop run_loop;
  582. observer.set_expectation(Window::OcclusionState::OCCLUDED);
  583. observer.set_quit_closure(run_loop.QuitClosure());
  584. HWND obscuring_hwnd =
  585. CreateNativeWindowWithBounds(gfx::Rect(20, 20, 110, 110));
  586. run_loop.Run();
  587. EXPECT_EQ(Window::OcclusionState::OCCLUDED,
  588. tracked_aura_window->GetOcclusionState());
  589. base::RunLoop run_loop2;
  590. observer.set_expectation(Window::OcclusionState::VISIBLE);
  591. observer.set_quit_closure(run_loop2.QuitClosure());
  592. tracked_aura_window->SetBounds(gfx::Rect(80, 80, 20, 20));
  593. run_loop2.Run();
  594. EXPECT_EQ(Window::OcclusionState::VISIBLE,
  595. tracked_aura_window->GetOcclusionState());
  596. base::RunLoop run_loop3;
  597. observer.set_expectation(Window::OcclusionState::OCCLUDED);
  598. observer.set_quit_closure(run_loop3.QuitClosure());
  599. SetNativeWindowBounds(obscuring_hwnd, gfx::Rect(140, 140, 110, 110));
  600. run_loop3.Run();
  601. EXPECT_EQ(Window::OcclusionState::OCCLUDED,
  602. tracked_aura_window->GetOcclusionState());
  603. }
  604. } // namespace aura