client_controlled_state_unittest.cc 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631
  1. // Copyright 2013 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 "ash/wm/client_controlled_state.h"
  5. #include "ash/public/cpp/shelf_config.h"
  6. #include "ash/public/cpp/shell_window_ids.h"
  7. #include "ash/shell.h"
  8. #include "ash/test/ash_test_base.h"
  9. #include "ash/wm/desks/desks_util.h"
  10. #include "ash/wm/pip/pip_positioner.h"
  11. #include "ash/wm/screen_pinning_controller.h"
  12. #include "ash/wm/splitview/split_view_controller.h"
  13. #include "ash/wm/tablet_mode/tablet_mode_controller.h"
  14. #include "ash/wm/window_state.h"
  15. #include "ash/wm/window_util.h"
  16. #include "ash/wm/wm_event.h"
  17. #include "ui/aura/client/aura_constants.h"
  18. #include "ui/views/widget/widget.h"
  19. #include "ui/views/widget/widget_delegate.h"
  20. #include "ui/wm/core/window_util.h"
  21. namespace ash {
  22. namespace {
  23. using ::chromeos::WindowStateType;
  24. constexpr gfx::Rect kInitialBounds(0, 0, 100, 100);
  25. class TestClientControlledStateDelegate
  26. : public ClientControlledState::Delegate {
  27. public:
  28. TestClientControlledStateDelegate() = default;
  29. TestClientControlledStateDelegate(const TestClientControlledStateDelegate&) =
  30. delete;
  31. TestClientControlledStateDelegate& operator=(
  32. const TestClientControlledStateDelegate&) = delete;
  33. ~TestClientControlledStateDelegate() override = default;
  34. void HandleWindowStateRequest(WindowState* window_state,
  35. WindowStateType next_state) override {
  36. EXPECT_FALSE(deleted_);
  37. old_state_ = window_state->GetStateType();
  38. new_state_ = next_state;
  39. }
  40. void HandleBoundsRequest(WindowState* window_state,
  41. WindowStateType requested_state,
  42. const gfx::Rect& bounds,
  43. int64_t display_id) override {
  44. requested_bounds_ = bounds;
  45. if (requested_state != window_state->GetStateType()) {
  46. DCHECK(requested_state == WindowStateType::kPrimarySnapped ||
  47. requested_state == WindowStateType::kSecondarySnapped);
  48. old_state_ = window_state->GetStateType();
  49. new_state_ = requested_state;
  50. }
  51. display_id_ = display_id;
  52. }
  53. WindowStateType old_state() const { return old_state_; }
  54. WindowStateType new_state() const { return new_state_; }
  55. const gfx::Rect& requested_bounds() const { return requested_bounds_; }
  56. int64_t display_id() const { return display_id_; }
  57. void Reset() {
  58. old_state_ = WindowStateType::kDefault;
  59. new_state_ = WindowStateType::kDefault;
  60. requested_bounds_.SetRect(0, 0, 0, 0);
  61. display_id_ = display::kInvalidDisplayId;
  62. }
  63. void mark_as_deleted() { deleted_ = true; }
  64. private:
  65. WindowStateType old_state_ = WindowStateType::kDefault;
  66. WindowStateType new_state_ = WindowStateType::kDefault;
  67. int64_t display_id_ = display::kInvalidDisplayId;
  68. gfx::Rect requested_bounds_;
  69. bool deleted_ = false;
  70. };
  71. class TestWidgetDelegate : public views::WidgetDelegateView {
  72. public:
  73. TestWidgetDelegate() = default;
  74. TestWidgetDelegate(const TestWidgetDelegate&) = delete;
  75. TestWidgetDelegate& operator=(const TestWidgetDelegate&) = delete;
  76. ~TestWidgetDelegate() override = default;
  77. void EnableSnap() {
  78. SetCanMaximize(true);
  79. SetCanResize(true);
  80. GetWidget()->OnSizeConstraintsChanged();
  81. }
  82. };
  83. } // namespace
  84. class ClientControlledStateTest : public AshTestBase {
  85. public:
  86. ClientControlledStateTest() = default;
  87. ClientControlledStateTest(const ClientControlledStateTest&) = delete;
  88. ClientControlledStateTest& operator=(const ClientControlledStateTest&) =
  89. delete;
  90. ~ClientControlledStateTest() override = default;
  91. void SetUp() override {
  92. AshTestBase::SetUp();
  93. widget_delegate_ = new TestWidgetDelegate();
  94. views::Widget::InitParams params;
  95. params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
  96. params.parent = Shell::GetPrimaryRootWindow()->GetChildById(
  97. desks_util::GetActiveDeskContainerId());
  98. params.bounds = kInitialBounds;
  99. params.delegate = widget_delegate_;
  100. widget_ = std::make_unique<views::Widget>();
  101. widget_->Init(std::move(params));
  102. WindowState* window_state = WindowState::Get(window());
  103. window_state->set_allow_set_bounds_direct(true);
  104. auto delegate = std::make_unique<TestClientControlledStateDelegate>();
  105. state_delegate_ = delegate.get();
  106. auto state = std::make_unique<ClientControlledState>(std::move(delegate));
  107. state_ = state.get();
  108. window_state->SetStateObject(std::move(state));
  109. widget_->Show();
  110. }
  111. void TearDown() override {
  112. widget_ = nullptr;
  113. AshTestBase::TearDown();
  114. }
  115. TestWidgetDelegate* widget_delegate() { return widget_delegate_; }
  116. protected:
  117. aura::Window* window() { return widget_->GetNativeWindow(); }
  118. WindowState* window_state() { return WindowState::Get(window()); }
  119. ClientControlledState* state() { return state_; }
  120. TestClientControlledStateDelegate* delegate() { return state_delegate_; }
  121. views::Widget* widget() { return widget_.get(); }
  122. ScreenPinningController* GetScreenPinningController() {
  123. return Shell::Get()->screen_pinning_controller();
  124. }
  125. private:
  126. ClientControlledState* state_ = nullptr;
  127. TestClientControlledStateDelegate* state_delegate_ = nullptr;
  128. TestWidgetDelegate* widget_delegate_ = nullptr; // owned by itself.
  129. std::unique_ptr<views::Widget> widget_;
  130. };
  131. // Make sure that calling Maximize()/Minimize()/Fullscreen() result in
  132. // sending the state change request and won't change the state immediately.
  133. // The state will be updated when ClientControlledState::EnterToNextState
  134. // is called.
  135. TEST_F(ClientControlledStateTest, Maximize) {
  136. widget()->Maximize();
  137. // The state shouldn't be updated until EnterToNextState is called.
  138. EXPECT_FALSE(widget()->IsMaximized());
  139. EXPECT_EQ(kInitialBounds, widget()->GetWindowBoundsInScreen());
  140. EXPECT_EQ(WindowStateType::kDefault, delegate()->old_state());
  141. EXPECT_EQ(WindowStateType::kMaximized, delegate()->new_state());
  142. // Now enters the new state.
  143. state()->EnterNextState(window_state(), delegate()->new_state());
  144. EXPECT_TRUE(widget()->IsMaximized());
  145. // Bounds is controlled by client.
  146. EXPECT_EQ(kInitialBounds, widget()->GetWindowBoundsInScreen());
  147. // Maximized request should be also sent. It is up to client impl
  148. // how to handle it.
  149. widget()->SetBounds(gfx::Rect(0, 0, 100, 100));
  150. EXPECT_EQ(gfx::Rect(0, 0, 100, 100), delegate()->requested_bounds());
  151. widget()->Restore();
  152. EXPECT_TRUE(widget()->IsMaximized());
  153. EXPECT_EQ(kInitialBounds, widget()->GetWindowBoundsInScreen());
  154. EXPECT_EQ(WindowStateType::kMaximized, delegate()->old_state());
  155. EXPECT_EQ(WindowStateType::kNormal, delegate()->new_state());
  156. state()->EnterNextState(window_state(), delegate()->new_state());
  157. EXPECT_FALSE(widget()->IsMaximized());
  158. EXPECT_EQ(kInitialBounds, widget()->GetWindowBoundsInScreen());
  159. }
  160. TEST_F(ClientControlledStateTest, Minimize) {
  161. widget()->Minimize();
  162. EXPECT_FALSE(widget()->IsMinimized());
  163. EXPECT_EQ(kInitialBounds, widget()->GetWindowBoundsInScreen());
  164. EXPECT_EQ(WindowStateType::kDefault, delegate()->old_state());
  165. EXPECT_EQ(WindowStateType::kMinimized, delegate()->new_state());
  166. state()->EnterNextState(window_state(), delegate()->new_state());
  167. EXPECT_TRUE(widget()->IsMinimized());
  168. EXPECT_EQ(kInitialBounds, widget()->GetWindowBoundsInScreen());
  169. widget()->Restore();
  170. EXPECT_TRUE(widget()->IsMinimized());
  171. EXPECT_EQ(kInitialBounds, widget()->GetWindowBoundsInScreen());
  172. EXPECT_EQ(WindowStateType::kMinimized, delegate()->old_state());
  173. EXPECT_EQ(WindowStateType::kNormal, delegate()->new_state());
  174. state()->EnterNextState(window_state(), delegate()->new_state());
  175. EXPECT_FALSE(widget()->IsMinimized());
  176. EXPECT_EQ(kInitialBounds, widget()->GetWindowBoundsInScreen());
  177. // use wm::Unminimize to unminimize.
  178. widget()->Minimize();
  179. EXPECT_FALSE(widget()->IsMinimized());
  180. EXPECT_EQ(kInitialBounds, widget()->GetWindowBoundsInScreen());
  181. EXPECT_EQ(WindowStateType::kNormal, delegate()->old_state());
  182. EXPECT_EQ(WindowStateType::kMinimized, delegate()->new_state());
  183. state()->EnterNextState(window_state(), delegate()->new_state());
  184. EXPECT_TRUE(widget()->IsMinimized());
  185. EXPECT_EQ(kInitialBounds, widget()->GetWindowBoundsInScreen());
  186. ::wm::Unminimize(widget()->GetNativeWindow());
  187. EXPECT_TRUE(widget()->IsMinimized());
  188. EXPECT_EQ(ui::SHOW_STATE_NORMAL, widget()->GetNativeWindow()->GetProperty(
  189. aura::client::kRestoreShowStateKey));
  190. EXPECT_EQ(kInitialBounds, widget()->GetWindowBoundsInScreen());
  191. EXPECT_EQ(WindowStateType::kMinimized, delegate()->old_state());
  192. EXPECT_EQ(WindowStateType::kNormal, delegate()->new_state());
  193. state()->EnterNextState(window_state(), delegate()->new_state());
  194. EXPECT_FALSE(widget()->IsMinimized());
  195. EXPECT_EQ(kInitialBounds, widget()->GetWindowBoundsInScreen());
  196. }
  197. TEST_F(ClientControlledStateTest, Fullscreen) {
  198. widget()->SetFullscreen(true);
  199. EXPECT_FALSE(widget()->IsFullscreen());
  200. EXPECT_EQ(kInitialBounds, widget()->GetWindowBoundsInScreen());
  201. EXPECT_EQ(WindowStateType::kDefault, delegate()->old_state());
  202. EXPECT_EQ(WindowStateType::kFullscreen, delegate()->new_state());
  203. state()->EnterNextState(window_state(), delegate()->new_state());
  204. EXPECT_TRUE(widget()->IsFullscreen());
  205. EXPECT_EQ(kInitialBounds, widget()->GetWindowBoundsInScreen());
  206. widget()->SetFullscreen(false);
  207. EXPECT_TRUE(widget()->IsFullscreen());
  208. EXPECT_EQ(WindowStateType::kFullscreen, delegate()->old_state());
  209. EXPECT_EQ(WindowStateType::kNormal, delegate()->new_state());
  210. state()->EnterNextState(window_state(), delegate()->new_state());
  211. EXPECT_FALSE(widget()->IsFullscreen());
  212. EXPECT_EQ(kInitialBounds, widget()->GetWindowBoundsInScreen());
  213. }
  214. // Make sure toggle fullscreen from maximized state goes back to
  215. // maximized state.
  216. TEST_F(ClientControlledStateTest, MaximizeToFullscreen) {
  217. widget()->Maximize();
  218. EXPECT_FALSE(widget()->IsMaximized());
  219. EXPECT_EQ(kInitialBounds, widget()->GetWindowBoundsInScreen());
  220. EXPECT_EQ(WindowStateType::kDefault, delegate()->old_state());
  221. EXPECT_EQ(WindowStateType::kMaximized, delegate()->new_state());
  222. state()->EnterNextState(window_state(), delegate()->new_state());
  223. EXPECT_TRUE(widget()->IsMaximized());
  224. EXPECT_EQ(kInitialBounds, widget()->GetWindowBoundsInScreen());
  225. widget()->SetFullscreen(true);
  226. EXPECT_TRUE(widget()->IsMaximized());
  227. EXPECT_EQ(kInitialBounds, widget()->GetWindowBoundsInScreen());
  228. EXPECT_EQ(WindowStateType::kMaximized, delegate()->old_state());
  229. EXPECT_EQ(WindowStateType::kFullscreen, delegate()->new_state());
  230. state()->EnterNextState(window_state(), delegate()->new_state());
  231. EXPECT_TRUE(widget()->IsFullscreen());
  232. EXPECT_EQ(kInitialBounds, widget()->GetWindowBoundsInScreen());
  233. widget()->SetFullscreen(false);
  234. EXPECT_TRUE(widget()->IsFullscreen());
  235. EXPECT_EQ(WindowStateType::kFullscreen, delegate()->old_state());
  236. EXPECT_EQ(WindowStateType::kMaximized, delegate()->new_state());
  237. state()->EnterNextState(window_state(), delegate()->new_state());
  238. EXPECT_TRUE(widget()->IsMaximized());
  239. EXPECT_EQ(kInitialBounds, widget()->GetWindowBoundsInScreen());
  240. widget()->Restore();
  241. EXPECT_TRUE(widget()->IsMaximized());
  242. EXPECT_EQ(WindowStateType::kMaximized, delegate()->old_state());
  243. EXPECT_EQ(WindowStateType::kNormal, delegate()->new_state());
  244. state()->EnterNextState(window_state(), delegate()->new_state());
  245. EXPECT_FALSE(widget()->IsMaximized());
  246. EXPECT_EQ(kInitialBounds, widget()->GetWindowBoundsInScreen());
  247. }
  248. TEST_F(ClientControlledStateTest, IgnoreWorkspace) {
  249. widget()->Maximize();
  250. state()->EnterNextState(window_state(), delegate()->new_state());
  251. EXPECT_TRUE(widget()->IsMaximized());
  252. delegate()->Reset();
  253. UpdateDisplay("1000x800");
  254. // Client is responsible to handle workspace change, so
  255. // no action should be taken.
  256. EXPECT_EQ(kInitialBounds, widget()->GetWindowBoundsInScreen());
  257. EXPECT_EQ(WindowStateType::kDefault, delegate()->old_state());
  258. EXPECT_EQ(WindowStateType::kDefault, delegate()->new_state());
  259. EXPECT_EQ(gfx::Rect(), delegate()->requested_bounds());
  260. }
  261. TEST_F(ClientControlledStateTest, SetBounds) {
  262. constexpr gfx::Rect new_bounds(100, 100, 100, 100);
  263. widget()->SetBounds(new_bounds);
  264. EXPECT_EQ(kInitialBounds, widget()->GetWindowBoundsInScreen());
  265. EXPECT_EQ(new_bounds, delegate()->requested_bounds());
  266. state()->set_bounds_locally(true);
  267. widget()->SetBounds(delegate()->requested_bounds());
  268. state()->set_bounds_locally(false);
  269. EXPECT_EQ(new_bounds, widget()->GetWindowBoundsInScreen());
  270. }
  271. TEST_F(ClientControlledStateTest, CenterWindow) {
  272. display::Screen* screen = display::Screen::GetScreen();
  273. gfx::Rect bounds = screen->GetPrimaryDisplay().work_area();
  274. const WMEvent center_event(WM_EVENT_CENTER);
  275. window_state()->OnWMEvent(&center_event);
  276. EXPECT_NEAR(bounds.CenterPoint().x(),
  277. delegate()->requested_bounds().CenterPoint().x(), 1);
  278. EXPECT_NEAR(bounds.CenterPoint().y(),
  279. delegate()->requested_bounds().CenterPoint().y(), 1);
  280. }
  281. TEST_F(ClientControlledStateTest, SnapWindow) {
  282. // Snap disabled.
  283. display::Screen* screen = display::Screen::GetScreen();
  284. gfx::Rect work_area = screen->GetPrimaryDisplay().work_area();
  285. ASSERT_FALSE(window_state()->CanResize());
  286. ASSERT_FALSE(window_state()->CanSnap());
  287. // The event should be ignored.
  288. const WindowSnapWMEvent snap_left_event(WM_EVENT_CYCLE_SNAP_PRIMARY);
  289. window_state()->OnWMEvent(&snap_left_event);
  290. EXPECT_FALSE(window_state()->IsSnapped());
  291. EXPECT_TRUE(delegate()->requested_bounds().IsEmpty());
  292. const WindowSnapWMEvent snap_right_event(WM_EVENT_CYCLE_SNAP_SECONDARY);
  293. window_state()->OnWMEvent(&snap_right_event);
  294. EXPECT_FALSE(window_state()->IsSnapped());
  295. EXPECT_TRUE(delegate()->requested_bounds().IsEmpty());
  296. // Snap enabled.
  297. widget_delegate()->EnableSnap();
  298. ASSERT_TRUE(window_state()->CanResize());
  299. ASSERT_TRUE(window_state()->CanSnap());
  300. window_state()->OnWMEvent(&snap_left_event);
  301. EXPECT_NEAR(work_area.CenterPoint().x(),
  302. delegate()->requested_bounds().right(), 1);
  303. EXPECT_EQ(work_area.height(), delegate()->requested_bounds().height());
  304. EXPECT_TRUE(delegate()->requested_bounds().origin().IsOrigin());
  305. EXPECT_EQ(WindowStateType::kDefault, delegate()->old_state());
  306. EXPECT_EQ(WindowStateType::kPrimarySnapped, delegate()->new_state());
  307. delegate()->Reset();
  308. window_state()->OnWMEvent(&snap_right_event);
  309. EXPECT_NEAR(work_area.CenterPoint().x(), delegate()->requested_bounds().x(),
  310. 1);
  311. EXPECT_EQ(work_area.height(), delegate()->requested_bounds().height());
  312. EXPECT_EQ(work_area.bottom_right(),
  313. delegate()->requested_bounds().bottom_right());
  314. EXPECT_EQ(WindowStateType::kDefault, delegate()->old_state());
  315. EXPECT_EQ(WindowStateType::kSecondarySnapped, delegate()->new_state());
  316. }
  317. TEST_F(ClientControlledStateTest, SnapInSecondaryDisplay) {
  318. UpdateDisplay("800x600, 600x500");
  319. widget()->SetBounds(gfx::Rect(800, 0, 100, 200));
  320. display::Screen* screen = display::Screen::GetScreen();
  321. const int64_t second_display_id = screen->GetAllDisplays()[1].id();
  322. EXPECT_EQ(second_display_id, screen->GetDisplayNearestWindow(window()).id());
  323. widget_delegate()->EnableSnap();
  324. // Make sure the requested bounds for snapped window is local to display.
  325. const WindowSnapWMEvent snap_left_event(WM_EVENT_CYCLE_SNAP_PRIMARY);
  326. window_state()->OnWMEvent(&snap_left_event);
  327. EXPECT_EQ(second_display_id, delegate()->display_id());
  328. EXPECT_EQ(gfx::Rect(0, 0, 300, 500 - ShelfConfig::Get()->shelf_size()),
  329. delegate()->requested_bounds());
  330. state()->EnterNextState(window_state(), delegate()->new_state());
  331. // Make sure moving to another display tries to update the bounds.
  332. auto first_display = screen->GetAllDisplays()[0];
  333. delegate()->Reset();
  334. state()->set_bounds_locally(true);
  335. window()->SetBoundsInScreen(delegate()->requested_bounds(), first_display);
  336. state()->set_bounds_locally(false);
  337. EXPECT_EQ(first_display.id(), delegate()->display_id());
  338. EXPECT_EQ(gfx::Rect(0, 0, 400, 600 - ShelfConfig::Get()->shelf_size()),
  339. delegate()->requested_bounds());
  340. }
  341. // Pin events should be applied immediately.
  342. TEST_F(ClientControlledStateTest, Pinned) {
  343. ASSERT_FALSE(window_state()->IsPinned());
  344. ASSERT_FALSE(GetScreenPinningController()->IsPinned());
  345. const WMEvent pin_event(WM_EVENT_PIN);
  346. window_state()->OnWMEvent(&pin_event);
  347. EXPECT_TRUE(window_state()->IsPinned());
  348. EXPECT_TRUE(GetScreenPinningController()->IsPinned());
  349. EXPECT_EQ(WindowStateType::kPinned, window_state()->GetStateType());
  350. EXPECT_EQ(WindowStateType::kDefault, delegate()->old_state());
  351. EXPECT_EQ(WindowStateType::kPinned, delegate()->new_state());
  352. // All state transition events are ignored except for NORMAL.
  353. widget()->Maximize();
  354. EXPECT_EQ(WindowStateType::kPinned, window_state()->GetStateType());
  355. EXPECT_TRUE(GetScreenPinningController()->IsPinned());
  356. widget()->Minimize();
  357. EXPECT_EQ(WindowStateType::kPinned, window_state()->GetStateType());
  358. EXPECT_TRUE(GetScreenPinningController()->IsPinned());
  359. EXPECT_TRUE(window()->IsVisible());
  360. widget()->SetFullscreen(true);
  361. EXPECT_EQ(WindowStateType::kPinned, window_state()->GetStateType());
  362. EXPECT_TRUE(GetScreenPinningController()->IsPinned());
  363. // WM/User cannot change the bounds of the pinned window.
  364. constexpr gfx::Rect new_bounds(100, 100, 200, 100);
  365. widget()->SetBounds(new_bounds);
  366. EXPECT_TRUE(delegate()->requested_bounds().IsEmpty());
  367. // But client can change the bounds of the pinned window.
  368. state()->set_bounds_locally(true);
  369. widget()->SetBounds(new_bounds);
  370. state()->set_bounds_locally(false);
  371. EXPECT_EQ(new_bounds, widget()->GetWindowBoundsInScreen());
  372. widget()->Restore();
  373. EXPECT_FALSE(window_state()->IsPinned());
  374. EXPECT_EQ(WindowStateType::kNormal, window_state()->GetStateType());
  375. EXPECT_FALSE(GetScreenPinningController()->IsPinned());
  376. // Two windows cannot be pinned simultaneously.
  377. auto widget2 = CreateTestWidget();
  378. WindowState* window_state_2 = WindowState::Get(widget2->GetNativeWindow());
  379. window_state_2->OnWMEvent(&pin_event);
  380. EXPECT_TRUE(window_state_2->IsPinned());
  381. EXPECT_TRUE(GetScreenPinningController()->IsPinned());
  382. // Pin request should fail.
  383. window_state()->OnWMEvent(&pin_event);
  384. EXPECT_FALSE(window_state()->IsPinned());
  385. }
  386. TEST_F(ClientControlledStateTest, TrustedPinnedBasic) {
  387. EXPECT_FALSE(window_state()->IsPinned());
  388. EXPECT_FALSE(GetScreenPinningController()->IsPinned());
  389. const WMEvent trusted_pin_event(WM_EVENT_TRUSTED_PIN);
  390. window_state()->OnWMEvent(&trusted_pin_event);
  391. EXPECT_TRUE(window_state()->IsPinned());
  392. EXPECT_TRUE(GetScreenPinningController()->IsPinned());
  393. EXPECT_EQ(WindowStateType::kTrustedPinned, window_state()->GetStateType());
  394. EXPECT_EQ(WindowStateType::kDefault, delegate()->old_state());
  395. EXPECT_EQ(WindowStateType::kTrustedPinned, delegate()->new_state());
  396. // All state transition events are ignored except for NORMAL.
  397. widget()->Maximize();
  398. EXPECT_EQ(WindowStateType::kTrustedPinned, window_state()->GetStateType());
  399. EXPECT_TRUE(GetScreenPinningController()->IsPinned());
  400. widget()->Minimize();
  401. EXPECT_EQ(WindowStateType::kTrustedPinned, window_state()->GetStateType());
  402. EXPECT_TRUE(GetScreenPinningController()->IsPinned());
  403. EXPECT_TRUE(window()->IsVisible());
  404. widget()->SetFullscreen(true);
  405. EXPECT_EQ(WindowStateType::kTrustedPinned, window_state()->GetStateType());
  406. EXPECT_TRUE(GetScreenPinningController()->IsPinned());
  407. // WM/User cannot change the bounds of the trusted-pinned window.
  408. constexpr gfx::Rect new_bounds(100, 100, 200, 100);
  409. widget()->SetBounds(new_bounds);
  410. EXPECT_TRUE(delegate()->requested_bounds().IsEmpty());
  411. // But client can change the bounds of the trusted-pinned window.
  412. state()->set_bounds_locally(true);
  413. widget()->SetBounds(new_bounds);
  414. state()->set_bounds_locally(false);
  415. EXPECT_EQ(new_bounds, widget()->GetWindowBoundsInScreen());
  416. widget()->Restore();
  417. EXPECT_FALSE(window_state()->IsPinned());
  418. EXPECT_EQ(WindowStateType::kNormal, window_state()->GetStateType());
  419. EXPECT_FALSE(GetScreenPinningController()->IsPinned());
  420. // Two windows cannot be trusted-pinned simultaneously.
  421. auto widget2 = CreateTestWidget();
  422. WindowState* window_state_2 = WindowState::Get(widget2->GetNativeWindow());
  423. window_state_2->OnWMEvent(&trusted_pin_event);
  424. EXPECT_TRUE(window_state_2->IsTrustedPinned());
  425. EXPECT_TRUE(GetScreenPinningController()->IsPinned());
  426. EXPECT_FALSE(window_state()->IsTrustedPinned());
  427. window_state()->OnWMEvent(&trusted_pin_event);
  428. EXPECT_FALSE(window_state()->IsTrustedPinned());
  429. EXPECT_TRUE(window_state_2->IsTrustedPinned());
  430. }
  431. TEST_F(ClientControlledStateTest, ClosePinned) {
  432. EXPECT_FALSE(window_state()->IsPinned());
  433. EXPECT_FALSE(GetScreenPinningController()->IsPinned());
  434. const WMEvent trusted_pin_event(WM_EVENT_TRUSTED_PIN);
  435. window_state()->OnWMEvent(&trusted_pin_event);
  436. EXPECT_TRUE(window_state()->IsPinned());
  437. EXPECT_TRUE(GetScreenPinningController()->IsPinned());
  438. delegate()->mark_as_deleted();
  439. widget()->CloseNow();
  440. }
  441. TEST_F(ClientControlledStateTest, MoveWindowToDisplay) {
  442. UpdateDisplay("600x500, 600x500");
  443. display::Screen* screen = display::Screen::GetScreen();
  444. const int64_t first_display_id = screen->GetAllDisplays()[0].id();
  445. const int64_t second_display_id = screen->GetAllDisplays()[1].id();
  446. EXPECT_EQ(first_display_id, screen->GetDisplayNearestWindow(window()).id());
  447. window_util::MoveWindowToDisplay(window(), second_display_id);
  448. // Make sure that the boundsChange request has correct destination
  449. // information.
  450. EXPECT_EQ(second_display_id, delegate()->display_id());
  451. EXPECT_EQ(window()->bounds(), delegate()->requested_bounds());
  452. }
  453. TEST_F(ClientControlledStateTest, MoveWindowToDisplayOutOfBounds) {
  454. UpdateDisplay("1000x500, 600x500");
  455. state()->set_bounds_locally(true);
  456. widget()->SetBounds(gfx::Rect(700, 0, 100, 200));
  457. state()->set_bounds_locally(false);
  458. EXPECT_EQ(gfx::Rect(700, 0, 100, 200), widget()->GetWindowBoundsInScreen());
  459. display::Screen* screen = display::Screen::GetScreen();
  460. const int64_t first_display_id = screen->GetAllDisplays()[0].id();
  461. const int64_t second_display_id = screen->GetAllDisplays()[1].id();
  462. EXPECT_EQ(first_display_id, screen->GetDisplayNearestWindow(window()).id());
  463. window_util::MoveWindowToDisplay(window(), second_display_id);
  464. // Make sure that the boundsChange request has correct destination
  465. // information.
  466. EXPECT_EQ(second_display_id, delegate()->display_id());
  467. // The bounds is constrained by
  468. // |AdjustBoundsToEnsureMinimumWindowVisibility| in the secondary
  469. // display.
  470. EXPECT_EQ(gfx::Rect(575, 0, 100, 200), delegate()->requested_bounds());
  471. }
  472. // Make sure disconnecting primary notifies the display id change.
  473. TEST_F(ClientControlledStateTest, DisconnectPrimary) {
  474. UpdateDisplay("600x500,600x500");
  475. SwapPrimaryDisplay();
  476. auto* screen = display::Screen::GetScreen();
  477. auto old_primary_id = screen->GetPrimaryDisplay().id();
  478. EXPECT_EQ(old_primary_id, window_state()->GetDisplay().id());
  479. gfx::Rect bounds = window()->bounds();
  480. UpdateDisplay("600x500");
  481. ASSERT_NE(old_primary_id, screen->GetPrimaryDisplay().id());
  482. EXPECT_EQ(delegate()->display_id(), screen->GetPrimaryDisplay().id());
  483. EXPECT_EQ(bounds, delegate()->requested_bounds());
  484. }
  485. TEST_F(ClientControlledStateTest,
  486. WmEventNormalIsResolvedToMaximizeInTabletMode) {
  487. Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
  488. ASSERT_EQ(true, Shell::Get()->tablet_mode_controller()->InTabletMode());
  489. window_state()->window()->SetProperty(
  490. aura::client::kResizeBehaviorKey,
  491. aura::client::kResizeBehaviorCanMaximize);
  492. const WMEvent normal_event(WM_EVENT_NORMAL);
  493. window_state()->OnWMEvent(&normal_event);
  494. EXPECT_EQ(WindowStateType::kMaximized, delegate()->new_state());
  495. }
  496. TEST_F(ClientControlledStateTest,
  497. IgnoreWmEventWhenWindowIsInTransitionalSnappedState) {
  498. auto* split_view_controller =
  499. SplitViewController::Get(window_state()->window());
  500. widget_delegate()->EnableSnap();
  501. split_view_controller->SnapWindow(window_state()->window(),
  502. SplitViewController::SnapPosition::RIGHT);
  503. EXPECT_EQ(WindowStateType::kSecondarySnapped, delegate()->new_state());
  504. EXPECT_FALSE(window_state()->IsSnapped());
  505. // Ensures the window is in a transitional snapped state.
  506. EXPECT_TRUE(split_view_controller->IsWindowInTransitionalState(
  507. window_state()->window()));
  508. EXPECT_EQ(WindowStateType::kSecondarySnapped, delegate()->new_state());
  509. EXPECT_FALSE(window_state()->IsSnapped());
  510. // Ignores WMEvent if in a transitional state.
  511. widget()->Maximize();
  512. EXPECT_NE(WindowStateType::kMaximized, delegate()->new_state());
  513. // Applies snap request.
  514. state()->EnterNextState(window_state(), delegate()->new_state());
  515. EXPECT_TRUE(window_state()->IsSnapped());
  516. // After exiting the transitional state, works normally.
  517. widget()->Maximize();
  518. EXPECT_EQ(WindowStateType::kMaximized, delegate()->new_state());
  519. }
  520. } // namespace ash