diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-01-29 16:35:13 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-02-01 15:33:35 +0000 |
commit | c8c2d1901aec01e934adf561a9fdf0cc776cdef8 (patch) | |
tree | 9157c3d9815e5870799e070b113813bec53e0535 /chromium/ui/message_center | |
parent | abefd5095b41dac94ca451d784ab6e27372e981a (diff) | |
download | qtwebengine-chromium-c8c2d1901aec01e934adf561a9fdf0cc776cdef8.tar.gz |
BASELINE: Update Chromium to 64.0.3282.139
Change-Id: I1cae68fe9c94ff7608b26b8382fc19862cdb293a
Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
Diffstat (limited to 'chromium/ui/message_center')
76 files changed, 2716 insertions, 1670 deletions
diff --git a/chromium/ui/message_center/BUILD.gn b/chromium/ui/message_center/BUILD.gn index b42aef2955c..d0faf6f4d20 100644 --- a/chromium/ui/message_center/BUILD.gn +++ b/chromium/ui/message_center/BUILD.gn @@ -57,6 +57,8 @@ component("message_center") { "//build/config/compiler:no_size_t_to_int_warning", ] sources = [ + "change_queue.cc", + "change_queue.h", "cocoa/notification_controller.h", "cocoa/notification_controller.mm", "cocoa/opaque_views.h", @@ -73,9 +75,6 @@ component("message_center") { "message_center_observer.h", "message_center_style.cc", "message_center_style.h", - "message_center_tray.cc", - "message_center_tray.h", - "message_center_tray_delegate.h", "message_center_types.h", "notification.cc", "notification.h", @@ -87,12 +86,15 @@ component("message_center") { "notification_list.h", "notification_types.cc", "notification_types.h", - "notifier_settings.cc", - "notifier_settings.h", + "notifier_id.cc", + "notifier_id.h", "popup_timer.cc", "popup_timer.h", "popup_timers_controller.cc", "popup_timers_controller.h", + "ui_controller.cc", + "ui_controller.h", + "ui_delegate.h", ] sources += get_target_outputs(":message_center_vector_icons") @@ -115,19 +117,18 @@ component("message_center") { # with message_center_unittests below. if (toolkit_views && !is_mac) { sources += [ - "notification_delegate_views.cc", "views/bounded_label.cc", "views/bounded_label.h", "views/constants.h", "views/desktop_popup_alignment_delegate.cc", "views/desktop_popup_alignment_delegate.h", - "views/message_center_controller.h", "views/message_popup_collection.cc", "views/message_popup_collection.h", "views/message_view.cc", "views/message_view.h", "views/message_view_context_menu_controller.cc", "views/message_view_context_menu_controller.h", + "views/message_view_delegate.h", "views/message_view_factory.cc", "views/message_view_factory.h", "views/notification_button.cc", @@ -136,6 +137,8 @@ component("message_center") { "views/notification_control_buttons_view.h", "views/notification_header_view.cc", "views/notification_header_view.h", + "views/notification_menu_model.cc", + "views/notification_menu_model.h", "views/notification_view.cc", "views/notification_view.h", "views/notification_view_md.cc", @@ -172,8 +175,8 @@ component("message_center") { sources += [ "notification.cc", "notification.h", - "notifier_settings.cc", - "notifier_settings.h", + "notifier_id.cc", + "notifier_id.h", ] } } @@ -186,8 +189,8 @@ if (enable_message_center) { sources = [ "fake_message_center.cc", "fake_message_center.h", - "fake_message_center_tray_delegate.cc", - "fake_message_center_tray_delegate.h", + "fake_ui_delegate.cc", + "fake_ui_delegate.h", ] deps = [ @@ -205,15 +208,15 @@ if (enable_message_center) { test("message_center_unittests") { sources = [ + "change_queue_unittest.cc", "cocoa/notification_controller_unittest.mm", "cocoa/popup_collection_unittest.mm", "cocoa/popup_controller_unittest.mm", "message_center_impl_unittest.cc", - "message_center_tray_unittest.cc", - "mojo/struct_traits_unittest.cc", "notification_delegate_unittest.cc", "notification_list_unittest.cc", "test/run_all_unittests.cc", + "ui_controller_unittest.cc", ] deps = [ @@ -221,7 +224,6 @@ if (enable_message_center) { ":test_support", "//base", "//base/test:test_support", - "//mojo/edk/system", "//skia", "//testing/gmock", "//testing/gtest", @@ -234,7 +236,6 @@ if (enable_message_center) { "//ui/gfx/geometry", "//ui/gl", "//ui/gl:test_support", - "//ui/message_center/mojo:test_interfaces", "//ui/message_center/public/cpp", "//ui/resources", "//ui/resources:ui_test_pak", @@ -246,6 +247,14 @@ if (enable_message_center) { "//ui/resources:ui_test_pak_data", ] + if (is_chromeos) { + sources += [ "mojo/struct_traits_unittest.cc" ] + deps += [ + "//mojo/edk/system", + "//ui/message_center/mojo:test_interfaces", + ] + } + if (is_mac) { deps += [ "//ui/gfx:test_support" ] } @@ -254,6 +263,7 @@ if (enable_message_center) { sources += [ "views/bounded_label_unittest.cc", "views/message_popup_collection_unittest.cc", + "views/notification_menu_model_unittest.cc", "views/notification_view_md_unittest.cc", "views/notification_view_unittest.cc", ] diff --git a/chromium/ui/message_center/change_queue.cc b/chromium/ui/message_center/change_queue.cc new file mode 100644 index 00000000000..a61dcd2989e --- /dev/null +++ b/chromium/ui/message_center/change_queue.cc @@ -0,0 +1,164 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/message_center/change_queue.h" + +#include "ui/message_center/message_center_impl.h" +#include "ui/message_center/notification.h" + +namespace message_center { + +// Change represents an operation made on a notification. Since it contains +// the final state of the notification, except complex cases, we generally +// optimize the list and keep only the last change for a particular notification +// that is in the notification list around. There are two ids; |id_| is the +// post-update notification id that has been assigned by an update, and +// |previous_id| is the previous id of the notification before the Change. +// The two ids are same unless the Change changes the id of the notification. +// See the comments of id() and previous_id() for reference. +class ChangeQueue::Change { + public: + Change(ChangeType type, + const std::string& id, + std::unique_ptr<Notification> notification); + ~Change(); + + // Used to transfer ownership of the contained notification. + std::unique_ptr<Notification> PassNotification(); + + Notification* notification() const { return notification_.get(); } + // Returns the post-update ID. It means: + // - ADD event: ID of the notification to be added. In this case, this must be + // same as |previous_id()|. + // - UPDATE event: ID of the notification after the change. If the change + // doesn't update its ID, this value is same as |previous_id()|. + // - DELETE event: ID of the notification to be deleted. This must be same as + // |previous_id()|. + const std::string& id() const { return id_; } + ChangeType type() const { return type_; } + bool by_user() const { return by_user_; } + void set_by_user(bool by_user) { by_user_ = by_user; } + // Returns the ID which is used in the notification list. In other word, it + // means the ID before the change. + const std::string& previous_id() const { return previous_id_; } + void set_type(const ChangeType new_type) { type_ = new_type; } + void ReplaceNotification(std::unique_ptr<Notification> new_notification); + + private: + ChangeType type_; + std::string id_; + std::string previous_id_; + bool by_user_; + std::unique_ptr<Notification> notification_; + + DISALLOW_COPY_AND_ASSIGN(Change); +}; + +//////////////////////////////////////////////////////////////////////////////// +// ChangeFinder + +struct ChangeFinder { + explicit ChangeFinder(const std::string& id) : id(id) {} + bool operator()(const std::unique_ptr<ChangeQueue::Change>& change) { + return change->id() == id; + } + + std::string id; +}; + +//////////////////////////////////////////////////////////////////////////////// +// ChangeQueue::Change + +ChangeQueue::Change::Change(ChangeType type, + const std::string& id, + std::unique_ptr<Notification> notification) + : type_(type), + previous_id_(id), + by_user_(false), + notification_(std::move(notification)) { + DCHECK(!id.empty()); + DCHECK(type != CHANGE_TYPE_DELETE || !notification_); + + id_ = notification_ ? notification_->id() : previous_id_; +} + +ChangeQueue::Change::~Change() {} + +std::unique_ptr<Notification> ChangeQueue::Change::PassNotification() { + return std::move(notification_); +} + +void ChangeQueue::Change::ReplaceNotification( + std::unique_ptr<Notification> new_notification) { + id_ = new_notification ? new_notification->id() : previous_id_; + notification_.swap(new_notification); +} + +//////////////////////////////////////////////////////////////////////////////// +// ChangeQueue + +ChangeQueue::ChangeQueue() = default; + +ChangeQueue::~ChangeQueue() = default; + +void ChangeQueue::ApplyChanges(MessageCenterImpl* message_center) { + while (!changes_.empty()) { + auto iter = changes_.begin(); + std::unique_ptr<Change> change(std::move(*iter)); + changes_.erase(iter); + ApplyChangeInternal(message_center, std::move(change)); + } +} + +void ChangeQueue::AddNotification(std::unique_ptr<Notification> notification) { + std::string id = notification->id(); + changes_.push_back( + std::make_unique<Change>(CHANGE_TYPE_ADD, id, std::move(notification))); +} + +void ChangeQueue::UpdateNotification( + const std::string& old_id, + std::unique_ptr<Notification> notification) { + changes_.push_back(std::make_unique<Change>(CHANGE_TYPE_UPDATE, old_id, + std::move(notification))); +} + +void ChangeQueue::RemoveNotification(const std::string& id, bool by_user) { + auto change = std::make_unique<Change>(CHANGE_TYPE_DELETE, id, nullptr); + change->set_by_user(by_user); + changes_.push_back(std::move(change)); +} + +Notification* ChangeQueue::GetLatestNotification(const std::string& id) const { + auto iter = + std::find_if(changes_.rbegin(), changes_.rend(), ChangeFinder(id)); + if (iter == changes_.rend()) + return nullptr; + + if ((*iter)->type() == CHANGE_TYPE_DELETE) + return nullptr; + + return (*iter)->notification(); +} + +void ChangeQueue::ApplyChangeInternal(MessageCenterImpl* message_center, + std::unique_ptr<Change> change) { + switch (change->type()) { + case CHANGE_TYPE_ADD: + message_center->AddNotificationImmediately(change->PassNotification()); + break; + case CHANGE_TYPE_UPDATE: + message_center->UpdateNotificationImmediately(change->previous_id(), + change->PassNotification()); + break; + case CHANGE_TYPE_DELETE: + message_center->RemoveNotificationImmediately(change->previous_id(), + change->by_user()); + break; + default: + NOTREACHED(); + } +} + +} // namespace message_center diff --git a/chromium/ui/message_center/change_queue.h b/chromium/ui/message_center/change_queue.h new file mode 100644 index 00000000000..b2c9d287c6b --- /dev/null +++ b/chromium/ui/message_center/change_queue.h @@ -0,0 +1,73 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_MESSAGE_CENTER_CHANGE_QUEUE_H_ +#define UI_MESSAGE_CENTER_CHANGE_QUEUE_H_ + +#include <vector> + +#include "base/macros.h" +#include "base/memory/ptr_util.h" +#include "ui/message_center/message_center_export.h" + +namespace message_center { + +class MessageCenterImpl; +class Notification; + +// ChangeQueue keeps track of all the changes. This class can be used for +// queuing changes during the message center can't be updated. +class MESSAGE_CENTER_EXPORT ChangeQueue { + public: + enum ChangeType { + CHANGE_TYPE_ADD = 0, + CHANGE_TYPE_UPDATE, + CHANGE_TYPE_DELETE + }; + + // Change represents an operation made on a notification. Since it contains + // the final state of the notification, we only keep the last change for a + // particular notification that is in the notification list around. There are + // two ids; |id_| is the newest notification id that has been assigned by an + // update, and |notification_list_id_| is the id of the notification it should + // be updating as it exists in the notification list. + class Change; + + ChangeQueue(); + ~ChangeQueue(); + + // Called when the message center has appropriate visibility. Modifies + // |message_center| but does not retain it. This also causes the queue to + // empty itself. + void ApplyChanges(MessageCenterImpl* message_center); + + // Causes a TYPE_ADD change to be added to the queue. + void AddNotification(std::unique_ptr<Notification> notification); + + // Causes a TYPE_UPDATE change to be added to the queue. + void UpdateNotification(const std::string& old_id, + std::unique_ptr<Notification> notification); + + // Causes a TYPE_DELETE change to be added to the queue. + void RemoveNotification(const std::string& id, bool by_user); + + // Returns the pointer to the Notification in the latest Change which has the + // given ID. If the Change is UPDATE, the ID after the change is searched. + // If the Change id REMOVE, this returns nullptr. + // ChangeQueue keeps retaining ownership of the Notification. The returned + // notification can be modified or be copied by caller. + Notification* GetLatestNotification(const std::string& id) const; + + private: + void ApplyChangeInternal(MessageCenterImpl* message_center, + std::unique_ptr<Change> change); + + std::vector<std::unique_ptr<Change>> changes_; + + DISALLOW_COPY_AND_ASSIGN(ChangeQueue); +}; + +} // namespace message_center + +#endif // UI_MESSAGE_CENTER_CHANGE_QUEUE_H_ diff --git a/chromium/ui/message_center/change_queue_unittest.cc b/chromium/ui/message_center/change_queue_unittest.cc new file mode 100644 index 00000000000..bf475f2d052 --- /dev/null +++ b/chromium/ui/message_center/change_queue_unittest.cc @@ -0,0 +1,133 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/message_center/change_queue.h" + +#include <utility> + +#include "base/strings/utf_string_conversions.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/message_center/message_center_impl.h" + +using base::UTF8ToUTF16; + +namespace message_center { + +namespace { + +Notification* CreateSimpleNotification(const std::string& id) { + RichNotificationData optional_fields; + optional_fields.buttons.push_back(ButtonInfo(UTF8ToUTF16("foo"))); + optional_fields.buttons.push_back(ButtonInfo(UTF8ToUTF16("foo"))); + return new Notification( + NOTIFICATION_TYPE_SIMPLE, id, UTF8ToUTF16("title"), UTF8ToUTF16(id), + gfx::Image() /* icon */, base::string16() /* display_source */, GURL(), + NotifierId(NotifierId::APPLICATION, "change_queue_test"), optional_fields, + NULL); +} + +} // namespace + +class ChangeQueueTest : public testing::Test { + public: + ChangeQueueTest() = default; + ~ChangeQueueTest() override = default; + + MessageCenterImpl* message_center() { return &message_center_; } + + private: + MessageCenterImpl message_center_; +}; + +TEST_F(ChangeQueueTest, Queueing) { + ChangeQueue queue; + std::string id("id1"); + std::string id2("id2"); + NotifierId notifier_id1(NotifierId::APPLICATION, "app1"); + + // First, add and update a notification to ensure updates happen + // normally. + std::unique_ptr<Notification> notification(CreateSimpleNotification(id)); + message_center()->AddNotification(std::move(notification)); + notification.reset(CreateSimpleNotification(id2)); + message_center()->UpdateNotification(id, std::move(notification)); + EXPECT_TRUE(message_center()->FindVisibleNotificationById(id2)); + EXPECT_FALSE(message_center()->FindVisibleNotificationById(id)); + + // Then update a notification; nothing should have happened. + notification.reset(CreateSimpleNotification(id)); + queue.UpdateNotification(id2, std::move(notification)); + EXPECT_TRUE(message_center()->FindVisibleNotificationById(id2)); + EXPECT_FALSE(message_center()->FindVisibleNotificationById(id)); + + // Apply the changes from the queue. + queue.ApplyChanges(message_center()); + + // Close the message center; then the update should have propagated. + EXPECT_FALSE(message_center()->FindVisibleNotificationById(id2)); + EXPECT_TRUE(message_center()->FindVisibleNotificationById(id)); +} + +TEST_F(ChangeQueueTest, SimpleQueueing) { + ChangeQueue queue; + std::string ids[6] = {"0", "1", "2", "3", "4", "5"}; + NotifierId notifier_id1(NotifierId::APPLICATION, "app1"); + + std::unique_ptr<Notification> notification; + // Prepare initial notifications on NotificationList. + // NL: ["0", "1", "2", "4"] + int i = 0; + for (; i < 3; i++) { + notification.reset(CreateSimpleNotification(ids[i])); + message_center()->AddNotification(std::move(notification)); + } + for (i = 0; i < 3; i++) { + EXPECT_TRUE(message_center()->FindVisibleNotificationById(ids[i])); + } + for (; i < 6; i++) { + EXPECT_FALSE(message_center()->FindVisibleNotificationById(ids[i])); + } + + // This should update notification "1" to have id "4". + notification.reset(CreateSimpleNotification(ids[4])); + queue.AddNotification(std::move(notification)); + + // Change the ID: "2" -> "5", "5" -> "3". + notification.reset(CreateSimpleNotification(ids[5])); + queue.UpdateNotification(ids[2], std::move(notification)); + notification.reset(CreateSimpleNotification(ids[3])); + queue.UpdateNotification(ids[5], std::move(notification)); + + // This should update notification "4" to "5". + notification.reset(CreateSimpleNotification(ids[5])); + queue.UpdateNotification(ids[4], std::move(notification)); + + // This should create a new "4", that doesn't overwrite the update from 4 + // before. + notification.reset(CreateSimpleNotification(ids[4])); + queue.AddNotification(std::move(notification)); + + // The NL should still be the same: ["0", "1", "2", "4"] + EXPECT_TRUE(message_center()->FindVisibleNotificationById(ids[0])); + EXPECT_TRUE(message_center()->FindVisibleNotificationById(ids[1])); + EXPECT_TRUE(message_center()->FindVisibleNotificationById(ids[2])); + EXPECT_FALSE(message_center()->FindVisibleNotificationById(ids[3])); + EXPECT_FALSE(message_center()->FindVisibleNotificationById(ids[4])); + EXPECT_FALSE(message_center()->FindVisibleNotificationById(ids[5])); + EXPECT_EQ(message_center()->GetVisibleNotifications().size(), 3u); + + // Apply the changes from the queue. + queue.ApplyChanges(message_center()); + + // Changes should be applied. + EXPECT_TRUE(message_center()->FindVisibleNotificationById(ids[0])); + EXPECT_TRUE(message_center()->FindVisibleNotificationById(ids[1])); + EXPECT_FALSE(message_center()->FindVisibleNotificationById(ids[2])); + EXPECT_TRUE(message_center()->FindVisibleNotificationById(ids[3])); + EXPECT_TRUE(message_center()->FindVisibleNotificationById(ids[4])); + EXPECT_TRUE(message_center()->FindVisibleNotificationById(ids[5])); + EXPECT_EQ(message_center()->GetVisibleNotifications().size(), 5u); +} + +} // namespace message_center diff --git a/chromium/ui/message_center/cocoa/notification_controller.mm b/chromium/ui/message_center/cocoa/notification_controller.mm index 580f72215a1..1b9ff3e657e 100644 --- a/chromium/ui/message_center/cocoa/notification_controller.mm +++ b/chromium/ui/message_center/cocoa/notification_controller.mm @@ -340,8 +340,7 @@ [rootView addSubview:[self createSmallImageInFrame:rootFrame]]; // Create the settings button. - if (notification_->delegate() && - notification_->delegate()->ShouldDisplaySettingsButton()) { + if (notification_->should_show_settings_button()) { [self configureSettingsButtonInFrame:rootFrame]; [rootView addSubview:settingsButton_]; } diff --git a/chromium/ui/message_center/cocoa/notification_controller_unittest.mm b/chromium/ui/message_center/cocoa/notification_controller_unittest.mm index 0d3c73378db..5b385a5ef00 100644 --- a/chromium/ui/message_center/cocoa/notification_controller_unittest.mm +++ b/chromium/ui/message_center/cocoa/notification_controller_unittest.mm @@ -24,16 +24,6 @@ using base::UTF8ToUTF16; namespace { -// A test delegate used for tests that deal with the notification settings -// button. -class NotificationSettingsDelegate - : public message_center::NotificationDelegate { - bool ShouldDisplaySettingsButton() override { return true; } - - private: - ~NotificationSettingsDelegate() override {} -}; - class MockMessageCenter : public message_center::FakeMessageCenter { public: MockMessageCenter() @@ -157,14 +147,14 @@ TEST_F(NotificationControllerTest, BasicLayout) { } TEST_F(NotificationControllerTest, NotificationSetttingsButtonLayout) { + message_center::RichNotificationData data; + data.settings_button_handler = SettingsButtonHandler::TRAY; std::unique_ptr<message_center::Notification> notification( new message_center::Notification( message_center::NOTIFICATION_TYPE_SIMPLE, "", ASCIIToUTF16("Added to circles"), ASCIIToUTF16("Jonathan and 5 others"), gfx::Image(), base::string16(), - GURL("https://plus.com"), DummyNotifierId(), - message_center::RichNotificationData(), - new NotificationSettingsDelegate())); + GURL("https://plus.com"), DummyNotifierId(), data, NULL)); base::scoped_nsobject<MCNotificationController> controller( [[MCNotificationController alloc] initWithNotification:notification.get() diff --git a/chromium/ui/message_center/cocoa/popup_collection.mm b/chromium/ui/message_center/cocoa/popup_collection.mm index d13a1bab6f9..b4df87a8a11 100644 --- a/chromium/ui/message_center/cocoa/popup_collection.mm +++ b/chromium/ui/message_center/cocoa/popup_collection.mm @@ -167,8 +167,8 @@ class PopupCollectionObserver : public message_center::MessageCenterObserver { NSRect screenFrame = [self screenFrame]; NSRect popupFrame = [popup bounds]; - CGFloat x = NSMaxX(screenFrame) - message_center::kMarginBetweenItems - - NSWidth(popupFrame); + CGFloat x = NSMaxX(screenFrame) - message_center::kMarginBetweenPopups - + NSWidth(popupFrame); CGFloat y = 0; MCPopupController* bottomPopup = [popups_ lastObject]; @@ -178,7 +178,7 @@ class PopupCollectionObserver : public message_center::MessageCenterObserver { y = NSMinY([bottomPopup bounds]); } - y -= message_center::kMarginBetweenItems + NSHeight(popupFrame); + y -= message_center::kMarginBetweenPopups + NSHeight(popupFrame); if (y > NSMinY(screenFrame)) { animatingNotificationIDs_.insert(notification->id()); @@ -264,8 +264,8 @@ class PopupCollectionObserver : public message_center::MessageCenterObserver { MCPopupController* popup = [popups_ objectAtIndex:i]; NSRect oldFrame = [popup bounds]; NSRect frame = oldFrame; - frame.origin.y = maxY - message_center::kMarginBetweenItems - - NSHeight(frame); + frame.origin.y = + maxY - message_center::kMarginBetweenPopups - NSHeight(frame); // If this popup does not fit on screen, stop repositioning and close this // and subsequent popups. diff --git a/chromium/ui/message_center/cocoa/popup_collection_unittest.mm b/chromium/ui/message_center/cocoa/popup_collection_unittest.mm index 3a7201fdb5b..b1e645240f5 100644 --- a/chromium/ui/message_center/cocoa/popup_collection_unittest.mm +++ b/chromium/ui/message_center/cocoa/popup_collection_unittest.mm @@ -85,8 +85,8 @@ class PopupCollectionTest : public ui::CocoaTest { CGFloat minY = NSMinY([[upper window] frame]); CGFloat maxY = NSMaxY([[lower window] frame]); CGFloat delta = minY - maxY; - EXPECT_EQ(message_center::kMarginBetweenItems, delta); - return delta == message_center::kMarginBetweenItems; + EXPECT_EQ(message_center::kMarginBetweenPopups, delta); + return delta == message_center::kMarginBetweenPopups; } void WaitForAnimationEnded() { @@ -154,7 +154,7 @@ TEST_F(PopupCollectionTest, LayoutSpacing) { AddThreeNotifications(); NSArray* popups = [collection_ popups]; - EXPECT_EQ(message_center::kMarginBetweenItems, + EXPECT_EQ(message_center::kMarginBetweenPopups, kScreenSize - NSMaxY([[[popups objectAtIndex:0] window] frame])); EXPECT_TRUE(CheckSpacingBetween([popups objectAtIndex:0], @@ -186,7 +186,7 @@ TEST_F(PopupCollectionTest, LayoutSpacing) { // Remove "1". center_->RemoveNotification("2", true); WaitForAnimationEnded(); - EXPECT_EQ(message_center::kMarginBetweenItems, + EXPECT_EQ(message_center::kMarginBetweenPopups, kScreenSize - NSMaxY([[[popups objectAtIndex:0] window] frame])); EXPECT_TRUE(CheckSpacingBetween([popups objectAtIndex:0], [popups objectAtIndex:1])); diff --git a/chromium/ui/message_center/fake_message_center.cc b/chromium/ui/message_center/fake_message_center.cc index 8adac41d1bd..0d507fc5d25 100644 --- a/chromium/ui/message_center/fake_message_center.cc +++ b/chromium/ui/message_center/fake_message_center.cc @@ -31,10 +31,6 @@ size_t FakeMessageCenter::NotificationCount() const { return 0u; } -size_t FakeMessageCenter::UnreadNotificationCount() const { - return 0u; -} - bool FakeMessageCenter::HasPopupNotifications() const { return false; } @@ -43,14 +39,6 @@ bool FakeMessageCenter::IsQuietMode() const { return false; } -bool FakeMessageCenter::IsLockedState() const { - return false; -} - -bool FakeMessageCenter::HasClickedListener(const std::string& id) { - return false; -} - message_center::Notification* FakeMessageCenter::FindVisibleNotificationById( const std::string& id) { for (auto* notification : GetVisibleNotifications()) { @@ -81,6 +69,9 @@ void FakeMessageCenter::RemoveNotification(const std::string& id, bool by_user) { } +void FakeMessageCenter::RemoveNotificationsForNotifierId( + const NotifierId& notifier_id) {} + void FakeMessageCenter::RemoveAllNotifications(bool by_user, RemoveType type) {} void FakeMessageCenter::SetNotificationIcon(const std::string& notification_id, @@ -104,29 +95,21 @@ void FakeMessageCenter::ClickOnNotificationButton(const std::string& id, int button_index) { } +void FakeMessageCenter::ClickOnNotificationButtonWithReply( + const std::string& id, + int button_index, + const base::string16& reply) {} + void FakeMessageCenter::ClickOnSettingsButton(const std::string& id) {} void FakeMessageCenter::MarkSinglePopupAsShown(const std::string& id, bool mark_notification_as_read) { } -void FakeMessageCenter::DisplayedNotification( - const std::string& id, - const DisplaySource source) { -} - -void FakeMessageCenter::SetNotifierSettingsProvider( - NotifierSettingsProvider* provider) { -} - -NotifierSettingsProvider* FakeMessageCenter::GetNotifierSettingsProvider() { - return NULL; -} - -void FakeMessageCenter::SetQuietMode(bool in_quiet_mode) { -} +void FakeMessageCenter::DisplayedNotification(const std::string& id, + const DisplaySource source) {} -void FakeMessageCenter::SetLockedState(bool locked) {} +void FakeMessageCenter::SetQuietMode(bool in_quiet_mode) {} void FakeMessageCenter::EnterQuietModeWithExpire( const base::TimeDelta& expires_in) { diff --git a/chromium/ui/message_center/fake_message_center.h b/chromium/ui/message_center/fake_message_center.h index c1c789b62a4..2a89a1e321f 100644 --- a/chromium/ui/message_center/fake_message_center.h +++ b/chromium/ui/message_center/fake_message_center.h @@ -25,11 +25,8 @@ class FakeMessageCenter : public MessageCenter { void AddNotificationBlocker(NotificationBlocker* blocker) override; void RemoveNotificationBlocker(NotificationBlocker* blocker) override; size_t NotificationCount() const override; - size_t UnreadNotificationCount() const override; bool HasPopupNotifications() const override; bool IsQuietMode() const override; - bool IsLockedState() const override; - bool HasClickedListener(const std::string& id) override; message_center::Notification* FindVisibleNotificationById( const std::string& id) override; const NotificationList::Notifications& GetVisibleNotifications() override; @@ -40,6 +37,7 @@ class FakeMessageCenter : public MessageCenter { std::unique_ptr<Notification> new_notification) override; void RemoveNotification(const std::string& id, bool by_user) override; + void RemoveNotificationsForNotifierId(const NotifierId& notifier_id) override; void RemoveAllNotifications(bool by_user, RemoveType type) override; void SetNotificationIcon(const std::string& notification_id, const gfx::Image& image) override; @@ -53,15 +51,15 @@ class FakeMessageCenter : public MessageCenter { void ClickOnNotification(const std::string& id) override; void ClickOnNotificationButton(const std::string& id, int button_index) override; + void ClickOnNotificationButtonWithReply(const std::string& id, + int button_index, + const base::string16& reply) override; void ClickOnSettingsButton(const std::string& id) override; void MarkSinglePopupAsShown(const std::string& id, bool mark_notification_as_read) override; void DisplayedNotification(const std::string& id, const DisplaySource source) override; - void SetNotifierSettingsProvider(NotifierSettingsProvider* provider) override; - NotifierSettingsProvider* GetNotifierSettingsProvider() override; void SetQuietMode(bool in_quiet_mode) override; - void SetLockedState(bool locked) override; void EnterQuietModeWithExpire(const base::TimeDelta& expires_in) override; void SetVisibility(Visibility visible) override; bool IsMessageCenterVisible() const override; diff --git a/chromium/ui/message_center/fake_message_center_tray_delegate.cc b/chromium/ui/message_center/fake_message_center_tray_delegate.cc deleted file mode 100644 index 748385425fd..00000000000 --- a/chromium/ui/message_center/fake_message_center_tray_delegate.cc +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/message_center/fake_message_center_tray_delegate.h" - -#include "base/message_loop/message_loop.h" -#include "ui/message_center/message_center_tray.h" - -namespace message_center { - -FakeMessageCenterTrayDelegate::FakeMessageCenterTrayDelegate( - MessageCenter* message_center) - : tray_(new MessageCenterTray(this, message_center)) {} - -FakeMessageCenterTrayDelegate::~FakeMessageCenterTrayDelegate() { -} - -void FakeMessageCenterTrayDelegate::OnMessageCenterTrayChanged() { -} - -bool FakeMessageCenterTrayDelegate::ShowPopups() { - return false; -} - -void FakeMessageCenterTrayDelegate::HidePopups() { -} - -bool FakeMessageCenterTrayDelegate::ShowMessageCenter(bool show_by_click) { - return false; -} - -void FakeMessageCenterTrayDelegate::HideMessageCenter() { -} - -bool FakeMessageCenterTrayDelegate::ShowNotifierSettings() { - return false; -} - -MessageCenterTray* FakeMessageCenterTrayDelegate::GetMessageCenterTray() { - return tray_.get(); -} - -} // namespace message_center diff --git a/chromium/ui/message_center/fake_message_center_tray_delegate.h b/chromium/ui/message_center/fake_message_center_tray_delegate.h deleted file mode 100644 index 2a7917db3f9..00000000000 --- a/chromium/ui/message_center/fake_message_center_tray_delegate.h +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2014 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_MESSAGE_CENTER_FAKE_MESSAGE_CENTER_TRAY_DELEGATE_H_ -#define UI_MESSAGE_CENTER_FAKE_MESSAGE_CENTER_TRAY_DELEGATE_H_ - -#include <memory> - -#include "base/callback.h" -#include "base/macros.h" -#include "ui/message_center/message_center_tray_delegate.h" - -namespace message_center { - -class MessageCenter; -class MessageCenterTray; - -// A message center tray delegate which does nothing. -class FakeMessageCenterTrayDelegate : public MessageCenterTrayDelegate { - public: - explicit FakeMessageCenterTrayDelegate(MessageCenter* message_center); - ~FakeMessageCenterTrayDelegate() override; - - // Overridden from MessageCenterTrayDelegate: - void OnMessageCenterTrayChanged() override; - bool ShowPopups() override; - void HidePopups() override; - bool ShowMessageCenter(bool show_by_click) override; - void HideMessageCenter() override; - bool ShowNotifierSettings() override; - MessageCenterTray* GetMessageCenterTray() override; - - private: - std::unique_ptr<MessageCenterTray> tray_; - base::Closure quit_closure_; - - DISALLOW_COPY_AND_ASSIGN(FakeMessageCenterTrayDelegate); -}; - -} // namespace message_center - -#endif // UI_MESSAGE_CENTER_FAKE_MESSAGE_CENTER_TRAY_DELEGATE_H_ diff --git a/chromium/ui/message_center/fake_ui_delegate.cc b/chromium/ui/message_center/fake_ui_delegate.cc new file mode 100644 index 00000000000..605b95b07b7 --- /dev/null +++ b/chromium/ui/message_center/fake_ui_delegate.cc @@ -0,0 +1,34 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/message_center/fake_ui_delegate.h" + +#include "base/message_loop/message_loop.h" +#include "ui/message_center/ui_controller.h" + +namespace message_center { + +FakeUiDelegate::FakeUiDelegate() : tray_(new UiController(this)) {} + +FakeUiDelegate::~FakeUiDelegate() {} + +void FakeUiDelegate::OnMessageCenterContentsChanged() {} + +bool FakeUiDelegate::ShowPopups() { + return false; +} + +void FakeUiDelegate::HidePopups() {} + +bool FakeUiDelegate::ShowMessageCenter(bool show_by_click) { + return false; +} + +void FakeUiDelegate::HideMessageCenter() {} + +bool FakeUiDelegate::ShowNotifierSettings() { + return false; +} + +} // namespace message_center diff --git a/chromium/ui/message_center/fake_ui_delegate.h b/chromium/ui/message_center/fake_ui_delegate.h new file mode 100644 index 00000000000..8729eba8ce3 --- /dev/null +++ b/chromium/ui/message_center/fake_ui_delegate.h @@ -0,0 +1,41 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_MESSAGE_CENTER_FAKE_UI_DELEGATE_H_ +#define UI_MESSAGE_CENTER_FAKE_UI_DELEGATE_H_ + +#include <memory> + +#include "base/callback.h" +#include "base/macros.h" +#include "ui/message_center/ui_delegate.h" + +namespace message_center { + +class UiController; + +// A message center UI delegate which does nothing. +class FakeUiDelegate : public UiDelegate { + public: + FakeUiDelegate(); + ~FakeUiDelegate() override; + + // Overridden from UiDelegate: + void OnMessageCenterContentsChanged() override; + bool ShowPopups() override; + void HidePopups() override; + bool ShowMessageCenter(bool show_by_click) override; + void HideMessageCenter() override; + bool ShowNotifierSettings() override; + + private: + std::unique_ptr<UiController> tray_; + base::Closure quit_closure_; + + DISALLOW_COPY_AND_ASSIGN(FakeUiDelegate); +}; + +} // namespace message_center + +#endif // UI_MESSAGE_CENTER_FAKE_UI_DELEGATE_H_ diff --git a/chromium/ui/message_center/message_center.h b/chromium/ui/message_center/message_center.h index 2ca6562c31c..fab8ce5964d 100644 --- a/chromium/ui/message_center/message_center.h +++ b/chromium/ui/message_center/message_center.h @@ -42,7 +42,6 @@ class MessagePopupCollectionTest; class MessageCenterObserver; class MessageCenterImplTest; class NotificationBlocker; -class NotifierSettingsProvider; class MESSAGE_CENTER_EXPORT MessageCenter { public: @@ -56,8 +55,8 @@ class MESSAGE_CENTER_EXPORT MessageCenter { // Creates the global message center object. static void Initialize(); - // Returns the global message center object. Returns NULL if Initialize is not - // called. + // Returns the global message center object. Returns null if Initialize is + // not called. static MessageCenter* Get(); // Destroys the global message_center object. @@ -69,14 +68,11 @@ class MESSAGE_CENTER_EXPORT MessageCenter { // Queries of current notification list status. virtual size_t NotificationCount() const = 0; - virtual size_t UnreadNotificationCount() const = 0; virtual bool HasPopupNotifications() const = 0; virtual bool IsQuietMode() const = 0; - virtual bool IsLockedState() const = 0; - virtual bool HasClickedListener(const std::string& id) = 0; - // Find the notification with the corresponding id. Returns NULL if not found. - // The returned instance is owned by the message center. + // Find the notification with the corresponding id. Returns null if not + // found. The returned instance is owned by the message center. virtual message_center::Notification* FindVisibleNotificationById( const std::string& id) = 0; @@ -106,6 +102,8 @@ class MESSAGE_CENTER_EXPORT MessageCenter { // Removes an existing notification. virtual void RemoveNotification(const std::string& id, bool by_user) = 0; + virtual void RemoveNotificationsForNotifierId( + const NotifierId& notifier_id) = 0; virtual void RemoveAllNotifications(bool by_user, RemoveType type) = 0; // Sets the icon image. Icon appears at the top-left of the notification. @@ -133,6 +131,14 @@ class MESSAGE_CENTER_EXPORT MessageCenter { virtual void ClickOnNotificationButton(const std::string& id, int button_index) = 0; + // This should be called by UI classes when a notification button with an + // input is clicked to trigger the notification's delegate callback and also + // update the message center observers. + virtual void ClickOnNotificationButtonWithReply( + const std::string& id, + int button_index, + const base::string16& reply) = 0; + // Called by the UI classes when the settings buttons is clicked // to trigger the notification's delegate and update the message // center observers. @@ -152,19 +158,9 @@ class MESSAGE_CENTER_EXPORT MessageCenter { const std::string& id, const DisplaySource source) = 0; - // Setter/getter of notifier settings provider. This will be a weak reference. - // This should be set at the initialization process. The getter may return - // NULL for tests. - virtual void SetNotifierSettingsProvider( - NotifierSettingsProvider* provider) = 0; - virtual NotifierSettingsProvider* GetNotifierSettingsProvider() = 0; - // This can be called to change the quiet mode state (without a timeout). virtual void SetQuietMode(bool in_quiet_mode) = 0; - // This can be called to change the lock mode state. - virtual void SetLockedState(bool locked) = 0; - // Temporarily enables quiet mode for |expires_in| time. virtual void EnterQuietModeWithExpire(const base::TimeDelta& expires_in) = 0; @@ -194,7 +190,7 @@ class MESSAGE_CENTER_EXPORT MessageCenter { friend class MessageCenterImplTest; friend class MessageCenterImplTestWithChangeQueue; friend class MessageCenterImplTestWithoutChangeQueue; - friend class MessageCenterTrayTest; + friend class UiControllerTest; friend class TrayViewControllerTest; friend class test::MessagePopupCollectionTest; virtual void DisableTimersForTest() = 0; diff --git a/chromium/ui/message_center/message_center_impl.cc b/chromium/ui/message_center/message_center_impl.cc index 64e45ddc3e4..80e10ed0c7e 100644 --- a/chromium/ui/message_center/message_center_impl.cc +++ b/chromium/ui/message_center/message_center_impl.cc @@ -9,6 +9,7 @@ #include <utility> #include <vector> +#include "base/auto_reset.h" #include "base/command_line.h" #include "base/macros.h" #include "base/memory/ptr_util.h" @@ -27,40 +28,51 @@ namespace message_center { +namespace internal { + //////////////////////////////////////////////////////////////////////////////// -// MessageCenterImpl::NotificationCache +// ScopedNotificationsIterationLock + +class ScopedNotificationsIterationLock { + public: + // Lock may be used recursively. + explicit ScopedNotificationsIterationLock(MessageCenterImpl* message_center) + : message_center_(message_center), + original_value_(message_center->iterating_) { + message_center_->iterating_ = true; + } -MessageCenterImpl::NotificationCache::NotificationCache() - : unread_count(0) {} + ~ScopedNotificationsIterationLock() { + DCHECK(message_center_->iterating_); + + // |original_value_| must be false. But handle the other case just in case. + message_center_->iterating_ = original_value_; + if (!original_value_) { + message_center_->notification_change_queue_->ApplyChanges( + message_center_); + } + } -MessageCenterImpl::NotificationCache::~NotificationCache() {} + private: + MessageCenterImpl* message_center_; + const bool original_value_; -void MessageCenterImpl::NotificationCache::Rebuild( - const NotificationList::Notifications& notifications) { - visible_notifications = notifications; - RecountUnread(); -} + DISALLOW_COPY_AND_ASSIGN(ScopedNotificationsIterationLock); +}; -void MessageCenterImpl::NotificationCache::RecountUnread() { - unread_count = 0; - for (auto* notification : visible_notifications) { - if (!notification->IsRead()) - ++unread_count; - } -} +} // namespace internal //////////////////////////////////////////////////////////////////////////////// // MessageCenterImpl MessageCenterImpl::MessageCenterImpl() : MessageCenter(), - popup_timers_controller_(new PopupTimersController(this)), - settings_provider_(NULL) { + popup_timers_controller_(new PopupTimersController(this)) { notification_list_.reset(new NotificationList(this)); + notification_change_queue_.reset(new ChangeQueue()); } MessageCenterImpl::~MessageCenterImpl() { - SetNotifierSettingsProvider(NULL); } void MessageCenterImpl::AddObserver(MessageCenterObserver* observer) { @@ -95,57 +107,49 @@ void MessageCenterImpl::RemoveNotificationBlocker( void MessageCenterImpl::OnBlockingStateChanged(NotificationBlocker* blocker) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - std::list<std::string> blocked_ids; + DCHECK(!iterating_); + std::list<const Notification*> blocked; NotificationList::PopupNotifications popups = - notification_list_->GetPopupNotifications(blockers_, &blocked_ids); + notification_list_->GetPopupNotifications(blockers_, &blocked); - for (const auto& id : blocked_ids) { + // Close already displayed pop-ups that are blocked now. + for (const auto* notification : blocked) { // Do not call MessageCenterImpl::MarkSinglePopupAsShown() directly here // just for performance reason. MessageCenterImpl::MarkSinglePopupAsShown() - // calls NotificationList::MarkSinglePopupAsShown() and then updates the - // unread count, but the whole cache will be recreated below. - notification_list_->MarkSinglePopupAsShown(id, true); + // calls NotificationList::MarkSinglePopupAsShown(), but the whole cache + // will be recreated below. + if (notification->IsRead()) + notification_list_->MarkSinglePopupAsShown(notification->id(), true); } - notification_cache_.Rebuild( - notification_list_->GetVisibleNotifications(blockers_)); + visible_notifications_ = + notification_list_->GetVisibleNotifications(blockers_); - for (const auto& id : blocked_ids) { - for (auto& observer : observer_list_) - observer.OnNotificationUpdated(id); + { + internal::ScopedNotificationsIterationLock lock(this); + for (const auto* notification : blocked) { + for (auto& observer : observer_list_) + observer.OnNotificationUpdated(notification->id()); + } } for (auto& observer : observer_list_) observer.OnBlockingStateChanged(blocker); } -void MessageCenterImpl::UpdateIconImage(const NotifierId& notifier_id, - const gfx::Image& icon) { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); -} - -void MessageCenterImpl::NotifierGroupChanged() { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); -} - -void MessageCenterImpl::NotifierEnabledChanged( - const NotifierId& notifier_id, bool enabled) { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - if (!enabled) { - RemoveNotificationsForNotifierId(notifier_id); - } -} - void MessageCenterImpl::SetVisibility(Visibility visibility) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + DCHECK(!iterating_); visible_ = (visibility == VISIBILITY_MESSAGE_CENTER); - if (visible_ && !locked_) { + if (visible_) { std::set<std::string> updated_ids; notification_list_->SetNotificationsShown(blockers_, &updated_ids); - notification_cache_.RecountUnread(); - for (const auto& id : updated_ids) { - for (auto& observer : observer_list_) - observer.OnNotificationUpdated(id); + { + internal::ScopedNotificationsIterationLock lock(this); + for (const auto& id : updated_ids) { + for (auto& observer : observer_list_) + observer.OnNotificationUpdated(id); + } } } @@ -160,12 +164,7 @@ bool MessageCenterImpl::IsMessageCenterVisible() const { size_t MessageCenterImpl::NotificationCount() const { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - return notification_cache_.visible_notifications.size(); -} - -size_t MessageCenterImpl::UnreadNotificationCount() const { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - return notification_cache_.unread_count; + return visible_notifications_.size(); } bool MessageCenterImpl::HasPopupNotifications() const { @@ -179,18 +178,6 @@ bool MessageCenterImpl::IsQuietMode() const { return notification_list_->quiet_mode(); } -bool MessageCenterImpl::IsLockedState() const { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - return locked_; -} - -bool MessageCenterImpl::HasClickedListener(const std::string& id) { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - scoped_refptr<NotificationDelegate> delegate = - notification_list_->GetNotificationDelegate(id); - return delegate.get() && delegate->HasClickedListener(); -} - message_center::Notification* MessageCenterImpl::FindVisibleNotificationById( const std::string& id) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); @@ -200,7 +187,7 @@ message_center::Notification* MessageCenterImpl::FindVisibleNotificationById( const NotificationList::Notifications& MessageCenterImpl::GetVisibleNotifications() { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - return notification_cache_.visible_notifications; + return visible_notifications_; } NotificationList::PopupNotifications @@ -219,12 +206,18 @@ void MessageCenterImpl::AddNotification( for (size_t i = 0; i < blockers_.size(); ++i) blockers_[i]->CheckState(); + if (iterating_) { + notification_change_queue_->AddNotification(std::move(notification)); + return; + } + AddNotificationImmediately(std::move(notification)); } void MessageCenterImpl::AddNotificationImmediately( std::unique_ptr<Notification> notification) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + DCHECK(!iterating_); const std::string id = notification->id(); // Sometimes the notification can be added with the same id and the @@ -232,13 +225,15 @@ void MessageCenterImpl::AddNotificationImmediately( // This is essentially an update rather than addition. bool already_exists = (notification_list_->GetNotificationById(id) != NULL); notification_list_->AddNotification(std::move(notification)); - notification_cache_.Rebuild( - notification_list_->GetVisibleNotifications(blockers_)); + visible_notifications_ = + notification_list_->GetVisibleNotifications(blockers_); if (already_exists) { + internal::ScopedNotificationsIterationLock lock(this); for (auto& observer : observer_list_) observer.OnNotificationUpdated(id); } else { + internal::ScopedNotificationsIterationLock lock(this); for (auto& observer : observer_list_) observer.OnNotificationAdded(id); } @@ -251,6 +246,12 @@ void MessageCenterImpl::UpdateNotification( for (size_t i = 0; i < blockers_.size(); ++i) blockers_[i]->CheckState(); + if (iterating_) { + notification_change_queue_->UpdateNotification(old_id, + std::move(new_notification)); + return; + } + UpdateNotificationImmediately(old_id, std::move(new_notification)); } @@ -258,15 +259,18 @@ void MessageCenterImpl::UpdateNotificationImmediately( const std::string& old_id, std::unique_ptr<Notification> new_notification) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + DCHECK(!iterating_); std::string new_id = new_notification->id(); notification_list_->UpdateNotificationMessage(old_id, std::move(new_notification)); - notification_cache_.Rebuild( - notification_list_->GetVisibleNotifications(blockers_)); + visible_notifications_ = + notification_list_->GetVisibleNotifications(blockers_); if (old_id == new_id) { + internal::ScopedNotificationsIterationLock lock(this); for (auto& observer : observer_list_) observer.OnNotificationUpdated(new_id); } else { + internal::ScopedNotificationsIterationLock lock(this); for (auto& observer : observer_list_) observer.OnNotificationRemoved(old_id, false); for (auto& observer : observer_list_) @@ -277,12 +281,20 @@ void MessageCenterImpl::UpdateNotificationImmediately( void MessageCenterImpl::RemoveNotification(const std::string& id, bool by_user) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + + if (iterating_) { + notification_change_queue_->RemoveNotification(id, by_user); + return; + } + RemoveNotificationImmediately(id, by_user); } void MessageCenterImpl::RemoveNotificationImmediately( const std::string& id, bool by_user) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + DCHECK(!iterating_); + Notification* notification = FindVisibleNotificationById(id); if (notification == NULL) return; @@ -301,10 +313,29 @@ void MessageCenterImpl::RemoveNotificationImmediately( delegate->Close(by_user); notification_list_->RemoveNotification(copied_id); - notification_cache_.Rebuild( - notification_list_->GetVisibleNotifications(blockers_)); - for (auto& observer : observer_list_) - observer.OnNotificationRemoved(copied_id, by_user); + visible_notifications_ = + notification_list_->GetVisibleNotifications(blockers_); + { + internal::ScopedNotificationsIterationLock lock(this); + for (auto& observer : observer_list_) + observer.OnNotificationRemoved(copied_id, by_user); + } +} + +Notification* MessageCenterImpl::GetLatestNotificationIncludingQueued( + const std::string& id) const { + Notification* queued_notification = + notification_change_queue_->GetLatestNotification(id); + if (queued_notification) { + DCHECK(iterating_); + return queued_notification; + } + + Notification* notification = notification_list_->GetNotificationById(id); + if (notification) + return notification; + + return nullptr; } void MessageCenterImpl::RemoveNotificationsForNotifierId( @@ -315,13 +346,14 @@ void MessageCenterImpl::RemoveNotificationsForNotifierId( for (auto* notification : notifications) RemoveNotification(notification->id(), false); if (!notifications.empty()) { - notification_cache_.Rebuild( - notification_list_->GetVisibleNotifications(blockers_)); + visible_notifications_ = + notification_list_->GetVisibleNotifications(blockers_); } } void MessageCenterImpl::RemoveAllNotifications(bool by_user, RemoveType type) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + DCHECK(!iterating_); bool remove_pinned = (type == RemoveType::ALL); const NotificationBlockers& blockers = @@ -343,19 +375,37 @@ void MessageCenterImpl::RemoveAllNotifications(bool by_user, RemoveType type) { } if (!ids.empty()) { - notification_cache_.Rebuild( - notification_list_->GetVisibleNotifications(blockers_)); + visible_notifications_ = + notification_list_->GetVisibleNotifications(blockers_); } - for (const auto& id : ids) { - for (auto& observer : observer_list_) - observer.OnNotificationRemoved(id, by_user); + { + internal::ScopedNotificationsIterationLock lock(this); + for (const auto& id : ids) { + for (auto& observer : observer_list_) + observer.OnNotificationRemoved(id, by_user); + } } } void MessageCenterImpl::SetNotificationIcon(const std::string& notification_id, const gfx::Image& image) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + + if (iterating_) { + Notification* notification = + GetLatestNotificationIncludingQueued(notification_id); + if (notification) { + std::unique_ptr<Notification> copied_notification = + std::make_unique<Notification>(*notification); + copied_notification->set_icon(image); + notification_change_queue_->UpdateNotification( + notification_id, std::move(copied_notification)); + } + return; + } + if (notification_list_->SetNotificationIcon(notification_id, image)) { + internal::ScopedNotificationsIterationLock lock(this); for (auto& observer : observer_list_) observer.OnNotificationUpdated(notification_id); } @@ -363,8 +413,22 @@ void MessageCenterImpl::SetNotificationIcon(const std::string& notification_id, void MessageCenterImpl::SetNotificationImage(const std::string& notification_id, const gfx::Image& image) { + if (iterating_) { + Notification* notification = + GetLatestNotificationIncludingQueued(notification_id); + if (notification) { + std::unique_ptr<Notification> copied_notification = + std::make_unique<Notification>(*notification); + copied_notification->set_image(image); + notification_change_queue_->UpdateNotification( + notification_id, std::move(copied_notification)); + } + return; + } + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); if (notification_list_->SetNotificationImage(notification_id, image)) { + internal::ScopedNotificationsIterationLock lock(this); for (auto& observer : observer_list_) observer.OnNotificationUpdated(notification_id); } @@ -373,9 +437,23 @@ void MessageCenterImpl::SetNotificationImage(const std::string& notification_id, void MessageCenterImpl::SetNotificationButtonIcon( const std::string& notification_id, int button_index, const gfx::Image& image) { + if (iterating_) { + Notification* notification = + GetLatestNotificationIncludingQueued(notification_id); + if (notification) { + std::unique_ptr<Notification> copied_notification = + std::make_unique<Notification>(*notification); + copied_notification->SetButtonIcon(button_index, image); + notification_change_queue_->UpdateNotification( + notification_id, std::move(copied_notification)); + } + return; + } + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); if (notification_list_->SetNotificationButtonIcon(notification_id, button_index, image)) { + internal::ScopedNotificationsIterationLock lock(this); for (auto& observer : observer_list_) observer.OnNotificationUpdated(notification_id); } @@ -383,6 +461,7 @@ void MessageCenterImpl::SetNotificationButtonIcon( void MessageCenterImpl::ClickOnNotification(const std::string& id) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + DCHECK(!iterating_); if (FindVisibleNotificationById(id) == NULL) return; #if defined(OS_CHROMEOS) @@ -393,14 +472,18 @@ void MessageCenterImpl::ClickOnNotification(const std::string& id) { notification_list_->GetNotificationDelegate(id); if (delegate.get()) delegate->Click(); - for (auto& observer : observer_list_) - observer.OnNotificationClicked(id); + { + internal::ScopedNotificationsIterationLock lock(this); + for (auto& observer : observer_list_) + observer.OnNotificationClicked(id); + } } void MessageCenterImpl::ClickOnNotificationButton(const std::string& id, int button_index) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - if (FindVisibleNotificationById(id) == NULL) + DCHECK(!iterating_); + if (!FindVisibleNotificationById(id)) return; #if defined(OS_CHROMEOS) if (HasPopupNotifications()) @@ -410,21 +493,52 @@ void MessageCenterImpl::ClickOnNotificationButton(const std::string& id, notification_list_->GetNotificationDelegate(id); if (delegate.get()) delegate->ButtonClick(button_index); - for (auto& observer : observer_list_) - observer.OnNotificationButtonClicked(id, button_index); + { + internal::ScopedNotificationsIterationLock lock(this); + for (auto& observer : observer_list_) + observer.OnNotificationButtonClicked(id, button_index); + } } -void MessageCenterImpl::ClickOnSettingsButton(const std::string& id) { +void MessageCenterImpl::ClickOnNotificationButtonWithReply( + const std::string& id, + int button_index, + const base::string16& reply) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + if (!FindVisibleNotificationById(id)) + return; +#if defined(OS_CHROMEOS) + if (HasPopupNotifications()) + MarkSinglePopupAsShown(id, true); +#endif scoped_refptr<NotificationDelegate> delegate = notification_list_->GetNotificationDelegate(id); - - bool handled_by_delegate = false; if (delegate.get()) - handled_by_delegate = delegate->SettingsClick(); + delegate->ButtonClickWithReply(button_index, reply); + { + internal::ScopedNotificationsIterationLock lock(this); + for (auto& observer : observer_list_) + observer.OnNotificationButtonClickedWithReply(id, button_index, reply); + } +} - for (auto& observer : observer_list_) - observer.OnNotificationSettingsClicked(handled_by_delegate); +void MessageCenterImpl::ClickOnSettingsButton(const std::string& id) { + DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + DCHECK(!iterating_); + Notification* notification = notification_list_->GetNotificationById(id); + + bool handled_by_delegate = + notification && + (notification->rich_notification_data().settings_button_handler == + SettingsButtonHandler::DELEGATE); + if (handled_by_delegate) + notification->delegate()->SettingsClick(); + + { + internal::ScopedNotificationsIterationLock lock(this); + for (auto& observer : observer_list_) + observer.OnNotificationSettingsClicked(handled_by_delegate); + } } void MessageCenterImpl::MarkSinglePopupAsShown(const std::string& id, @@ -432,13 +546,21 @@ void MessageCenterImpl::MarkSinglePopupAsShown(const std::string& id, DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); if (FindVisibleNotificationById(id) == NULL) return; + +// This method doesn't check the |iterating_| flag, since this is called +// during iteration. +// TODO(yoshiki): Investigate and not to call this method during iteration if +// necessary. + #if !defined(OS_CHROMEOS) return this->RemoveNotification(id, false); #else notification_list_->MarkSinglePopupAsShown(id, mark_notification_as_read); - notification_cache_.RecountUnread(); - for (auto& observer : observer_list_) - observer.OnNotificationUpdated(id); + { + internal::ScopedNotificationsIterationLock lock(this); + for (auto& observer : observer_list_) + observer.OnNotificationUpdated(id); + } #endif // defined(OS_CHROMEOS) } @@ -446,35 +568,24 @@ void MessageCenterImpl::DisplayedNotification( const std::string& id, const DisplaySource source) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); + + // This method may be called from the handlers, so we shouldn't manipulate + // notifications in this method. + if (FindVisibleNotificationById(id) == NULL) return; if (HasPopupNotifications()) notification_list_->MarkSinglePopupAsDisplayed(id); - notification_cache_.RecountUnread(); scoped_refptr<NotificationDelegate> delegate = notification_list_->GetNotificationDelegate(id); if (delegate.get()) delegate->Display(); - for (auto& observer : observer_list_) - observer.OnNotificationDisplayed(id, source); -} - -void MessageCenterImpl::SetNotifierSettingsProvider( - NotifierSettingsProvider* provider) { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - if (settings_provider_) { - settings_provider_->RemoveObserver(this); - settings_provider_ = NULL; + { + internal::ScopedNotificationsIterationLock lock(this); + for (auto& observer : observer_list_) + observer.OnNotificationDisplayed(id, source); } - settings_provider_ = provider; - if (settings_provider_) - settings_provider_->AddObserver(this); -} - -NotifierSettingsProvider* MessageCenterImpl::GetNotifierSettingsProvider() { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - return settings_provider_; } void MessageCenterImpl::SetQuietMode(bool in_quiet_mode) { @@ -487,15 +598,6 @@ void MessageCenterImpl::SetQuietMode(bool in_quiet_mode) { quiet_mode_timer_.reset(); } -void MessageCenterImpl::SetLockedState(bool locked) { - DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); - if (locked != locked_) { - locked_ = locked; - for (auto& observer : observer_list_) - observer.OnLockedStateChanged(locked); - } -} - void MessageCenterImpl::EnterQuietModeWithExpire( const base::TimeDelta& expires_in) { DCHECK_CALLED_ON_VALID_THREAD(thread_checker_); diff --git a/chromium/ui/message_center/message_center_impl.h b/chromium/ui/message_center/message_center_impl.h index 014cd59dec9..be7cbb87844 100644 --- a/chromium/ui/message_center/message_center_impl.h +++ b/chromium/ui/message_center/message_center_impl.h @@ -16,19 +16,24 @@ #include "base/threading/thread_checker.h" #include "base/time/time.h" #include "base/timer/timer.h" +#include "ui/message_center/change_queue.h" #include "ui/message_center/message_center.h" #include "ui/message_center/message_center_observer.h" #include "ui/message_center/message_center_types.h" #include "ui/message_center/notification_blocker.h" -#include "ui/message_center/notifier_settings.h" +#include "ui/message_center/notifier_id.h" #include "ui/message_center/popup_timers_controller.h" namespace message_center { +namespace internal { +class ScopedNotificationsIterationLock; +}; + // The default implementation of MessageCenter. -class MessageCenterImpl : public MessageCenter, - public NotificationBlocker::Observer, - public message_center::NotifierSettingsObserver { +class MESSAGE_CENTER_EXPORT MessageCenterImpl + : public MessageCenter, + public NotificationBlocker::Observer { public: MessageCenterImpl(); ~MessageCenterImpl() override; @@ -41,11 +46,8 @@ class MessageCenterImpl : public MessageCenter, void SetVisibility(Visibility visible) override; bool IsMessageCenterVisible() const override; size_t NotificationCount() const override; - size_t UnreadNotificationCount() const override; bool HasPopupNotifications() const override; bool IsQuietMode() const override; - bool IsLockedState() const override; - bool HasClickedListener(const std::string& id) override; message_center::Notification* FindVisibleNotificationById( const std::string& id) override; const NotificationList::Notifications& GetVisibleNotifications() override; @@ -55,6 +57,7 @@ class MessageCenterImpl : public MessageCenter, const std::string& old_id, std::unique_ptr<Notification> new_notification) override; void RemoveNotification(const std::string& id, bool by_user) override; + void RemoveNotificationsForNotifierId(const NotifierId& notifier_id) override; void RemoveAllNotifications(bool by_user, RemoveType type) override; void SetNotificationIcon(const std::string& notification_id, const gfx::Image& image) override; @@ -66,15 +69,15 @@ class MessageCenterImpl : public MessageCenter, void ClickOnNotification(const std::string& id) override; void ClickOnNotificationButton(const std::string& id, int button_index) override; + void ClickOnNotificationButtonWithReply(const std::string& id, + int button_index, + const base::string16& reply) override; void ClickOnSettingsButton(const std::string& id) override; void MarkSinglePopupAsShown(const std::string& id, bool mark_notification_as_read) override; void DisplayedNotification(const std::string& id, const DisplaySource source) override; - void SetNotifierSettingsProvider(NotifierSettingsProvider* provider) override; - NotifierSettingsProvider* GetNotifierSettingsProvider() override; void SetQuietMode(bool in_quiet_mode) override; - void SetLockedState(bool locked) override; void EnterQuietModeWithExpire(const base::TimeDelta& expires_in) override; void RestartPopupTimers() override; void PausePopupTimers() override; @@ -84,13 +87,6 @@ class MessageCenterImpl : public MessageCenter, // NotificationBlocker::Observer overrides: void OnBlockingStateChanged(NotificationBlocker* blocker) override; - // message_center::NotifierSettingsObserver overrides: - void UpdateIconImage(const NotifierId& notifier_id, - const gfx::Image& icon) override; - void NotifierGroupChanged() override; - void NotifierEnabledChanged(const NotifierId& notifier_id, - bool enabled) override; - // Unexposed methods: void AddNotificationImmediately(std::unique_ptr<Notification> notification); void UpdateNotificationImmediately( @@ -102,31 +98,27 @@ class MessageCenterImpl : public MessageCenter, void DisableTimersForTest() override; private: - struct NotificationCache { - NotificationCache(); - ~NotificationCache(); - void Rebuild(const NotificationList::Notifications& notifications); - void RecountUnread(); + friend internal::ScopedNotificationsIterationLock; + class ScopedNotificationsLock; - NotificationList::Notifications visible_notifications; - size_t unread_count; - }; - - void RemoveNotificationsForNotifierId(const NotifierId& notifier_id); + Notification* GetLatestNotificationIncludingQueued( + const std::string& id) const; THREAD_CHECKER(thread_checker_); std::unique_ptr<NotificationList> notification_list_; - NotificationCache notification_cache_; + NotificationList::Notifications visible_notifications_; base::ObserverList<MessageCenterObserver> observer_list_; std::unique_ptr<PopupTimersController> popup_timers_controller_; std::unique_ptr<base::OneShotTimer> quiet_mode_timer_; - NotifierSettingsProvider* settings_provider_; std::vector<NotificationBlocker*> blockers_; + std::unique_ptr<ChangeQueue> notification_change_queue_; - bool locked_ = false; bool visible_ = false; + // modified by ScopedNotificationsIterationLock. + bool iterating_ = false; + base::string16 product_os_name_; DISALLOW_COPY_AND_ASSIGN(MessageCenterImpl); diff --git a/chromium/ui/message_center/message_center_impl_unittest.cc b/chromium/ui/message_center/message_center_impl_unittest.cc index 4bc2a912607..9921fa5f0f7 100644 --- a/chromium/ui/message_center/message_center_impl_unittest.cc +++ b/chromium/ui/message_center/message_center_impl_unittest.cc @@ -24,13 +24,55 @@ #include "ui/message_center/message_center_types.h" #include "ui/message_center/notification_blocker.h" #include "ui/message_center/notification_types.h" -#include "ui/message_center/notifier_settings.h" +#include "ui/message_center/notifier_id.h" #include "ui/message_center/public/cpp/message_center_constants.h" using base::UTF8ToUTF16; namespace message_center { +namespace { + +class CheckObserver : public MessageCenterObserver { + public: + CheckObserver(MessageCenter* message_center, const std::string& target_id) + : message_center_(message_center), target_id_(target_id) { + DCHECK(message_center); + DCHECK(!target_id.empty()); + } + + void OnNotificationUpdated(const std::string& notification_id) override { + EXPECT_TRUE(message_center_->FindVisibleNotificationById(target_id_)); + } + + private: + MessageCenter* message_center_; + std::string target_id_; + + DISALLOW_COPY_AND_ASSIGN(CheckObserver); +}; + +class RemoveObserver : public MessageCenterObserver { + public: + RemoveObserver(MessageCenter* message_center, const std::string& target_id) + : message_center_(message_center), target_id_(target_id) { + DCHECK(message_center); + DCHECK(!target_id.empty()); + } + + void OnNotificationUpdated(const std::string& notification_id) override { + message_center_->RemoveNotification(target_id_, false); + } + + private: + MessageCenter* message_center_; + std::string target_id_; + + DISALLOW_COPY_AND_ASSIGN(RemoveObserver); +}; + +} // anonymous namespace + class MessageCenterImplTest : public testing::Test, public MessageCenterObserver { public: @@ -52,9 +94,6 @@ class MessageCenterImplTest : public testing::Test, } MessageCenter* message_center() const { return message_center_; } - NotifierSettingsObserver* notifier_settings_observer() const { - return static_cast<NotifierSettingsObserver*>(message_center_impl()); - } MessageCenterImpl* message_center_impl() const { return reinterpret_cast<MessageCenterImpl*>(message_center_); } @@ -316,10 +355,15 @@ TEST_F(MessageCenterImplTest, PopupTimersControllerRestartOnUpdate) { popup_timers_controller->OnNotificationDisplayed("id1", DISPLAY_SOURCE_POPUP); ASSERT_EQ(popup_timers_controller->timer_finished(), 0); +#if defined(OS_CHROMEOS) + const int dismiss_time = kAutocloseDefaultDelaySeconds; +#else + const int dismiss_time = kAutocloseHighPriorityDelaySeconds; +#endif + // Fast forward the |task_runner| by one second less than the auto-close timer // frequency for Web Notifications. (As set by the |notifier_id|.) - task_runner->FastForwardBy( - base::TimeDelta::FromSeconds(kAutocloseHighPriorityDelaySeconds - 1)); + task_runner->FastForwardBy(base::TimeDelta::FromSeconds(dismiss_time - 1)); ASSERT_EQ(popup_timers_controller->timer_finished(), 0); // Trigger a replacement of the notification in the timer controller. @@ -327,8 +371,7 @@ TEST_F(MessageCenterImplTest, PopupTimersControllerRestartOnUpdate) { // Fast forward the |task_runner| by one second less than the auto-close timer // frequency for Web Notifications again. It should have been reset. - task_runner->FastForwardBy( - base::TimeDelta::FromSeconds(kAutocloseHighPriorityDelaySeconds - 1)); + task_runner->FastForwardBy(base::TimeDelta::FromSeconds(dismiss_time - 1)); ASSERT_EQ(popup_timers_controller->timer_finished(), 0); // Now fast forward the |task_runner| by two seconds (to avoid flakiness), @@ -359,6 +402,10 @@ TEST_F(MessageCenterImplTest, NotificationBlocker) { EXPECT_EQ(2u, message_center()->GetPopupNotifications().size()); EXPECT_EQ(2u, message_center()->GetVisibleNotifications().size()); + // "id1" is displayed as a pop-up so that it will be closed when blocked. + message_center()->DisplayedNotification("id1", + message_center::DISPLAY_SOURCE_POPUP); + // Block all notifications. All popups are gone and message center should be // hidden. blocker1.SetNotificationsEnabled(false); @@ -381,10 +428,14 @@ TEST_F(MessageCenterImplTest, NotificationBlocker) { EXPECT_TRUE(message_center()->GetPopupNotifications().empty()); EXPECT_EQ(2u, message_center()->GetVisibleNotifications().size()); - // Unblock both blockers, which recovers the global state, but the popups - // aren't shown. + // Unblock both blockers, which recovers the global state, the displayed + // pop-ups before blocking aren't shown but the never-displayed ones will + // be shown. blocker2.SetNotificationsEnabled(true); - EXPECT_TRUE(message_center()->GetPopupNotifications().empty()); + NotificationList::PopupNotifications popups = + message_center()->GetPopupNotifications(); + EXPECT_EQ(1u, popups.size()); + EXPECT_TRUE(PopupNotificationsContain(popups, "id2")); EXPECT_EQ(2u, message_center()->GetVisibleNotifications().size()); } @@ -400,6 +451,10 @@ TEST_F(MessageCenterImplTest, NotificationsDuringBlocked) { EXPECT_EQ(1u, message_center()->GetPopupNotifications().size()); EXPECT_EQ(1u, message_center()->GetVisibleNotifications().size()); + // "id1" is displayed as a pop-up so that it will be closed when blocked. + message_center()->DisplayedNotification("id1", + message_center::DISPLAY_SOURCE_POPUP); + // Create a notification during blocked. Still no popups. blocker.SetNotificationsEnabled(false); message_center()->AddNotification(std::unique_ptr<Notification>( @@ -437,6 +492,10 @@ TEST_F(MessageCenterImplTest, NotificationBlockerAllowsPopups) { base::string16() /* display_source */, GURL(), notifier_id2, RichNotificationData(), NULL))); + // "id1" is displayed as a pop-up so that it will be closed when blocked. + message_center()->DisplayedNotification("id1", + message_center::DISPLAY_SOURCE_POPUP); + // "id1" is closed but "id2" is still visible as a popup. blocker.SetNotificationsEnabled(false); NotificationList::PopupNotifications popups = @@ -640,41 +699,6 @@ TEST_F(MessageCenterImplTest, RemoveAllNotificationsWithPinned) { } #endif -#if defined(OS_CHROMEOS) -TEST_F(MessageCenterImplTest, CachedUnreadCount) { - message_center()->AddNotification( - std::unique_ptr<Notification>(CreateSimpleNotification("id1"))); - message_center()->AddNotification( - std::unique_ptr<Notification>(CreateSimpleNotification("id2"))); - message_center()->AddNotification( - std::unique_ptr<Notification>(CreateSimpleNotification("id3"))); - ASSERT_EQ(3u, message_center()->UnreadNotificationCount()); - - // Mark 'displayed' on all notifications by using for-loop. This shouldn't - // recreate |notifications| inside of the loop. - const NotificationList::Notifications& notifications = - message_center()->GetVisibleNotifications(); - for (NotificationList::Notifications::const_iterator iter = - notifications.begin(); iter != notifications.end(); ++iter) { - message_center()->DisplayedNotification( - (*iter)->id(), message_center::DISPLAY_SOURCE_MESSAGE_CENTER); - } - EXPECT_EQ(0u, message_center()->UnreadNotificationCount()); - - // Imitate the timeout, which recovers the unread count. Again, this shouldn't - // recreate |notifications| inside of the loop. - for (NotificationList::Notifications::const_iterator iter = - notifications.begin(); iter != notifications.end(); ++iter) { - message_center()->MarkSinglePopupAsShown((*iter)->id(), false); - } - EXPECT_EQ(3u, message_center()->UnreadNotificationCount()); - - // Opening the message center will reset the unread count. - message_center()->SetVisibility(VISIBILITY_MESSAGE_CENTER); - EXPECT_EQ(0u, message_center()->UnreadNotificationCount()); -} -#endif // OS_CHROMEOS - TEST_F(MessageCenterImplTest, NotifierEnabledChanged) { ASSERT_EQ(0u, message_center()->NotificationCount()); message_center()->AddNotification(std::unique_ptr<Notification>( @@ -695,24 +719,19 @@ TEST_F(MessageCenterImplTest, NotifierEnabledChanged) { CreateSimpleNotificationWithNotifierId("id2-5", "app2"))); ASSERT_EQ(8u, message_center()->NotificationCount()); - // Enabling an extension should have no effect on the count. - notifier_settings_observer()->NotifierEnabledChanged( - NotifierId(NotifierId::APPLICATION, "app1"), true); - ASSERT_EQ(8u, message_center()->NotificationCount()); - // Removing all of app2's notifications should only leave app1's. - notifier_settings_observer()->NotifierEnabledChanged( - NotifierId(NotifierId::APPLICATION, "app2"), false); + message_center()->RemoveNotificationsForNotifierId( + NotifierId(NotifierId::APPLICATION, "app2")); ASSERT_EQ(3u, message_center()->NotificationCount()); // Removal operations should be idempotent. - notifier_settings_observer()->NotifierEnabledChanged( - NotifierId(NotifierId::APPLICATION, "app2"), false); + message_center()->RemoveNotificationsForNotifierId( + NotifierId(NotifierId::APPLICATION, "app2")); ASSERT_EQ(3u, message_center()->NotificationCount()); // Now we remove the remaining notifications. - notifier_settings_observer()->NotifierEnabledChanged( - NotifierId(NotifierId::APPLICATION, "app1"), false); + message_center()->RemoveNotificationsForNotifierId( + NotifierId(NotifierId::APPLICATION, "app1")); ASSERT_EQ(0u, message_center()->NotificationCount()); } @@ -768,5 +787,36 @@ TEST_F(MessageCenterImplTest, RemoveWhileMessageCenterVisible) { EXPECT_FALSE(message_center()->FindVisibleNotificationById(id)); } +TEST_F(MessageCenterImplTest, RemoveWhileIteratingObserver) { + std::string id("id1"); + CheckObserver check1(message_center(), id); + CheckObserver check2(message_center(), id); + RemoveObserver remove(message_center(), id); + + // Prepare a notification + std::unique_ptr<Notification> notification(CreateSimpleNotification(id)); + message_center()->AddNotification(std::move(notification)); + EXPECT_TRUE(message_center()->FindVisibleNotificationById(id)); + + // Install the test handlers + message_center()->AddObserver(&check1); + message_center()->AddObserver(&remove); + message_center()->AddObserver(&check2); + + // Update the notification. The notification will be removed in the observer, + // but the actual removal will be done at the end of the iteration. + // Notification keeps alive during iteration. This is checked by + // CheckObserver. + notification.reset(CreateSimpleNotification(id)); + message_center()->UpdateNotification(id, std::move(notification)); + + // Notification is removed correctly. + EXPECT_FALSE(message_center()->FindVisibleNotificationById(id)); + + message_center()->RemoveObserver(&check1); + message_center()->RemoveObserver(&remove); + message_center()->RemoveObserver(&check2); +} + } // namespace internal } // namespace message_center diff --git a/chromium/ui/message_center/message_center_observer.h b/chromium/ui/message_center/message_center_observer.h index e738bcb910d..feb358bd0a9 100644 --- a/chromium/ui/message_center/message_center_observer.h +++ b/chromium/ui/message_center/message_center_observer.h @@ -40,6 +40,13 @@ class MESSAGE_CENTER_EXPORT MessageCenterObserver { virtual void OnNotificationButtonClicked(const std::string& notification_id, int button_index) {} + // Called when a click event happens on a button with an input indexed by + // |button_index| of the notification associated with |notification_id|. + virtual void OnNotificationButtonClickedWithReply( + const std::string& notification_id, + int button_index, + const base::string16& reply) {} + // Called when notification settings button is clicked. The |handled| argument // indicates whether the notification delegate already handled the operation. virtual void OnNotificationSettingsClicked(bool handled) {} @@ -57,9 +64,6 @@ class MESSAGE_CENTER_EXPORT MessageCenterObserver { // quiet mode expires. virtual void OnQuietModeChanged(bool in_quiet_mode) {} - // Called when the user locks (or unlocks) the screen. - virtual void OnLockedStateChanged(bool locked) {} - // Called when the blocking state of |blocker| is changed. virtual void OnBlockingStateChanged(NotificationBlocker* blocker) {} }; diff --git a/chromium/ui/message_center/message_center_tray.cc b/chromium/ui/message_center/message_center_tray.cc deleted file mode 100644 index e24ad71e928..00000000000 --- a/chromium/ui/message_center/message_center_tray.cc +++ /dev/null @@ -1,260 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/message_center/message_center_tray.h" - -#include <memory> - -#include "base/macros.h" -#include "base/observer_list.h" -#include "base/strings/utf_string_conversions.h" -#include "build/build_config.h" -#include "ui/base/l10n/l10n_util.h" -#include "ui/base/models/simple_menu_model.h" -#include "ui/message_center/message_center.h" -#include "ui/message_center/message_center_tray_delegate.h" -#include "ui/message_center/message_center_types.h" -#include "ui/message_center/notification_blocker.h" -#include "ui/strings/grit/ui_strings.h" - -namespace message_center { - -namespace { - -// Menu constants -const int kTogglePermissionCommand = 0; -const int kShowSettingsCommand = 1; - -// The model of the context menu for a notification card. -class NotificationMenuModel : public ui::SimpleMenuModel, - public ui::SimpleMenuModel::Delegate { - public: - NotificationMenuModel(MessageCenterTray* tray, - const Notification& notification); - ~NotificationMenuModel() override; - - // Overridden from ui::SimpleMenuModel::Delegate: - bool IsCommandIdChecked(int command_id) const override; - bool IsCommandIdEnabled(int command_id) const override; - void ExecuteCommand(int command_id, int event_flags) override; - - private: - MessageCenterTray* tray_; - Notification notification_; - DISALLOW_COPY_AND_ASSIGN(NotificationMenuModel); -}; - -NotificationMenuModel::NotificationMenuModel(MessageCenterTray* tray, - const Notification& notification) - : ui::SimpleMenuModel(this), tray_(tray), notification_(notification) { - DCHECK(!notification.display_source().empty()); - AddItem(kTogglePermissionCommand, - l10n_util::GetStringFUTF16(IDS_MESSAGE_CENTER_NOTIFIER_DISABLE, - notification.display_source())); - -#if defined(OS_CHROMEOS) - // Add settings menu item. - AddItem(kShowSettingsCommand, - l10n_util::GetStringUTF16(IDS_MESSAGE_CENTER_SETTINGS)); -#endif -} - -NotificationMenuModel::~NotificationMenuModel() { -} - -bool NotificationMenuModel::IsCommandIdChecked(int command_id) const { - return false; -} - -bool NotificationMenuModel::IsCommandIdEnabled(int command_id) const { - // TODO(estade): commands shouldn't always be enabled. For example, a - // notification's enabled state might be controlled by policy. See - // http://crbug.com/771269 - return true; -} - -void NotificationMenuModel::ExecuteCommand(int command_id, int event_flags) { - switch (command_id) { - case kTogglePermissionCommand: - notification_.delegate()->DisableNotification(); - // TODO(estade): this will not close other open notifications from the - // same site. - MessageCenter::Get()->RemoveNotification(notification_.id(), false); - break; - case kShowSettingsCommand: - tray_->ShowNotifierSettingsBubble(); - break; - default: - NOTREACHED(); - } -} - -} // namespace - -MessageCenterTray::MessageCenterTray( - MessageCenterTrayDelegate* delegate, - message_center::MessageCenter* message_center) - : message_center_(message_center), - message_center_visible_(false), - popups_visible_(false), - delegate_(delegate) { - message_center_->AddObserver(this); -} - -MessageCenterTray::~MessageCenterTray() { - message_center_->RemoveObserver(this); -} - -bool MessageCenterTray::ShowMessageCenterBubble(bool show_by_click) { - if (message_center_visible_) - return true; - - HidePopupBubbleInternal(); - - message_center_visible_ = delegate_->ShowMessageCenter(show_by_click); - if (message_center_visible_) { - message_center_->SetVisibility(message_center::VISIBILITY_MESSAGE_CENTER); - NotifyMessageCenterTrayChanged(); - } - return message_center_visible_; -} - -bool MessageCenterTray::HideMessageCenterBubble() { - if (!message_center_visible_) - return false; - delegate_->HideMessageCenter(); - MarkMessageCenterHidden(); - return true; -} - -void MessageCenterTray::MarkMessageCenterHidden() { - if (!message_center_visible_) - return; - message_center_visible_ = false; - message_center_->SetVisibility(message_center::VISIBILITY_TRANSIENT); - - // Some notifications (like system ones) should appear as popups again - // after the message center is closed. - if (message_center_->HasPopupNotifications()) { - ShowPopupBubble(); - return; - } - - NotifyMessageCenterTrayChanged(); -} - -void MessageCenterTray::ShowPopupBubble() { - if (message_center_visible_) - return; - - if (popups_visible_) { - NotifyMessageCenterTrayChanged(); - return; - } - - if (!message_center_->HasPopupNotifications()) - return; - - popups_visible_ = delegate_->ShowPopups(); - - NotifyMessageCenterTrayChanged(); -} - -bool MessageCenterTray::HidePopupBubble() { - if (!popups_visible_) - return false; - HidePopupBubbleInternal(); - NotifyMessageCenterTrayChanged(); - - return true; -} - -void MessageCenterTray::HidePopupBubbleInternal() { - if (!popups_visible_) - return; - - delegate_->HidePopups(); - popups_visible_ = false; -} - -void MessageCenterTray::ShowNotifierSettingsBubble() { - if (popups_visible_) - HidePopupBubbleInternal(); - - message_center_visible_ = delegate_->ShowNotifierSettings(); - message_center_->SetVisibility(message_center::VISIBILITY_SETTINGS); - - NotifyMessageCenterTrayChanged(); -} - -std::unique_ptr<ui::MenuModel> MessageCenterTray::CreateNotificationMenuModel( - const Notification& notification) { - return std::make_unique<NotificationMenuModel>(this, notification); -} - -void MessageCenterTray::OnNotificationAdded( - const std::string& notification_id) { - OnMessageCenterChanged(); -} - -void MessageCenterTray::OnNotificationRemoved( - const std::string& notification_id, - bool by_user) { - OnMessageCenterChanged(); -} - -void MessageCenterTray::OnNotificationUpdated( - const std::string& notification_id) { - OnMessageCenterChanged(); -} - -void MessageCenterTray::OnNotificationClicked( - const std::string& notification_id) { - if (popups_visible_) - OnMessageCenterChanged(); -} - -void MessageCenterTray::OnNotificationButtonClicked( - const std::string& notification_id, - int button_index) { - if (popups_visible_) - OnMessageCenterChanged(); -} - -void MessageCenterTray::OnNotificationSettingsClicked(bool handled) { - if (!handled) - ShowNotifierSettingsBubble(); -} - -void MessageCenterTray::OnNotificationDisplayed( - const std::string& notification_id, - const DisplaySource source) { - NotifyMessageCenterTrayChanged(); -} - -void MessageCenterTray::OnQuietModeChanged(bool in_quiet_mode) { - NotifyMessageCenterTrayChanged(); -} - -void MessageCenterTray::OnBlockingStateChanged(NotificationBlocker* blocker) { - OnMessageCenterChanged(); -} - -void MessageCenterTray::OnMessageCenterChanged() { - if (message_center_visible_ && message_center_->NotificationCount() == 0) - HideMessageCenterBubble(); - - if (popups_visible_ && !message_center_->HasPopupNotifications()) - HidePopupBubbleInternal(); - else if (!popups_visible_ && message_center_->HasPopupNotifications()) - ShowPopupBubble(); - - NotifyMessageCenterTrayChanged(); -} - -void MessageCenterTray::NotifyMessageCenterTrayChanged() { - delegate_->OnMessageCenterTrayChanged(); -} - -} // namespace message_center diff --git a/chromium/ui/message_center/message_center_tray_unittest.cc b/chromium/ui/message_center/message_center_tray_unittest.cc deleted file mode 100644 index c215306b658..00000000000 --- a/chromium/ui/message_center/message_center_tray_unittest.cc +++ /dev/null @@ -1,299 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/message_center/message_center_tray.h" - -#include <utility> - -#include "base/macros.h" -#include "base/strings/utf_string_conversions.h" -#include "testing/gtest/include/gtest/gtest.h" -#include "ui/base/models/menu_model.h" -#include "ui/message_center/message_center.h" -#include "ui/message_center/notification.h" -#include "ui/message_center/notification_types.h" - -using base::ASCIIToUTF16; - -namespace message_center { -namespace { - -class TestNotificationDelegate : public message_center::NotificationDelegate { - public: - TestNotificationDelegate() = default; - - private: - ~TestNotificationDelegate() override = default; - - DISALLOW_COPY_AND_ASSIGN(TestNotificationDelegate); -}; - -class MockDelegate : public MessageCenterTrayDelegate { - public: - MockDelegate() {} - ~MockDelegate() override {} - void OnMessageCenterTrayChanged() override {} - bool ShowPopups() override { return show_message_center_success_; } - void HidePopups() override {} - bool ShowMessageCenter(bool show_by_click) override { - return show_popups_success_; - } - void HideMessageCenter() override {} - bool ShowNotifierSettings() override { return true; } - - MessageCenterTray* GetMessageCenterTray() override { return nullptr; } - - bool show_popups_success_ = true; - bool show_message_center_success_ = true; - - private: - DISALLOW_COPY_AND_ASSIGN(MockDelegate); -}; - -} // namespace - -class MessageCenterTrayTest : public testing::Test { - public: - MessageCenterTrayTest() {} - ~MessageCenterTrayTest() override {} - - void SetUp() override { - MessageCenter::Initialize(); - delegate_.reset(new MockDelegate); - message_center_ = MessageCenter::Get(); - message_center_tray_.reset( - new MessageCenterTray(delegate_.get(), message_center_)); - } - - void TearDown() override { - message_center_tray_.reset(); - delegate_.reset(); - message_center_ = NULL; - MessageCenter::Shutdown(); - } - - protected: - NotifierId DummyNotifierId() { - return NotifierId(); - } - - Notification* AddNotification(const std::string& id) { - return AddNotification(id, DummyNotifierId()); - } - - Notification* AddNotification(const std::string& id, NotifierId notifier_id) { - std::unique_ptr<Notification> notification( - new Notification(message_center::NOTIFICATION_TYPE_SIMPLE, id, - ASCIIToUTF16("Test Web Notification"), - ASCIIToUTF16("Notification message body."), - gfx::Image(), ASCIIToUTF16("www.test.org"), GURL(), - notifier_id, message_center::RichNotificationData(), - new TestNotificationDelegate())); - Notification* notification_ptr = notification.get(); - message_center_->AddNotification(std::move(notification)); - return notification_ptr; - } - std::unique_ptr<MockDelegate> delegate_; - std::unique_ptr<MessageCenterTray> message_center_tray_; - MessageCenter* message_center_; - - private: - DISALLOW_COPY_AND_ASSIGN(MessageCenterTrayTest); -}; - -TEST_F(MessageCenterTrayTest, BasicMessageCenter) { - ASSERT_FALSE(message_center_tray_->popups_visible()); - ASSERT_FALSE(message_center_tray_->message_center_visible()); - - bool shown = - message_center_tray_->ShowMessageCenterBubble(false /* show_by_click */); - EXPECT_TRUE(shown); - - ASSERT_FALSE(message_center_tray_->popups_visible()); - ASSERT_TRUE(message_center_tray_->message_center_visible()); - - message_center_tray_->HideMessageCenterBubble(); - - ASSERT_FALSE(message_center_tray_->popups_visible()); - ASSERT_FALSE(message_center_tray_->message_center_visible()); - - message_center_tray_->ShowMessageCenterBubble(false /* show_by_click */); - - ASSERT_FALSE(message_center_tray_->popups_visible()); - ASSERT_TRUE(message_center_tray_->message_center_visible()); - - message_center_tray_->HideMessageCenterBubble(); - - ASSERT_FALSE(message_center_tray_->popups_visible()); - ASSERT_FALSE(message_center_tray_->message_center_visible()); -} - -TEST_F(MessageCenterTrayTest, BasicPopup) { - ASSERT_FALSE(message_center_tray_->popups_visible()); - ASSERT_FALSE(message_center_tray_->message_center_visible()); - - message_center_tray_->ShowPopupBubble(); - - ASSERT_FALSE(message_center_tray_->popups_visible()); - ASSERT_FALSE(message_center_tray_->message_center_visible()); - - AddNotification("BasicPopup"); - - ASSERT_TRUE(message_center_tray_->popups_visible()); - ASSERT_FALSE(message_center_tray_->message_center_visible()); - - message_center_tray_->HidePopupBubble(); - - ASSERT_FALSE(message_center_tray_->popups_visible()); - ASSERT_FALSE(message_center_tray_->message_center_visible()); -} - -TEST_F(MessageCenterTrayTest, MessageCenterClosesPopups) { - ASSERT_FALSE(message_center_tray_->popups_visible()); - ASSERT_FALSE(message_center_tray_->message_center_visible()); - - AddNotification("MessageCenterClosesPopups"); - - ASSERT_TRUE(message_center_tray_->popups_visible()); - ASSERT_FALSE(message_center_tray_->message_center_visible()); - - bool shown = - message_center_tray_->ShowMessageCenterBubble(false /* show_by_click */); - EXPECT_TRUE(shown); - - ASSERT_FALSE(message_center_tray_->popups_visible()); - ASSERT_TRUE(message_center_tray_->message_center_visible()); - - // The notification is queued if it's added when message center is visible. - AddNotification("MessageCenterClosesPopups2"); - - message_center_tray_->ShowPopupBubble(); - - ASSERT_FALSE(message_center_tray_->popups_visible()); - ASSERT_TRUE(message_center_tray_->message_center_visible()); - - message_center_tray_->HideMessageCenterBubble(); - - // There is no queued notification. - ASSERT_FALSE(message_center_tray_->popups_visible()); - ASSERT_FALSE(message_center_tray_->message_center_visible()); - - message_center_tray_->ShowMessageCenterBubble(false /* show_by_click */); - message_center_tray_->HideMessageCenterBubble(); - ASSERT_FALSE(message_center_tray_->popups_visible()); - ASSERT_FALSE(message_center_tray_->message_center_visible()); -} - -TEST_F(MessageCenterTrayTest, MessageCenterReopenPopupsForSystemPriority) { - ASSERT_FALSE(message_center_tray_->popups_visible()); - ASSERT_FALSE(message_center_tray_->message_center_visible()); - - std::unique_ptr<Notification> notification(new Notification( - message_center::NOTIFICATION_TYPE_SIMPLE, - "MessageCenterReopnPopupsForSystemPriority", - ASCIIToUTF16("Test Web Notification"), - ASCIIToUTF16("Notification message body."), gfx::Image(), - ASCIIToUTF16("www.test.org"), GURL(), DummyNotifierId(), - message_center::RichNotificationData(), NULL /* delegate */)); - notification->SetSystemPriority(); - message_center_->AddNotification(std::move(notification)); - - ASSERT_TRUE(message_center_tray_->popups_visible()); - ASSERT_FALSE(message_center_tray_->message_center_visible()); - - bool shown = - message_center_tray_->ShowMessageCenterBubble(false /* show_by_click */); - EXPECT_TRUE(shown); - - ASSERT_FALSE(message_center_tray_->popups_visible()); - ASSERT_TRUE(message_center_tray_->message_center_visible()); - - message_center_tray_->HideMessageCenterBubble(); - - ASSERT_TRUE(message_center_tray_->popups_visible()); - ASSERT_FALSE(message_center_tray_->message_center_visible()); -} - -TEST_F(MessageCenterTrayTest, ShowBubbleFails) { - // Now the delegate will signal that it was unable to show a bubble. - delegate_->show_popups_success_ = false; - delegate_->show_message_center_success_ = false; - - ASSERT_FALSE(message_center_tray_->popups_visible()); - ASSERT_FALSE(message_center_tray_->message_center_visible()); - - AddNotification("ShowBubbleFails"); - - message_center_tray_->ShowPopupBubble(); - - ASSERT_FALSE(message_center_tray_->popups_visible()); - ASSERT_FALSE(message_center_tray_->message_center_visible()); - - bool shown = - message_center_tray_->ShowMessageCenterBubble(false /* show_by_click */); - EXPECT_FALSE(shown); - - ASSERT_FALSE(message_center_tray_->popups_visible()); - ASSERT_FALSE(message_center_tray_->message_center_visible()); - - message_center_tray_->HideMessageCenterBubble(); - - ASSERT_FALSE(message_center_tray_->popups_visible()); - ASSERT_FALSE(message_center_tray_->message_center_visible()); - - message_center_tray_->ShowMessageCenterBubble(false /* show_by_click */); - - ASSERT_FALSE(message_center_tray_->popups_visible()); - ASSERT_FALSE(message_center_tray_->message_center_visible()); - - message_center_tray_->HidePopupBubble(); - - ASSERT_FALSE(message_center_tray_->popups_visible()); - ASSERT_FALSE(message_center_tray_->message_center_visible()); -} - -TEST_F(MessageCenterTrayTest, ContextMenuTestWithMessageCenter) { - const std::string id1 = "id1"; - const std::string id2 = "id2"; - const std::string id3 = "id3"; - Notification* notification1 = AddNotification(id1); - - base::string16 display_source = ASCIIToUTF16("www.test.org"); - NotifierId notifier_id = DummyNotifierId(); - - NotifierId notifier_id2(NotifierId::APPLICATION, "sample-app"); - std::unique_ptr<Notification> notification(new Notification( - message_center::NOTIFICATION_TYPE_SIMPLE, id2, - ASCIIToUTF16("Test Web Notification"), - ASCIIToUTF16("Notification message body."), gfx::Image(), display_source, - GURL(), notifier_id2, message_center::RichNotificationData(), - new TestNotificationDelegate())); - message_center_->AddNotification(std::move(notification)); - - AddNotification(id3); - - std::unique_ptr<ui::MenuModel> model( - message_center_tray_->CreateNotificationMenuModel(*notification1)); -#if defined(OS_CHROMEOS) - EXPECT_EQ(2, model->GetItemCount()); - - // The second item is to open the settings. - EXPECT_TRUE(model->IsEnabledAt(0)); - EXPECT_TRUE(model->IsEnabledAt(1)); - model->ActivatedAt(1); - EXPECT_TRUE(message_center_tray_->message_center_visible()); -#else - EXPECT_EQ(1, model->GetItemCount()); -#endif - - message_center_tray_->HideMessageCenterBubble(); - - // The first item is to disable notifications from the notifier id. It also - // removes the notification. - EXPECT_EQ(3u, message_center_->GetVisibleNotifications().size()); - model->ActivatedAt(0); - EXPECT_EQ(2u, message_center_->GetVisibleNotifications().size()); -} - -} // namespace message_center diff --git a/chromium/ui/message_center/mojo/BUILD.gn b/chromium/ui/message_center/mojo/BUILD.gn index a19b11a29a3..ca859f3b1f2 100644 --- a/chromium/ui/message_center/mojo/BUILD.gn +++ b/chromium/ui/message_center/mojo/BUILD.gn @@ -7,11 +7,12 @@ import("//mojo/public/tools/bindings/mojom.gni") mojom("mojo") { sources = [ "notification.mojom", + "notifier_id.mojom", ] public_deps = [ "//mojo/common:common_custom_types", - "//skia/public/interfaces:interfaces", + "//ui/gfx/image/mojo:interfaces", "//url/mojo:url_mojom_gurl", ] } @@ -31,6 +32,8 @@ source_set("struct_traits") { sources = [ "notification_struct_traits.cc", "notification_struct_traits.h", + "notifier_id_struct_traits.cc", + "notifier_id_struct_traits.h", ] public_deps = [ @@ -38,8 +41,8 @@ source_set("struct_traits") { "//base", "//mojo/common:struct_traits", "//mojo/public/cpp/bindings", - "//skia/public/interfaces", "//ui/gfx", + "//ui/gfx/image/mojo:struct_traits", "//ui/message_center", "//url/mojo:url_mojom_gurl", ] diff --git a/chromium/ui/message_center/mojo/DEPS b/chromium/ui/message_center/mojo/DEPS index e8a0f7d5ac5..6cfa565faca 100644 --- a/chromium/ui/message_center/mojo/DEPS +++ b/chromium/ui/message_center/mojo/DEPS @@ -1,4 +1,3 @@ include_rules = [ "+mojo/common", - "+skia/public", ] diff --git a/chromium/ui/message_center/mojo/notification.mojom b/chromium/ui/message_center/mojo/notification.mojom index a9651b3eec6..b3764872188 100644 --- a/chromium/ui/message_center/mojo/notification.mojom +++ b/chromium/ui/message_center/mojo/notification.mojom @@ -5,18 +5,46 @@ module message_center.mojom; import "mojo/common/string16.mojom"; -import "skia/public/interfaces/bitmap.mojom"; +import "ui/gfx/image/mojo/image.mojom"; import "url/mojo/url.mojom"; +// Matches message_center::NotificationType. +enum NotificationType { + SIMPLE = 0, + BASE_FORMAT = 1, + IMAGE = 2, + MULTIPLE = 3, + PROGRESS = 4, + CUSTOM = 5, +}; + +// These fields and their meanings are identical to those in +// message_center::RichNotificationData. +// TODO(estade): Add the rest of the fields for RichNotificationData. +struct RichNotificationData { + int32 progress; + mojo.common.mojom.String16 progress_status; + bool should_make_spoken_feedback_for_popup_updates; + bool clickable; + bool pinned; + mojo.common.mojom.String16 accessible_name; + uint32 accent_color; + bool use_image_as_icon; +}; + // TODO(mhashmi): Add the rest of the fields for a Notification struct Notification { + NotificationType type; + // TODO(mhashmi): Server-side code (in Ash) needs to make sure this id won't // collide with ids from different clients string id; mojo.common.mojom.String16 title; mojo.common.mojom.String16 message; - skia.mojom.Bitmap? icon; + gfx.mojom.ImageSkia? icon; mojo.common.mojom.String16 display_source; url.mojom.Url origin_url; + + RichNotificationData optional_fields; }; diff --git a/chromium/ui/message_center/mojo/notification.typemap b/chromium/ui/message_center/mojo/notification.typemap index f31b7ed39e6..1e613e5e66d 100644 --- a/chromium/ui/message_center/mojo/notification.typemap +++ b/chromium/ui/message_center/mojo/notification.typemap @@ -6,7 +6,11 @@ mojom = "//ui/message_center/mojo/notification.mojom" public_headers = [ "//ui/message_center/notification.h" ] traits_headers = [ "//ui/message_center/mojo/notification_struct_traits.h" ] deps = [ + "//ui/gfx/image/mojo:struct_traits", "//ui/message_center/mojo:struct_traits", ] -type_mappings = - [ "message_center.mojom.Notification=message_center::Notification" ] +type_mappings = [ + "message_center.mojom.RichNotificationData=message_center::RichNotificationData", + "message_center.mojom.NotificationType=message_center::NotificationType", + "message_center.mojom.Notification=message_center::Notification", +] diff --git a/chromium/ui/message_center/mojo/notification_struct_traits.cc b/chromium/ui/message_center/mojo/notification_struct_traits.cc index 3d5d510e1be..6f771b9f2e5 100644 --- a/chromium/ui/message_center/mojo/notification_struct_traits.cc +++ b/chromium/ui/message_center/mojo/notification_struct_traits.cc @@ -5,70 +5,141 @@ #include "ui/message_center/mojo/notification_struct_traits.h" #include "mojo/common/common_custom_types_struct_traits.h" -#include "skia/public/interfaces/bitmap_skbitmap_struct_traits.h" +#include "ui/gfx/image/mojo/image_skia_struct_traits.h" #include "url/mojo/url_gurl_struct_traits.h" namespace mojo { +using message_center::mojom::NotificationDataView; +using message_center::mojom::RichNotificationDataDataView; +using message_center::Notification; +using message_center::RichNotificationData; +using NotificationStructTraits = + StructTraits<NotificationDataView, Notification>; +using RichNotificationDataStructTraits = + StructTraits<RichNotificationDataDataView, RichNotificationData>; + +// static +int RichNotificationDataStructTraits::progress( + const message_center::RichNotificationData& r) { + return r.progress; +} + +// static +const base::string16& RichNotificationDataStructTraits::progress_status( + const message_center::RichNotificationData& r) { + return r.progress_status; +} + +// static +bool RichNotificationDataStructTraits:: + should_make_spoken_feedback_for_popup_updates( + const message_center::RichNotificationData& r) { + return r.should_make_spoken_feedback_for_popup_updates; +} + +// static +bool RichNotificationDataStructTraits::clickable( + const message_center::RichNotificationData& r) { + return r.clickable; +} + +// static +bool RichNotificationDataStructTraits::pinned( + const message_center::RichNotificationData& r) { + return r.pinned; +} + +// static +const base::string16& RichNotificationDataStructTraits::accessible_name( + const message_center::RichNotificationData& r) { + return r.accessible_name; +} + +// static +SkColor RichNotificationDataStructTraits::accent_color( + const message_center::RichNotificationData& r) { + return r.accent_color; +} + // static -const std::string& StructTraits< - message_center::mojom::NotificationDataView, - message_center::Notification>::id(const message_center::Notification& n) { +bool RichNotificationDataStructTraits::use_image_as_icon( + const message_center::RichNotificationData& r) { + return r.use_image_as_icon; +} + +// static +bool RichNotificationDataStructTraits::Read(RichNotificationDataDataView data, + RichNotificationData* out) { + out->progress = data.progress(); + out->should_make_spoken_feedback_for_popup_updates = + data.should_make_spoken_feedback_for_popup_updates(); + out->clickable = data.clickable(); + out->pinned = data.pinned(); + out->accent_color = data.accent_color(); + out->use_image_as_icon = data.use_image_as_icon(); + return data.ReadProgressStatus(&out->progress_status) && + data.ReadAccessibleName(&out->accessible_name); +} + +// static +message_center::NotificationType NotificationStructTraits::type( + const Notification& n) { + return n.type(); +} + +// static +const std::string& NotificationStructTraits::id(const Notification& n) { return n.id(); } // static -const base::string16& StructTraits<message_center::mojom::NotificationDataView, - message_center::Notification>:: - title(const message_center::Notification& n) { +const base::string16& NotificationStructTraits::title(const Notification& n) { return n.title(); } // static -const base::string16& StructTraits<message_center::mojom::NotificationDataView, - message_center::Notification>:: - message(const message_center::Notification& n) { +const base::string16& NotificationStructTraits::message(const Notification& n) { return n.message(); } // static -const SkBitmap& StructTraits< - message_center::mojom::NotificationDataView, - message_center::Notification>::icon(const message_center::Notification& n) { - // TODO(mhashmi): Remove this when we have a gfx::Image mojom - static const SkBitmap kNullSkBitmap; - gfx::Image icon = n.icon(); - return icon.IsEmpty() ? kNullSkBitmap : *icon.ToSkBitmap(); +gfx::ImageSkia NotificationStructTraits::icon(const Notification& n) { + return n.icon().AsImageSkia(); } // static -const base::string16& StructTraits<message_center::mojom::NotificationDataView, - message_center::Notification>:: - display_source(const message_center::Notification& n) { +const base::string16& NotificationStructTraits::display_source( + const Notification& n) { return n.display_source(); } // static -const GURL& StructTraits<message_center::mojom::NotificationDataView, - message_center::Notification>:: - origin_url(const message_center::Notification& n) { +const GURL& NotificationStructTraits::origin_url(const Notification& n) { return n.origin_url(); } // static -bool StructTraits<message_center::mojom::NotificationDataView, - message_center::Notification>:: - Read(message_center::mojom::NotificationDataView data, - message_center::Notification* out) { - SkBitmap icon; - if (!data.ReadId(&out->id_) || !data.ReadTitle(&out->title_) || - !data.ReadMessage(&out->message_) || - !data.ReadDisplaySource(&out->display_source_) || !data.ReadIcon(&icon) || - !data.ReadOriginUrl(&out->origin_url_)) { +const RichNotificationData& NotificationStructTraits::optional_fields( + const Notification& n) { + return n.rich_notification_data(); +} + +// static +bool NotificationStructTraits::Read(NotificationDataView data, + Notification* out) { + gfx::ImageSkia icon; + if (!data.ReadIcon(&icon)) return false; - } - out->icon_ = gfx::Image::CreateFrom1xBitmap(icon); - return true; + out->set_icon(gfx::Image(icon)); + return EnumTraits<message_center::mojom::NotificationType, + message_center::NotificationType>::FromMojom(data.type(), + &out->type_) && + data.ReadId(&out->id_) && data.ReadTitle(&out->title_) && + data.ReadMessage(&out->message_) && + data.ReadDisplaySource(&out->display_source_) && + data.ReadOriginUrl(&out->origin_url_) && + data.ReadOptionalFields(&out->optional_fields_); } } // namespace mojo diff --git a/chromium/ui/message_center/mojo/notification_struct_traits.h b/chromium/ui/message_center/mojo/notification_struct_traits.h index 174a9984ee6..b1ebb81b3d4 100644 --- a/chromium/ui/message_center/mojo/notification_struct_traits.h +++ b/chromium/ui/message_center/mojo/notification_struct_traits.h @@ -5,21 +5,94 @@ #ifndef UI_MESSAGE_CENTER_NOTIFICATION_STRUCT_TRAITS_H_ #define UI_MESSAGE_CENTER_NOTIFICATION_STRUCT_TRAITS_H_ +#include "third_party/skia/include/core/SkColor.h" #include "ui/message_center/mojo/notification.mojom-shared.h" #include "ui/message_center/notification.h" namespace mojo { template <> +struct EnumTraits<message_center::mojom::NotificationType, + message_center::NotificationType> { + static message_center::mojom::NotificationType ToMojom( + message_center::NotificationType type) { + switch (type) { + case message_center::NOTIFICATION_TYPE_SIMPLE: + return message_center::mojom::NotificationType::SIMPLE; + case message_center::NOTIFICATION_TYPE_BASE_FORMAT: + return message_center::mojom::NotificationType::BASE_FORMAT; + case message_center::NOTIFICATION_TYPE_IMAGE: + return message_center::mojom::NotificationType::IMAGE; + case message_center::NOTIFICATION_TYPE_MULTIPLE: + return message_center::mojom::NotificationType::MULTIPLE; + case message_center::NOTIFICATION_TYPE_PROGRESS: + return message_center::mojom::NotificationType::PROGRESS; + case message_center::NOTIFICATION_TYPE_CUSTOM: + return message_center::mojom::NotificationType::CUSTOM; + } + NOTREACHED(); + return message_center::mojom::NotificationType::SIMPLE; + } + + static bool FromMojom(message_center::mojom::NotificationType input, + message_center::NotificationType* out) { + switch (input) { + case message_center::mojom::NotificationType::SIMPLE: + *out = message_center::NOTIFICATION_TYPE_SIMPLE; + return true; + case message_center::mojom::NotificationType::BASE_FORMAT: + *out = message_center::NOTIFICATION_TYPE_BASE_FORMAT; + return true; + case message_center::mojom::NotificationType::IMAGE: + *out = message_center::NOTIFICATION_TYPE_IMAGE; + return true; + case message_center::mojom::NotificationType::MULTIPLE: + *out = message_center::NOTIFICATION_TYPE_MULTIPLE; + return true; + case message_center::mojom::NotificationType::PROGRESS: + *out = message_center::NOTIFICATION_TYPE_PROGRESS; + return true; + case message_center::mojom::NotificationType::CUSTOM: + *out = message_center::NOTIFICATION_TYPE_CUSTOM; + return true; + } + NOTREACHED(); + return false; + } +}; + +template <> +struct StructTraits<message_center::mojom::RichNotificationDataDataView, + message_center::RichNotificationData> { + static int progress(const message_center::RichNotificationData& r); + static const base::string16& progress_status( + const message_center::RichNotificationData& r); + static bool should_make_spoken_feedback_for_popup_updates( + const message_center::RichNotificationData& r); + static bool clickable(const message_center::RichNotificationData& r); + static bool pinned(const message_center::RichNotificationData& r); + static const base::string16& accessible_name( + const message_center::RichNotificationData& r); + static SkColor accent_color(const message_center::RichNotificationData& r); + static bool use_image_as_icon(const message_center::RichNotificationData& r); + static bool Read(message_center::mojom::RichNotificationDataDataView data, + message_center::RichNotificationData* out); +}; + +template <> struct StructTraits<message_center::mojom::NotificationDataView, message_center::Notification> { + static message_center::NotificationType type( + const message_center::Notification& n); static const std::string& id(const message_center::Notification& n); static const base::string16& title(const message_center::Notification& n); static const base::string16& message(const message_center::Notification& n); - static const SkBitmap& icon(const message_center::Notification& n); + static gfx::ImageSkia icon(const message_center::Notification& n); static const base::string16& display_source( const message_center::Notification& n); static const GURL& origin_url(const message_center::Notification& n); + static const message_center::RichNotificationData& optional_fields( + const message_center::Notification& n); static bool Read(message_center::mojom::NotificationDataView data, message_center::Notification* out); }; diff --git a/chromium/ui/message_center/mojo/notifier_id.mojom b/chromium/ui/message_center/mojo/notifier_id.mojom new file mode 100644 index 00000000000..cf7a9a6167f --- /dev/null +++ b/chromium/ui/message_center/mojo/notifier_id.mojom @@ -0,0 +1,26 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +module message_center.mojom; + +import "mojo/common/string16.mojom"; +import "url/mojo/url.mojom"; + +// Equivalent to message_center::NotifierId::NotifierType. Used in UMA, so it +// should not be reordered. +enum NotifierType { + APPLICATION, + ARC_APPLICATION, + WEB_PAGE, + SYSTEM_COMPONENT, + SIZE, +}; + +// Equivalent to message_center::NotifierId. +struct NotifierId { + NotifierType type; + string id; + url.mojom.Url url; + string profile_id; +}; diff --git a/chromium/ui/message_center/mojo/notifier_id.typemap b/chromium/ui/message_center/mojo/notifier_id.typemap new file mode 100644 index 00000000000..00a923cd955 --- /dev/null +++ b/chromium/ui/message_center/mojo/notifier_id.typemap @@ -0,0 +1,14 @@ +# Copyright 2017 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +mojom = "//ui/message_center/mojo/notifier_id.mojom" +public_headers = [ "//ui/message_center/notifier_id.h" ] +traits_headers = [ "//ui/message_center/mojo/notifier_id_struct_traits.h" ] +deps = [ + "//ui/message_center/mojo:struct_traits", +] +type_mappings = [ + "message_center.mojom.NotifierId=message_center::NotifierId", + "message_center.mojom.NotifierType=message_center::NotifierId::NotifierType", +] diff --git a/chromium/ui/message_center/mojo/notifier_id_struct_traits.cc b/chromium/ui/message_center/mojo/notifier_id_struct_traits.cc new file mode 100644 index 00000000000..424df76756c --- /dev/null +++ b/chromium/ui/message_center/mojo/notifier_id_struct_traits.cc @@ -0,0 +1,47 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/message_center/mojo/notifier_id_struct_traits.h" + +#include "mojo/common/common_custom_types_struct_traits.h" +#include "url/mojo/url_gurl_struct_traits.h" + +namespace mojo { + +using NotifierIdStructTraits = + StructTraits<message_center::mojom::NotifierIdDataView, + message_center::NotifierId>; + +// static +const message_center::NotifierId::NotifierType& NotifierIdStructTraits::type( + const message_center::NotifierId& n) { + return n.type; +} + +// static +const std::string& NotifierIdStructTraits::id( + const message_center::NotifierId& n) { + return n.id; +} + +// static +const GURL& NotifierIdStructTraits::url(const message_center::NotifierId& n) { + return n.url; +} + +// static +const std::string& NotifierIdStructTraits::profile_id( + const message_center::NotifierId& n) { + return n.profile_id; +} + +// static +bool NotifierIdStructTraits::Read( + message_center::mojom::NotifierIdDataView data, + message_center::NotifierId* out) { + return data.ReadType(&out->type) && data.ReadId(&out->id) && + data.ReadUrl(&out->url) && data.ReadProfileId(&out->profile_id); +} + +} // namespace mojo diff --git a/chromium/ui/message_center/mojo/notifier_id_struct_traits.h b/chromium/ui/message_center/mojo/notifier_id_struct_traits.h new file mode 100644 index 00000000000..bf55f0c266c --- /dev/null +++ b/chromium/ui/message_center/mojo/notifier_id_struct_traits.h @@ -0,0 +1,71 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_MESSAGE_CENTER_NOTIFIER_ID_STRUCT_TRAITS_H_ +#define UI_MESSAGE_CENTER_NOTIFIER_ID_STRUCT_TRAITS_H_ + +#include "ui/message_center/mojo/notifier_id.mojom-shared.h" +#include "ui/message_center/notifier_id.h" + +namespace mojo { + +template <> +struct EnumTraits<message_center::mojom::NotifierType, + message_center::NotifierId::NotifierType> { + static message_center::mojom::NotifierType ToMojom( + message_center::NotifierId::NotifierType input) { + switch (input) { + case message_center::NotifierId::APPLICATION: + return message_center::mojom::NotifierType::APPLICATION; + case message_center::NotifierId::ARC_APPLICATION: + return message_center::mojom::NotifierType::ARC_APPLICATION; + case message_center::NotifierId::WEB_PAGE: + return message_center::mojom::NotifierType::WEB_PAGE; + case message_center::NotifierId::SYSTEM_COMPONENT: + return message_center::mojom::NotifierType::SYSTEM_COMPONENT; + case message_center::NotifierId::SIZE: + break; + } + NOTREACHED(); + return message_center::mojom::NotifierType::SIZE; + } + + static bool FromMojom(message_center::mojom::NotifierType input, + message_center::NotifierId::NotifierType* out) { + switch (input) { + case message_center::mojom::NotifierType::APPLICATION: + *out = message_center::NotifierId::APPLICATION; + return true; + case message_center::mojom::NotifierType::ARC_APPLICATION: + *out = message_center::NotifierId::ARC_APPLICATION; + return true; + case message_center::mojom::NotifierType::WEB_PAGE: + *out = message_center::NotifierId::WEB_PAGE; + return true; + case message_center::mojom::NotifierType::SYSTEM_COMPONENT: + *out = message_center::NotifierId::SYSTEM_COMPONENT; + return true; + case message_center::mojom::NotifierType::SIZE: + break; + } + NOTREACHED(); + return false; + } +}; + +template <> +struct StructTraits<message_center::mojom::NotifierIdDataView, + message_center::NotifierId> { + static const message_center::NotifierId::NotifierType& type( + const message_center::NotifierId& n); + static const std::string& id(const message_center::NotifierId& n); + static const GURL& url(const message_center::NotifierId& n); + static const std::string& profile_id(const message_center::NotifierId& n); + static bool Read(message_center::mojom::NotifierIdDataView data, + message_center::NotifierId* out); +}; + +} // namespace mojo + +#endif // UI_MESSAGE_CENTER_NOTIFIER_ID_STRUCT_TRAITS_H_ diff --git a/chromium/ui/message_center/mojo/struct_traits_unittest.cc b/chromium/ui/message_center/mojo/struct_traits_unittest.cc index ebe69ac221c..784a0e5bc52 100644 --- a/chromium/ui/message_center/mojo/struct_traits_unittest.cc +++ b/chromium/ui/message_center/mojo/struct_traits_unittest.cc @@ -11,7 +11,7 @@ #include "ui/gfx/image/image_unittest_util.h" #include "ui/message_center/mojo/traits_test_service.mojom.h" #include "ui/message_center/notification.h" -#include "ui/message_center/notifier_settings.h" +#include "ui/message_center/notifier_id.h" namespace message_center { namespace { @@ -28,6 +28,7 @@ class StructTraitsTest : public testing::Test, public mojom::TraitsTestService { } void Compare(const Notification& input, const Notification& output) { + EXPECT_EQ(input.type(), output.type()); EXPECT_EQ(input.id(), output.id()); EXPECT_EQ(input.title(), output.title()); EXPECT_EQ(input.message(), output.message()); @@ -35,6 +36,16 @@ class StructTraitsTest : public testing::Test, public mojom::TraitsTestService { EXPECT_EQ(input.icon().Height(), output.icon().Height()); EXPECT_EQ(input.display_source(), output.display_source()); EXPECT_EQ(input.origin_url(), output.origin_url()); + EXPECT_EQ(input.progress(), output.progress()); + EXPECT_EQ(input.progress_status(), output.progress_status()); + EXPECT_EQ(input.rich_notification_data() + .should_make_spoken_feedback_for_popup_updates, + output.rich_notification_data() + .should_make_spoken_feedback_for_popup_updates); + EXPECT_EQ(input.clickable(), output.clickable()); + EXPECT_EQ(input.accessible_name(), output.accessible_name()); + EXPECT_EQ(input.accent_color(), output.accent_color()); + EXPECT_EQ(input.use_image_as_icon(), output.use_image_as_icon()); } private: @@ -63,10 +74,21 @@ TEST_F(StructTraitsTest, Notification) { NotifierId notifier_id(NotifierId::NotifierType::APPLICATION, id); Notification input(type, id, title, message, icon, display_source, origin_url, notifier_id, RichNotificationData(), nullptr); + mojom::TraitsTestServicePtr proxy = GetTraitsTestProxy(); Notification output; proxy->EchoNotification(input, &output); Compare(input, output); + + // Set some optional fields to non-default values and test again. + input.set_type(NotificationType::NOTIFICATION_TYPE_PROGRESS); + input.set_progress(50); + input.set_progress_status(base::ASCIIToUTF16("progress text")); + input.set_clickable(!input.clickable()); + input.set_accent_color(SK_ColorMAGENTA); + input.set_use_image_as_icon(!input.use_image_as_icon()); + proxy->EchoNotification(input, &output); + Compare(input, output); } TEST_F(StructTraitsTest, EmptyIconNotification) { diff --git a/chromium/ui/message_center/mojo/typemaps.gni b/chromium/ui/message_center/mojo/typemaps.gni index 211a0d7f8f2..a7f5b3d481c 100644 --- a/chromium/ui/message_center/mojo/typemaps.gni +++ b/chromium/ui/message_center/mojo/typemaps.gni @@ -2,4 +2,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -typemaps = [ "//ui/message_center/mojo/notification.typemap" ] +typemaps = [ + "//ui/message_center/mojo/notification.typemap", + "//ui/message_center/mojo/notifier_id.typemap", +] diff --git a/chromium/ui/message_center/notification.cc b/chromium/ui/message_center/notification.cc index 9b2616a481b..4ef3cb68563 100644 --- a/chromium/ui/message_center/notification.cc +++ b/chromium/ui/message_center/notification.cc @@ -33,6 +33,13 @@ const gfx::ImageSkia CreateSolidColorImage(int width, return gfx::ImageSkia::CreateFrom1xBitmap(bitmap); } +gfx::Image DeepCopyImage(const gfx::Image& image) { + if (image.IsEmpty()) + return gfx::Image(); + std::unique_ptr<gfx::ImageSkia> image_skia(image.CopyImageSkia()); + return gfx::Image(*image_skia); +} + } // namespace NotificationItem::NotificationItem(const base::string16& title, @@ -60,6 +67,7 @@ RichNotificationData::RichNotificationData(const RichNotificationData& other) context_message(other.context_message), image(other.image), small_image(other.small_image), + vector_small_image(other.vector_small_image), items(other.items), progress(other.progress), progress_status(other.progress_status), @@ -75,7 +83,9 @@ RichNotificationData::RichNotificationData(const RichNotificationData& other) silent(other.silent), accessible_name(other.accessible_name), accent_color(other.accent_color), - use_image_as_icon(other.use_image_as_icon) { + use_image_as_icon(other.use_image_as_icon), + settings_button_handler(other.settings_button_handler), + fullscreen_visibility(other.fullscreen_visibility) { } RichNotificationData::~RichNotificationData() = default; @@ -156,6 +166,29 @@ Notification& Notification::operator=(const Notification& other) { Notification::~Notification() = default; +// static +std::unique_ptr<Notification> Notification::DeepCopy( + const Notification& notification, + bool include_body_image, + bool include_small_image, + bool include_icon_images) { + std::unique_ptr<Notification> notification_copy = + std::make_unique<Notification>(notification); + notification_copy->set_icon(DeepCopyImage(notification_copy->icon())); + notification_copy->set_image(include_body_image + ? DeepCopyImage(notification_copy->image()) + : gfx::Image()); + notification_copy->set_small_image( + include_small_image ? notification_copy->small_image() : gfx::Image()); + for (size_t i = 0; i < notification_copy->buttons().size(); i++) { + notification_copy->SetButtonIcon( + i, include_icon_images + ? DeepCopyImage(notification_copy->buttons()[i].icon) + : gfx::Image()); + } + return notification_copy; +} + bool Notification::IsRead() const { return is_read_ || optional_fields_.priority == MIN_PRIORITY; } @@ -212,6 +245,7 @@ std::unique_ptr<Notification> Notification::CreateSystemNotification( const gfx::Image& icon, const std::string& system_component_id, const base::Closure& click_callback) { + DCHECK(!click_callback.is_null()); std::unique_ptr<Notification> notification = CreateSystemNotification( NOTIFICATION_TYPE_SIMPLE, notification_id, title, message, icon, base::string16() /* display_source */, GURL(), @@ -219,6 +253,7 @@ std::unique_ptr<Notification> Notification::CreateSystemNotification( RichNotificationData(), new HandleNotificationClickedDelegate(click_callback), gfx::kNoneIcon, SystemNotificationWarningLevel::CRITICAL_WARNING); + notification->set_clickable(true); notification->SetSystemPriority(); return notification; } diff --git a/chromium/ui/message_center/notification.h b/chromium/ui/message_center/notification.h index 6025b06ce05..17b50bfc879 100644 --- a/chromium/ui/message_center/notification.h +++ b/chromium/ui/message_center/notification.h @@ -24,7 +24,7 @@ #include "ui/message_center/message_center_export.h" #include "ui/message_center/notification_delegate.h" #include "ui/message_center/notification_types.h" -#include "ui/message_center/notifier_settings.h" +#include "ui/message_center/notifier_id.h" #include "url/gurl.h" namespace gfx { @@ -54,6 +54,12 @@ enum class ButtonType { TEXT }; +enum class SettingsButtonHandler { + NONE, // No button. This is the default. + TRAY, // Button shown, the tray handles clicks. Only used on Chrome OS. + DELEGATE // Button shown, notification's delegate handles action. +}; + enum class SystemNotificationWarningLevel { NORMAL, WARNING, CRITICAL_WARNING }; // Represents a button to be shown as part of a notification. @@ -79,6 +85,15 @@ struct MESSAGE_CENTER_EXPORT ButtonInfo { base::string16 placeholder; }; +// TODO(estade): add an ALWAYS value to mark notifications as additionally +// visible over system fullscreen windows such as Chrome OS login so we don't +// need to centrally track Ash system notification IDs. +enum class FullscreenVisibility { + NONE, // Don't show the notification over fullscreen (default). + OVER_USER, // Show over the current fullscreened client window. + // windows (like Chrome OS login). +}; + // Represents rich features available for notifications. class MESSAGE_CENTER_EXPORT RichNotificationData { public: @@ -179,11 +194,18 @@ class MESSAGE_CENTER_EXPORT RichNotificationData { // and hides the icon when the notification is expanded. // This is only effective when new style notification is enabled. bool use_image_as_icon = false; + + // Controls whether a settings button should appear on the notification. See + // enum definition. TODO(estade): turn this into a boolean. See + // crbug.com/780342 + SettingsButtonHandler settings_button_handler = SettingsButtonHandler::NONE; + + FullscreenVisibility fullscreen_visibility = FullscreenVisibility::NONE; }; class MESSAGE_CENTER_EXPORT Notification { public: - // Default constructor needed for generated mojom files + // Default constructor needed for generated mojom files. Notification(); // Creates a new notification. @@ -228,6 +250,15 @@ class MESSAGE_CENTER_EXPORT Notification { virtual ~Notification(); + // Performs a deep copy of |notification|, including images and (optionally) + // the body image, small image, and icon images which are not supported on all + // platforms. + static std::unique_ptr<Notification> DeepCopy( + const Notification& notification, + bool include_body_image, + bool include_small_image, + bool include_icon_images); + // Copies the internal on-memory state from |base|, i.e. shown_as_popup, // is_read and never_timeout. void CopyState(Notification* base); @@ -410,19 +441,35 @@ class MESSAGE_CENTER_EXPORT Notification { optional_fields_.use_image_as_icon = use_image_as_icon; } + bool should_show_settings_button() const { + return optional_fields_.settings_button_handler != + SettingsButtonHandler::NONE; + } + + FullscreenVisibility fullscreen_visibility() const { + return optional_fields_.fullscreen_visibility; + } + void set_fullscreen_visibility(FullscreenVisibility visibility) { + optional_fields_.fullscreen_visibility = visibility; + } + NotificationDelegate* delegate() const { return delegate_.get(); } const RichNotificationData& rich_notification_data() const { return optional_fields_; } + void set_delegate(scoped_refptr<NotificationDelegate> delegate) { + DCHECK(!delegate_); + delegate_ = delegate; + } + // Set the priority to SYSTEM. The system priority user needs to call this // method explicitly, to avoid setting it accidentally. void SetSystemPriority(); // Delegate actions. void Display() const { delegate()->Display(); } - bool HasClickedListener() const { return delegate()->HasClickedListener(); } void Click() const { delegate()->Click(); } void ButtonClick(int index) const { delegate()->ButtonClick(index); } void Close(bool by_user) const { delegate()->Close(by_user); } diff --git a/chromium/ui/message_center/notification_delegate.cc b/chromium/ui/message_center/notification_delegate.cc index b35ee321f10..95b3fe3e91b 100644 --- a/chromium/ui/message_center/notification_delegate.cc +++ b/chromium/ui/message_center/notification_delegate.cc @@ -14,8 +14,6 @@ void NotificationDelegate::Display() {} void NotificationDelegate::Close(bool by_user) {} -bool NotificationDelegate::HasClickedListener() { return false; } - void NotificationDelegate::Click() {} void NotificationDelegate::ButtonClick(int button_index) {} @@ -25,13 +23,7 @@ void NotificationDelegate::ButtonClickWithReply(int button_index, NOTIMPLEMENTED(); } -bool NotificationDelegate::SettingsClick() { - return false; -} - -bool NotificationDelegate::ShouldDisplaySettingsButton() { - return false; -} +void NotificationDelegate::SettingsClick() {} void NotificationDelegate::DisableNotification() {} @@ -44,10 +36,6 @@ HandleNotificationClickedDelegate::HandleNotificationClickedDelegate( HandleNotificationClickedDelegate::~HandleNotificationClickedDelegate() {} -bool HandleNotificationClickedDelegate::HasClickedListener() { - return !closure_.is_null(); -} - void HandleNotificationClickedDelegate::Click() { if (!closure_.is_null()) closure_.Run(); @@ -68,8 +56,4 @@ void HandleNotificationButtonClickDelegate::ButtonClick(int button_index) { button_callback_.Run(button_index); } -bool NotificationDelegate::ShouldDisplayOverFullscreen() const { - return false; -} - } // namespace message_center diff --git a/chromium/ui/message_center/notification_delegate.h b/chromium/ui/message_center/notification_delegate.h index 00633d1e663..97108481dd4 100644 --- a/chromium/ui/message_center/notification_delegate.h +++ b/chromium/ui/message_center/notification_delegate.h @@ -16,10 +16,6 @@ namespace message_center { -class MessageCenterController; -class MessageView; -class Notification; - // Delegate for a notification. This class has two roles: to implement callback // methods for notification, and to provide an identity of the associated // notification. @@ -33,9 +29,6 @@ class MESSAGE_CENTER_EXPORT NotificationDelegate // user explicitly (as opposed to timeout/script), |by_user| should be true. virtual void Close(bool by_user); - // Returns true if the delegate can handle click event. - virtual bool HasClickedListener(); - // To be called when a desktop notification is clicked. virtual void Click(); @@ -48,30 +41,13 @@ class MESSAGE_CENTER_EXPORT NotificationDelegate virtual void ButtonClickWithReply(int button_index, const base::string16& reply); - // To be called when the user clicks the settings button in a notification. - // Returns whether the settings click was handled by the delegate. - virtual bool SettingsClick(); - - // To be called in order to detect if a settings button should be displayed. - // This also controls whether a context menu is enabled (as the context menu - // is also used for controlling settings). - virtual bool ShouldDisplaySettingsButton(); + // To be called when the user clicks the settings button in a notification + // which has a CUSTOM settings button action. + virtual void SettingsClick(); // Called when the user attempts to disable the notification. virtual void DisableNotification(); -#if defined(TOOLKIT_VIEWS) && !defined(OS_MACOSX) - // To be called to construct the message view for notifications whose type is - // NOTIFICATION_TYPE_CUSTOM. - virtual std::unique_ptr<MessageView> CreateCustomMessageView( - MessageCenterController* controller, - const Notification& notification); -#endif - - // Indicates whether this notification should be displayed when there is - // fullscreen content being displayed. - virtual bool ShouldDisplayOverFullscreen() const; - protected: virtual ~NotificationDelegate() {} @@ -87,7 +63,6 @@ class MESSAGE_CENTER_EXPORT HandleNotificationClickedDelegate // message_center::NotificationDelegate overrides: void Click() override; - bool HasClickedListener() override; protected: ~HandleNotificationClickedDelegate() override; diff --git a/chromium/ui/message_center/notification_delegate_unittest.cc b/chromium/ui/message_center/notification_delegate_unittest.cc index 3a8fe7135a5..d168cfe8173 100644 --- a/chromium/ui/message_center/notification_delegate_unittest.cc +++ b/chromium/ui/message_center/notification_delegate_unittest.cc @@ -47,7 +47,6 @@ TEST_F(NotificationDelegateTest, ClickedDelegate) { base::Bind(&NotificationDelegateTest::ClickCallback, base::Unretained(this)))); - EXPECT_TRUE(delegate->HasClickedListener()); delegate->Click(); EXPECT_EQ(1, GetClickedCallbackAndReset()); @@ -60,7 +59,6 @@ TEST_F(NotificationDelegateTest, NullClickedDelegate) { scoped_refptr<HandleNotificationClickedDelegate> delegate( new HandleNotificationClickedDelegate(base::Closure())); - EXPECT_FALSE(delegate->HasClickedListener()); delegate->Click(); EXPECT_EQ(0, GetClickedCallbackAndReset()); @@ -68,4 +66,4 @@ TEST_F(NotificationDelegateTest, NullClickedDelegate) { EXPECT_EQ(0, GetClickedCallbackAndReset()); } -} +} // namespace message_center diff --git a/chromium/ui/message_center/notification_delegate_views.cc b/chromium/ui/message_center/notification_delegate_views.cc deleted file mode 100644 index 6f1bc726b1e..00000000000 --- a/chromium/ui/message_center/notification_delegate_views.cc +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2016 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ui/message_center/notification_delegate.h" - -#include "ui/message_center/views/message_view.h" - -namespace message_center { - -std::unique_ptr<MessageView> NotificationDelegate::CreateCustomMessageView( - MessageCenterController* controller, - const Notification& notification) { - return nullptr; -} - -} // namespace message_center diff --git a/chromium/ui/message_center/notification_list.cc b/chromium/ui/message_center/notification_list.cc index 4b240c04956..303b61f748a 100644 --- a/chromium/ui/message_center/notification_list.cc +++ b/chromium/ui/message_center/notification_list.cc @@ -168,17 +168,17 @@ bool NotificationList::HasPopupNotifications( for (const auto& notification : notifications_) { if (notification->priority() < DEFAULT_PRIORITY) break; - if (!ShouldShowNotificationAsPopup(*notification.get(), blockers)) - continue; - if (!notification->shown_as_popup()) + if (!notification->shown_as_popup() && + ShouldShowNotificationAsPopup(*notification.get(), blockers)) { return true; + } } return false; } NotificationList::PopupNotifications NotificationList::GetPopupNotifications( const NotificationBlockers& blockers, - std::list<std::string>* blocked_ids) { + std::list<const Notification*>* blocked) { PopupNotifications result; size_t default_priority_popup_count = 0; @@ -194,8 +194,8 @@ NotificationList::PopupNotifications NotificationList::GetPopupNotifications( continue; if (!ShouldShowNotificationAsPopup(*notification, blockers)) { - if (blocked_ids) - blocked_ids->push_back(notification->id()); + if (blocked) + blocked->push_back(notification); continue; } @@ -288,18 +288,6 @@ size_t NotificationList::NotificationCount( return GetVisibleNotifications(blockers).size(); } -size_t NotificationList::UnreadCount( - const NotificationBlockers& blockers) const { - Notifications notifications = GetVisibleNotifications(blockers); - size_t unread_count = 0; - for (Notifications::const_iterator iter = notifications.begin(); - iter != notifications.end(); ++iter) { - if (!(*iter)->IsRead()) - ++unread_count; - } - return unread_count; -} - NotificationList::OwnedNotifications::iterator NotificationList::GetNotification(const std::string& id) { for (auto iter = notifications_.begin(); iter != notifications_.end(); diff --git a/chromium/ui/message_center/notification_list.h b/chromium/ui/message_center/notification_list.h index c942fd156bf..9acf7fabcfe 100644 --- a/chromium/ui/message_center/notification_list.h +++ b/chromium/ui/message_center/notification_list.h @@ -110,12 +110,12 @@ class MESSAGE_CENTER_EXPORT NotificationList { // Returns the recent notifications of the priority higher then LOW, // that have not been shown as a popup. kMaxVisiblePopupNotifications are // used to limit the number of notifications for the DEFAULT priority. - // It also stores the list of notification ids which is blocked by |blockers| - // to |blocked_ids|. |blocked_ids| can be NULL if the caller doesn't care - // which notifications are blocked. + // It also stores the list of notifications which are blocked by |blockers| + // to |blocked|. |blocked| can be NULL if the caller doesn't care which + // notifications are blocked. PopupNotifications GetPopupNotifications( const NotificationBlockers& blockers, - std::list<std::string>* blocked_ids); + std::list<const Notification*>* blocked); // Marks a specific popup item as shown. Set |mark_notification_as_read| to // true in case marking the notification as read too. @@ -145,7 +145,6 @@ class MESSAGE_CENTER_EXPORT NotificationList { Notifications GetVisibleNotifications( const NotificationBlockers& blockers) const; size_t NotificationCount(const NotificationBlockers& blockers) const; - size_t UnreadCount(const NotificationBlockers& blockers) const; private: friend class NotificationListTest; diff --git a/chromium/ui/message_center/notification_list_unittest.cc b/chromium/ui/message_center/notification_list_unittest.cc index 2bef6740e73..5f90837e180 100644 --- a/chromium/ui/message_center/notification_list_unittest.cc +++ b/chromium/ui/message_center/notification_list_unittest.cc @@ -17,7 +17,7 @@ #include "ui/message_center/fake_message_center.h" #include "ui/message_center/notification_blocker.h" #include "ui/message_center/notification_types.h" -#include "ui/message_center/notifier_settings.h" +#include "ui/message_center/notifier_id.h" #include "ui/message_center/public/cpp/message_center_constants.h" using base::UTF8ToUTF16; @@ -130,13 +130,11 @@ const char NotificationListTest::kExtensionId[] = "ext"; TEST_F(NotificationListTest, Basic) { ASSERT_EQ(0u, notification_list()->NotificationCount(blockers())); - ASSERT_EQ(0u, notification_list()->UnreadCount(blockers())); std::string id0 = AddNotification(); EXPECT_EQ(1u, notification_list()->NotificationCount(blockers())); std::string id1 = AddNotification(); EXPECT_EQ(2u, notification_list()->NotificationCount(blockers())); - EXPECT_EQ(2u, notification_list()->UnreadCount(blockers())); EXPECT_TRUE(notification_list()->HasPopupNotifications(blockers())); EXPECT_TRUE(notification_list()->GetNotificationById(id0)); @@ -152,7 +150,6 @@ TEST_F(NotificationListTest, Basic) { notification_list()->RemoveNotification(id0); EXPECT_EQ(1u, notification_list()->NotificationCount(blockers())); - EXPECT_EQ(1u, notification_list()->UnreadCount(blockers())); AddNotification(); EXPECT_EQ(2u, notification_list()->NotificationCount(blockers())); @@ -161,28 +158,13 @@ TEST_F(NotificationListTest, Basic) { TEST_F(NotificationListTest, MessageCenterVisible) { AddNotification(); EXPECT_EQ(1u, notification_list()->NotificationCount(blockers())); - ASSERT_EQ(1u, notification_list()->UnreadCount(blockers())); ASSERT_EQ(1u, GetPopupCounts()); // Resets the unread count and popup counts. notification_list()->SetNotificationsShown(blockers(), NULL); - ASSERT_EQ(0u, notification_list()->UnreadCount(blockers())); ASSERT_EQ(0u, GetPopupCounts()); } -TEST_F(NotificationListTest, UnreadCount) { - std::string id0 = AddNotification(); - std::string id1 = AddNotification(); - ASSERT_EQ(2u, notification_list()->UnreadCount(blockers())); - - notification_list()->MarkSinglePopupAsDisplayed(id0); - EXPECT_EQ(1u, notification_list()->UnreadCount(blockers())); - notification_list()->MarkSinglePopupAsDisplayed(id0); - EXPECT_EQ(1u, notification_list()->UnreadCount(blockers())); - notification_list()->MarkSinglePopupAsDisplayed(id1); - EXPECT_EQ(0u, notification_list()->UnreadCount(blockers())); -} - TEST_F(NotificationListTest, UpdateNotification) { std::string id0 = AddNotification(); std::string replaced = id0 + "_replaced"; @@ -300,7 +282,6 @@ TEST_F(NotificationListTest, OldPopupShouldNotBeHidden) { TEST_F(NotificationListTest, Priority) { ASSERT_EQ(0u, notification_list()->NotificationCount(blockers())); - ASSERT_EQ(0u, notification_list()->UnreadCount(blockers())); // Default priority has the limit on the number of the popups. for (size_t i = 0; i <= kMaxVisiblePopupNotifications; ++i) @@ -311,18 +292,15 @@ TEST_F(NotificationListTest, Priority) { // Low priority: not visible to popups. notification_list()->SetNotificationsShown(blockers(), NULL); - EXPECT_EQ(0u, notification_list()->UnreadCount(blockers())); AddPriorityNotification(LOW_PRIORITY); EXPECT_EQ(kMaxVisiblePopupNotifications + 2, notification_list()->NotificationCount(blockers())); - EXPECT_EQ(1u, notification_list()->UnreadCount(blockers())); EXPECT_EQ(0u, GetPopupCounts()); // Minimum priority: doesn't update the unread count. AddPriorityNotification(MIN_PRIORITY); EXPECT_EQ(kMaxVisiblePopupNotifications + 3, notification_list()->NotificationCount(blockers())); - EXPECT_EQ(1u, notification_list()->UnreadCount(blockers())); EXPECT_EQ(0u, GetPopupCounts()); NotificationList::Notifications notifications = @@ -344,7 +322,6 @@ TEST_F(NotificationListTest, Priority) { TEST_F(NotificationListTest, HasPopupsWithPriority) { ASSERT_EQ(0u, notification_list()->NotificationCount(blockers())); - ASSERT_EQ(0u, notification_list()->UnreadCount(blockers())); AddPriorityNotification(MIN_PRIORITY); AddPriorityNotification(MAX_PRIORITY); @@ -354,7 +331,6 @@ TEST_F(NotificationListTest, HasPopupsWithPriority) { TEST_F(NotificationListTest, HasPopupsWithSystemPriority) { ASSERT_EQ(0u, notification_list()->NotificationCount(blockers())); - ASSERT_EQ(0u, notification_list()->UnreadCount(blockers())); std::string normal_id = AddPriorityNotification(DEFAULT_PRIORITY); std::string system_id = AddNotification(); @@ -549,7 +525,6 @@ TEST_F(NotificationListTest, MarkSinglePopupAsShown) { notification_list()->MarkSinglePopupAsShown(id2, true); notification_list()->MarkSinglePopupAsShown(id3, false); EXPECT_EQ(3u, notification_list()->NotificationCount(blockers())); - EXPECT_EQ(1u, notification_list()->UnreadCount(blockers())); EXPECT_EQ(1u, GetPopupCounts()); NotificationList::PopupNotifications popups = GetPopups(); EXPECT_EQ(id1, (*popups.begin())->id()); @@ -614,26 +589,6 @@ TEST_F(NotificationListTest, QuietMode) { // TODO(mukai): Add test of quiet mode with expiration. } -// Verifies that unread_count doesn't become negative. -TEST_F(NotificationListTest, UnreadCountNoNegative) { - std::string id = AddNotification(); - EXPECT_EQ(1u, notification_list()->UnreadCount(blockers())); - - notification_list()->MarkSinglePopupAsDisplayed(id); - EXPECT_EQ(0u, notification_list()->UnreadCount(blockers())); - notification_list()->MarkSinglePopupAsShown( - id, false /* mark_notification_as_read */); - EXPECT_EQ(1u, notification_list()->UnreadCount(blockers())); - - // Updates the notification and verifies unread_count doesn't change. - std::unique_ptr<Notification> updated_notification(new Notification( - message_center::NOTIFICATION_TYPE_SIMPLE, id, UTF8ToUTF16("updated"), - UTF8ToUTF16("updated"), gfx::Image(), base::string16(), GURL(), - NotifierId(), RichNotificationData(), NULL)); - notification_list()->AddNotification(std::move(updated_notification)); - EXPECT_EQ(1u, notification_list()->UnreadCount(blockers())); -} - TEST_F(NotificationListTest, TestPushingShownNotification) { // Create a notification and mark it as shown. std::string id1; diff --git a/chromium/ui/message_center/notifier_settings.cc b/chromium/ui/message_center/notifier_id.cc index e7099fd55cf..6bf32a3352f 100644 --- a/chromium/ui/message_center/notifier_settings.cc +++ b/chromium/ui/message_center/notifier_id.cc @@ -2,27 +2,21 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "ui/message_center/notifier_id.h" #include "base/logging.h" #include "base/strings/string_number_conversions.h" -#include "ui/message_center/notifier_settings.h" namespace message_center { -NotifierId::NotifierId(NotifierType type, - const std::string& id) - : type(type), - id(id) { +NotifierId::NotifierId() : type(SYSTEM_COMPONENT) {} + +NotifierId::NotifierId(NotifierType type, const std::string& id) + : type(type), id(id) { DCHECK(type != WEB_PAGE); DCHECK(!id.empty()); } -NotifierId::NotifierId(const GURL& url) - : type(WEB_PAGE), - url(url) {} - -NotifierId::NotifierId() - : type(SYSTEM_COMPONENT) { -} +NotifierId::NotifierId(const GURL& url) : type(WEB_PAGE), url(url) {} NotifierId::NotifierId(const NotifierId& other) = default; @@ -52,21 +46,4 @@ bool NotifierId::operator<(const NotifierId& other) const { return id < other.id; } -Notifier::Notifier(const NotifierId& notifier_id, - const base::string16& name, - bool enabled) - : notifier_id(notifier_id), - name(name), - enabled(enabled) { -} - -Notifier::~Notifier() { -} - -NotifierGroup::NotifierGroup(const base::string16& name, - const base::string16& login_info) - : name(name), login_info(login_info) {} - -NotifierGroup::~NotifierGroup() {} - } // namespace message_center diff --git a/chromium/ui/message_center/notifier_id.h b/chromium/ui/message_center/notifier_id.h new file mode 100644 index 00000000000..ac11acd0f48 --- /dev/null +++ b/chromium/ui/message_center/notifier_id.h @@ -0,0 +1,65 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_MESSAGE_CENTER_NOTIFIER_ID_H_ +#define UI_MESSAGE_CENTER_NOTIFIER_ID_H_ + +#include <stddef.h> + +#include <string> +#include <vector> + +#include "base/macros.h" +#include "base/strings/string16.h" +#include "ui/gfx/image/image.h" +#include "ui/message_center/message_center_export.h" +#include "url/gurl.h" + +namespace message_center { + +// A struct that identifies the source of notifications. For example, a web page +// might send multiple notifications but they'd all have the same NotifierId. +// TODO(estade): move to public directory. +struct MESSAGE_CENTER_EXPORT NotifierId { + // This enum is being used for histogram reporting and the elements should not + // be re-ordered. + enum NotifierType : int { + APPLICATION = 0, + ARC_APPLICATION = 1, + WEB_PAGE = 2, + SYSTEM_COMPONENT = 3, + SIZE, + }; + + // Default constructor needed for generated mojom files and tests. + NotifierId(); + + // Constructor for non WEB_PAGE type. + NotifierId(NotifierType type, const std::string& id); + + // Constructor for WEB_PAGE type. + explicit NotifierId(const GURL& url); + + NotifierId(const NotifierId& other); + + bool operator==(const NotifierId& other) const; + // Allows NotifierId to be used as a key in std::map. + bool operator<(const NotifierId& other) const; + + NotifierType type; + + // The identifier of the app notifier. Empty if it's WEB_PAGE. + std::string id; + + // The URL pattern of the notifer. + GURL url; + + // The identifier of the profile where the notification is created. This is + // used for ChromeOS multi-profile support and can be empty. + std::string profile_id; +}; + +} // namespace message_center + +#endif // UI_MESSAGE_CENTER_NOTIFIER_ID_H_ diff --git a/chromium/ui/message_center/notifier_settings.h b/chromium/ui/message_center/notifier_settings.h deleted file mode 100644 index 39880de566a..00000000000 --- a/chromium/ui/message_center/notifier_settings.h +++ /dev/null @@ -1,208 +0,0 @@ -// Copyright (c) 2013 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef UI_MESSAGE_CENTER_NOTIFIER_SETTINGS_H_ -#define UI_MESSAGE_CENTER_NOTIFIER_SETTINGS_H_ - -#include <stddef.h> - -#include <string> -#include <vector> - -#include "base/gtest_prod_util.h" -#include "base/macros.h" -#include "base/strings/string16.h" -#include "ui/gfx/image/image.h" -#include "ui/message_center/message_center_export.h" -#include "url/gurl.h" - -class MessageCenterNotificationsTest; -class MessageCenterTrayBridgeTest; - -namespace ash { -class WebNotificationTrayTest; -} - -namespace message_center { -namespace test { -class MessagePopupCollectionTest; -} - -class MessageCenterNotificationManagerTest; -class Notification; -class NotifierSettingsDelegate; -class NotifierSettingsProvider; - -// Brings up the settings dialog and returns a weak reference to the delegate, -// which is typically the view. If the dialog already exists, it is brought to -// the front, otherwise it is created. -MESSAGE_CENTER_EXPORT NotifierSettingsDelegate* ShowSettings( - NotifierSettingsProvider* provider, - gfx::NativeView context); - -// A struct that identifies the source of notifications. For example, a web page -// might send multiple notifications but they'd all have the same NotifierId. -// TODO(estade): rename to Notifier. -struct MESSAGE_CENTER_EXPORT NotifierId { - // This enum is being used for histogram reporting and the elements should not - // be re-ordered. - enum NotifierType : int { - APPLICATION = 0, - ARC_APPLICATION = 1, - WEB_PAGE = 2, - SYSTEM_COMPONENT = 3, - SIZE, - }; - - // Constructor for non WEB_PAGE type. - NotifierId(NotifierType type, const std::string& id); - - // Constructor for WEB_PAGE type. - explicit NotifierId(const GURL& url); - - NotifierId(const NotifierId& other); - - bool operator==(const NotifierId& other) const; - // Allows NotifierId to be used as a key in std::map. - bool operator<(const NotifierId& other) const; - - NotifierType type; - - // The identifier of the app notifier. Empty if it's WEB_PAGE. - std::string id; - - // The URL pattern of the notifer. - GURL url; - - // The identifier of the profile where the notification is created. This is - // used for ChromeOS multi-profile support and can be empty. - std::string profile_id; - - private: - friend class MessageCenterNotificationManagerTest; - friend class MessageCenterTrayTest; - friend class Notification; - friend class NotificationControllerTest; - friend class PopupCollectionTest; - friend class TrayViewControllerTest; - friend class ::MessageCenterNotificationsTest; - friend class ::MessageCenterTrayBridgeTest; - friend class ash::WebNotificationTrayTest; - friend class test::MessagePopupCollectionTest; - FRIEND_TEST_ALL_PREFIXES(PopupControllerTest, Creation); - FRIEND_TEST_ALL_PREFIXES(NotificationListTest, UnreadCountNoNegative); - FRIEND_TEST_ALL_PREFIXES(NotificationListTest, TestHasNotificationOfType); - - // The default constructor which doesn't specify the notifier. Used for tests. - NotifierId(); -}; - -// A struct to hold UI information about notifiers. The data is used by -// NotifierSettingsView. TODO(estade): rename to NotifierUiData. -struct MESSAGE_CENTER_EXPORT Notifier { - Notifier(const NotifierId& notifier_id, - const base::string16& name, - bool enabled); - ~Notifier(); - - NotifierId notifier_id; - - // The human-readable name of the notifier such like the extension name. - // It can be empty. - base::string16 name; - - // True if the source is allowed to send notifications. True is default. - bool enabled; - - // The icon image of the notifier. The extension icon or favicon. - gfx::Image icon; - - private: - DISALLOW_COPY_AND_ASSIGN(Notifier); -}; - -struct MESSAGE_CENTER_EXPORT NotifierGroup { - NotifierGroup(const base::string16& name, const base::string16& login_info); - ~NotifierGroup(); - - // Display name of a notifier group. - const base::string16 name; - - // More display information about the notifier group. - base::string16 login_info; - - private: - DISALLOW_COPY_AND_ASSIGN(NotifierGroup); -}; - -// An observer class implemented by the view of the NotifierSettings to get -// notified when the controller has changed data. -class MESSAGE_CENTER_EXPORT NotifierSettingsObserver { - public: - // Called when an icon in the controller has been updated. - virtual void UpdateIconImage(const NotifierId& notifier_id, - const gfx::Image& icon) = 0; - - // Called when any change happens to the set of notifier groups. - virtual void NotifierGroupChanged() = 0; - - // Called when a notifier is enabled or disabled. - virtual void NotifierEnabledChanged(const NotifierId& notifier_id, - bool enabled) = 0; -}; - -// A class used by NotifierSettingsView to integrate with a setting system -// for the clients of this module. -class MESSAGE_CENTER_EXPORT NotifierSettingsProvider { - public: - virtual ~NotifierSettingsProvider() {} - - // Sets the delegate. - virtual void AddObserver(NotifierSettingsObserver* observer) = 0; - virtual void RemoveObserver(NotifierSettingsObserver* observer) = 0; - - // Returns the number of notifier groups available. - virtual size_t GetNotifierGroupCount() const = 0; - - // Requests the model for a particular notifier group. - virtual const message_center::NotifierGroup& GetNotifierGroupAt( - size_t index) const = 0; - - // Returns true if the notifier group at |index| is active. - virtual bool IsNotifierGroupActiveAt(size_t index) const = 0; - - // Informs the settings provider that further requests to GetNotifierList - // should return notifiers for the specified notifier group. - virtual void SwitchToNotifierGroup(size_t index) = 0; - - // Requests the currently active notifier group. - virtual const message_center::NotifierGroup& GetActiveNotifierGroup() - const = 0; - - // Provides the current notifier list in |notifiers|. - virtual void GetNotifierList( - std::vector<std::unique_ptr<Notifier>>* notifiers) = 0; - - // Called when the |enabled| for the given notifier has been changed by user - // operation. - virtual void SetNotifierEnabled(const NotifierId& notifier_id, - bool enabled) = 0; - - // Called when the settings window is closed. - virtual void OnNotifierSettingsClosing() = 0; - - // Called to determine if a particular notifier can respond to a request for - // more information. - virtual bool NotifierHasAdvancedSettings(const NotifierId& notifier_id) - const = 0; - - // Called upon request for more information about a particular notifier. - virtual void OnNotifierAdvancedSettingsRequested( - const NotifierId& notifier_id, - const std::string* notification_id) = 0; -}; - -} // namespace message_center - -#endif // UI_MESSAGE_CENTER_NOTIFIER_SETTINGS_H_ diff --git a/chromium/ui/message_center/popup_timers_controller.cc b/chromium/ui/message_center/popup_timers_controller.cc index 49e5d85c683..359f1049129 100644 --- a/chromium/ui/message_center/popup_timers_controller.cc +++ b/chromium/ui/message_center/popup_timers_controller.cc @@ -14,10 +14,20 @@ namespace message_center { namespace { base::TimeDelta GetTimeoutForNotification(Notification* notification) { - if (notification->notifier_id().type == NotifierId::WEB_PAGE || - notification->priority() > DEFAULT_PRIORITY) { +// Web Notifications are given a longer on-screen time on non-Chrome OS +// platforms as there is no notification center to dismiss them to. +#if defined(OS_CHROMEOS) + const bool use_high_priority_delay = + notification->priority() > DEFAULT_PRIORITY; +#else + const bool use_high_priority_delay = + notification->priority() > DEFAULT_PRIORITY || + notification->notifier_id().type == NotifierId::WEB_PAGE; +#endif + + if (use_high_priority_delay) return base::TimeDelta::FromSeconds(kAutocloseHighPriorityDelaySeconds); - } + return base::TimeDelta::FromSeconds(kAutocloseDefaultDelaySeconds); } diff --git a/chromium/ui/message_center/public/cpp/message_center_constants.h b/chromium/ui/message_center/public/cpp/message_center_constants.h index f07818a0861..cc0489c6a88 100644 --- a/chromium/ui/message_center/public/cpp/message_center_constants.h +++ b/chromium/ui/message_center/public/cpp/message_center_constants.h @@ -93,9 +93,9 @@ const SkColor kNotificationDefaultAccentColor = gfx::kChromeIconGrey; // Not used when --enabled-new-style-notification is set. const size_t kNotificationMaximumItems = 5; -// Timing. Web Notifications always use high-priority timings. Given the absence -// of a notification center on non-Chrome OS platforms, this improves users' -// ability to interact with the toasts. +// Timing. Web Notifications always use high-priority timings except on +// Chrome OS. Given the absence of a notification center on non-Chrome OS +// platforms, this improves users' ability to interact with the toasts. const int kAutocloseDefaultDelaySeconds = 8; const int kAutocloseHighPriorityDelaySeconds = 25; @@ -127,9 +127,17 @@ const int kContextMessageLineLimit = 1; // Around notifications //////////////////////////////////////////////////////// -// DIP dimensions (H = horizontal, V = vertical). -const int kMarginBetweenItems = 10; // H & V space around & between - // notifications. +#if defined(OS_CHROMEOS) +// Horizontal & vertical thickness of the border around the notifications in the +// notification list. +constexpr int kNotificationBorderThickness = 1; +// Horizontal & vertical space around & between notifications in the +// notification list. +constexpr int kMarginBetweenItemsInList = 8; +#endif + +// Horizontal & vertical space around & between popup notifications. +constexpr int kMarginBetweenPopups = 10; // Shadow in the tray. const SkColor kShadowColor = SkColorSetARGB(0.3 * 255, 0, 0, 0); diff --git a/chromium/ui/message_center/ui_controller.cc b/chromium/ui/message_center/ui_controller.cc new file mode 100644 index 00000000000..3fd67d1474d --- /dev/null +++ b/chromium/ui/message_center/ui_controller.cc @@ -0,0 +1,201 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/message_center/ui_controller.h" + +#include <memory> + +#include "base/macros.h" +#include "base/observer_list.h" +#include "base/strings/utf_string_conversions.h" +#include "base/threading/thread_task_runner_handle.h" +#include "build/build_config.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/models/simple_menu_model.h" +#include "ui/message_center/message_center.h" +#include "ui/message_center/message_center_types.h" +#include "ui/message_center/notification_blocker.h" +#include "ui/message_center/ui_delegate.h" +#include "ui/message_center/views/notification_menu_model.h" +#include "ui/strings/grit/ui_strings.h" + +namespace message_center { + +UiController::UiController(UiDelegate* delegate) + : message_center_(MessageCenter::Get()), + message_center_visible_(false), + popups_visible_(false), + delegate_(delegate) { + message_center_->AddObserver(this); +} + +UiController::~UiController() { + message_center_->RemoveObserver(this); +} + +bool UiController::ShowMessageCenterBubble(bool show_by_click) { + if (message_center_visible_) + return true; + + HidePopupBubbleInternal(); + + message_center_visible_ = delegate_->ShowMessageCenter(show_by_click); + if (message_center_visible_) { + message_center_->SetVisibility(message_center::VISIBILITY_MESSAGE_CENTER); + NotifyUiControllerChanged(); + } + return message_center_visible_; +} + +bool UiController::HideMessageCenterBubble() { +#if defined(OS_CHROMEOS) + // TODO(yoshiki): Move the message center bubble related logic to ash/. + hide_empty_message_center_callback_.reset(); +#endif + + if (!message_center_visible_) + return false; + delegate_->HideMessageCenter(); + MarkMessageCenterHidden(); + + return true; +} + +void UiController::MarkMessageCenterHidden() { + if (!message_center_visible_) + return; + message_center_visible_ = false; + message_center_->SetVisibility(message_center::VISIBILITY_TRANSIENT); + + // Some notifications (like system ones) should appear as popups again + // after the message center is closed. + if (message_center_->HasPopupNotifications()) { + ShowPopupBubble(); + return; + } + + NotifyUiControllerChanged(); +} + +void UiController::ShowPopupBubble() { + if (message_center_visible_) + return; + + if (popups_visible_) { + NotifyUiControllerChanged(); + return; + } + + if (!message_center_->HasPopupNotifications()) + return; + + popups_visible_ = delegate_->ShowPopups(); + + NotifyUiControllerChanged(); +} + +bool UiController::HidePopupBubble() { + if (!popups_visible_) + return false; + HidePopupBubbleInternal(); + NotifyUiControllerChanged(); + + return true; +} + +void UiController::HidePopupBubbleInternal() { + if (!popups_visible_) + return; + + delegate_->HidePopups(); + popups_visible_ = false; +} + +void UiController::ShowNotifierSettingsBubble() { + if (popups_visible_) + HidePopupBubbleInternal(); + + message_center_visible_ = delegate_->ShowNotifierSettings(); + message_center_->SetVisibility(message_center::VISIBILITY_SETTINGS); + + NotifyUiControllerChanged(); +} + +void UiController::OnNotificationAdded(const std::string& notification_id) { + OnMessageCenterChanged(); +} + +void UiController::OnNotificationRemoved(const std::string& notification_id, + bool by_user) { + OnMessageCenterChanged(); +} + +void UiController::OnNotificationUpdated(const std::string& notification_id) { + OnMessageCenterChanged(); +} + +void UiController::OnNotificationClicked(const std::string& notification_id) { + if (popups_visible_) + OnMessageCenterChanged(); +} + +void UiController::OnNotificationButtonClicked( + const std::string& notification_id, + int button_index) { + if (popups_visible_) + OnMessageCenterChanged(); +} + +void UiController::OnNotificationSettingsClicked(bool handled) { + if (!handled) + ShowNotifierSettingsBubble(); +} + +void UiController::OnNotificationDisplayed(const std::string& notification_id, + const DisplaySource source) { + NotifyUiControllerChanged(); +} + +void UiController::OnQuietModeChanged(bool in_quiet_mode) { + NotifyUiControllerChanged(); +} + +void UiController::OnBlockingStateChanged(NotificationBlocker* blocker) { + OnMessageCenterChanged(); +} + +void UiController::OnMessageCenterChanged() { +#if defined(OS_CHROMEOS) + // TODO(yoshiki): Move the message center bubble related logic to ash/. + if (message_center_visible_ && message_center_->NotificationCount() == 0) { + if (hide_empty_message_center_callback_) + return; + + hide_empty_message_center_callback_ = + std::make_unique<base::CancelableClosure>(base::Bind( + base::IgnoreResult(&UiController::HideMessageCenterBubble), + base::Unretained(this))); + base::ThreadTaskRunnerHandle::Get()->PostTask( + FROM_HERE, hide_empty_message_center_callback_->callback()); + return; + } + + // Cancel the callback if necessary. + if (hide_empty_message_center_callback_) + hide_empty_message_center_callback_.reset(); +#endif + + if (popups_visible_ && !message_center_->HasPopupNotifications()) + HidePopupBubbleInternal(); + else if (!popups_visible_ && message_center_->HasPopupNotifications()) + ShowPopupBubble(); + + NotifyUiControllerChanged(); +} + +void UiController::NotifyUiControllerChanged() { + delegate_->OnMessageCenterContentsChanged(); +} + +} // namespace message_center diff --git a/chromium/ui/message_center/message_center_tray.h b/chromium/ui/message_center/ui_controller.h index 7377d3bcddf..bbe26caa972 100644 --- a/chromium/ui/message_center/message_center_tray.h +++ b/chromium/ui/message_center/ui_controller.h @@ -2,33 +2,30 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_MESSAGE_CENTER_MESSAGE_CENTER_TRAY_H_ -#define UI_MESSAGE_CENTER_MESSAGE_CENTER_TRAY_H_ +#ifndef UI_MESSAGE_CENTER_UI_CONTROLLER_H_ +#define UI_MESSAGE_CENTER_UI_CONTROLLER_H_ +#include "base/cancelable_callback.h" #include "base/macros.h" #include "base/observer_list.h" #include "base/strings/string16.h" #include "ui/message_center/message_center_export.h" #include "ui/message_center/message_center_observer.h" -#include "ui/message_center/message_center_tray_delegate.h" -#include "ui/message_center/notifier_settings.h" - -namespace ui { -class MenuModel; -} +#include "ui/message_center/notifier_id.h" +#include "ui/message_center/ui_delegate.h" namespace message_center { class MessageCenter; -// Class that observes a MessageCenter. Manages the popup and message center -// bubbles. Tells the MessageCenterTrayHost when the tray is changed, as well -// as when bubbles are shown and hidden. -class MESSAGE_CENTER_EXPORT MessageCenterTray : public MessageCenterObserver { +// Class that observes a MessageCenter and reacts to changes in the list of +// notifications. Manages the popup and message center bubbles. Tells the +// UiDelegate when the tray is changed, as well as when bubbles are shown and +// hidden. +class MESSAGE_CENTER_EXPORT UiController : public MessageCenterObserver { public: - MessageCenterTray(MessageCenterTrayDelegate* delegate, - message_center::MessageCenter* message_center); - ~MessageCenterTray() override; + explicit UiController(UiDelegate* delegate); + ~UiController() override; // Shows or updates the message center bubble and hides the popup bubble. Set // |show_by_click| to true if bubble is shown by mouse or gesture click. @@ -53,13 +50,9 @@ class MESSAGE_CENTER_EXPORT MessageCenterTray : public MessageCenterObserver { // Toggles the visibility of the settings view in the message center bubble. void ShowNotifierSettingsBubble(); - // Creates a model for the context menu for a notification card. - std::unique_ptr<ui::MenuModel> CreateNotificationMenuModel( - const Notification& notification); - bool message_center_visible() { return message_center_visible_; } bool popups_visible() { return popups_visible_; } - MessageCenterTrayDelegate* delegate() { return delegate_; } + UiDelegate* delegate() { return delegate_; } const message_center::MessageCenter* message_center() const { return message_center_; } @@ -81,20 +74,21 @@ class MESSAGE_CENTER_EXPORT MessageCenterTray : public MessageCenterObserver { private: void OnMessageCenterChanged(); - void NotifyMessageCenterTrayChanged(); + void NotifyUiControllerChanged(); void HidePopupBubbleInternal(); - // |message_center_| is a weak pointer that must live longer than - // MessageCenterTray. message_center::MessageCenter* message_center_; - bool message_center_visible_; - bool popups_visible_; - // |delegate_| is a weak pointer that must live longer than MessageCenterTray. - MessageCenterTrayDelegate* delegate_; + bool message_center_visible_ = false; + bool popups_visible_ = false; + UiDelegate* delegate_; + +#if defined(OS_CHROMEOS) + std::unique_ptr<base::CancelableClosure> hide_empty_message_center_callback_; +#endif - DISALLOW_COPY_AND_ASSIGN(MessageCenterTray); + DISALLOW_COPY_AND_ASSIGN(UiController); }; } // namespace message_center -#endif // UI_MESSAGE_CENTER_MESSAGE_CENTER_TRAY_H_ +#endif // UI_MESSAGE_CENTER_UI_CONTROLLER_H_ diff --git a/chromium/ui/message_center/ui_controller_unittest.cc b/chromium/ui/message_center/ui_controller_unittest.cc new file mode 100644 index 00000000000..75795f7277e --- /dev/null +++ b/chromium/ui/message_center/ui_controller_unittest.cc @@ -0,0 +1,251 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/message_center/ui_controller.h" + +#include <utility> + +#include "base/macros.h" +#include "base/strings/utf_string_conversions.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/base/models/menu_model.h" +#include "ui/message_center/message_center.h" +#include "ui/message_center/notification.h" +#include "ui/message_center/notification_types.h" + +using base::ASCIIToUTF16; + +namespace message_center { +namespace { + +class TestNotificationDelegate : public message_center::NotificationDelegate { + public: + TestNotificationDelegate() = default; + + private: + ~TestNotificationDelegate() override = default; + + DISALLOW_COPY_AND_ASSIGN(TestNotificationDelegate); +}; + +class MockDelegate : public UiDelegate { + public: + MockDelegate() {} + ~MockDelegate() override {} + void OnMessageCenterContentsChanged() override {} + bool ShowPopups() override { return show_message_center_success_; } + void HidePopups() override {} + bool ShowMessageCenter(bool show_by_click) override { + return show_popups_success_; + } + void HideMessageCenter() override {} + bool ShowNotifierSettings() override { return true; } + + bool show_popups_success_ = true; + bool show_message_center_success_ = true; + + private: + DISALLOW_COPY_AND_ASSIGN(MockDelegate); +}; + +} // namespace + +class UiControllerTest : public testing::Test { + public: + UiControllerTest() {} + ~UiControllerTest() override {} + + void SetUp() override { + MessageCenter::Initialize(); + delegate_.reset(new MockDelegate); + message_center_ = MessageCenter::Get(); + ui_controller_.reset(new UiController(delegate_.get())); + } + + void TearDown() override { + ui_controller_.reset(); + delegate_.reset(); + message_center_ = NULL; + MessageCenter::Shutdown(); + } + + protected: + NotifierId DummyNotifierId() { return NotifierId(); } + + Notification* AddNotification(const std::string& id) { + return AddNotification(id, DummyNotifierId()); + } + + Notification* AddNotification(const std::string& id, NotifierId notifier_id) { + std::unique_ptr<Notification> notification( + new Notification(message_center::NOTIFICATION_TYPE_SIMPLE, id, + ASCIIToUTF16("Test Web Notification"), + ASCIIToUTF16("Notification message body."), + gfx::Image(), ASCIIToUTF16("www.test.org"), GURL(), + notifier_id, message_center::RichNotificationData(), + new TestNotificationDelegate())); + Notification* notification_ptr = notification.get(); + message_center_->AddNotification(std::move(notification)); + return notification_ptr; + } + std::unique_ptr<MockDelegate> delegate_; + std::unique_ptr<UiController> ui_controller_; + MessageCenter* message_center_; + + private: + DISALLOW_COPY_AND_ASSIGN(UiControllerTest); +}; + +TEST_F(UiControllerTest, BasicMessageCenter) { + ASSERT_FALSE(ui_controller_->popups_visible()); + ASSERT_FALSE(ui_controller_->message_center_visible()); + + bool shown = + ui_controller_->ShowMessageCenterBubble(false /* show_by_click */); + EXPECT_TRUE(shown); + + ASSERT_FALSE(ui_controller_->popups_visible()); + ASSERT_TRUE(ui_controller_->message_center_visible()); + + ui_controller_->HideMessageCenterBubble(); + + ASSERT_FALSE(ui_controller_->popups_visible()); + ASSERT_FALSE(ui_controller_->message_center_visible()); + + ui_controller_->ShowMessageCenterBubble(false /* show_by_click */); + + ASSERT_FALSE(ui_controller_->popups_visible()); + ASSERT_TRUE(ui_controller_->message_center_visible()); + + ui_controller_->HideMessageCenterBubble(); + + ASSERT_FALSE(ui_controller_->popups_visible()); + ASSERT_FALSE(ui_controller_->message_center_visible()); +} + +TEST_F(UiControllerTest, BasicPopup) { + ASSERT_FALSE(ui_controller_->popups_visible()); + ASSERT_FALSE(ui_controller_->message_center_visible()); + + ui_controller_->ShowPopupBubble(); + + ASSERT_FALSE(ui_controller_->popups_visible()); + ASSERT_FALSE(ui_controller_->message_center_visible()); + + AddNotification("BasicPopup"); + + ASSERT_TRUE(ui_controller_->popups_visible()); + ASSERT_FALSE(ui_controller_->message_center_visible()); + + ui_controller_->HidePopupBubble(); + + ASSERT_FALSE(ui_controller_->popups_visible()); + ASSERT_FALSE(ui_controller_->message_center_visible()); +} + +TEST_F(UiControllerTest, MessageCenterClosesPopups) { + ASSERT_FALSE(ui_controller_->popups_visible()); + ASSERT_FALSE(ui_controller_->message_center_visible()); + + AddNotification("MessageCenterClosesPopups"); + + ASSERT_TRUE(ui_controller_->popups_visible()); + ASSERT_FALSE(ui_controller_->message_center_visible()); + + bool shown = + ui_controller_->ShowMessageCenterBubble(false /* show_by_click */); + EXPECT_TRUE(shown); + + ASSERT_FALSE(ui_controller_->popups_visible()); + ASSERT_TRUE(ui_controller_->message_center_visible()); + + // The notification is queued if it's added when message center is visible. + AddNotification("MessageCenterClosesPopups2"); + + ui_controller_->ShowPopupBubble(); + + ASSERT_FALSE(ui_controller_->popups_visible()); + ASSERT_TRUE(ui_controller_->message_center_visible()); + + ui_controller_->HideMessageCenterBubble(); + + // There is no queued notification. + ASSERT_FALSE(ui_controller_->popups_visible()); + ASSERT_FALSE(ui_controller_->message_center_visible()); + + ui_controller_->ShowMessageCenterBubble(false /* show_by_click */); + ui_controller_->HideMessageCenterBubble(); + ASSERT_FALSE(ui_controller_->popups_visible()); + ASSERT_FALSE(ui_controller_->message_center_visible()); +} + +TEST_F(UiControllerTest, MessageCenterReopenPopupsForSystemPriority) { + ASSERT_FALSE(ui_controller_->popups_visible()); + ASSERT_FALSE(ui_controller_->message_center_visible()); + + std::unique_ptr<Notification> notification(new Notification( + message_center::NOTIFICATION_TYPE_SIMPLE, + "MessageCenterReopnPopupsForSystemPriority", + ASCIIToUTF16("Test Web Notification"), + ASCIIToUTF16("Notification message body."), gfx::Image(), + ASCIIToUTF16("www.test.org"), GURL(), DummyNotifierId(), + message_center::RichNotificationData(), NULL /* delegate */)); + notification->SetSystemPriority(); + message_center_->AddNotification(std::move(notification)); + + ASSERT_TRUE(ui_controller_->popups_visible()); + ASSERT_FALSE(ui_controller_->message_center_visible()); + + bool shown = + ui_controller_->ShowMessageCenterBubble(false /* show_by_click */); + EXPECT_TRUE(shown); + + ASSERT_FALSE(ui_controller_->popups_visible()); + ASSERT_TRUE(ui_controller_->message_center_visible()); + + ui_controller_->HideMessageCenterBubble(); + + ASSERT_TRUE(ui_controller_->popups_visible()); + ASSERT_FALSE(ui_controller_->message_center_visible()); +} + +TEST_F(UiControllerTest, ShowBubbleFails) { + // Now the delegate will signal that it was unable to show a bubble. + delegate_->show_popups_success_ = false; + delegate_->show_message_center_success_ = false; + + ASSERT_FALSE(ui_controller_->popups_visible()); + ASSERT_FALSE(ui_controller_->message_center_visible()); + + AddNotification("ShowBubbleFails"); + + ui_controller_->ShowPopupBubble(); + + ASSERT_FALSE(ui_controller_->popups_visible()); + ASSERT_FALSE(ui_controller_->message_center_visible()); + + bool shown = + ui_controller_->ShowMessageCenterBubble(false /* show_by_click */); + EXPECT_FALSE(shown); + + ASSERT_FALSE(ui_controller_->popups_visible()); + ASSERT_FALSE(ui_controller_->message_center_visible()); + + ui_controller_->HideMessageCenterBubble(); + + ASSERT_FALSE(ui_controller_->popups_visible()); + ASSERT_FALSE(ui_controller_->message_center_visible()); + + ui_controller_->ShowMessageCenterBubble(false /* show_by_click */); + + ASSERT_FALSE(ui_controller_->popups_visible()); + ASSERT_FALSE(ui_controller_->message_center_visible()); + + ui_controller_->HidePopupBubble(); + + ASSERT_FALSE(ui_controller_->popups_visible()); + ASSERT_FALSE(ui_controller_->message_center_visible()); +} + +} // namespace message_center diff --git a/chromium/ui/message_center/message_center_tray_delegate.h b/chromium/ui/message_center/ui_delegate.h index 11a13667be7..89d302673ac 100644 --- a/chromium/ui/message_center/message_center_tray_delegate.h +++ b/chromium/ui/message_center/ui_delegate.h @@ -2,23 +2,22 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_MESSAGE_CENTER_MESSAGE_CENTER_TRAY_DELEGATE_H_ -#define UI_MESSAGE_CENTER_MESSAGE_CENTER_TRAY_DELEGATE_H_ +#ifndef UI_MESSAGE_CENTER_UI_DELEGATE_H_ +#define UI_MESSAGE_CENTER_UI_DELEGATE_H_ #include "ui/message_center/message_center_export.h" namespace message_center { -class MessageCenterTray; - -// A MessageCenterTrayDelegate class is responsible for managing the various UI -// surfaces that should be displayed when the MessageCenter is changed. -class MESSAGE_CENTER_EXPORT MessageCenterTrayDelegate { +// A UiDelegate class is responsible for managing the various UI surfaces +// (MessageCenter and popups) as notifications are added and updated. +// Implementations are platform specific. +class MESSAGE_CENTER_EXPORT UiDelegate { public: - virtual ~MessageCenterTrayDelegate() {}; + virtual ~UiDelegate(){}; // Called whenever a change to the visible UI has occurred. - virtual void OnMessageCenterTrayChanged() = 0; + virtual void OnMessageCenterContentsChanged() = 0; // Display the popup bubbles for new notifications to the user. Returns true // if popups were actually displayed to the user. @@ -38,14 +37,8 @@ class MESSAGE_CENTER_EXPORT MessageCenterTrayDelegate { // Display the notifier settings as a bubble. virtual bool ShowNotifierSettings() = 0; - - // Show a platform-specific UI that informs the user how to open the message - // center. - virtual void DisplayFirstRunBalloon() {} - - virtual MessageCenterTray* GetMessageCenterTray() = 0; }; } // namespace message_center -#endif // UI_MESSAGE_CENTER_MESSAGE_CENTER_TRAY_DELEGATE_H_ +#endif // UI_MESSAGE_CENTER_UI_DELEGATE_H_ diff --git a/chromium/ui/message_center/views/bounded_label.cc b/chromium/ui/message_center/views/bounded_label.cc index 64d88e4bf4f..334cc34f830 100644 --- a/chromium/ui/message_center/views/bounded_label.cc +++ b/chromium/ui/message_center/views/bounded_label.cc @@ -346,6 +346,19 @@ void BoundedLabel::GetAccessibleNodeData(ui::AXNodeData* node_data) { label_->GetAccessibleNodeData(node_data); } +views::View* BoundedLabel::GetTooltipHandlerForPoint(const gfx::Point& point) { + if (GetSizeForWidthAndLines(width(), -1).height() <= + GetHeightForWidth(width())) { + return nullptr; + } + return HitTestPoint(point) ? this : nullptr; +} + +bool BoundedLabel::GetTooltipText(const gfx::Point& p, + base::string16* tooltip) const { + return label_->GetTooltipText(p, tooltip); +} + void BoundedLabel::OnBoundsChanged(const gfx::Rect& previous_bounds) { label_->SetBoundsRect(bounds()); views::View::OnBoundsChanged(previous_bounds); diff --git a/chromium/ui/message_center/views/bounded_label.h b/chromium/ui/message_center/views/bounded_label.h index 8d204cdac19..1ae1a16f581 100644 --- a/chromium/ui/message_center/views/bounded_label.h +++ b/chromium/ui/message_center/views/bounded_label.h @@ -58,6 +58,9 @@ class MESSAGE_CENTER_EXPORT BoundedLabel : public views::View { void OnPaint(gfx::Canvas* canvas) override; bool CanProcessEventsWithinSubtree() const override; void GetAccessibleNodeData(ui::AXNodeData* node_data) override; + views::View* GetTooltipHandlerForPoint(const gfx::Point& point) override; + bool GetTooltipText(const gfx::Point& p, + base::string16* tooltip) const override; protected: // Overridden from views::View. diff --git a/chromium/ui/message_center/views/desktop_popup_alignment_delegate.cc b/chromium/ui/message_center/views/desktop_popup_alignment_delegate.cc index fbfb591b35d..56aa3b95cc6 100644 --- a/chromium/ui/message_center/views/desktop_popup_alignment_delegate.cc +++ b/chromium/ui/message_center/views/desktop_popup_alignment_delegate.cc @@ -35,14 +35,13 @@ void DesktopPopupAlignmentDelegate::StartObserving(display::Screen* screen) { int DesktopPopupAlignmentDelegate::GetToastOriginX( const gfx::Rect& toast_bounds) const { if (IsFromLeft()) - return work_area_.x() + kMarginBetweenItems; - return work_area_.right() - kMarginBetweenItems - toast_bounds.width(); + return work_area_.x() + kMarginBetweenPopups; + return work_area_.right() - kMarginBetweenPopups - toast_bounds.width(); } int DesktopPopupAlignmentDelegate::GetBaseLine() const { - return IsTopDown() - ? work_area_.y() + kMarginBetweenItems - : work_area_.bottom() - kMarginBetweenItems; + return IsTopDown() ? work_area_.y() + kMarginBetweenPopups + : work_area_.bottom() - kMarginBetweenPopups; } gfx::Rect DesktopPopupAlignmentDelegate::GetWorkArea() const { diff --git a/chromium/ui/message_center/views/message_popup_collection.cc b/chromium/ui/message_center/views/message_popup_collection.cc index 989a427dce4..4fa28767e3d 100644 --- a/chromium/ui/message_center/views/message_popup_collection.cc +++ b/chromium/ui/message_center/views/message_popup_collection.cc @@ -17,10 +17,10 @@ #include "ui/gfx/animation/animation_delegate.h" #include "ui/gfx/animation/slide_animation.h" #include "ui/message_center/message_center.h" -#include "ui/message_center/message_center_tray.h" #include "ui/message_center/notification.h" #include "ui/message_center/notification_list.h" #include "ui/message_center/public/cpp/message_center_constants.h" +#include "ui/message_center/ui_controller.h" #include "ui/message_center/views/message_view.h" #include "ui/message_center/views/message_view_context_menu_controller.h" #include "ui/message_center/views/message_view_factory.h" @@ -43,13 +43,13 @@ const int kMouseExitedDeferTimeoutMs = 200; // The margin between messages (and between the anchor unless // first_item_has_no_margin was specified). -const int kToastMarginY = kMarginBetweenItems; +const int kToastMarginY = kMarginBetweenPopups; } // namespace. MessagePopupCollection::MessagePopupCollection( MessageCenter* message_center, - MessageCenterTray* tray, + UiController* tray, PopupAlignmentDelegate* alignment_delegate) : message_center_(message_center), tray_(tray), @@ -58,7 +58,7 @@ MessagePopupCollection::MessagePopupCollection( latest_toast_entered_(NULL), user_is_closing_toasts_by_clicking_(false), target_top_edge_(0), - context_menu_controller_(new MessageViewContextMenuController(this)), + context_menu_controller_(new MessageViewContextMenuController()), weak_factory_(this) { DCHECK(message_center_); defer_timer_.reset(new base::OneShotTimer); @@ -103,22 +103,20 @@ void MessagePopupCollection::RemoveNotification( } } -std::unique_ptr<ui::MenuModel> MessagePopupCollection::CreateMenuModel( - const Notification& notification) { - return tray_->CreateNotificationMenuModel(notification); -} - -bool MessagePopupCollection::HasClickedListener( - const std::string& notification_id) { - return message_center_->HasClickedListener(notification_id); -} - void MessagePopupCollection::ClickOnNotificationButton( const std::string& notification_id, int button_index) { message_center_->ClickOnNotificationButton(notification_id, button_index); } +void MessagePopupCollection::ClickOnNotificationButtonWithReply( + const std::string& notification_id, + int button_index, + const base::string16& reply) { + message_center_->ClickOnNotificationButtonWithReply(notification_id, + button_index, reply); +} + void MessagePopupCollection::ClickOnSettingsButton( const std::string& notification_id) { message_center_->ClickOnSettingsButton(notification_id); @@ -203,11 +201,10 @@ void MessagePopupCollection::UpdateWidgets() { #endif // defined(OS_CHROMEOS) view->SetExpanded(true); - // TODO(yoshiki): Temporary disable context menu on custom notifications. - // See crbug.com/750307 for detail. + // TODO(yoshiki): Temporarily disable context menu on custom (arc) + // notifications. See crbug.com/750307 for detail. if (notification.type() != NOTIFICATION_TYPE_CUSTOM && - notification.delegate() && - notification.delegate()->ShouldDisplaySettingsButton()) { + notification.should_show_settings_button()) { view->set_context_menu_controller(context_menu_controller_.get()); } @@ -226,7 +223,7 @@ void MessagePopupCollection::UpdateWidgets() { // There will be no contents already since this is a new ToastContentsView. toast->SetContents(view, /*a11y_feedback_for_updates=*/false); toasts_.push_back(toast); - view->set_controller(toast); + view->set_delegate(toast); gfx::Size preferred_size = toast->GetPreferredSize(); gfx::Point origin( diff --git a/chromium/ui/message_center/views/message_popup_collection.h b/chromium/ui/message_center/views/message_popup_collection.h index cc7de5cc064..e02ea7ed447 100644 --- a/chromium/ui/message_center/views/message_popup_collection.h +++ b/chromium/ui/message_center/views/message_popup_collection.h @@ -17,7 +17,7 @@ #include "ui/gfx/geometry/rect.h" #include "ui/message_center/message_center_export.h" #include "ui/message_center/message_center_observer.h" -#include "ui/message_center/views/message_center_controller.h" +#include "ui/message_center/views/message_view_delegate.h" #include "ui/message_center/views/toast_contents_view.h" #include "ui/views/widget/widget_observer.h" @@ -39,7 +39,7 @@ class MessagePopupCollectionTest; } class MessageCenter; -class MessageCenterTray; +class UiController; class MessageViewContextMenuController; class PopupAlignmentDelegate; @@ -49,23 +49,23 @@ class PopupAlignmentDelegate; // contents of each toast are for the message center and layout strategy would // be slightly different. class MESSAGE_CENTER_EXPORT MessagePopupCollection - : public MessageCenterController, + : public MessageViewDelegate, public MessageCenterObserver { public: MessagePopupCollection(MessageCenter* message_center, - MessageCenterTray* tray, + UiController* tray, PopupAlignmentDelegate* alignment_delegate); ~MessagePopupCollection() override; - // Overridden from MessageCenterController: + // Overridden from MessageViewDelegate: void ClickOnNotification(const std::string& notification_id) override; void RemoveNotification(const std::string& notification_id, bool by_user) override; - std::unique_ptr<ui::MenuModel> CreateMenuModel( - const Notification& notification) override; - bool HasClickedListener(const std::string& notification_id) override; void ClickOnNotificationButton(const std::string& notification_id, int button_index) override; + void ClickOnNotificationButtonWithReply(const std::string& notification_id, + int button_index, + const base::string16& reply) override; void ClickOnSettingsButton(const std::string& notification_id) override; void UpdateNotificationSize(const std::string& notification_id) override; @@ -147,7 +147,7 @@ class MESSAGE_CENTER_EXPORT MessagePopupCollection gfx::Rect GetToastRectAt(size_t index) const; MessageCenter* message_center_; - MessageCenterTray* tray_; + UiController* tray_; Toasts toasts_; PopupAlignmentDelegate* alignment_delegate_; diff --git a/chromium/ui/message_center/views/message_popup_collection_unittest.cc b/chromium/ui/message_center/views/message_popup_collection_unittest.cc index 8c75aa126eb..f1c48b1fd8f 100644 --- a/chromium/ui/message_center/views/message_popup_collection_unittest.cc +++ b/chromium/ui/message_center/views/message_popup_collection_unittest.cc @@ -271,7 +271,7 @@ void MessagePopupCollectionTest::CheckedAnimationDelegate:: auto poorly_aligned = std::adjacent_find( toasts_->begin(), toasts_->end(), [](ToastContentsView* top, ToastContentsView* bottom) { - return ComputeYDistance(*top, *bottom) != kMarginBetweenItems; + return ComputeYDistance(*top, *bottom) != kMarginBetweenPopups; }); if (poorly_aligned != toasts_->end()) error_msg_ = calling_func + " CheckToastsAreAligned: distance between: " + @@ -279,7 +279,7 @@ void MessagePopupCollectionTest::CheckedAnimationDelegate:: (*std::next(poorly_aligned))->id() + ": " + std::to_string(ComputeYDistance(**poorly_aligned, **std::next(poorly_aligned))) + - " expected: " + std::to_string(kMarginBetweenItems) + + " expected: " + std::to_string(kMarginBetweenPopups) + "\nLayout:\n" + YPositionsToString(*toasts_); } diff --git a/chromium/ui/message_center/views/message_view.cc b/chromium/ui/message_center/views/message_view.cc index 4513fe66590..43e9d7c2671 100644 --- a/chromium/ui/message_center/views/message_view.cc +++ b/chromium/ui/message_center/views/message_view.cc @@ -15,7 +15,7 @@ #include "ui/gfx/shadow_value.h" #include "ui/message_center/message_center.h" #include "ui/message_center/public/cpp/message_center_constants.h" -#include "ui/message_center/views/message_center_controller.h" +#include "ui/message_center/views/message_view_delegate.h" #include "ui/strings/grit/ui_strings.h" #include "ui/views/background.h" #include "ui/views/border.h" @@ -29,11 +29,12 @@ namespace { #if defined(OS_CHROMEOS) -const int kShadowCornerRadius = 2; +const int kBorderCorderRadius = 2; +const SkColor kBorderColor = SkColorSetARGB(0x1F, 0x0, 0x0, 0x0); #else const int kShadowCornerRadius = 0; -#endif const int kShadowElevation = 2; +#endif // Creates a text for spoken feedback from the data contained in the // notification. @@ -60,9 +61,9 @@ base::string16 CreateAccessibleName( namespace message_center { -MessageView::MessageView(MessageCenterController* controller, +MessageView::MessageView(MessageViewDelegate* delegate, const Notification& notification) - : controller_(controller), + : delegate_(delegate), notification_id_(notification.id()), slide_out_controller_(this, this) { SetFocusBehavior(FocusBehavior::ALWAYS); @@ -92,15 +93,13 @@ void MessageView::UpdateWithNotification(const Notification& notification) { slide_out_controller_.set_enabled(!GetPinned()); } -// static -gfx::Insets MessageView::GetShadowInsets() { - return -gfx::ShadowValue::GetMargin( - gfx::ShadowDetails::Get(kShadowElevation, kShadowCornerRadius).values); -} - void MessageView::SetIsNested() { is_nested_ = true; +#if defined(OS_CHROMEOS) + SetBorder(views::CreateRoundedRectBorder(kNotificationBorderThickness, + kBorderCorderRadius, kBorderColor)); +#else const auto& shadow = gfx::ShadowDetails::Get(kShadowElevation, kShadowCornerRadius); gfx::Insets ninebox_insets = gfx::ShadowValue::GetBlurRegion(shadow.values) + @@ -109,6 +108,7 @@ void MessageView::SetIsNested() { std::unique_ptr<views::Painter>(views::Painter::CreateImagePainter( shadow.ninebox_image, ninebox_insets)), -gfx::ShadowValue::GetMargin(shadow.values))); +#endif } void MessageView::SetExpanded(bool expanded) { @@ -120,6 +120,14 @@ bool MessageView::IsExpanded() const { return false; } +void MessageView::OnContainerAnimationStarted() { + // Not implemented by default. +} + +void MessageView::OnContainerAnimationEnded() { + // Not implemented by default. +} + void MessageView::GetAccessibleNodeData(ui::AXNodeData* node_data) { node_data->role = ui::AX_ROLE_BUTTON; node_data->AddStringAttribute( @@ -133,7 +141,7 @@ bool MessageView::OnMousePressed(const ui::MouseEvent& event) { if (!event.IsOnlyLeftMouseButton()) return false; - controller_->ClickOnNotification(notification_id_); + delegate_->ClickOnNotification(notification_id_); return true; } @@ -142,11 +150,11 @@ bool MessageView::OnKeyPressed(const ui::KeyEvent& event) { return false; if (event.key_code() == ui::VKEY_RETURN) { - controller_->ClickOnNotification(notification_id_); + delegate_->ClickOnNotification(notification_id_); return true; } else if ((event.key_code() == ui::VKEY_DELETE || event.key_code() == ui::VKEY_BACK)) { - controller_->RemoveNotification(notification_id_, true); // By user. + delegate_->RemoveNotification(notification_id_, true); // By user. return true; } @@ -159,7 +167,7 @@ bool MessageView::OnKeyReleased(const ui::KeyEvent& event) { if (event.flags() != ui::EF_NONE || event.key_code() != ui::VKEY_SPACE) return false; - controller_->ClickOnNotification(notification_id_); + delegate_->ClickOnNotification(notification_id_); return true; } @@ -211,7 +219,7 @@ void MessageView::OnGestureEvent(ui::GestureEvent* event) { } case ui::ET_GESTURE_TAP: { SetDrawBackgroundAsActive(false); - controller_->ClickOnNotification(notification_id_); + delegate_->ClickOnNotification(notification_id_); event->SetHandled(); return; } @@ -235,7 +243,7 @@ ui::Layer* MessageView::GetSlideOutLayer() { void MessageView::OnSlideChanged() {} void MessageView::OnSlideOut() { - controller_->RemoveNotification(notification_id_, true); // By user. + delegate_->RemoveNotification(notification_id_, true); // By user. } bool MessageView::GetPinned() const { @@ -243,11 +251,11 @@ bool MessageView::GetPinned() const { } void MessageView::OnCloseButtonPressed() { - controller_->RemoveNotification(notification_id_, true); // By user. + delegate_->RemoveNotification(notification_id_, true); // By user. } void MessageView::OnSettingsButtonPressed() { - controller_->ClickOnSettingsButton(notification_id_); + delegate_->ClickOnSettingsButton(notification_id_); } void MessageView::SetDrawBackgroundAsActive(bool active) { diff --git a/chromium/ui/message_center/views/message_view.h b/chromium/ui/message_center/views/message_view.h index f62ee165e3c..64dfa472632 100644 --- a/chromium/ui/message_center/views/message_view.h +++ b/chromium/ui/message_center/views/message_view.h @@ -29,7 +29,7 @@ namespace message_center { class Notification; class NotificationControlButtonsView; -class MessageCenterController; +class MessageViewDelegate; // An base class for a notification entry. Contains background and other // elements shared by derived notification views. @@ -37,16 +37,12 @@ class MESSAGE_CENTER_EXPORT MessageView : public views::View, public views::SlideOutController::Delegate { public: - MessageView(MessageCenterController* controller, - const Notification& notification); + MessageView(MessageViewDelegate* delegate, const Notification& notification); ~MessageView() override; // Updates this view with the new data contained in the notification. virtual void UpdateWithNotification(const Notification& notification); - // Returns the insets for the shadow it will have for rich notification. - static gfx::Insets GetShadowInsets(); - // Creates a shadow around the notification and changes slide-out behavior. void SetIsNested(); @@ -58,6 +54,13 @@ class MESSAGE_CENTER_EXPORT MessageView virtual void SetExpanded(bool expanded); virtual bool IsExpanded() const; + // Invoked when the container view of MessageView (e.g. MessageCenterView in + // ash) is starting the animation that possibly hides some part of + // the MessageView. + // During the animation, MessageView should comply with the Z order in views. + virtual void OnContainerAnimationStarted(); + virtual void OnContainerAnimationEnded(); + void OnCloseButtonPressed(); void OnSettingsButtonPressed(); @@ -81,9 +84,7 @@ class MESSAGE_CENTER_EXPORT MessageView void set_scroller(views::ScrollView* scroller) { scroller_ = scroller; } std::string notification_id() const { return notification_id_; } - void set_controller(MessageCenterController* controller) { - controller_ = controller; - } + void set_delegate(MessageViewDelegate* delegate) { delegate_ = delegate; } #if defined(OS_CHROMEOS) // By calling this, all notifications are treated as non-pinned forcibly. @@ -102,10 +103,10 @@ class MESSAGE_CENTER_EXPORT MessageView views::View* background_view() { return background_view_; } views::ScrollView* scroller() { return scroller_; } - MessageCenterController* controller() { return controller_; } + MessageViewDelegate* delegate() { return delegate_; } private: - MessageCenterController* controller_; // Weak, lives longer then views. + MessageViewDelegate* delegate_; std::string notification_id_; views::View* background_view_ = nullptr; // Owned by views hierarchy. views::ScrollView* scroller_ = nullptr; diff --git a/chromium/ui/message_center/views/message_view_context_menu_controller.cc b/chromium/ui/message_center/views/message_view_context_menu_controller.cc index 367fcd8d620..fb871b6d6cb 100644 --- a/chromium/ui/message_center/views/message_view_context_menu_controller.cc +++ b/chromium/ui/message_center/views/message_view_context_menu_controller.cc @@ -6,21 +6,18 @@ #include "ui/base/models/menu_model.h" #include "ui/message_center/message_center.h" -#include "ui/message_center/views/message_center_controller.h" #include "ui/message_center/views/message_view.h" +#include "ui/message_center/views/message_view_delegate.h" +#include "ui/message_center/views/notification_menu_model.h" #include "ui/views/controls/menu/menu_model_adapter.h" #include "ui/views/controls/menu/menu_runner.h" #include "ui/views/widget/widget.h" namespace message_center { -MessageViewContextMenuController::MessageViewContextMenuController( - MessageCenterController* controller) - : controller_(controller) { -} +MessageViewContextMenuController::MessageViewContextMenuController() = default; -MessageViewContextMenuController::~MessageViewContextMenuController() { -} +MessageViewContextMenuController::~MessageViewContextMenuController() = default; void MessageViewContextMenuController::ShowContextMenuForView( views::View* source, @@ -31,7 +28,7 @@ void MessageViewContextMenuController::ShowContextMenuForView( Notification* notification = MessageCenter::Get()->FindVisibleNotificationById( message_view->notification_id()); - menu_model_ = controller_->CreateMenuModel(*notification); + menu_model_ = std::make_unique<NotificationMenuModel>(*notification); if (!menu_model_ || menu_model_->GetItemCount() == 0) return; diff --git a/chromium/ui/message_center/views/message_view_context_menu_controller.h b/chromium/ui/message_center/views/message_view_context_menu_controller.h index db49a305dd4..49596008f3e 100644 --- a/chromium/ui/message_center/views/message_view_context_menu_controller.h +++ b/chromium/ui/message_center/views/message_view_context_menu_controller.h @@ -22,13 +22,11 @@ class MenuRunner; } // namespace views namespace message_center { -class MessageCenterController; class MESSAGE_CENTER_EXPORT MessageViewContextMenuController : public views::ContextMenuController { public: - explicit MessageViewContextMenuController( - MessageCenterController* controller); + explicit MessageViewContextMenuController(); ~MessageViewContextMenuController() override; private: @@ -40,8 +38,6 @@ class MESSAGE_CENTER_EXPORT MessageViewContextMenuController // Callback for MenuModelAdapter void OnMenuClosed(); - MessageCenterController* controller_; - std::unique_ptr<ui::MenuModel> menu_model_; std::unique_ptr<views::MenuModelAdapter> menu_model_adapter_; std::unique_ptr<views::MenuRunner> menu_runner_; diff --git a/chromium/ui/message_center/views/message_center_controller.h b/chromium/ui/message_center/views/message_view_delegate.h index 544be9cc786..b4bee17b816 100644 --- a/chromium/ui/message_center/views/message_center_controller.h +++ b/chromium/ui/message_center/views/message_view_delegate.h @@ -2,32 +2,31 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef UI_MESSAGE_CENTER_VIEWS_MESSAGE_CENTER_CONTROLLER_H_ -#define UI_MESSAGE_CENTER_VIEWS_MESSAGE_CENTER_CONTROLLER_H_ +#ifndef UI_MESSAGE_CENTER_VIEWS_MESSAGE_VIEW_DELEGATE_H_ +#define UI_MESSAGE_CENTER_VIEWS_MESSAGE_VIEW_DELEGATE_H_ #include <memory> #include <string> -#include "base/strings/string16.h" #include "ui/base/models/menu_model.h" -#include "ui/message_center/notifier_settings.h" namespace message_center { // Interface used by views to report clicks and other user actions. The views // by themselves do not know how to perform those operations, they ask -// MessageCenterController to do them. Implemented by MessageCenterView and +// MessageViewDelegate to do them. Implemented by MessageCenterView and // MessagePopupCollection. -class MessageCenterController { +class MessageViewDelegate { public: virtual void ClickOnNotification(const std::string& notification_id) = 0; virtual void RemoveNotification(const std::string& notification_id, bool by_user) = 0; - virtual std::unique_ptr<ui::MenuModel> CreateMenuModel( - const Notification& notification) = 0; - virtual bool HasClickedListener(const std::string& notification_id) = 0; virtual void ClickOnNotificationButton(const std::string& notification_id, int button_index) = 0; + virtual void ClickOnNotificationButtonWithReply( + const std::string& notification_id, + int button_index, + const base::string16& reply) = 0; virtual void ClickOnSettingsButton(const std::string& notification_id) = 0; // For ArcCustomNotificationView, size changes might come after the // notification update over Mojo. Provide a method here to notify when @@ -38,4 +37,4 @@ class MessageCenterController { } // namespace message_center -#endif // UI_MESSAGE_CENTER_VIEWS_MESSAGE_CENTER_CONTROLLER_H_ +#endif // UI_MESSAGE_CENTER_VIEWS_MESSAGE_VIEW_DELEGATE_H_ diff --git a/chromium/ui/message_center/views/message_view_factory.cc b/chromium/ui/message_center/views/message_view_factory.cc index 5e9c400fb27..74465ab5802 100644 --- a/chromium/ui/message_center/views/message_view_factory.cc +++ b/chromium/ui/message_center/views/message_view_factory.cc @@ -5,6 +5,7 @@ #include "ui/message_center/views/message_view_factory.h" #include "base/command_line.h" +#include "base/lazy_instance.h" #include "ui/message_center/notification_types.h" #include "ui/message_center/public/cpp/message_center_switches.h" #include "ui/message_center/views/notification_view.h" @@ -16,8 +17,15 @@ namespace message_center { +namespace { + +base::LazyInstance<MessageViewFactory::CustomMessageViewFactoryFunction>::Leaky + g_custom_view_factory = LAZY_INSTANCE_INITIALIZER; + +} // namespace + // static -MessageView* MessageViewFactory::Create(MessageCenterController* controller, +MessageView* MessageViewFactory::Create(MessageViewDelegate* controller, const Notification& notification, bool top_level) { MessageView* notification_view = nullptr; @@ -33,14 +41,10 @@ MessageView* MessageViewFactory::Create(MessageCenterController* controller, else notification_view = new NotificationView(controller, notification); break; -#if defined(TOOLKIT_VIEWS) && !defined(OS_MACOSX) case NOTIFICATION_TYPE_CUSTOM: notification_view = - notification.delegate() - ->CreateCustomMessageView(controller, notification) - .release(); + g_custom_view_factory.Get().Run(controller, notification).release(); break; -#endif default: // If the caller asks for an unrecognized kind of view (entirely possible // if an application is running on an older version of this code that @@ -70,4 +74,15 @@ MessageView* MessageViewFactory::Create(MessageCenterController* controller, return notification_view; } +// static +void MessageViewFactory::SetCustomNotificationViewFactory( + const CustomMessageViewFactoryFunction& factory_function) { + g_custom_view_factory.Get() = factory_function; +} + +// static +bool MessageViewFactory::HasCustomNotificationViewFactory() { + return !g_custom_view_factory.Get().is_null(); +} + } // namespace message_center diff --git a/chromium/ui/message_center/views/message_view_factory.h b/chromium/ui/message_center/views/message_view_factory.h index 9ff2251ff54..22d5ac71cdb 100644 --- a/chromium/ui/message_center/views/message_view_factory.h +++ b/chromium/ui/message_center/views/message_view_factory.h @@ -7,9 +7,13 @@ #include "ui/message_center/message_center_export.h" +#include <memory> + +#include "base/callback_forward.h" + namespace message_center { -class MessageCenterController; +class MessageViewDelegate; class MessageView; class Notification; @@ -19,10 +23,28 @@ class Notification; // notifications on Linux with Aura. class MESSAGE_CENTER_EXPORT MessageViewFactory { public: + // A function that creates MessageView for a NOTIFICATION_TYPE_CUSTOM + // notification. + typedef base::Callback<std::unique_ptr<message_center::MessageView>( + message_center::MessageViewDelegate*, + const message_center::Notification&)> + CustomMessageViewFactoryFunction; + // |controller| may be NULL, but has to be set before the view is shown. - static MessageView* Create(MessageCenterController* controller, + static MessageView* Create(MessageViewDelegate* controller, const Notification& notification, bool top_level); + + // Sets the function that will be invoked to create a custom notification + // view. This should be a repeating callback. It's an error to attempt to show + // a custom notification without first having called this function. Currently, + // only ARC uses custom notifications, so this doesn't need to distinguish + // between various sources of custom notification. + static void SetCustomNotificationViewFactory( + const CustomMessageViewFactoryFunction& factory_function); + + // Returns whether the custom view factory function has already been set. + static bool HasCustomNotificationViewFactory(); }; } // namespace message_center diff --git a/chromium/ui/message_center/views/notification_header_view.cc b/chromium/ui/message_center/views/notification_header_view.cc index c7dec90a69d..c537e36ccc4 100644 --- a/chromium/ui/message_center/views/notification_header_view.cc +++ b/chromium/ui/message_center/views/notification_header_view.cc @@ -189,14 +189,16 @@ NotificationHeaderView::NotificationHeaderView( DCHECK_EQ(kInnerHeaderHeight, app_icon_view_->GetPreferredSize().height()); app_info_container->AddChildView(app_icon_view_); - // Font list for text views. - const gfx::FontList& font_list = GetHeaderTextFontList(); - // The height must be 15px to match with the mock. + // Font list for text views. The height must be 15px to match with the mock. + gfx::FontList font_list = GetHeaderTextFontList(); DCHECK_EQ(15, font_list.GetHeight()); + const int font_list_height = font_list.GetHeight(); + // App name view app_name_view_ = new views::Label(base::string16()); app_name_view_->SetFontList(font_list); + app_name_view_->SetLineHeight(font_list_height); app_name_view_->SetHorizontalAlignment(gfx::ALIGN_LEFT); app_name_view_->SetEnabledColor(accent_color_); app_name_view_->SetBorder(views::CreateEmptyBorder(kTextViewPadding)); @@ -207,6 +209,7 @@ NotificationHeaderView::NotificationHeaderView( summary_text_divider_ = new views::Label(base::WideToUTF16(kNotificationHeaderDivider)); summary_text_divider_->SetFontList(font_list); + summary_text_divider_->SetLineHeight(font_list_height); summary_text_divider_->SetHorizontalAlignment(gfx::ALIGN_LEFT); summary_text_divider_->SetBorder(views::CreateEmptyBorder(kTextViewPadding)); summary_text_divider_->SetVisible(false); @@ -217,6 +220,7 @@ NotificationHeaderView::NotificationHeaderView( // Summary text view summary_text_view_ = new views::Label(base::string16()); summary_text_view_->SetFontList(font_list); + summary_text_view_->SetLineHeight(font_list_height); summary_text_view_->SetHorizontalAlignment(gfx::ALIGN_LEFT); summary_text_view_->SetBorder(views::CreateEmptyBorder(kTextViewPadding)); summary_text_view_->SetVisible(false); @@ -228,6 +232,7 @@ NotificationHeaderView::NotificationHeaderView( timestamp_divider_ = new views::Label(base::WideToUTF16(kNotificationHeaderDivider)); timestamp_divider_->SetFontList(font_list); + timestamp_divider_->SetLineHeight(font_list_height); timestamp_divider_->SetHorizontalAlignment(gfx::ALIGN_LEFT); timestamp_divider_->SetBorder(views::CreateEmptyBorder(kTextViewPadding)); timestamp_divider_->SetVisible(false); @@ -238,6 +243,7 @@ NotificationHeaderView::NotificationHeaderView( // Timestamp view timestamp_view_ = new views::Label(base::string16()); timestamp_view_->SetFontList(font_list); + timestamp_view_->SetLineHeight(font_list_height); timestamp_view_->SetHorizontalAlignment(gfx::ALIGN_LEFT); timestamp_view_->SetBorder(views::CreateEmptyBorder(kTextViewPadding)); timestamp_view_->SetVisible(false); @@ -348,7 +354,7 @@ bool NotificationHeaderView::IsExpandButtonEnabled() { } std::unique_ptr<views::InkDrop> NotificationHeaderView::CreateInkDrop() { - return base::MakeUnique<views::InkDropStub>(); + return std::make_unique<views::InkDropStub>(); } void NotificationHeaderView::UpdateSummaryTextVisibility() { diff --git a/chromium/ui/message_center/views/notification_menu_model.cc b/chromium/ui/message_center/views/notification_menu_model.cc new file mode 100644 index 00000000000..de4219d2262 --- /dev/null +++ b/chromium/ui/message_center/views/notification_menu_model.cc @@ -0,0 +1,69 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/message_center/views/notification_menu_model.h" + +#include <memory> + +#include "base/macros.h" +#include "base/strings/utf_string_conversions.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/models/simple_menu_model.h" +#include "ui/message_center/message_center.h" +#include "ui/strings/grit/ui_strings.h" + +namespace message_center { + +namespace { + +// Menu constants +const int kTogglePermissionCommand = 0; +const int kShowSettingsCommand = 1; + +} // namespace + +NotificationMenuModel::NotificationMenuModel(const Notification& notification) + : ui::SimpleMenuModel(this), notification_(notification) { + DCHECK(!notification.display_source().empty()); + AddItem(kTogglePermissionCommand, + l10n_util::GetStringFUTF16(IDS_MESSAGE_CENTER_NOTIFIER_DISABLE, + notification.display_source())); + +#if defined(OS_CHROMEOS) + // Add settings menu item. + AddItem(kShowSettingsCommand, + l10n_util::GetStringUTF16(IDS_MESSAGE_CENTER_SETTINGS)); +#endif +} + +NotificationMenuModel::~NotificationMenuModel() {} + +bool NotificationMenuModel::IsCommandIdChecked(int command_id) const { + return false; +} + +bool NotificationMenuModel::IsCommandIdEnabled(int command_id) const { + // TODO(estade): commands shouldn't always be enabled. For example, a + // notification's enabled state might be controlled by policy. See + // http://crbug.com/771269 + return true; +} + +void NotificationMenuModel::ExecuteCommand(int command_id, int event_flags) { + switch (command_id) { + case kTogglePermissionCommand: + notification_.delegate()->DisableNotification(); + // TODO(estade): this will not close other open notifications from the + // same site. + MessageCenter::Get()->RemoveNotification(notification_.id(), false); + break; + case kShowSettingsCommand: + MessageCenter::Get()->ClickOnSettingsButton(notification_.id()); + break; + default: + NOTREACHED(); + } +} + +} // namespace message_center diff --git a/chromium/ui/message_center/views/notification_menu_model.h b/chromium/ui/message_center/views/notification_menu_model.h new file mode 100644 index 00000000000..324b9893f46 --- /dev/null +++ b/chromium/ui/message_center/views/notification_menu_model.h @@ -0,0 +1,35 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef UI_MESSAGE_CENTER_NOTIFICATION_MENU_MODEL_H_ +#define UI_MESSAGE_CENTER_NOTIFICATION_MENU_MODEL_H_ + +#include "base/macros.h" +#include "ui/base/models/simple_menu_model.h" +#include "ui/message_center/message_center_export.h" +#include "ui/message_center/notification.h" + +namespace message_center { + +// The model of the context menu for a notification card. +class MESSAGE_CENTER_EXPORT NotificationMenuModel + : public ui::SimpleMenuModel, + public ui::SimpleMenuModel::Delegate { + public: + NotificationMenuModel(const Notification& notification); + ~NotificationMenuModel() override; + + // Overridden from ui::SimpleMenuModel::Delegate: + bool IsCommandIdChecked(int command_id) const override; + bool IsCommandIdEnabled(int command_id) const override; + void ExecuteCommand(int command_id, int event_flags) override; + + private: + Notification notification_; + DISALLOW_COPY_AND_ASSIGN(NotificationMenuModel); +}; + +} // namespace message_center + +#endif // UI_MESSAGE_CENTER_UI_CONTROLLER_H_ diff --git a/chromium/ui/message_center/views/notification_menu_model_unittest.cc b/chromium/ui/message_center/views/notification_menu_model_unittest.cc new file mode 100644 index 00000000000..1ba2f38bc18 --- /dev/null +++ b/chromium/ui/message_center/views/notification_menu_model_unittest.cc @@ -0,0 +1,113 @@ +// Copyright 2017 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ui/message_center/views/notification_menu_model.h" + +#include <utility> + +#include "base/macros.h" +#include "base/strings/utf_string_conversions.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "ui/base/models/menu_model.h" +#include "ui/message_center/message_center.h" +#include "ui/message_center/notification.h" +#include "ui/message_center/notification_types.h" +#include "ui/message_center/ui_controller.h" + +namespace message_center { +namespace { + +class TestNotificationDelegate : public message_center::NotificationDelegate { + public: + TestNotificationDelegate() = default; + + private: + ~TestNotificationDelegate() override = default; + + DISALLOW_COPY_AND_ASSIGN(TestNotificationDelegate); +}; + +} // namespace + +class NotificationMenuModelTest : public testing::Test { + public: + NotificationMenuModelTest() = default; + ~NotificationMenuModelTest() override = default; + + void SetUp() override { + MessageCenter::Initialize(); + message_center_ = MessageCenter::Get(); + } + + void TearDown() override { + message_center_ = NULL; + MessageCenter::Shutdown(); + } + + protected: + NotifierId DummyNotifierId() { return NotifierId(); } + + Notification* AddNotification(const std::string& id) { + return AddNotification(id, DummyNotifierId()); + } + + Notification* AddNotification(const std::string& id, NotifierId notifier_id) { + std::unique_ptr<Notification> notification(new Notification( + message_center::NOTIFICATION_TYPE_SIMPLE, id, + base::ASCIIToUTF16("Test Web Notification"), + base::ASCIIToUTF16("Notification message body."), gfx::Image(), + base::ASCIIToUTF16("www.test.org"), GURL(), notifier_id, + message_center::RichNotificationData(), + new TestNotificationDelegate())); + Notification* notification_ptr = notification.get(); + message_center_->AddNotification(std::move(notification)); + return notification_ptr; + } + MessageCenter* message_center_; + + private: + DISALLOW_COPY_AND_ASSIGN(NotificationMenuModelTest); +}; + +TEST_F(NotificationMenuModelTest, ContextMenuTestWithMessageCenter) { + const std::string id1 = "id1"; + const std::string id2 = "id2"; + const std::string id3 = "id3"; + AddNotification(id1); + + base::string16 display_source = base::ASCIIToUTF16("www.test.org"); + NotifierId notifier_id = DummyNotifierId(); + + NotifierId notifier_id2(NotifierId::APPLICATION, "sample-app"); + std::unique_ptr<Notification> notification = std::make_unique<Notification>( + message_center::NOTIFICATION_TYPE_SIMPLE, id2, + base::ASCIIToUTF16("Test Web Notification"), + base::ASCIIToUTF16("Notification message body."), gfx::Image(), + display_source, GURL(), notifier_id2, + message_center::RichNotificationData(), new TestNotificationDelegate()); + + std::unique_ptr<ui::MenuModel> model( + std::make_unique<NotificationMenuModel>(*notification)); + message_center_->AddNotification(std::move(notification)); + + AddNotification(id3); +#if defined(OS_CHROMEOS) + EXPECT_EQ(2, model->GetItemCount()); + + // The second item is to open the settings. + EXPECT_TRUE(model->IsEnabledAt(0)); + EXPECT_TRUE(model->IsEnabledAt(1)); + model->ActivatedAt(1); +#else + EXPECT_EQ(1, model->GetItemCount()); +#endif + + // The first item is to disable notifications from the notifier id. It also + // removes the notification. + EXPECT_EQ(3u, message_center_->GetVisibleNotifications().size()); + model->ActivatedAt(0); + EXPECT_EQ(2u, message_center_->GetVisibleNotifications().size()); +} + +} // namespace message_center diff --git a/chromium/ui/message_center/views/notification_view.cc b/chromium/ui/message_center/views/notification_view.cc index 4a2a5de8896..b86b8bf0c07 100644 --- a/chromium/ui/message_center/views/notification_view.cc +++ b/chromium/ui/message_center/views/notification_view.cc @@ -28,7 +28,7 @@ #include "ui/message_center/public/cpp/message_center_constants.h" #include "ui/message_center/views/bounded_label.h" #include "ui/message_center/views/constants.h" -#include "ui/message_center/views/message_center_controller.h" +#include "ui/message_center/views/message_view_delegate.h" #include "ui/message_center/views/notification_button.h" #include "ui/message_center/views/notification_control_buttons_view.h" #include "ui/message_center/views/padded_button.h" @@ -177,9 +177,9 @@ void NotificationView::CreateOrUpdateViews(const Notification& notification) { CreateOrUpdateActionButtonViews(notification); } -NotificationView::NotificationView(MessageCenterController* controller, +NotificationView::NotificationView(MessageViewDelegate* delegate, const Notification& notification) - : MessageView(controller, notification), + : MessageView(delegate, notification), clickable_(notification.clickable()) { // Create the top_view_, which collects into a vertical box all content // at the top of the notification (to the right of the icon) except for the @@ -330,11 +330,8 @@ void NotificationView::ScrollRectToVisible(const gfx::Rect& rect) { } gfx::NativeCursor NotificationView::GetCursor(const ui::MouseEvent& event) { - // TODO(estade): remove NotificationDelegate::HasClickedListener. - if (clickable_ || controller()->HasClickedListener(notification_id())) - return views::GetNativeHandCursor(); - - return views::View::GetCursor(event); + return clickable_ ? views::GetNativeHandCursor() + : views::View::GetCursor(event); } @@ -368,7 +365,7 @@ void NotificationView::ButtonPressed(views::Button* sender, // See if the button pressed was an action button. for (size_t i = 0; i < action_buttons_.size(); ++i) { if (sender == action_buttons_[i]) { - controller()->ClickOnNotificationButton(id, i); + delegate()->ClickOnNotificationButton(id, i); return; } } @@ -643,8 +640,7 @@ void NotificationView::CreateOrUpdateActionButtonViews( void NotificationView::UpdateControlButtonsVisibilityWithNotification( const Notification& notification) { control_buttons_view_->ShowSettingsButton( - notification.delegate() && - notification.delegate()->ShouldDisplaySettingsButton()); + notification.should_show_settings_button()); control_buttons_view_->ShowCloseButton(!GetPinned()); UpdateControlButtonsVisibility(); } diff --git a/chromium/ui/message_center/views/notification_view.h b/chromium/ui/message_center/views/notification_view.h index 972a21ff9f4..7ff5b45b266 100644 --- a/chromium/ui/message_center/views/notification_view.h +++ b/chromium/ui/message_center/views/notification_view.h @@ -36,7 +36,7 @@ class MESSAGE_CENTER_EXPORT NotificationView public views::ButtonListener, public views::ViewTargeterDelegate { public: - NotificationView(MessageCenterController* controller, + NotificationView(MessageViewDelegate* delegate, const Notification& notification); ~NotificationView() override; diff --git a/chromium/ui/message_center/views/notification_view_md.cc b/chromium/ui/message_center/views/notification_view_md.cc index a896f00173a..529cdef87ba 100644 --- a/chromium/ui/message_center/views/notification_view_md.cc +++ b/chromium/ui/message_center/views/notification_view_md.cc @@ -25,7 +25,7 @@ #include "ui/message_center/vector_icons.h" #include "ui/message_center/views/bounded_label.h" #include "ui/message_center/views/constants.h" -#include "ui/message_center/views/message_center_controller.h" +#include "ui/message_center/views/message_view_delegate.h" #include "ui/message_center/views/notification_control_buttons_view.h" #include "ui/message_center/views/notification_header_view.h" #include "ui/message_center/views/padded_button.h" @@ -64,6 +64,7 @@ constexpr gfx::Size kLargeImageMinSize(328, 0); constexpr gfx::Size kLargeImageMaxSize(328, 218); constexpr gfx::Insets kLeftContentPadding(2, 4, 0, 4); constexpr gfx::Insets kLeftContentPaddingWithIcon(2, 4, 0, 12); +constexpr gfx::Insets kNotificationInputPadding(0, 16, 0, 16); // Background of inline actions area. const SkColor kActionsRowBackgroundColor = SkColorSetRGB(0xee, 0xee, 0xee); @@ -81,6 +82,11 @@ const SkColor kLargeImageBackgroundColor = SkColorSetRGB(0xf5, 0xf5, 0xf5); const SkColor kRegularTextColorMD = SkColorSetRGB(0x21, 0x21, 0x21); const SkColor kDimTextColorMD = SkColorSetRGB(0x75, 0x75, 0x75); +// The text color and the background color of inline reply input field. +const SkColor kInputTextColor = SkColorSetRGB(0xFF, 0xFF, 0xFF); +const SkColor kInputPlaceholderColor = SkColorSetARGB(0x8A, 0xFF, 0xFF, 0xFF); +const SkColor kInputBackgroundColor = SkColorSetRGB(0x33, 0x67, 0xD6); + // Max number of lines for message_view_. constexpr int kMaxLinesForMessageView = 1; constexpr int kMaxLinesForExpandedMessageView = 4; @@ -102,6 +108,12 @@ constexpr int kMessageViewWidth = // "Roboto-Regular, 13sp" is specified in the mock. constexpr int kTextFontSize = 13; +// In progress notification, if both the title and the message are long, the +// message would be prioritized and the title would be elided. +// However, it is not perferable that we completely omit the title, so +// the ratio of the message width is limited to this value. +constexpr double kProgressNotificationMessageRatio = 0.7; + // FontList for the texts except for the header. gfx::FontList GetTextFontList() { gfx::Font default_font; @@ -197,22 +209,24 @@ void CompactTitleMessageView::OnPaint(gfx::Canvas* canvas) { const gfx::FontList& font_list = GetTextFontList(); - // Elides title and message. The behavior is based on Android's one. - // * If the title is too long, only the title is shown. - // * If the message is too long, the full content of the title is shown, + // Elides title and message. + // * If the message is too long, the message occupies at most + // kProgressNotificationMessageRatio of the width. + // * If the title is too long, the full content of the message is shown, // kCompactTitleMessageViewSpacing is added between them, and the elided - // message is shown. + // title is shown. // * If they are short enough, the title is left-aligned and the message is // right-aligned. - const int original_title_width = - gfx::Canvas::GetStringWidthF(title, font_list); - if (original_title_width >= width()) - message.clear(); - title = gfx::ElideText(title, font_list, width(), gfx::ELIDE_TAIL); - const int title_width = gfx::Canvas::GetStringWidthF(title, font_list); - const int message_width = - std::max(0, width() - title_width - kCompactTitleMessageViewSpacing); - message = gfx::ElideText(message, font_list, message_width, gfx::ELIDE_TAIL); + message = gfx::ElideText( + message, font_list, + title.empty() + ? width() + : static_cast<int>(kProgressNotificationMessageRatio * width()), + gfx::ELIDE_TAIL); + const int message_width = gfx::Canvas::GetStringWidthF(message, font_list); + const int title_width = + std::max(0, width() - message_width - kCompactTitleMessageViewSpacing); + title = gfx::ElideText(title, font_list, title_width, gfx::ELIDE_TAIL); title_view_->SetText(title); message_view_->SetText(message); @@ -302,10 +316,14 @@ const char* LargeImageContainerView::GetClassName() const { // NotificationButtonMD //////////////////////////////////////////////////////// NotificationButtonMD::NotificationButtonMD(views::ButtonListener* listener, - const base::string16& text) + bool is_inline_reply, + const base::string16& label, + const base::string16& placeholder) : views::LabelButton(listener, - base::i18n::ToUpper(text), - views::style::CONTEXT_BUTTON_MD) { + base::i18n::ToUpper(label), + views::style::CONTEXT_BUTTON_MD), + is_inline_reply_(is_inline_reply), + placeholder_(placeholder) { SetHorizontalAlignment(gfx::ALIGN_CENTER); SetInkDropMode(views::LabelButton::InkDropMode::ON); set_has_ink_drop_action_on_click(true); @@ -335,6 +353,38 @@ NotificationButtonMD::CreateInkDropHighlight() const { return highlight; } +// NotificationInputMD ///////////////////////////////////////////////////////// + +NotificationInputMD::NotificationInputMD(NotificationInputDelegate* delegate) + : delegate_(delegate), index_(0) { + set_controller(this); + SetTextColor(kInputTextColor); + SetBackgroundColor(kInputBackgroundColor); + set_placeholder_text_color(kInputPlaceholderColor); + SetBorder(views::CreateEmptyBorder(kNotificationInputPadding)); +} + +NotificationInputMD::~NotificationInputMD() = default; + +bool NotificationInputMD::HandleKeyEvent(views::Textfield* sender, + const ui::KeyEvent& event) { + if (event.type() == ui::ET_KEY_PRESSED && + event.key_code() == ui::VKEY_RETURN) { + delegate_->OnNotificationInputSubmit(index_, text()); + return true; + } + return event.type() == ui::ET_KEY_RELEASED; +} + +void NotificationInputMD::set_placeholder(const base::string16& placeholder) { + if (placeholder.empty()) { + set_placeholder_text(l10n_util::GetStringUTF16( + IDS_MESSAGE_CENTER_NOTIFICATION_INLINE_REPLY_PLACEHOLDER)); + } else { + set_placeholder_text(placeholder); + } +} + // //////////////////////////////////////////////////////////// // NotificationViewMD // //////////////////////////////////////////////////////////// @@ -343,19 +393,25 @@ views::View* NotificationViewMD::TargetForRect(views::View* root, const gfx::Rect& rect) { CHECK_EQ(root, this); - // TODO(tdanderson): Modify this function to support rect-based event + // TODO(tetsui): Modify this function to support rect-based event // targeting. Using the center point of |rect| preserves this function's // expected behavior for the time being. gfx::Point point = rect.CenterPoint(); // Want to return this for underlying views, otherwise GetCursor is not // called. But buttons are exceptions, they'll have their own event handlings. - std::vector<views::View*> buttons(action_buttons_.begin(), - action_buttons_.end()); + std::vector<views::View*> buttons; if (header_row_->expand_button()) buttons.push_back(header_row_->expand_button()); buttons.push_back(header_row_); + if (action_buttons_row_->visible()) { + buttons.insert(buttons.end(), action_buttons_.begin(), + action_buttons_.end()); + } + if (inline_reply_->visible()) + buttons.push_back(inline_reply_); + for (size_t i = 0; i < buttons.size(); ++i) { gfx::Point point_in_child = point; ConvertPointToTarget(this, buttons[i], &point_in_child); @@ -383,7 +439,7 @@ void NotificationViewMD::CreateOrUpdateViews(const Notification& notification) { CreateOrUpdateActionButtonViews(notification); } -NotificationViewMD::NotificationViewMD(MessageCenterController* controller, +NotificationViewMD::NotificationViewMD(MessageViewDelegate* controller, const Notification& notification) : MessageView(controller, notification), clickable_(notification.clickable()) { @@ -421,15 +477,27 @@ NotificationViewMD::NotificationViewMD(MessageCenterController* controller, right_content_->SetLayoutManager(new views::FillLayout()); content_row_->AddChildView(right_content_); - // |action_row_| contains inline action button. + // |action_row_| contains inline action buttons and inline textfield. actions_row_ = new views::View(); - actions_row_->SetLayoutManager( + actions_row_->SetVisible(false); + actions_row_->SetLayoutManager(new views::FillLayout()); + AddChildView(actions_row_); + + // |action_buttons_row_| contains inline action buttons. + action_buttons_row_ = new views::View(); + action_buttons_row_->SetLayoutManager( new views::BoxLayout(views::BoxLayout::kHorizontal, kActionsRowPadding, kActionsRowHorizontalSpacing)); - actions_row_->SetBackground( + action_buttons_row_->SetBackground( views::CreateSolidBackground(kActionsRowBackgroundColor)); - actions_row_->SetVisible(false); - AddChildView(actions_row_); + action_buttons_row_->SetVisible(false); + actions_row_->AddChildView(action_buttons_row_); + + // |inline_reply_| is a textfield for inline reply. + inline_reply_ = new NotificationInputMD(this); + inline_reply_->SetVisible(false); + + actions_row_->AddChildView(inline_reply_); CreateOrUpdateViews(notification); UpdateControlButtonsVisibilityWithNotification(notification); @@ -473,10 +541,20 @@ void NotificationViewMD::ScrollRectToVisible(const gfx::Rect& rect) { } gfx::NativeCursor NotificationViewMD::GetCursor(const ui::MouseEvent& event) { - if (clickable_ || controller()->HasClickedListener(notification_id())) - return views::GetNativeHandCursor(); + // Do not change the cursor on a notification that isn't clickable. + if (!clickable_) + return views::View::GetCursor(event); + + // Do not change the cursor on the actions row. + if (expanded_) { + DCHECK(actions_row_); + gfx::Point point_in_child = event.location(); + ConvertPointToTarget(this, actions_row_, &point_in_child); + if (actions_row_->HitTestPoint(point_in_child)) + return views::View::GetCursor(event); + } - return views::View::GetCursor(event); + return views::GetNativeHandCursor(); } void NotificationViewMD::OnMouseEntered(const ui::MouseEvent& event) { @@ -489,6 +567,22 @@ void NotificationViewMD::OnMouseExited(const ui::MouseEvent& event) { UpdateControlButtonsVisibility(); } +bool NotificationViewMD::OnMousePressed(const ui::MouseEvent& event) { + if (!event.IsOnlyLeftMouseButton()) + return false; + + // Ignore click of actions row outside action buttons. + if (expanded_) { + DCHECK(actions_row_); + gfx::Point point_in_child = event.location(); + ConvertPointToTarget(this, actions_row_, &point_in_child); + if (actions_row_->HitTestPoint(point_in_child)) + return true; + } + + return MessageView::OnMousePressed(event); +} + void NotificationViewMD::UpdateWithNotification( const Notification& notification) { MessageView::UpdateWithNotification(notification); @@ -503,8 +597,7 @@ void NotificationViewMD::UpdateWithNotification( void NotificationViewMD::UpdateControlButtonsVisibilityWithNotification( const Notification& notification) { control_buttons_view_->ShowSettingsButton( - notification.delegate() && - notification.delegate()->ShouldDisplaySettingsButton()); + notification.should_show_settings_button()); control_buttons_view_->ShowCloseButton(!GetPinned()); UpdateControlButtonsVisibility(); } @@ -527,13 +620,28 @@ void NotificationViewMD::ButtonPressed(views::Button* sender, // See if the button pressed was an action button. for (size_t i = 0; i < action_buttons_.size(); ++i) { - if (sender == action_buttons_[i]) { - controller()->ClickOnNotificationButton(id, i); - return; + if (sender != action_buttons_[i]) + continue; + if (action_buttons_[i]->is_inline_reply()) { + inline_reply_->set_index(i); + inline_reply_->set_placeholder(action_buttons_[i]->placeholder()); + inline_reply_->SetVisible(true); + action_buttons_row_->SetVisible(false); + Layout(); + SchedulePaint(); + } else { + delegate()->ClickOnNotificationButton(id, i); } + return; } } +void NotificationViewMD::OnNotificationInputSubmit(size_t index, + const base::string16& text) { + delegate()->ClickOnNotificationButtonWithReply(notification_id(), index, + text); +} + bool NotificationViewMD::IsCloseButtonFocused() const { return control_buttons_view_->IsCloseButtonFocused(); } @@ -801,10 +909,12 @@ void NotificationViewMD::CreateOrUpdateActionButtonViews( for (size_t i = 0; i < buttons.size(); ++i) { ButtonInfo button_info = buttons[i]; if (new_buttons) { - NotificationButtonMD* button = - new NotificationButtonMD(this, button_info.title); + bool is_inline_reply = + button_info.type == message_center::ButtonType::TEXT; + NotificationButtonMD* button = new NotificationButtonMD( + this, is_inline_reply, button_info.title, button_info.placeholder); action_buttons_.push_back(button); - actions_row_->AddChildView(button); + action_buttons_row_->AddChildView(button); } else { action_buttons_[i]->SetText(button_info.title); action_buttons_[i]->SchedulePaint(); @@ -839,7 +949,7 @@ bool NotificationViewMD::IsExpandable() { return true; } // Expandable if there is at least one inline action. - if (actions_row_->has_children()) + if (action_buttons_row_->has_children()) return true; // Expandable if the notification has image. @@ -867,7 +977,13 @@ void NotificationViewMD::UpdateViewForExpandedState(bool expanded) { } if (image_container_view_) image_container_view_->SetVisible(expanded); - actions_row_->SetVisible(expanded && actions_row_->has_children()); + + actions_row_->SetVisible(expanded && (action_buttons_row_->has_children())); + if (!expanded) { + action_buttons_row_->SetVisible(true); + inline_reply_->SetVisible(false); + } + for (size_t i = kMaxLinesForMessageView; i < item_views_.size(); ++i) { item_views_[i]->SetVisible(expanded); } @@ -924,8 +1040,8 @@ void NotificationViewMD::SetExpanded(bool expanded) { UpdateViewForExpandedState(expanded_); content_row_->InvalidateLayout(); - if (controller()) - controller()->UpdateNotificationSize(notification_id()); + if (delegate()) + delegate()->UpdateNotificationSize(notification_id()); } void NotificationViewMD::Activate() { diff --git a/chromium/ui/message_center/views/notification_view_md.h b/chromium/ui/message_center/views/notification_view_md.h index b4240094bc9..cf2ff5d0ba3 100644 --- a/chromium/ui/message_center/views/notification_view_md.h +++ b/chromium/ui/message_center/views/notification_view_md.h @@ -13,6 +13,8 @@ #include "ui/message_center/views/message_view.h" #include "ui/views/controls/button/button.h" #include "ui/views/controls/button/label_button.h" +#include "ui/views/controls/textfield/textfield.h" +#include "ui/views/controls/textfield/textfield_controller.h" #include "ui/views/view_targeter_delegate.h" namespace views { @@ -103,8 +105,15 @@ class LargeImageContainerView : public views::View { // This button capitalizes the given label string. class NotificationButtonMD : public views::LabelButton { public: + // |is_inline_reply| is true when the notification action takes text as the + // return value i.e. the notification action is inline reply. + // The input field would be shown when the button is clicked. + // |placeholder| is placeholder text shown on the input field. Only used when + // |is_inline_reply| is true. NotificationButtonMD(views::ButtonListener* listener, - const base::string16& text); + bool is_inline_reply, + const base::string16& label, + const base::string16& placeholder); ~NotificationButtonMD() override; void SetText(const base::string16& text) override; @@ -115,20 +124,56 @@ class NotificationButtonMD : public views::LabelButton { SkColor enabled_color_for_testing() { return label()->enabled_color(); } + bool is_inline_reply() const { return is_inline_reply_; } + const base::string16& placeholder() const { return placeholder_; } + private: + const bool is_inline_reply_; + const base::string16 placeholder_; + DISALLOW_COPY_AND_ASSIGN(NotificationButtonMD); }; +class NotificationInputDelegate { + public: + virtual void OnNotificationInputSubmit(size_t index, + const base::string16& text) = 0; + virtual ~NotificationInputDelegate() = default; +}; + +class NotificationInputMD : public views::Textfield, + public views::TextfieldController { + public: + NotificationInputMD(NotificationInputDelegate* delegate); + ~NotificationInputMD() override; + + bool HandleKeyEvent(views::Textfield* sender, + const ui::KeyEvent& key_event) override; + + void set_index(size_t index) { index_ = index; } + void set_placeholder(const base::string16& placeholder); + + private: + NotificationInputDelegate* const delegate_; + + // |index_| is the notification action index that should be passed as the + // argument of MessageViewDelegate::ClickOnNotificationButtonWithReply. + size_t index_ = 0; + + DISALLOW_COPY_AND_ASSIGN(NotificationInputMD); +}; + // View that displays all current types of notification (web, basic, image, and // list) except the custom notification. Future notification types may be // handled by other classes, in which case instances of those classes would be // returned by the Create() factory method below. class MESSAGE_CENTER_EXPORT NotificationViewMD : public MessageView, + public NotificationInputDelegate, public views::ButtonListener, public views::ViewTargeterDelegate { public: - NotificationViewMD(MessageCenterController* controller, + NotificationViewMD(MessageViewDelegate* controller, const Notification& notification); ~NotificationViewMD() override; @@ -141,6 +186,7 @@ class MESSAGE_CENTER_EXPORT NotificationViewMD gfx::NativeCursor GetCursor(const ui::MouseEvent& event) override; void OnMouseEntered(const ui::MouseEvent& event) override; void OnMouseExited(const ui::MouseEvent& event) override; + bool OnMousePressed(const ui::MouseEvent& event) override; // Overridden from MessageView: void UpdateWithNotification(const Notification& notification) override; @@ -152,6 +198,10 @@ class MESSAGE_CENTER_EXPORT NotificationViewMD bool IsExpanded() const override; void SetExpanded(bool expanded) override; + // Overridden from NotificationInputDelegate: + void OnNotificationInputSubmit(size_t index, + const base::string16& text) override; + // views::ViewTargeterDelegate: views::View* TargetForRect(views::View* root, const gfx::Rect& rect) override; @@ -160,6 +210,8 @@ class MESSAGE_CENTER_EXPORT NotificationViewMD FRIEND_TEST_ALL_PREFIXES(NotificationViewMDTest, TestIconSizing); FRIEND_TEST_ALL_PREFIXES(NotificationViewMDTest, UpdateButtonsStateTest); FRIEND_TEST_ALL_PREFIXES(NotificationViewMDTest, UpdateButtonCountTest); + FRIEND_TEST_ALL_PREFIXES(NotificationViewMDTest, TestActionButtonClick); + FRIEND_TEST_ALL_PREFIXES(NotificationViewMDTest, TestInlineReply); FRIEND_TEST_ALL_PREFIXES(NotificationViewMDTest, ExpandLongMessage); FRIEND_TEST_ALL_PREFIXES(NotificationViewMDTest, TestAccentColor); FRIEND_TEST_ALL_PREFIXES(NotificationViewMDTest, UseImageAsIcon); @@ -221,6 +273,8 @@ class MESSAGE_CENTER_EXPORT NotificationViewMD std::vector<ItemView*> item_views_; views::ProgressBar* progress_bar_view_ = nullptr; CompactTitleMessageView* compact_title_message_view_ = nullptr; + views::View* action_buttons_row_ = nullptr; + NotificationInputMD* inline_reply_ = nullptr; std::unique_ptr<ui::EventHandler> click_activator_; diff --git a/chromium/ui/message_center/views/notification_view_md_unittest.cc b/chromium/ui/message_center/views/notification_view_md_unittest.cc index 1ef5ecadea3..6ebc86921a1 100644 --- a/chromium/ui/message_center/views/notification_view_md_unittest.cc +++ b/chromium/ui/message_center/views/notification_view_md_unittest.cc @@ -15,7 +15,7 @@ #include "ui/gfx/canvas.h" #include "ui/message_center/public/cpp/message_center_constants.h" #include "ui/message_center/views/bounded_label.h" -#include "ui/message_center/views/message_center_controller.h" +#include "ui/message_center/views/message_view_delegate.h" #include "ui/message_center/views/notification_control_buttons_view.h" #include "ui/message_center/views/notification_header_view.h" #include "ui/message_center/views/padded_button.h" @@ -33,7 +33,7 @@ namespace message_center { static const SkColor kBitmapColor = SK_ColorGREEN; class NotificationViewMDTest : public views::ViewsTestBase, - public MessageCenterController { + public MessageViewDelegate { public: NotificationViewMDTest(); ~NotificationViewMDTest() override; @@ -42,15 +42,15 @@ class NotificationViewMDTest : public views::ViewsTestBase, void SetUp() override; void TearDown() override; - // Overridden from MessageCenterController: + // Overridden from MessageViewDelegate: void ClickOnNotification(const std::string& notification_id) override; void RemoveNotification(const std::string& notification_id, bool by_user) override; - std::unique_ptr<ui::MenuModel> CreateMenuModel( - const Notification& notification) override; - bool HasClickedListener(const std::string& notification_id) override; void ClickOnNotificationButton(const std::string& notification_id, int button_index) override; + void ClickOnNotificationButtonWithReply(const std::string& notification_id, + int button_index, + const base::string16& reply) override; void ClickOnSettingsButton(const std::string& notification_id) override; void UpdateNotificationSize(const std::string& notification_id) override; @@ -81,6 +81,11 @@ class NotificationViewMDTest : public views::ViewsTestBase, void ScrollBy(int dx); views::View* GetCloseButton(); + bool expecting_button_click_ = false; + bool expecting_reply_submission_ = false; + int clicked_button_index_ = -1; + base::string16 submitted_reply_string_; + private: std::set<std::string> removed_ids_; @@ -123,6 +128,13 @@ void NotificationViewMDTest::SetUp() { widget_->SetContentsView(notification_view_.get()); widget_->SetSize(notification_view_->GetPreferredSize()); widget_->Show(); + widget_->widget_delegate()->set_can_activate(true); + widget_->Activate(); + + expecting_button_click_ = false; + expecting_reply_submission_ = false; + clicked_button_index_ = -1; + submitted_reply_string_.clear(); } void NotificationViewMDTest::TearDown() { @@ -143,23 +155,25 @@ void NotificationViewMDTest::RemoveNotification( removed_ids_.insert(notification_id); } -std::unique_ptr<ui::MenuModel> NotificationViewMDTest::CreateMenuModel( - const Notification& notification) { - // For this test, this method should not be invoked. - NOTREACHED(); - return nullptr; -} - -bool NotificationViewMDTest::HasClickedListener( - const std::string& notification_id) { - return true; -} - void NotificationViewMDTest::ClickOnNotificationButton( const std::string& notification_id, int button_index) { - // For this test, this method should not be invoked. - NOTREACHED(); + if (!expecting_button_click_) { + ADD_FAILURE() << "ClickOnNotificationButton should not be invoked."; + } + clicked_button_index_ = button_index; +} + +void NotificationViewMDTest::ClickOnNotificationButtonWithReply( + const std::string& notification_id, + int button_index, + const base::string16& reply) { + if (!expecting_reply_submission_) { + ADD_FAILURE() + << "ClickOnNotificationButtonWithReply should not be invoked."; + } + clicked_button_index_ = button_index; + submitted_reply_string_ = reply; } void NotificationViewMDTest::ClickOnSettingsButton( @@ -424,6 +438,83 @@ TEST_F(NotificationViewMDTest, UpdateButtonCountTest) { notification_view()->action_buttons_[0]->state()); } +TEST_F(NotificationViewMDTest, TestActionButtonClick) { + expecting_button_click_ = true; + + notification()->set_buttons(CreateButtons(2)); + notification_view()->UpdateWithNotification(*notification()); + widget()->Show(); + + ui::test::EventGenerator generator(widget()->GetNativeWindow()); + + // Action buttons are hidden by collapsed state. + if (!notification_view()->expanded_) + notification_view()->ToggleExpanded(); + EXPECT_TRUE(notification_view()->actions_row_->visible()); + + // Now construct a mouse click event 1 pixel inside the boundary of the action + // button. + gfx::Point cursor_location(1, 1); + views::View::ConvertPointToScreen(notification_view()->action_buttons_[1], + &cursor_location); + generator.MoveMouseTo(cursor_location); + generator.ClickLeftButton(); + + EXPECT_EQ(1, clicked_button_index_); +} + +TEST_F(NotificationViewMDTest, TestInlineReply) { + expecting_reply_submission_ = true; + + std::vector<ButtonInfo> buttons = CreateButtons(2); + buttons[1].type = ButtonType::TEXT; + notification()->set_buttons(buttons); + notification_view()->UpdateWithNotification(*notification()); + widget()->Show(); + + ui::test::EventGenerator generator(widget()->GetNativeWindow()); + + // Action buttons are hidden by collapsed state. + if (!notification_view()->expanded_) + notification_view()->ToggleExpanded(); + EXPECT_TRUE(notification_view()->actions_row_->visible()); + + // Now construct a mouse click event 1 pixel inside the boundary of the action + // button. + gfx::Point cursor_location(1, 1); + views::View::ConvertPointToScreen(notification_view()->action_buttons_[1], + &cursor_location); + generator.MoveMouseTo(cursor_location); + generator.ClickLeftButton(); + + // Nothing should be submitted at this point. + EXPECT_EQ(-1, clicked_button_index_); + + // Toggling should hide the inline textfield. + EXPECT_TRUE(notification_view()->inline_reply_->visible()); + notification_view()->ToggleExpanded(); + notification_view()->ToggleExpanded(); + EXPECT_FALSE(notification_view()->inline_reply_->visible()); + + // Click the button again and focus on the inline textfield. + generator.ClickLeftButton(); + generator.ClickLeftButton(); + EXPECT_TRUE(notification_view()->inline_reply_->visible()); + EXPECT_TRUE(notification_view()->inline_reply_->HasFocus()); + + // Type the text and submit. + ui::KeyboardCode keycodes[] = {ui::VKEY_T, ui::VKEY_E, ui::VKEY_S, ui::VKEY_T, + ui::VKEY_RETURN}; + + for (ui::KeyboardCode keycode : keycodes) { + generator.PressKey(keycode, ui::EF_NONE); + generator.ReleaseKey(keycode, ui::EF_NONE); + } + + EXPECT_EQ(1, clicked_button_index_); + EXPECT_EQ(base::ASCIIToUTF16("test"), submitted_reply_string_); +} + TEST_F(NotificationViewMDTest, SlideOut) { ui::ScopedAnimationDurationScaleMode zero_duration_scope( ui::ScopedAnimationDurationScaleMode::ZERO_DURATION); diff --git a/chromium/ui/message_center/views/notification_view_unittest.cc b/chromium/ui/message_center/views/notification_view_unittest.cc index d11904f7a74..a394a89d6d7 100644 --- a/chromium/ui/message_center/views/notification_view_unittest.cc +++ b/chromium/ui/message_center/views/notification_view_unittest.cc @@ -27,7 +27,7 @@ #include "ui/message_center/notification_list.h" #include "ui/message_center/notification_types.h" #include "ui/message_center/views/constants.h" -#include "ui/message_center/views/message_center_controller.h" +#include "ui/message_center/views/message_view_delegate.h" #include "ui/message_center/views/message_view_factory.h" #include "ui/message_center/views/notification_button.h" #include "ui/message_center/views/notification_control_buttons_view.h" @@ -45,19 +45,10 @@ namespace message_center { -// A test delegate used for tests that deal with the notification settings -// button. -class NotificationSettingsDelegate : public NotificationDelegate { - bool ShouldDisplaySettingsButton() override { return true; } - - private: - ~NotificationSettingsDelegate() override {} -}; - /* Test fixture ***************************************************************/ class NotificationViewTest : public views::ViewsTestBase, - public MessageCenterController { + public MessageViewDelegate { public: NotificationViewTest(); ~NotificationViewTest() override; @@ -72,15 +63,15 @@ class NotificationViewTest : public views::ViewsTestBase, Notification* notification() { return notification_.get(); } RichNotificationData* data() { return data_.get(); } - // Overridden from MessageCenterController: + // Overridden from MessageViewDelegate: void ClickOnNotification(const std::string& notification_id) override; void RemoveNotification(const std::string& notification_id, bool by_user) override; - std::unique_ptr<ui::MenuModel> CreateMenuModel( - const Notification& notification) override; - bool HasClickedListener(const std::string& notification_id) override; void ClickOnNotificationButton(const std::string& notification_id, int button_index) override; + void ClickOnNotificationButtonWithReply(const std::string& notification_id, + int button_index, + const base::string16& reply) override; void ClickOnSettingsButton(const std::string& notification_id) override; void UpdateNotificationSize(const std::string& notification_id) override; @@ -286,21 +277,17 @@ void NotificationViewTest::RemoveNotification( removed_ids_.insert(notification_id); } -std::unique_ptr<ui::MenuModel> NotificationViewTest::CreateMenuModel( - const Notification& notification) { +void NotificationViewTest::ClickOnNotificationButton( + const std::string& notification_id, + int button_index) { // For this test, this method should not be invoked. NOTREACHED(); - return nullptr; -} - -bool NotificationViewTest::HasClickedListener( - const std::string& notification_id) { - return true; } -void NotificationViewTest::ClickOnNotificationButton( +void NotificationViewTest::ClickOnNotificationButtonWithReply( const std::string& notification_id, - int button_index) { + int button_index, + const base::string16& reply) { // For this test, this method should not be invoked. NOTREACHED(); } @@ -345,15 +332,13 @@ TEST_F(NotificationViewTest, CreateOrUpdateTest) { } TEST_F(NotificationViewTest, CreateOrUpdateTestSettingsButton) { - scoped_refptr<NotificationSettingsDelegate> delegate = - new NotificationSettingsDelegate(); - Notification notf(NOTIFICATION_TYPE_BASE_FORMAT, - std::string("notification id"), base::UTF8ToUTF16("title"), - base::UTF8ToUTF16("message"), CreateTestImage(80, 80), - base::UTF8ToUTF16("display source"), - GURL("https://hello.com"), - NotifierId(NotifierId::APPLICATION, "extension_id"), - *data(), delegate.get()); + data()->settings_button_handler = SettingsButtonHandler::TRAY; + Notification notf( + NOTIFICATION_TYPE_BASE_FORMAT, std::string("notification id"), + base::UTF8ToUTF16("title"), base::UTF8ToUTF16("message"), + CreateTestImage(80, 80), base::UTF8ToUTF16("display source"), + GURL("https://hello.com"), + NotifierId(NotifierId::APPLICATION, "extension_id"), *data(), nullptr); notification_view()->UpdateWithNotification(notf); EXPECT_TRUE(NULL != notification_view()->title_view_); @@ -555,15 +540,13 @@ TEST_F(NotificationViewTest, UpdateButtonCountTest) { } TEST_F(NotificationViewTest, SettingsButtonTest) { - scoped_refptr<NotificationSettingsDelegate> delegate = - new NotificationSettingsDelegate(); - Notification notf(NOTIFICATION_TYPE_BASE_FORMAT, - std::string("notification id"), base::UTF8ToUTF16("title"), - base::UTF8ToUTF16("message"), CreateTestImage(80, 80), - base::UTF8ToUTF16("display source"), - GURL("https://hello.com"), - NotifierId(NotifierId::APPLICATION, "extension_id"), - *data(), delegate.get()); + data()->settings_button_handler = SettingsButtonHandler::TRAY; + Notification notf( + NOTIFICATION_TYPE_BASE_FORMAT, std::string("notification id"), + base::UTF8ToUTF16("title"), base::UTF8ToUTF16("message"), + CreateTestImage(80, 80), base::UTF8ToUTF16("display source"), + GURL("https://hello.com"), + NotifierId(NotifierId::APPLICATION, "extension_id"), *data(), nullptr); notification_view()->UpdateWithNotification(notf); widget()->Show(); diff --git a/chromium/ui/message_center/views/toast_contents_view.cc b/chromium/ui/message_center/views/toast_contents_view.cc index cc6138b2ad3..d8a2cf67842 100644 --- a/chromium/ui/message_center/views/toast_contents_view.cc +++ b/chromium/ui/message_center/views/toast_contents_view.cc @@ -357,21 +357,6 @@ void ToastContentsView::RemoveNotification( collection_->RemoveNotification(notification_id, by_user); } -std::unique_ptr<ui::MenuModel> ToastContentsView::CreateMenuModel( - const Notification& notification) { - // Should not reach, the context menu should be handled in - // MessagePopupCollection. - NOTREACHED(); - return nullptr; -} - -bool ToastContentsView::HasClickedListener( - const std::string& notification_id) { - if (!collection_) - return false; - return collection_->HasClickedListener(notification_id); -} - void ToastContentsView::ClickOnNotificationButton( const std::string& notification_id, int button_index) { @@ -379,6 +364,15 @@ void ToastContentsView::ClickOnNotificationButton( collection_->ClickOnNotificationButton(notification_id, button_index); } +void ToastContentsView::ClickOnNotificationButtonWithReply( + const std::string& notification_id, + int button_index, + const base::string16& reply) { + if (collection_) + collection_->ClickOnNotificationButtonWithReply(notification_id, + button_index, reply); +} + void ToastContentsView::CreateWidget( PopupAlignmentDelegate* alignment_delegate) { views::Widget::InitParams params(views::Widget::InitParams::TYPE_POPUP); diff --git a/chromium/ui/message_center/views/toast_contents_view.h b/chromium/ui/message_center/views/toast_contents_view.h index 4cd4ce0f28b..441c9d3f3fc 100644 --- a/chromium/ui/message_center/views/toast_contents_view.h +++ b/chromium/ui/message_center/views/toast_contents_view.h @@ -13,7 +13,7 @@ #include "ui/gfx/geometry/rect.h" #include "ui/gfx/geometry/size.h" #include "ui/message_center/message_center_export.h" -#include "ui/message_center/views/message_center_controller.h" +#include "ui/message_center/views/message_view_delegate.h" #include "ui/views/widget/widget_delegate.h" #include "ui/views/widget/widget_observer.h" @@ -37,14 +37,14 @@ class MessageView; class Notification; class PopupAlignmentDelegate; -// The widget host for a popup. Also implements MessageCenterController +// The widget host for a popup. Also implements MessageViewDelegate // which delegates over to MessagePopupCollection, but takes care about // checking the weakref since MessagePopupCollection may disappear before // widget/views are closed/destructed. class MESSAGE_CENTER_EXPORT ToastContentsView : public views::WidgetDelegateView, public views::WidgetObserver, - public MessageCenterController, + public MessageViewDelegate, public gfx::AnimationDelegate { public: static const char kViewClassName[]; @@ -93,15 +93,15 @@ class MESSAGE_CENTER_EXPORT ToastContentsView private: friend class test::MessagePopupCollectionTest; - // Overridden from MessageCenterController: + // Overridden from MessageViewDelegate: void ClickOnNotification(const std::string& notification_id) override; void RemoveNotification(const std::string& notification_id, bool by_user) override; - std::unique_ptr<ui::MenuModel> CreateMenuModel( - const Notification& notification) override; - bool HasClickedListener(const std::string& notification_id) override; void ClickOnNotificationButton(const std::string& notification_id, int button_index) override; + void ClickOnNotificationButtonWithReply(const std::string& notification_id, + int button_index, + const base::string16& reply) override; void ClickOnSettingsButton(const std::string& notification_id) override; void UpdateNotificationSize(const std::string& notification_id) override; |