shelf_window_watcher_unittest.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  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/shelf/shelf_window_watcher.h"
  5. #include <memory>
  6. #include "ash/public/cpp/shelf_item.h"
  7. #include "ash/public/cpp/shelf_model.h"
  8. #include "ash/public/cpp/shell_window_ids.h"
  9. #include "ash/public/cpp/window_properties.h"
  10. #include "ash/root_window_controller.h"
  11. #include "ash/session/session_controller_impl.h"
  12. #include "ash/shell.h"
  13. #include "ash/test/ash_test_base.h"
  14. #include "ash/wm/desks/desks_util.h"
  15. #include "ash/wm/window_resizer.h"
  16. #include "ash/wm/window_state.h"
  17. #include "base/strings/string_number_conversions.h"
  18. #include "third_party/skia/include/core/SkBitmap.h"
  19. #include "ui/aura/client/aura_constants.h"
  20. #include "ui/aura/window.h"
  21. #include "ui/base/hit_test.h"
  22. #include "ui/base/resource/resource_bundle.h"
  23. #include "ui/gfx/image/image_skia.h"
  24. #include "ui/resources/grit/ui_resources.h"
  25. #include "ui/views/widget/widget.h"
  26. #include "ui/wm/core/transient_window_controller.h"
  27. namespace ash {
  28. namespace {
  29. // Create a test 1x1 icon image with a given |color|.
  30. gfx::ImageSkia CreateImageSkiaIcon(SkColor color) {
  31. SkBitmap bitmap;
  32. bitmap.allocN32Pixels(1, 1);
  33. bitmap.eraseColor(color);
  34. return gfx::ImageSkia::CreateFrom1xBitmap(bitmap);
  35. }
  36. static ShelfID CreateShelfItem(aura::Window* window) {
  37. static int id = 0;
  38. ShelfID shelf_id(base::NumberToString(id++));
  39. window->SetProperty(kShelfIDKey, shelf_id.Serialize());
  40. window->SetProperty(kShelfItemTypeKey, static_cast<int32_t>(TYPE_DIALOG));
  41. return shelf_id;
  42. }
  43. using ShelfWindowWatcherTest = AshTestBase;
  44. // Ensure shelf items are added and removed as windows are opened and closed.
  45. TEST_F(ShelfWindowWatcherTest, OpenAndClose) {
  46. ShelfModel* model = ShelfModel::Get();
  47. ASSERT_EQ(0, model->item_count());
  48. // Windows with valid ShelfItemType and ShelfID properties get shelf items.
  49. std::unique_ptr<views::Widget> widget1 = CreateTestWidget(
  50. nullptr, desks_util::GetActiveDeskContainerId(), gfx::Rect());
  51. CreateShelfItem(widget1->GetNativeWindow());
  52. EXPECT_EQ(1, model->item_count());
  53. std::unique_ptr<views::Widget> widget2 = CreateTestWidget(
  54. nullptr, desks_util::GetActiveDeskContainerId(), gfx::Rect());
  55. CreateShelfItem(widget2->GetNativeWindow());
  56. EXPECT_EQ(2, model->item_count());
  57. // Each ShelfItem is removed when the associated window is destroyed.
  58. widget1.reset();
  59. EXPECT_EQ(1, model->item_count());
  60. widget2.reset();
  61. EXPECT_EQ(0, model->item_count());
  62. }
  63. TEST_F(ShelfWindowWatcherTest, CreateAndRemoveShelfItemProperties) {
  64. ShelfModel* model = ShelfModel::Get();
  65. // Creating windows without a valid ShelfItemType does not add items.
  66. std::unique_ptr<views::Widget> widget1 = CreateTestWidget(
  67. nullptr, desks_util::GetActiveDeskContainerId(), gfx::Rect());
  68. std::unique_ptr<views::Widget> widget2 = CreateTestWidget(
  69. nullptr, desks_util::GetActiveDeskContainerId(), gfx::Rect());
  70. EXPECT_EQ(0, model->item_count());
  71. // Create a ShelfItem for the first window.
  72. ShelfID id_w1 = CreateShelfItem(widget1->GetNativeWindow());
  73. EXPECT_EQ(1, model->item_count());
  74. int index_w1 = model->ItemIndexByID(id_w1);
  75. EXPECT_EQ(STATUS_RUNNING, model->items()[index_w1].status);
  76. // Create a ShelfItem for the second window.
  77. ShelfID id_w2 = CreateShelfItem(widget2->GetNativeWindow());
  78. EXPECT_EQ(2, model->item_count());
  79. int index_w2 = model->ItemIndexByID(id_w2);
  80. EXPECT_EQ(STATUS_RUNNING, model->items()[index_w2].status);
  81. // ShelfItem is removed when the type property is cleared.
  82. widget1->GetNativeWindow()->SetProperty(kShelfItemTypeKey,
  83. static_cast<int32_t>(TYPE_UNDEFINED));
  84. EXPECT_EQ(1, model->item_count());
  85. widget2->GetNativeWindow()->SetProperty(kShelfItemTypeKey,
  86. static_cast<int32_t>(TYPE_UNDEFINED));
  87. EXPECT_EQ(0, model->item_count());
  88. // Clearing twice doesn't do anything.
  89. widget2->GetNativeWindow()->SetProperty(kShelfItemTypeKey,
  90. static_cast<int32_t>(TYPE_UNDEFINED));
  91. EXPECT_EQ(0, model->item_count());
  92. // Closing the windows once the properties are cleared doesn't do anything.
  93. widget1->CloseNow();
  94. EXPECT_EQ(0, model->item_count());
  95. widget2->CloseNow();
  96. EXPECT_EQ(0, model->item_count());
  97. }
  98. TEST_F(ShelfWindowWatcherTest, UpdateWindowProperty) {
  99. ShelfModel* model = ShelfModel::Get();
  100. // Create a ShelfItem for a new window.
  101. std::unique_ptr<views::Widget> widget = CreateTestWidget(
  102. nullptr, desks_util::GetActiveDeskContainerId(), gfx::Rect());
  103. ShelfID id = CreateShelfItem(widget->GetNativeWindow());
  104. EXPECT_EQ(1, model->item_count());
  105. int index = model->ItemIndexByID(id);
  106. EXPECT_EQ(STATUS_RUNNING, model->items()[index].status);
  107. // No new item is created after updating a launcher item.
  108. EXPECT_EQ(1, model->item_count());
  109. // index and id are not changed after updating a launcher item.
  110. EXPECT_EQ(index, model->ItemIndexByID(id));
  111. EXPECT_EQ(id, model->items()[index].id);
  112. }
  113. TEST_F(ShelfWindowWatcherTest, MaximizeAndRestoreWindow) {
  114. ShelfModel* model = ShelfModel::Get();
  115. // Create a ShelfItem for a new window.
  116. std::unique_ptr<views::Widget> widget = CreateTestWidget(
  117. nullptr, desks_util::GetActiveDeskContainerId(), gfx::Rect());
  118. ShelfID id = CreateShelfItem(widget->GetNativeWindow());
  119. EXPECT_EQ(1, model->item_count());
  120. int index = model->ItemIndexByID(id);
  121. EXPECT_EQ(STATUS_RUNNING, model->items()[index].status);
  122. // Maximize the window.
  123. WindowState* window_state = WindowState::Get(widget->GetNativeWindow());
  124. EXPECT_FALSE(window_state->IsMaximized());
  125. window_state->Maximize();
  126. EXPECT_TRUE(window_state->IsMaximized());
  127. // No new item is created after maximizing the window.
  128. EXPECT_EQ(1, model->item_count());
  129. // index and id are not changed after maximizing the window.
  130. EXPECT_EQ(index, model->ItemIndexByID(id));
  131. EXPECT_EQ(id, model->items()[index].id);
  132. // Restore the window.
  133. window_state->Restore();
  134. EXPECT_FALSE(window_state->IsMaximized());
  135. // No new item is created after restoring the window.
  136. EXPECT_EQ(1, model->item_count());
  137. // Index and id are not changed after maximizing the window.
  138. EXPECT_EQ(index, model->ItemIndexByID(id));
  139. EXPECT_EQ(id, model->items()[index].id);
  140. }
  141. // Check |window|'s item is not changed during the dragging.
  142. // TODO(simonhong): Add a test for removing a Window during the dragging.
  143. TEST_F(ShelfWindowWatcherTest, DragWindow) {
  144. ShelfModel* model = ShelfModel::Get();
  145. // Create a ShelfItem for a new window.
  146. std::unique_ptr<views::Widget> widget = CreateTestWidget(
  147. nullptr, desks_util::GetActiveDeskContainerId(), gfx::Rect());
  148. ShelfID id = CreateShelfItem(widget->GetNativeWindow());
  149. EXPECT_EQ(1, model->item_count());
  150. int index = model->ItemIndexByID(id);
  151. EXPECT_EQ(STATUS_RUNNING, model->items()[index].status);
  152. // Simulate dragging of the window and check its item is not changed.
  153. std::unique_ptr<WindowResizer> resizer(
  154. CreateWindowResizer(widget->GetNativeWindow(), gfx::PointF(), HTCAPTION,
  155. ::wm::WINDOW_MOVE_SOURCE_MOUSE));
  156. ASSERT_TRUE(resizer.get());
  157. resizer->Drag(gfx::PointF(50, 50), 0);
  158. resizer->CompleteDrag();
  159. // Index and id are not changed after dragging the window.
  160. EXPECT_EQ(index, model->ItemIndexByID(id));
  161. EXPECT_EQ(id, model->items()[index].id);
  162. }
  163. // Ensure dialogs get shelf items.
  164. TEST_F(ShelfWindowWatcherTest, DialogWindows) {
  165. ShelfModel* model = ShelfModel::Get();
  166. // An item is created for a dialog window.
  167. std::unique_ptr<views::Widget> dialog_widget = CreateTestWidget(
  168. nullptr, desks_util::GetActiveDeskContainerId(), gfx::Rect());
  169. aura::Window* dialog = dialog_widget->GetNativeWindow();
  170. dialog->SetProperty(kShelfIDKey, ShelfID("a").Serialize());
  171. dialog->SetProperty(kShelfItemTypeKey, static_cast<int32_t>(TYPE_DIALOG));
  172. EXPECT_EQ(1, model->item_count());
  173. // An item is not created for an app window.
  174. std::unique_ptr<views::Widget> app_widget = CreateTestWidget(
  175. nullptr, desks_util::GetActiveDeskContainerId(), gfx::Rect());
  176. aura::Window* app = app_widget->GetNativeWindow();
  177. app->SetProperty(kShelfIDKey, ShelfID("c").Serialize());
  178. app->SetProperty(kShelfItemTypeKey, static_cast<int32_t>(TYPE_APP));
  179. EXPECT_EQ(1, model->item_count());
  180. app_widget.reset();
  181. // Each ShelfItem is removed when the associated window is destroyed.
  182. dialog_widget.reset();
  183. EXPECT_EQ(0, model->item_count());
  184. }
  185. // Ensure items use the app icon and window icon aura::Window properties.
  186. TEST_F(ShelfWindowWatcherTest, ItemIcon) {
  187. ShelfModel* model = ShelfModel::Get();
  188. // Create a ShelfItem for a window; it should have a default icon.
  189. std::unique_ptr<views::Widget> widget = CreateTestWidget(
  190. nullptr, desks_util::GetActiveDeskContainerId(), gfx::Rect());
  191. aura::Window* window = widget->GetNativeWindow();
  192. ShelfID id = CreateShelfItem(window);
  193. EXPECT_EQ(1, model->item_count());
  194. ui::ResourceBundle& rb = ui::ResourceBundle::GetSharedInstance();
  195. gfx::Image default_image = rb.GetImageNamed(IDR_DEFAULT_FAVICON_32);
  196. EXPECT_TRUE(model->items()[0].image.BackedBySameObjectAs(
  197. default_image.AsImageSkia()));
  198. // Setting a window icon should update the item icon.
  199. gfx::ImageSkia red = CreateImageSkiaIcon(SK_ColorRED);
  200. window->SetProperty(aura::client::kWindowIconKey, std::move(red));
  201. EXPECT_EQ(SK_ColorRED, model->items()[0].image.bitmap()->getColor(0, 0));
  202. // Setting an app icon should override the window icon.
  203. gfx::ImageSkia blue = CreateImageSkiaIcon(SK_ColorBLUE);
  204. window->SetProperty(aura::client::kAppIconKey, std::move(blue));
  205. EXPECT_EQ(SK_ColorBLUE, model->items()[0].image.bitmap()->getColor(0, 0));
  206. // Clearing the app icon should restore the window icon to the shelf item.
  207. window->ClearProperty(aura::client::kAppIconKey);
  208. EXPECT_EQ(SK_ColorRED, model->items()[0].image.bitmap()->getColor(0, 0));
  209. }
  210. TEST_F(ShelfWindowWatcherTest, DontCreateShelfEntriesForChildWindows) {
  211. ShelfModel* model = ShelfModel::Get();
  212. std::unique_ptr<aura::Window> window =
  213. std::make_unique<aura::Window>(nullptr, aura::client::WINDOW_TYPE_NORMAL);
  214. window->Init(ui::LAYER_NOT_DRAWN);
  215. window->SetProperty(kShelfIDKey, ShelfID("a").Serialize());
  216. window->SetProperty(kShelfItemTypeKey, static_cast<int32_t>(TYPE_DIALOG));
  217. Shell::GetPrimaryRootWindow()
  218. ->GetChildById(desks_util::GetActiveDeskContainerId())
  219. ->AddChild(window.get());
  220. window->Show();
  221. EXPECT_EQ(1, model->item_count());
  222. std::unique_ptr<aura::Window> child =
  223. std::make_unique<aura::Window>(nullptr, aura::client::WINDOW_TYPE_NORMAL);
  224. child->Init(ui::LAYER_NOT_DRAWN);
  225. child->SetProperty(kShelfIDKey, ShelfID("b").Serialize());
  226. child->SetProperty(kShelfItemTypeKey, static_cast<int32_t>(TYPE_DIALOG));
  227. window->AddChild(child.get());
  228. child->Show();
  229. // There should not be a new shelf item for |child|.
  230. EXPECT_EQ(1, model->item_count());
  231. child.reset();
  232. EXPECT_EQ(1, model->item_count());
  233. window.reset();
  234. EXPECT_EQ(0, model->item_count());
  235. }
  236. TEST_F(ShelfWindowWatcherTest, CreateShelfEntriesForTransientWindows) {
  237. ShelfModel* model = ShelfModel::Get();
  238. std::unique_ptr<aura::Window> window =
  239. std::make_unique<aura::Window>(nullptr, aura::client::WINDOW_TYPE_NORMAL);
  240. window->Init(ui::LAYER_NOT_DRAWN);
  241. window->SetProperty(kShelfIDKey, ShelfID("a").Serialize());
  242. window->SetProperty(kShelfItemTypeKey, static_cast<int32_t>(TYPE_DIALOG));
  243. Shell::GetPrimaryRootWindow()
  244. ->GetChildById(desks_util::GetActiveDeskContainerId())
  245. ->AddChild(window.get());
  246. window->Show();
  247. EXPECT_EQ(1, model->item_count());
  248. std::unique_ptr<aura::Window> transient =
  249. std::make_unique<aura::Window>(nullptr, aura::client::WINDOW_TYPE_NORMAL);
  250. transient->Init(ui::LAYER_NOT_DRAWN);
  251. transient->SetProperty(kShelfIDKey,
  252. new std::string(ShelfID("b").Serialize()));
  253. transient->SetProperty(kShelfItemTypeKey, static_cast<int32_t>(TYPE_DIALOG));
  254. Shell::GetPrimaryRootWindow()
  255. ->GetChildById(desks_util::GetActiveDeskContainerId())
  256. ->AddChild(transient.get());
  257. ::wm::TransientWindowController::Get()->AddTransientChild(window.get(),
  258. transient.get());
  259. transient->Show();
  260. // There should be a new shelf item for |transient|.
  261. EXPECT_EQ(2, model->item_count());
  262. transient.reset();
  263. EXPECT_EQ(1, model->item_count());
  264. window.reset();
  265. EXPECT_EQ(0, model->item_count());
  266. }
  267. // Ensures ShelfWindowWatcher supports windows opened prior to session start.
  268. using ShelfWindowWatcherSessionStartTest = NoSessionAshTestBase;
  269. TEST_F(ShelfWindowWatcherSessionStartTest, PreExistingWindow) {
  270. ShelfModel* model = ShelfModel::Get();
  271. ASSERT_FALSE(
  272. Shell::Get()->session_controller()->IsActiveUserSessionStarted());
  273. EXPECT_EQ(0, model->item_count());
  274. // Construct a window that should get a shelf item once the session starts.
  275. std::unique_ptr<views::Widget> widget = CreateTestWidget(
  276. nullptr, desks_util::GetActiveDeskContainerId(), gfx::Rect());
  277. CreateShelfItem(widget->GetNativeWindow());
  278. EXPECT_EQ(0, model->item_count());
  279. // Start the test user session; ShelfWindowWatcher will find the open window.
  280. CreateUserSessions(1);
  281. EXPECT_EQ(1, model->item_count());
  282. }
  283. } // namespace
  284. } // namespace ash