Home | History | Annotate | Download | only in web_notification
      1 // Copyright (c) 2012 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 
      5 #include "ash/system/web_notification/web_notification_tray.h"
      6 
      7 #include <vector>
      8 
      9 #include "ash/display/display_manager.h"
     10 #include "ash/root_window_controller.h"
     11 #include "ash/shelf/shelf_layout_manager.h"
     12 #include "ash/shelf/shelf_widget.h"
     13 #include "ash/shell.h"
     14 #include "ash/system/status_area_widget.h"
     15 #include "ash/system/tray/system_tray.h"
     16 #include "ash/system/tray/system_tray_item.h"
     17 #include "ash/test/ash_test_base.h"
     18 #include "ash/test/status_area_widget_test_helper.h"
     19 #include "ash/test/test_system_tray_delegate.h"
     20 #include "ash/wm/window_state.h"
     21 #include "base/strings/stringprintf.h"
     22 #include "base/strings/utf_string_conversions.h"
     23 #include "ui/aura/client/aura_constants.h"
     24 #include "ui/aura/test/event_generator.h"
     25 #include "ui/aura/window.h"
     26 #include "ui/gfx/display.h"
     27 #include "ui/gfx/screen.h"
     28 #include "ui/message_center/message_center_style.h"
     29 #include "ui/message_center/message_center_tray.h"
     30 #include "ui/message_center/notification_list.h"
     31 #include "ui/message_center/notification_types.h"
     32 #include "ui/message_center/views/message_center_bubble.h"
     33 #include "ui/message_center/views/message_popup_collection.h"
     34 #include "ui/views/controls/label.h"
     35 #include "ui/views/layout/fill_layout.h"
     36 #include "ui/views/view.h"
     37 #include "ui/views/widget/widget.h"
     38 
     39 namespace ash {
     40 
     41 namespace {
     42 
     43 WebNotificationTray* GetTray() {
     44   return StatusAreaWidgetTestHelper::GetStatusAreaWidget()->
     45       web_notification_tray();
     46 }
     47 
     48 WebNotificationTray* GetSecondaryTray() {
     49   StatusAreaWidget* status_area_widget =
     50       StatusAreaWidgetTestHelper::GetSecondaryStatusAreaWidget();
     51   if (status_area_widget)
     52     return status_area_widget->web_notification_tray();
     53   return NULL;
     54 }
     55 
     56 message_center::MessageCenter* GetMessageCenter() {
     57   return GetTray()->message_center();
     58 }
     59 
     60 SystemTray* GetSystemTray() {
     61   return StatusAreaWidgetTestHelper::GetStatusAreaWidget()->system_tray();
     62 }
     63 
     64 // Trivial item implementation for testing PopupAndSystemTray test case.
     65 class TestItem : public SystemTrayItem {
     66  public:
     67   TestItem() : SystemTrayItem(GetSystemTray()) {}
     68 
     69   virtual views::View* CreateDefaultView(user::LoginStatus status) OVERRIDE {
     70     views::View* default_view = new views::View;
     71     default_view->SetLayoutManager(new views::FillLayout);
     72     default_view->AddChildView(new views::Label(base::UTF8ToUTF16("Default")));
     73     return default_view;
     74   }
     75 
     76   virtual views::View* CreateNotificationView(
     77       user::LoginStatus status) OVERRIDE {
     78     return new views::View;
     79   }
     80 
     81  private:
     82   DISALLOW_COPY_AND_ASSIGN(TestItem);
     83 };
     84 
     85 }  // namespace
     86 
     87 class WebNotificationTrayTest : public test::AshTestBase {
     88  public:
     89   WebNotificationTrayTest() {}
     90   virtual ~WebNotificationTrayTest() {}
     91 
     92   virtual void TearDown() OVERRIDE {
     93     GetMessageCenter()->RemoveAllNotifications(false);
     94     test::AshTestBase::TearDown();
     95   }
     96 
     97  protected:
     98   void AddNotification(const std::string& id) {
     99     scoped_ptr<message_center::Notification> notification;
    100     notification.reset(new message_center::Notification(
    101         message_center::NOTIFICATION_TYPE_SIMPLE,
    102         id,
    103         base::ASCIIToUTF16("Test Web Notification"),
    104         base::ASCIIToUTF16("Notification message body."),
    105         gfx::Image(),
    106         base::ASCIIToUTF16("www.test.org"),
    107         message_center::NotifierId(),
    108         message_center::RichNotificationData(),
    109         NULL /* delegate */));
    110     GetMessageCenter()->AddNotification(notification.Pass());
    111   }
    112 
    113   void UpdateNotification(const std::string& old_id,
    114                           const std::string& new_id) {
    115     scoped_ptr<message_center::Notification> notification;
    116     notification.reset(new message_center::Notification(
    117         message_center::NOTIFICATION_TYPE_SIMPLE,
    118         new_id,
    119         base::ASCIIToUTF16("Updated Web Notification"),
    120         base::ASCIIToUTF16("Updated message body."),
    121         gfx::Image(),
    122         base::ASCIIToUTF16("www.test.org"),
    123         message_center::NotifierId(),
    124         message_center::RichNotificationData(),
    125         NULL /* delegate */));
    126     GetMessageCenter()->UpdateNotification(old_id, notification.Pass());
    127   }
    128 
    129   void RemoveNotification(const std::string& id) {
    130     GetMessageCenter()->RemoveNotification(id, false);
    131   }
    132 
    133   views::Widget* GetWidget() {
    134     return GetTray()->GetWidget();
    135   }
    136 
    137   gfx::Rect GetPopupWorkArea() {
    138     return GetPopupWorkAreaForTray(GetTray());
    139   }
    140 
    141   gfx::Rect GetPopupWorkAreaForTray(WebNotificationTray* tray) {
    142     return tray->popup_collection_->work_area_;
    143   }
    144 
    145   bool IsPopupVisible() {
    146     return GetTray()->IsPopupVisible();
    147   }
    148 
    149  private:
    150   DISALLOW_COPY_AND_ASSIGN(WebNotificationTrayTest);
    151 };
    152 
    153 TEST_F(WebNotificationTrayTest, WebNotifications) {
    154   // TODO(mukai): move this test case to ui/message_center.
    155   ASSERT_TRUE(GetWidget());
    156 
    157   // Add a notification.
    158   AddNotification("test_id1");
    159   EXPECT_EQ(1u, GetMessageCenter()->NotificationCount());
    160   EXPECT_TRUE(GetMessageCenter()->FindVisibleNotificationById("test_id1"));
    161   AddNotification("test_id2");
    162   AddNotification("test_id2");
    163   EXPECT_EQ(2u, GetMessageCenter()->NotificationCount());
    164   EXPECT_TRUE(GetMessageCenter()->FindVisibleNotificationById("test_id2"));
    165 
    166   // Ensure that updating a notification does not affect the count.
    167   UpdateNotification("test_id2", "test_id3");
    168   UpdateNotification("test_id3", "test_id3");
    169   EXPECT_EQ(2u, GetMessageCenter()->NotificationCount());
    170   EXPECT_FALSE(GetMessageCenter()->FindVisibleNotificationById("test_id2"));
    171 
    172   // Ensure that Removing the first notification removes it from the tray.
    173   RemoveNotification("test_id1");
    174   EXPECT_FALSE(GetMessageCenter()->FindVisibleNotificationById("test_id1"));
    175   EXPECT_EQ(1u, GetMessageCenter()->NotificationCount());
    176 
    177   // Remove the remianing notification.
    178   RemoveNotification("test_id3");
    179   EXPECT_EQ(0u, GetMessageCenter()->NotificationCount());
    180   EXPECT_FALSE(GetMessageCenter()->FindVisibleNotificationById("test_id3"));
    181 }
    182 
    183 TEST_F(WebNotificationTrayTest, WebNotificationPopupBubble) {
    184   // TODO(mukai): move this test case to ui/message_center.
    185   ASSERT_TRUE(GetWidget());
    186 
    187   // Adding a notification should show the popup bubble.
    188   AddNotification("test_id1");
    189   EXPECT_TRUE(GetTray()->IsPopupVisible());
    190 
    191   // Updating a notification should not hide the popup bubble.
    192   AddNotification("test_id2");
    193   UpdateNotification("test_id2", "test_id3");
    194   EXPECT_TRUE(GetTray()->IsPopupVisible());
    195 
    196   // Removing the first notification should not hide the popup bubble.
    197   RemoveNotification("test_id1");
    198   EXPECT_TRUE(GetTray()->IsPopupVisible());
    199 
    200   // Removing the visible notification should hide the popup bubble.
    201   RemoveNotification("test_id3");
    202   EXPECT_FALSE(GetTray()->IsPopupVisible());
    203 
    204   // Now test that we can show multiple popups and then show the message center.
    205   AddNotification("test_id4");
    206   AddNotification("test_id5");
    207   EXPECT_TRUE(GetTray()->IsPopupVisible());
    208 
    209   GetTray()->message_center_tray_->ShowMessageCenterBubble();
    210   GetTray()->message_center_tray_->HideMessageCenterBubble();
    211 
    212   EXPECT_FALSE(GetTray()->IsPopupVisible());
    213 }
    214 
    215 using message_center::NotificationList;
    216 
    217 
    218 // Flakily fails. http://crbug.com/229791
    219 TEST_F(WebNotificationTrayTest, DISABLED_ManyMessageCenterNotifications) {
    220   // Add the max visible notifications +1, ensure the correct visible number.
    221   size_t notifications_to_add =
    222       message_center::kMaxVisibleMessageCenterNotifications + 1;
    223   for (size_t i = 0; i < notifications_to_add; ++i) {
    224     std::string id = base::StringPrintf("test_id%d", static_cast<int>(i));
    225     AddNotification(id);
    226   }
    227   bool shown = GetTray()->message_center_tray_->ShowMessageCenterBubble();
    228   EXPECT_TRUE(shown);
    229   RunAllPendingInMessageLoop();
    230   EXPECT_TRUE(GetTray()->message_center_bubble() != NULL);
    231   EXPECT_EQ(notifications_to_add,
    232             GetMessageCenter()->NotificationCount());
    233   EXPECT_EQ(message_center::kMaxVisibleMessageCenterNotifications,
    234             GetTray()->GetMessageCenterBubbleForTest()->
    235                 NumMessageViewsForTest());
    236 }
    237 
    238 // Flakily times out. http://crbug.com/229792
    239 TEST_F(WebNotificationTrayTest, DISABLED_ManyPopupNotifications) {
    240   // Add the max visible popup notifications +1, ensure the correct num visible.
    241   size_t notifications_to_add =
    242       message_center::kMaxVisiblePopupNotifications + 1;
    243   for (size_t i = 0; i < notifications_to_add; ++i) {
    244     std::string id = base::StringPrintf("test_id%d", static_cast<int>(i));
    245     AddNotification(id);
    246   }
    247   GetTray()->ShowPopups();
    248   EXPECT_TRUE(GetTray()->IsPopupVisible());
    249   EXPECT_EQ(notifications_to_add,
    250             GetMessageCenter()->NotificationCount());
    251   NotificationList::PopupNotifications popups =
    252       GetMessageCenter()->GetPopupNotifications();
    253   EXPECT_EQ(message_center::kMaxVisiblePopupNotifications, popups.size());
    254 }
    255 
    256 #if defined(OS_CHROMEOS)
    257 // Display notification is ChromeOS only.
    258 #define MAYBE_PopupShownOnBothDisplays PopupShownOnBothDisplays
    259 #define MAYBE_PopupAndSystemTrayMultiDisplay PopupAndSystemTrayMultiDisplay
    260 #else
    261 #define MAYBE_PopupShownOnBothDisplays DISABLED_PopupShownOnBothDisplays
    262 #define MAYBE_PopupAndSystemTrayMultiDisplay \
    263   DISABLED_PopupAndSystemTrayMultiDisplay
    264 #endif
    265 
    266 // Verifies if the notification appears on both displays when extended mode.
    267 TEST_F(WebNotificationTrayTest, MAYBE_PopupShownOnBothDisplays) {
    268   if (!SupportsMultipleDisplays())
    269     return;
    270 
    271   // Enables to appear the notification for display changes.
    272   test::TestSystemTrayDelegate* tray_delegate =
    273       static_cast<test::TestSystemTrayDelegate*>(
    274           Shell::GetInstance()->system_tray_delegate());
    275   tray_delegate->set_should_show_display_notification(true);
    276 
    277   UpdateDisplay("400x400,200x200");
    278   // UpdateDisplay() creates the display notifications, so popup is visible.
    279   EXPECT_TRUE(GetTray()->IsPopupVisible());
    280   WebNotificationTray* secondary_tray = GetSecondaryTray();
    281   ASSERT_TRUE(secondary_tray);
    282   EXPECT_TRUE(secondary_tray->IsPopupVisible());
    283 
    284   // Transition to mirroring and then back to extended display, which recreates
    285   // root window controller and shelf with having notifications. This code
    286   // verifies it doesn't cause crash and popups are still visible. See
    287   // http://crbug.com/263664
    288   DisplayManager* display_manager = Shell::GetInstance()->display_manager();
    289 
    290   display_manager->SetSecondDisplayMode(DisplayManager::MIRRORING);
    291   UpdateDisplay("400x400,200x200");
    292   EXPECT_TRUE(GetTray()->IsPopupVisible());
    293   EXPECT_FALSE(GetSecondaryTray());
    294 
    295   display_manager->SetSecondDisplayMode(DisplayManager::EXTENDED);
    296   UpdateDisplay("400x400,200x200");
    297   EXPECT_TRUE(GetTray()->IsPopupVisible());
    298   secondary_tray = GetSecondaryTray();
    299   ASSERT_TRUE(secondary_tray);
    300   EXPECT_TRUE(secondary_tray->IsPopupVisible());
    301 }
    302 
    303 #if defined(OS_CHROMEOS)
    304 // PopupAndSystemTray may fail in platforms other than ChromeOS because the
    305 // RootWindow's bound can be bigger than gfx::Display's work area so that
    306 // openingsystem tray doesn't affect at all the work area of popups.
    307 #define MAYBE_PopupAndSystemTray PopupAndSystemTray
    308 #define MAYBE_PopupAndAutoHideShelf PopupAndAutoHideShelf
    309 #define MAYBE_PopupAndFullscreen PopupAndFullscreen
    310 #else
    311 #define MAYBE_PopupAndSystemTray DISABLED_PopupAndSystemTray
    312 #define MAYBE_PopupAndAutoHideShelf DISABLED_PopupAndAutoHideShelf
    313 #define MAYBE_PopupAndFullscreen DISABLED_PopupAndFullscreen
    314 #endif
    315 
    316 TEST_F(WebNotificationTrayTest, MAYBE_PopupAndSystemTray) {
    317   TestItem* test_item = new TestItem;
    318   GetSystemTray()->AddTrayItem(test_item);
    319 
    320   AddNotification("test_id");
    321   EXPECT_TRUE(GetTray()->IsPopupVisible());
    322   gfx::Rect work_area = GetPopupWorkArea();
    323 
    324   // System tray is created, the popup's work area should be narrowed but still
    325   // visible.
    326   GetSystemTray()->ShowDefaultView(BUBBLE_CREATE_NEW);
    327   EXPECT_TRUE(GetTray()->IsPopupVisible());
    328   gfx::Rect work_area_with_tray = GetPopupWorkArea();
    329   EXPECT_GT(work_area.size().GetArea(), work_area_with_tray.size().GetArea());
    330 
    331   // System tray notification is also created, the popup's work area is narrowed
    332   // even more, but still visible.
    333   GetSystemTray()->ShowNotificationView(test_item);
    334   EXPECT_TRUE(GetTray()->IsPopupVisible());
    335   gfx::Rect work_area_with_tray_notification = GetPopupWorkArea();
    336   EXPECT_GT(work_area.size().GetArea(),
    337             work_area_with_tray_notification.size().GetArea());
    338   EXPECT_GT(work_area_with_tray.size().GetArea(),
    339             work_area_with_tray_notification.size().GetArea());
    340 
    341   // Close system tray, only system tray notifications.
    342   GetSystemTray()->ClickedOutsideBubble();
    343   EXPECT_TRUE(GetTray()->IsPopupVisible());
    344   gfx::Rect work_area_with_notification = GetPopupWorkArea();
    345   EXPECT_GT(work_area.size().GetArea(),
    346             work_area_with_notification.size().GetArea());
    347   EXPECT_LT(work_area_with_tray_notification.size().GetArea(),
    348             work_area_with_notification.size().GetArea());
    349 
    350   // Close the system tray notifications.
    351   GetSystemTray()->HideNotificationView(test_item);
    352   EXPECT_TRUE(GetTray()->IsPopupVisible());
    353   EXPECT_EQ(work_area.ToString(), GetPopupWorkArea().ToString());
    354 }
    355 
    356 TEST_F(WebNotificationTrayTest, MAYBE_PopupAndAutoHideShelf) {
    357   AddNotification("test_id");
    358   EXPECT_TRUE(GetTray()->IsPopupVisible());
    359   gfx::Rect work_area = GetPopupWorkArea();
    360 
    361   // Shelf's auto-hide state won't be HIDDEN unless window exists.
    362   scoped_ptr<aura::Window> window(
    363       CreateTestWindowInShellWithBounds(gfx::Rect(1, 2, 3, 4)));
    364   ShelfLayoutManager* shelf =
    365       Shell::GetPrimaryRootWindowController()->GetShelfLayoutManager();
    366   shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
    367 
    368   EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
    369   gfx::Rect work_area_auto_hidden = GetPopupWorkArea();
    370   EXPECT_LT(work_area.size().GetArea(), work_area_auto_hidden.size().GetArea());
    371 
    372   // Close the window, which shows the shelf.
    373   window.reset();
    374   EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->auto_hide_state());
    375   gfx::Rect work_area_auto_shown = GetPopupWorkArea();
    376   EXPECT_EQ(work_area.ToString(), work_area_auto_shown.ToString());
    377 
    378   // Create the system tray during auto-hide.
    379   window.reset(CreateTestWindowInShellWithBounds(gfx::Rect(1, 2, 3, 4)));
    380   TestItem* test_item = new TestItem;
    381   GetSystemTray()->AddTrayItem(test_item);
    382   GetSystemTray()->ShowDefaultView(BUBBLE_CREATE_NEW);
    383 
    384   EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->auto_hide_state());
    385   EXPECT_TRUE(GetTray()->IsPopupVisible());
    386   gfx::Rect work_area_with_tray = GetPopupWorkArea();
    387   EXPECT_GT(work_area_auto_shown.size().GetArea(),
    388             work_area_with_tray.size().GetArea());
    389 
    390   // Create tray notification.
    391   GetSystemTray()->ShowNotificationView(test_item);
    392   EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->auto_hide_state());
    393   gfx::Rect work_area_with_tray_notification = GetPopupWorkArea();
    394   EXPECT_GT(work_area_with_tray.size().GetArea(),
    395             work_area_with_tray_notification.size().GetArea());
    396 
    397   // Close the system tray.
    398   GetSystemTray()->ClickedOutsideBubble();
    399   shelf->UpdateAutoHideState();
    400   RunAllPendingInMessageLoop();
    401   EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
    402   gfx::Rect work_area_hidden_with_tray_notification = GetPopupWorkArea();
    403   EXPECT_LT(work_area_with_tray_notification.size().GetArea(),
    404             work_area_hidden_with_tray_notification.size().GetArea());
    405   EXPECT_GT(work_area_auto_hidden.size().GetArea(),
    406             work_area_hidden_with_tray_notification.size().GetArea());
    407 
    408   // Close the window again, which shows the shelf.
    409   window.reset();
    410   EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->auto_hide_state());
    411   gfx::Rect work_area_shown_with_tray_notification = GetPopupWorkArea();
    412   EXPECT_GT(work_area_hidden_with_tray_notification.size().GetArea(),
    413             work_area_shown_with_tray_notification.size().GetArea());
    414   EXPECT_GT(work_area_auto_shown.size().GetArea(),
    415             work_area_shown_with_tray_notification.size().GetArea());
    416 }
    417 
    418 TEST_F(WebNotificationTrayTest, MAYBE_PopupAndFullscreen) {
    419   AddNotification("test_id");
    420   EXPECT_TRUE(IsPopupVisible());
    421   gfx::Rect work_area = GetPopupWorkArea();
    422 
    423   // Checks the work area for normal auto-hidden state.
    424   scoped_ptr<aura::Window> window(
    425       CreateTestWindowInShellWithBounds(gfx::Rect(1, 2, 3, 4)));
    426   ShelfLayoutManager* shelf =
    427       Shell::GetPrimaryRootWindowController()->GetShelfLayoutManager();
    428   shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_ALWAYS);
    429   EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
    430   gfx::Rect work_area_auto_hidden = GetPopupWorkArea();
    431   shelf->SetAutoHideBehavior(SHELF_AUTO_HIDE_BEHAVIOR_NEVER);
    432 
    433   // Put |window| into fullscreen without forcing the shelf to hide. Currently,
    434   // this is used by immersive fullscreen and forces the shelf to be auto
    435   // hidden.
    436   wm::GetWindowState(window.get())->set_hide_shelf_when_fullscreen(false);
    437   window->SetProperty(aura::client::kShowStateKey, ui::SHOW_STATE_FULLSCREEN);
    438   RunAllPendingInMessageLoop();
    439 
    440   // The work area for auto-hidden status of fullscreen is a bit larger
    441   // since it doesn't even have the 3-pixel width.
    442   EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
    443   gfx::Rect work_area_fullscreen_hidden = GetPopupWorkArea();
    444   EXPECT_EQ(work_area_auto_hidden.ToString(),
    445             work_area_fullscreen_hidden.ToString());
    446 
    447   // Move the mouse cursor at the bottom, which shows the shelf.
    448   aura::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
    449   gfx::Point bottom_right =
    450       Shell::GetScreen()->GetPrimaryDisplay().bounds().bottom_right();
    451   bottom_right.Offset(-1, -1);
    452   generator.MoveMouseTo(bottom_right);
    453   shelf->UpdateAutoHideStateNow();
    454   EXPECT_EQ(SHELF_AUTO_HIDE_SHOWN, shelf->auto_hide_state());
    455   EXPECT_EQ(work_area.ToString(), GetPopupWorkArea().ToString());
    456 
    457   generator.MoveMouseTo(work_area.CenterPoint());
    458   shelf->UpdateAutoHideStateNow();
    459   EXPECT_EQ(SHELF_AUTO_HIDE_HIDDEN, shelf->auto_hide_state());
    460   EXPECT_EQ(work_area_auto_hidden.ToString(), GetPopupWorkArea().ToString());
    461 }
    462 
    463 TEST_F(WebNotificationTrayTest, MAYBE_PopupAndSystemTrayMultiDisplay) {
    464   UpdateDisplay("800x600,600x400");
    465 
    466   AddNotification("test_id");
    467   gfx::Rect work_area = GetPopupWorkArea();
    468   gfx::Rect work_area_second = GetPopupWorkAreaForTray(GetSecondaryTray());
    469 
    470   // System tray is created on the primary display. The popups in the secondary
    471   // tray aren't affected.
    472   GetSystemTray()->ShowDefaultView(BUBBLE_CREATE_NEW);
    473   EXPECT_GT(work_area.size().GetArea(), GetPopupWorkArea().size().GetArea());
    474   EXPECT_EQ(work_area_second.ToString(),
    475             GetPopupWorkAreaForTray(GetSecondaryTray()).ToString());
    476 }
    477 
    478 }  // namespace ash
    479