ash_message_popup_collection_unittest.cc 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436
  1. // Copyright 2014 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/system/message_center/ash_message_popup_collection.h"
  5. #include <memory>
  6. #include <utility>
  7. #include <vector>
  8. #include "ash/keyboard/ui/keyboard_ui_controller.h"
  9. #include "ash/keyboard/ui/keyboard_util.h"
  10. #include "ash/public/cpp/keyboard/keyboard_switches.h"
  11. #include "ash/public/cpp/shelf_types.h"
  12. #include "ash/public/cpp/shell_window_ids.h"
  13. #include "ash/root_window_controller.h"
  14. #include "ash/shelf/shelf.h"
  15. #include "ash/shell.h"
  16. #include "ash/test/ash_test_base.h"
  17. #include "ash/wm/desks/desks_util.h"
  18. #include "ash/wm/overview/overview_controller.h"
  19. #include "base/command_line.h"
  20. #include "base/test/scoped_feature_list.h"
  21. #include "ui/display/manager/display_manager.h"
  22. #include "ui/display/screen.h"
  23. #include "ui/gfx/animation/linear_animation.h"
  24. #include "ui/gfx/geometry/rect.h"
  25. #include "ui/message_center/message_center.h"
  26. #include "ui/message_center/public/cpp/message_center_constants.h"
  27. #include "ui/message_center/views/message_popup_collection.h"
  28. #include "ui/message_center/views/message_popup_view.h"
  29. namespace ash {
  30. namespace {
  31. class TestMessagePopupCollection : public AshMessagePopupCollection {
  32. public:
  33. explicit TestMessagePopupCollection(Shelf* shelf)
  34. : AshMessagePopupCollection(shelf) {}
  35. TestMessagePopupCollection(const TestMessagePopupCollection&) = delete;
  36. TestMessagePopupCollection& operator=(const TestMessagePopupCollection&) =
  37. delete;
  38. ~TestMessagePopupCollection() override = default;
  39. bool popup_shown() const { return popup_shown_; }
  40. protected:
  41. void NotifyPopupAdded(message_center::MessagePopupView* popup) override {
  42. AshMessagePopupCollection::NotifyPopupAdded(popup);
  43. popup_shown_ = true;
  44. notification_id_ = popup->message_view()->notification_id();
  45. }
  46. void NotifyPopupRemoved(const std::string& notification_id) override {
  47. AshMessagePopupCollection::NotifyPopupRemoved(notification_id);
  48. EXPECT_EQ(notification_id_, notification_id);
  49. popup_shown_ = false;
  50. notification_id_.clear();
  51. }
  52. private:
  53. bool popup_shown_ = false;
  54. std::string notification_id_;
  55. };
  56. } // namespace
  57. class AshMessagePopupCollectionTest : public AshTestBase,
  58. public testing::WithParamInterface<bool> {
  59. public:
  60. AshMessagePopupCollectionTest() = default;
  61. AshMessagePopupCollectionTest(const AshMessagePopupCollectionTest&) = delete;
  62. AshMessagePopupCollectionTest& operator=(
  63. const AshMessagePopupCollectionTest&) = delete;
  64. ~AshMessagePopupCollectionTest() override = default;
  65. void SetUp() override {
  66. scoped_feature_list_ = std::make_unique<base::test::ScopedFeatureList>();
  67. scoped_feature_list_->InitWithFeatureState(features::kNotificationsRefresh,
  68. IsNotificationsRefreshEnabled());
  69. base::CommandLine::ForCurrentProcess()->AppendSwitch(
  70. keyboard::switches::kEnableVirtualKeyboard);
  71. AshTestBase::SetUp();
  72. SetPopupCollection(
  73. std::make_unique<AshMessagePopupCollection>(GetPrimaryShelf()));
  74. }
  75. void TearDown() override {
  76. popup_collection_.reset();
  77. AshTestBase::TearDown();
  78. }
  79. bool IsNotificationsRefreshEnabled() const { return GetParam(); }
  80. protected:
  81. enum Position { TOP_LEFT, TOP_RIGHT, BOTTOM_LEFT, BOTTOM_RIGHT, OUTSIDE };
  82. AshMessagePopupCollection* popup_collection() {
  83. return popup_collection_.get();
  84. }
  85. void UpdateWorkArea(AshMessagePopupCollection* popup_collection,
  86. const display::Display& display) {
  87. popup_collection->StartObserving(display::Screen::GetScreen(), display);
  88. // Update the layout
  89. popup_collection->UpdateWorkArea();
  90. }
  91. void SetPopupCollection(std::unique_ptr<AshMessagePopupCollection> delegate) {
  92. if (!delegate.get()) {
  93. popup_collection_.reset();
  94. return;
  95. }
  96. popup_collection_ = std::move(delegate);
  97. UpdateWorkArea(popup_collection_.get(),
  98. display::Screen::GetScreen()->GetPrimaryDisplay());
  99. }
  100. Position GetPositionInDisplay(const gfx::Point& point) {
  101. const gfx::Rect work_area =
  102. display::Screen::GetScreen()->GetPrimaryDisplay().work_area();
  103. const gfx::Point center_point = work_area.CenterPoint();
  104. if (work_area.x() > point.x() || work_area.y() > point.y() ||
  105. work_area.right() < point.x() || work_area.bottom() < point.y()) {
  106. return OUTSIDE;
  107. }
  108. if (center_point.x() < point.x())
  109. return (center_point.y() < point.y()) ? BOTTOM_RIGHT : TOP_RIGHT;
  110. else
  111. return (center_point.y() < point.y()) ? BOTTOM_LEFT : TOP_LEFT;
  112. }
  113. gfx::Rect GetWorkArea() { return popup_collection_->work_area_; }
  114. std::unique_ptr<message_center::Notification> CreateNotification(
  115. const std::string& id) {
  116. return std::make_unique<message_center::Notification>(
  117. message_center::NOTIFICATION_TYPE_SIMPLE, id, u"test_title",
  118. u"test message", ui::ImageModel(),
  119. std::u16string() /* display_source */, GURL(),
  120. message_center::NotifierId(), message_center::RichNotificationData(),
  121. new message_center::NotificationDelegate());
  122. }
  123. std::string AddNotification() {
  124. std::string id = base::NumberToString(notification_id_++);
  125. message_center::MessageCenter::Get()->AddNotification(
  126. CreateNotification(id));
  127. return id;
  128. }
  129. private:
  130. int notification_id_ = 0;
  131. std::unique_ptr<AshMessagePopupCollection> popup_collection_;
  132. std::unique_ptr<base::test::ScopedFeatureList> scoped_feature_list_;
  133. };
  134. INSTANTIATE_TEST_SUITE_P(All,
  135. AshMessagePopupCollectionTest,
  136. testing::Bool() /* IsNotificationsRefreshEnabled() */);
  137. TEST_P(AshMessagePopupCollectionTest, ShelfAlignment) {
  138. const gfx::Rect toast_size(0, 0, 10, 10);
  139. UpdateDisplay("601x600");
  140. gfx::Point toast_point;
  141. toast_point.set_x(popup_collection()->GetToastOriginX(toast_size));
  142. toast_point.set_y(popup_collection()->GetBaseline());
  143. EXPECT_EQ(BOTTOM_RIGHT, GetPositionInDisplay(toast_point));
  144. EXPECT_FALSE(popup_collection()->IsTopDown());
  145. EXPECT_FALSE(popup_collection()->IsFromLeft());
  146. GetPrimaryShelf()->SetAlignment(ShelfAlignment::kRight);
  147. toast_point.set_x(popup_collection()->GetToastOriginX(toast_size));
  148. toast_point.set_y(popup_collection()->GetBaseline());
  149. EXPECT_EQ(BOTTOM_RIGHT, GetPositionInDisplay(toast_point));
  150. EXPECT_FALSE(popup_collection()->IsTopDown());
  151. EXPECT_FALSE(popup_collection()->IsFromLeft());
  152. GetPrimaryShelf()->SetAlignment(ShelfAlignment::kLeft);
  153. toast_point.set_x(popup_collection()->GetToastOriginX(toast_size));
  154. toast_point.set_y(popup_collection()->GetBaseline());
  155. EXPECT_EQ(BOTTOM_LEFT, GetPositionInDisplay(toast_point));
  156. EXPECT_FALSE(popup_collection()->IsTopDown());
  157. EXPECT_TRUE(popup_collection()->IsFromLeft());
  158. }
  159. TEST_P(AshMessagePopupCollectionTest, LockScreen) {
  160. const gfx::Rect toast_size(0, 0, 10, 10);
  161. GetPrimaryShelf()->SetAlignment(ShelfAlignment::kLeft);
  162. gfx::Point toast_point;
  163. toast_point.set_x(popup_collection()->GetToastOriginX(toast_size));
  164. toast_point.set_y(popup_collection()->GetBaseline());
  165. EXPECT_EQ(BOTTOM_LEFT, GetPositionInDisplay(toast_point));
  166. EXPECT_FALSE(popup_collection()->IsTopDown());
  167. EXPECT_TRUE(popup_collection()->IsFromLeft());
  168. BlockUserSession(BLOCKED_BY_LOCK_SCREEN);
  169. toast_point.set_x(popup_collection()->GetToastOriginX(toast_size));
  170. toast_point.set_y(popup_collection()->GetBaseline());
  171. EXPECT_EQ(BOTTOM_RIGHT, GetPositionInDisplay(toast_point));
  172. EXPECT_FALSE(popup_collection()->IsTopDown());
  173. EXPECT_FALSE(popup_collection()->IsFromLeft());
  174. }
  175. TEST_P(AshMessagePopupCollectionTest, AutoHide) {
  176. const gfx::Rect toast_size(0, 0, 10, 10);
  177. UpdateDisplay("601x600");
  178. int origin_x = popup_collection()->GetToastOriginX(toast_size);
  179. int baseline = popup_collection()->GetBaseline();
  180. // Create a window, otherwise autohide doesn't work.
  181. std::unique_ptr<views::Widget> widget = CreateTestWidget(
  182. nullptr, desks_util::GetActiveDeskContainerId(), gfx::Rect(0, 0, 50, 50));
  183. Shelf* shelf = GetPrimaryShelf();
  184. shelf->SetAutoHideBehavior(ShelfAutoHideBehavior::kAlways);
  185. EXPECT_EQ(origin_x, popup_collection()->GetToastOriginX(toast_size));
  186. EXPECT_LT(baseline, popup_collection()->GetBaseline());
  187. }
  188. TEST_P(AshMessagePopupCollectionTest, DisplayResize) {
  189. const gfx::Rect toast_size(0, 0, 10, 10);
  190. UpdateDisplay("601x600");
  191. int origin_x = popup_collection()->GetToastOriginX(toast_size);
  192. int baseline = popup_collection()->GetBaseline();
  193. UpdateDisplay("801x800");
  194. EXPECT_LT(origin_x, popup_collection()->GetToastOriginX(toast_size));
  195. EXPECT_LT(baseline, popup_collection()->GetBaseline());
  196. UpdateDisplay("500x400");
  197. EXPECT_GT(origin_x, popup_collection()->GetToastOriginX(toast_size));
  198. EXPECT_GT(baseline, popup_collection()->GetBaseline());
  199. }
  200. TEST_P(AshMessagePopupCollectionTest, DockedMode) {
  201. const gfx::Rect toast_size(0, 0, 10, 10);
  202. UpdateDisplay("601x600");
  203. int origin_x = popup_collection()->GetToastOriginX(toast_size);
  204. int baseline = popup_collection()->GetBaseline();
  205. // Emulate the docked mode; enter to an extended mode, then invoke
  206. // OnNativeDisplaysChanged() with the info for the secondary display only.
  207. UpdateDisplay("601x600,801x800");
  208. std::vector<display::ManagedDisplayInfo> new_info;
  209. new_info.push_back(display_manager()->GetDisplayInfo(
  210. display_manager()->GetDisplayAt(1u).id()));
  211. display_manager()->OnNativeDisplaysChanged(new_info);
  212. EXPECT_LT(origin_x, popup_collection()->GetToastOriginX(toast_size));
  213. EXPECT_LT(baseline, popup_collection()->GetBaseline());
  214. }
  215. TEST_P(AshMessagePopupCollectionTest, TrayHeight) {
  216. const gfx::Rect toast_size(0, 0, 10, 10);
  217. UpdateDisplay("601x600");
  218. int origin_x = popup_collection()->GetToastOriginX(toast_size);
  219. int baseline = popup_collection()->GetBaseline();
  220. // Simulate the system tray bubble being open.
  221. const int kTrayHeight = 100;
  222. popup_collection()->SetTrayBubbleHeight(kTrayHeight);
  223. EXPECT_EQ(origin_x, popup_collection()->GetToastOriginX(toast_size));
  224. EXPECT_EQ(baseline - kTrayHeight - message_center::kMarginBetweenPopups,
  225. popup_collection()->GetBaseline());
  226. }
  227. TEST_P(AshMessagePopupCollectionTest, Extended) {
  228. UpdateDisplay("601x600,801x800");
  229. SetPopupCollection(
  230. std::make_unique<AshMessagePopupCollection>(GetPrimaryShelf()));
  231. display::Display second_display = GetSecondaryDisplay();
  232. Shelf* second_shelf =
  233. Shell::GetRootWindowControllerWithDisplayId(second_display.id())->shelf();
  234. AshMessagePopupCollection for_2nd_display(second_shelf);
  235. UpdateWorkArea(&for_2nd_display, second_display);
  236. // Make sure that the toast position on the secondary display is
  237. // positioned correctly.
  238. EXPECT_LT(1300, for_2nd_display.GetToastOriginX(gfx::Rect(0, 0, 10, 10)));
  239. EXPECT_LT(700, for_2nd_display.GetBaseline());
  240. }
  241. TEST_P(AshMessagePopupCollectionTest, MixedFullscreenNone) {
  242. UpdateDisplay("601x600,801x800");
  243. Shelf* shelf1 = GetPrimaryShelf();
  244. TestMessagePopupCollection collection1(shelf1);
  245. UpdateWorkArea(&collection1, GetPrimaryDisplay());
  246. Shelf* shelf2 =
  247. Shell::GetRootWindowControllerWithDisplayId(GetSecondaryDisplay().id())
  248. ->shelf();
  249. TestMessagePopupCollection collection2(shelf2);
  250. UpdateWorkArea(&collection2, GetSecondaryDisplay());
  251. // No fullscreens, both receive notification.
  252. std::unique_ptr<views::Widget> widget1 = CreateTestWidget();
  253. widget1->SetFullscreen(false);
  254. AddNotification();
  255. EXPECT_TRUE(collection1.popup_shown());
  256. EXPECT_TRUE(collection2.popup_shown());
  257. // Set screen 1 to fullscreen, popup closes on screen 1, stays on screen 2.
  258. widget1->SetFullscreen(true);
  259. EXPECT_FALSE(collection1.popup_shown());
  260. EXPECT_TRUE(collection2.popup_shown());
  261. }
  262. TEST_P(AshMessagePopupCollectionTest, MixedFullscreenSome) {
  263. UpdateDisplay("601x600,801x800");
  264. Shelf* shelf1 = GetPrimaryShelf();
  265. TestMessagePopupCollection collection1(shelf1);
  266. UpdateWorkArea(&collection1, GetPrimaryDisplay());
  267. Shelf* shelf2 =
  268. Shell::GetRootWindowControllerWithDisplayId(GetSecondaryDisplay().id())
  269. ->shelf();
  270. TestMessagePopupCollection collection2(shelf2);
  271. UpdateWorkArea(&collection2, GetSecondaryDisplay());
  272. // One fullscreen, non-fullscreen receives notification.
  273. std::unique_ptr<views::Widget> widget = CreateTestWidget();
  274. widget->SetFullscreen(true);
  275. AddNotification();
  276. EXPECT_FALSE(collection1.popup_shown());
  277. EXPECT_TRUE(collection2.popup_shown());
  278. // Fullscreen toggles, notification now on both.
  279. widget->SetFullscreen(false);
  280. EXPECT_TRUE(collection1.popup_shown());
  281. EXPECT_TRUE(collection2.popup_shown());
  282. }
  283. TEST_P(AshMessagePopupCollectionTest, MixedFullscreenAll) {
  284. UpdateDisplay("601x600,801x800");
  285. Shelf* shelf1 = GetPrimaryShelf();
  286. TestMessagePopupCollection collection1(shelf1);
  287. UpdateWorkArea(&collection1, GetPrimaryDisplay());
  288. Shelf* shelf2 =
  289. Shell::GetRootWindowControllerWithDisplayId(GetSecondaryDisplay().id())
  290. ->shelf();
  291. TestMessagePopupCollection collection2(shelf2);
  292. UpdateWorkArea(&collection2, GetSecondaryDisplay());
  293. std::unique_ptr<views::Widget> widget1 = CreateTestWidget();
  294. std::unique_ptr<views::Widget> widget2 =
  295. CreateTestWidget(nullptr, desks_util::GetActiveDeskContainerId(),
  296. gfx::Rect(700, 0, 50, 50));
  297. // Both fullscreen, no notifications.
  298. widget1->SetFullscreen(true);
  299. widget2->SetFullscreen(true);
  300. AddNotification();
  301. EXPECT_FALSE(collection1.popup_shown());
  302. EXPECT_FALSE(collection2.popup_shown());
  303. // Toggle 1, then the other.
  304. widget1->SetFullscreen(false);
  305. EXPECT_TRUE(collection1.popup_shown());
  306. EXPECT_FALSE(collection2.popup_shown());
  307. widget2->SetFullscreen(false);
  308. EXPECT_TRUE(collection1.popup_shown());
  309. EXPECT_TRUE(collection2.popup_shown());
  310. }
  311. TEST_P(AshMessagePopupCollectionTest, Unified) {
  312. display_manager()->SetUnifiedDesktopEnabled(true);
  313. // Reset the delegate as the primary display's shelf will be destroyed during
  314. // transition.
  315. SetPopupCollection(nullptr);
  316. UpdateDisplay("601x600,801x800");
  317. SetPopupCollection(
  318. std::make_unique<AshMessagePopupCollection>(GetPrimaryShelf()));
  319. EXPECT_GT(600, popup_collection()->GetToastOriginX(gfx::Rect(0, 0, 10, 10)));
  320. }
  321. // Tests that when the keyboard is showing that notifications appear above it,
  322. // and that they return to normal once the keyboard is gone.
  323. TEST_P(AshMessagePopupCollectionTest, KeyboardShowing) {
  324. ASSERT_TRUE(keyboard::IsKeyboardEnabled());
  325. ASSERT_TRUE(
  326. keyboard::KeyboardUIController::Get()->IsKeyboardOverscrollEnabled());
  327. UpdateDisplay("601x600");
  328. int baseline = popup_collection()->GetBaseline();
  329. Shelf* shelf = GetPrimaryShelf();
  330. gfx::Rect keyboard_bounds(0, 300, 601, 300);
  331. shelf->SetVirtualKeyboardBoundsForTesting(keyboard_bounds);
  332. int keyboard_baseline = popup_collection()->GetBaseline();
  333. EXPECT_NE(baseline, keyboard_baseline);
  334. EXPECT_GT(keyboard_bounds.y(), keyboard_baseline);
  335. shelf->SetVirtualKeyboardBoundsForTesting(gfx::Rect());
  336. EXPECT_EQ(baseline, popup_collection()->GetBaseline());
  337. }
  338. // Tests that notification bubble baseline is correct when entering and exiting
  339. // overview with a full screen window.
  340. TEST_P(AshMessagePopupCollectionTest, BaselineInOverview) {
  341. UpdateDisplay("800x600");
  342. ASSERT_TRUE(GetPrimaryShelf()->IsHorizontalAlignment());
  343. ASSERT_EQ(SHELF_VISIBLE, GetPrimaryShelf()->GetVisibilityState());
  344. const int baseline_with_visible_shelf = popup_collection()->GetBaseline();
  345. std::unique_ptr<views::Widget> widget = CreateTestWidget();
  346. widget->SetFullscreen(true);
  347. ASSERT_EQ(SHELF_HIDDEN, GetPrimaryShelf()->GetVisibilityState());
  348. const int baseline_with_hidden_shelf = popup_collection()->GetBaseline();
  349. EXPECT_NE(baseline_with_visible_shelf, baseline_with_hidden_shelf);
  350. auto* overview_controller = Shell::Get()->overview_controller();
  351. EnterOverview();
  352. EXPECT_TRUE(overview_controller->InOverviewSession());
  353. const int baseline_in_overview = popup_collection()->GetBaseline();
  354. EXPECT_EQ(baseline_in_overview, baseline_with_visible_shelf);
  355. ExitOverview();
  356. EXPECT_FALSE(overview_controller->InOverviewSession());
  357. const int baseline_no_overview = popup_collection()->GetBaseline();
  358. EXPECT_EQ(baseline_no_overview, baseline_with_hidden_shelf);
  359. }
  360. } // namespace ash