diff options
author | johnnyg@chromium.org <johnnyg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-07-12 17:13:49 +0000 |
---|---|---|
committer | johnnyg@chromium.org <johnnyg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-07-12 17:13:49 +0000 |
commit | 04b0c9332361ac2f7de9455d0df15290db94c693 (patch) | |
tree | fdc8b47e5f7c57b3e7382a7a236e5b92675b3f87 /chrome/browser/notifications | |
parent | 9e5c05aa2d4404c2a9946b49df19c2f781116b29 (diff) | |
download | chromium_src-04b0c9332361ac2f7de9455d0df15290db94c693.zip chromium_src-04b0c9332361ac2f7de9455d0df15290db94c693.tar.gz chromium_src-04b0c9332361ac2f7de9455d0df15290db94c693.tar.bz2 |
Implement BiDi support and replace ID support for notifications.
BUG=none
TEST=layout tests
Review URL: http://codereview.chromium.org/1917004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@52090 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/notifications')
8 files changed, 186 insertions, 90 deletions
diff --git a/chrome/browser/notifications/balloon.cc b/chrome/browser/notifications/balloon.cc index 4d9a94f..969bde1 100644 --- a/chrome/browser/notifications/balloon.cc +++ b/chrome/browser/notifications/balloon.cc @@ -38,10 +38,12 @@ void Balloon::Show() { notification_->Display(); if (balloon_view_.get()) { balloon_view_->Show(this); + balloon_view_->RepositionToBalloon(); } } void Balloon::Update(const Notification& notification) { + notification_->Close(false); notification_.reset(new Notification(notification)); notification_->Display(); if (balloon_view_.get()) { diff --git a/chrome/browser/notifications/desktop_notification_service.cc b/chrome/browser/notifications/desktop_notification_service.cc index 4a5f3ab..bc66077 100644 --- a/chrome/browser/notifications/desktop_notification_service.cc +++ b/chrome/browser/notifications/desktop_notification_service.cc @@ -36,12 +36,14 @@ #include "third_party/WebKit/WebKit/chromium/public/WebNotificationPresenter.h" using WebKit::WebNotificationPresenter; +using WebKit::WebTextDirection; const ContentSetting kDefaultSetting = CONTENT_SETTING_ASK; // static string16 DesktopNotificationService::CreateDataUrl( - const GURL& icon_url, const string16& title, const string16& body) { + const GURL& icon_url, const string16& title, const string16& body, + WebTextDirection dir) { int resource; string16 line_name; string16 line; @@ -51,6 +53,9 @@ string16 DesktopNotificationService::CreateDataUrl( subst.push_back(icon_url.spec()); subst.push_back(EscapeForHTML(UTF16ToUTF8(title))); subst.push_back(EscapeForHTML(UTF16ToUTF8(body))); + // icon float position + subst.push_back(dir == WebKit::WebTextDirectionRightToLeft ? + "right" : "left"); } else if (title.empty() || body.empty()) { resource = IDR_NOTIFICATION_1LINE_HTML; line = title.empty() ? body : title; @@ -64,6 +69,9 @@ string16 DesktopNotificationService::CreateDataUrl( subst.push_back(EscapeForHTML(UTF16ToUTF8(title))); subst.push_back(EscapeForHTML(UTF16ToUTF8(body))); } + // body text direction + subst.push_back(dir == WebKit::WebTextDirectionRightToLeft ? + "rtl" : "ltr"); const base::StringPiece template_html( ResourceBundle::GetSharedInstance().GetRawDataResource( @@ -529,37 +537,32 @@ bool DesktopNotificationService::CancelDesktopNotification( scoped_refptr<NotificationObjectProxy> proxy( new NotificationObjectProxy(process_id, route_id, notification_id, false)); - Notification notif(GURL(), GURL(), L"", proxy); + // TODO(johnnyg): clean up this "empty" notification. + Notification notif(GURL(), GURL(), L"", ASCIIToUTF16(""), proxy); return ui_manager_->Cancel(notif); } bool DesktopNotificationService::ShowDesktopNotification( - const GURL& origin, const GURL& url, int process_id, int route_id, - DesktopNotificationSource source, int notification_id) { + const ViewHostMsg_ShowNotification_Params& params, + int process_id, int route_id, DesktopNotificationSource source) { DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); + const GURL& origin = params.origin; NotificationObjectProxy* proxy = new NotificationObjectProxy(process_id, route_id, - notification_id, + params.notification_id, source == WorkerNotification); - Notification notif(origin, url, DisplayNameForOrigin(origin), proxy); - ShowNotification(notif); - return true; -} - -bool DesktopNotificationService::ShowDesktopNotificationText( - const GURL& origin, const GURL& icon, const string16& title, - const string16& text, int process_id, int route_id, - DesktopNotificationSource source, int notification_id) { - DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); - NotificationObjectProxy* proxy = - new NotificationObjectProxy(process_id, route_id, - notification_id, - source == WorkerNotification); - // "upconvert" the string parameters to a data: URL. - string16 data_url = CreateDataUrl(icon, title, text); + GURL contents; + if (params.is_html) { + contents = params.contents_url; + } else { + // "upconvert" the string parameters to a data: URL. + contents = GURL( + CreateDataUrl(params.icon_url, params.title, params.body, + params.direction)); + } Notification notif( - origin, GURL(data_url), DisplayNameForOrigin(origin), proxy); + origin, contents, DisplayNameForOrigin(origin), params.replace_id, proxy); ShowNotification(notif); return true; } diff --git a/chrome/browser/notifications/desktop_notification_service.h b/chrome/browser/notifications/desktop_notification_service.h index 3f97425..ae38b18 100644 --- a/chrome/browser/notifications/desktop_notification_service.h +++ b/chrome/browser/notifications/desktop_notification_service.h @@ -14,6 +14,7 @@ #include "chrome/common/notification_registrar.h" #include "chrome/common/notification_service.h" #include "googleurl/src/gurl.h" +#include "third_party/WebKit/WebKit/chromium/public/WebTextDirection.h" class NotificationUIManager; class NotificationsPrefsCache; @@ -21,6 +22,7 @@ class PrefService; class Profile; class Task; class TabContents; +struct ViewHostMsg_ShowNotification_Params; // The DesktopNotificationService is an object, owned by the Profile, // which provides the creation of desktop "toasts" to web pages and workers. @@ -44,23 +46,13 @@ class DesktopNotificationService : public NotificationObserver { int callback_context, TabContents* tab); - // Takes a notification object and shows it in the UI. - void ShowNotification(const Notification& notification); - - // Two ShowNotification methods: getting content either from remote - // URL or as local parameters. These are called on the UI thread - // in response to IPCs from a child process running script. |origin| - // is the origin of the script. |source| indicates whether the - // script is in a worker or page. |notification_id| is an opaque - // value to be passed back to the process when events occur on - // this notification. - bool ShowDesktopNotification(const GURL& origin, const GURL& url, - int process_id, int route_id, DesktopNotificationSource source, - int notification_id); - bool ShowDesktopNotificationText(const GURL& origin, const GURL& icon, - const string16& title, const string16& text, int process_id, - int route_id, DesktopNotificationSource source, int notification_id); - + // ShowNotification is called on the UI thread handling IPCs from a child + // process, identified by |process_id| and |route_id|. |source| indicates + // whether the script is in a worker or page. |params| contains all the + // other parameters supplied by the worker or page. + bool ShowDesktopNotification( + const ViewHostMsg_ShowNotification_Params& params, + int process_id, int route_id, DesktopNotificationSource source); // Cancels a notification. If it has already been shown, it will be // removed from the screen. If it hasn't been shown yet, it won't be @@ -83,8 +75,10 @@ class DesktopNotificationService : public NotificationObserver { // Creates a data:xxxx URL which contains the full HTML for a notification // using supplied icon, title, and text, run through a template which contains // the standard formatting for notifications. - static string16 CreateDataUrl(const GURL& icon_url, const string16& title, - const string16& body); + static string16 CreateDataUrl(const GURL& icon_url, + const string16& title, + const string16& body, + WebKit::WebTextDirection dir); // The default content setting determines how to handle origins that haven't // been allowed or denied yet. @@ -112,6 +106,9 @@ class DesktopNotificationService : public NotificationObserver { void StartObserving(); void StopObserving(); + // Takes a notification object and shows it in the UI. + void ShowNotification(const Notification& notification); + // Save a permission change to the profile. void PersistPermissionChange(const GURL& origin, bool is_allowed); diff --git a/chrome/browser/notifications/desktop_notifications_unittest.cc b/chrome/browser/notifications/desktop_notifications_unittest.cc index 5cd03c3..459d9a0 100644 --- a/chrome/browser/notifications/desktop_notifications_unittest.cc +++ b/chrome/browser/notifications/desktop_notifications_unittest.cc @@ -21,6 +21,7 @@ void MockBalloonCollection::Add(const Notification& notification, Notification test_notification(notification.origin_url(), notification.content_url(), notification.display_source(), + string16(), /* replace_id */ log_proxy_.get()); BalloonCollectionImpl::Add(test_notification, profile); } @@ -29,6 +30,7 @@ bool MockBalloonCollection::Remove(const Notification& notification) { Notification test_notification(notification.origin_url(), notification.content_url(), notification.display_source(), + string16(), /* replace_id */ log_proxy_.get()); return BalloonCollectionImpl::Remove(test_notification); } @@ -87,18 +89,35 @@ void DesktopNotificationsTest::TearDown() { ui_manager_.reset(NULL); } +ViewHostMsg_ShowNotification_Params +DesktopNotificationsTest::StandardTestNotification() { + ViewHostMsg_ShowNotification_Params params; + params.notification_id = 0; + params.origin = GURL("http://www.google.com"); + params.is_html = false; + params.icon_url = GURL("/icon.png"); + params.title = ASCIIToUTF16("Title"); + params.body = ASCIIToUTF16("Text"); + return params; +} + TEST_F(DesktopNotificationsTest, TestShow) { - EXPECT_TRUE(service_->ShowDesktopNotificationText( - GURL("http://www.google.com"), - GURL("/icon.png"), ASCIIToUTF16("Title"), ASCIIToUTF16("Text"), - 0, 0, DesktopNotificationService::PageNotification, 1)); + ViewHostMsg_ShowNotification_Params params = StandardTestNotification(); + params.notification_id = 1; + + EXPECT_TRUE(service_->ShowDesktopNotification( + params, 0, 0, DesktopNotificationService::PageNotification)); MessageLoopForUI::current()->RunAllPending(); EXPECT_EQ(1, balloon_collection_->count()); + ViewHostMsg_ShowNotification_Params params2; + params2.origin = GURL("http://www.google.com"); + params2.is_html = true; + params2.contents_url = GURL("http://www.google.com/notification.html"); + params2.notification_id = 2; + EXPECT_TRUE(service_->ShowDesktopNotification( - GURL("http://www.google.com"), - GURL("http://www.google.com/notification.html"), - 0, 0, DesktopNotificationService::PageNotification, 2)); + params2, 0, 0, DesktopNotificationService::PageNotification)); MessageLoopForUI::current()->RunAllPending(); EXPECT_EQ(2, balloon_collection_->count()); @@ -108,11 +127,12 @@ TEST_F(DesktopNotificationsTest, TestShow) { } TEST_F(DesktopNotificationsTest, TestClose) { + ViewHostMsg_ShowNotification_Params params = StandardTestNotification(); + params.notification_id = 1; + // Request a notification; should open a balloon. - EXPECT_TRUE(service_->ShowDesktopNotificationText( - GURL("http://www.google.com"), - GURL("/icon.png"), ASCIIToUTF16("Title"), ASCIIToUTF16("Text"), - 0, 0, DesktopNotificationService::PageNotification, 1)); + EXPECT_TRUE(service_->ShowDesktopNotification( + params, 0, 0, DesktopNotificationService::PageNotification)); MessageLoopForUI::current()->RunAllPending(); EXPECT_EQ(1, balloon_collection_->count()); @@ -130,12 +150,14 @@ TEST_F(DesktopNotificationsTest, TestCancel) { int process_id = 0; int route_id = 0; int notification_id = 1; + + ViewHostMsg_ShowNotification_Params params = StandardTestNotification(); + params.notification_id = notification_id; + // Request a notification; should open a balloon. - EXPECT_TRUE(service_->ShowDesktopNotificationText( - GURL("http://www.google.com"), - GURL("/icon.png"), ASCIIToUTF16("Title"), ASCIIToUTF16("Text"), - process_id, route_id, DesktopNotificationService::PageNotification, - notification_id)); + EXPECT_TRUE(service_->ShowDesktopNotification( + params, process_id, route_id, + DesktopNotificationService::PageNotification)); MessageLoopForUI::current()->RunAllPending(); EXPECT_EQ(1, balloon_collection_->count()); @@ -154,15 +176,15 @@ TEST_F(DesktopNotificationsTest, TestCancel) { #if defined(OS_WIN) || defined(TOOLKIT_VIEWS) TEST_F(DesktopNotificationsTest, TestPositioning) { + ViewHostMsg_ShowNotification_Params params = StandardTestNotification(); std::string expected_log; // Create some toasts. After each but the first, make sure there // is a minimum separation between the toasts. int last_top = 0; for (int id = 0; id <= 3; ++id) { - EXPECT_TRUE(service_->ShowDesktopNotificationText( - GURL("http://www.google.com"), - GURL("/icon.png"), ASCIIToUTF16("Title"), ASCIIToUTF16("Text"), - 0, 0, DesktopNotificationService::PageNotification, id)); + params.notification_id = id; + EXPECT_TRUE(service_->ShowDesktopNotification( + params, 0, 0, DesktopNotificationService::PageNotification)); expected_log.append("notification displayed\n"); int top = balloon_collection_->UppermostVerticalPosition(); if (id > 0) @@ -174,21 +196,29 @@ TEST_F(DesktopNotificationsTest, TestPositioning) { } TEST_F(DesktopNotificationsTest, TestVariableSize) { + ViewHostMsg_ShowNotification_Params params; + params.origin = GURL("http://long.google.com"); + params.is_html = false; + params.icon_url = GURL("/icon.png"); + params.title = ASCIIToUTF16("Really Really Really Really Really Really " + "Really Really Really Really Really Really " + "Really Really Really Really Really Really " + "Really Long Title"), + params.body = ASCIIToUTF16("Text"); + params.notification_id = 0; + std::string expected_log; // Create some toasts. After each but the first, make sure there // is a minimum separation between the toasts. - EXPECT_TRUE(service_->ShowDesktopNotificationText( - GURL("http://long.google.com"), GURL("/icon.png"), - ASCIIToUTF16("Really Really Really Really Really Really " - "Really Really Really Really Really Really " - "Really Really Really Really Really Really Really Long Title"), - ASCIIToUTF16("Text"), - 0, 0, DesktopNotificationService::PageNotification, 0)); + EXPECT_TRUE(service_->ShowDesktopNotification( + params, 0, 0, DesktopNotificationService::PageNotification)); expected_log.append("notification displayed\n"); - EXPECT_TRUE(service_->ShowDesktopNotificationText( - GURL("http://short.google.com"), GURL("/icon.png"), - ASCIIToUTF16("Short title"), ASCIIToUTF16("Text"), - 0, 0, DesktopNotificationService::PageNotification, 1)); + + params.origin = GURL("http://short.google.com"); + params.title = ASCIIToUTF16("Short title"); + params.notification_id = 1; + EXPECT_TRUE(service_->ShowDesktopNotification( + params, 0, 0, DesktopNotificationService::PageNotification)); expected_log.append("notification displayed\n"); std::deque<Balloon*>& balloons = balloon_collection_->balloons(); @@ -213,13 +243,13 @@ TEST_F(DesktopNotificationsTest, TestQueueing) { int route_id = 0; // Request lots of identical notifications. + ViewHostMsg_ShowNotification_Params params = StandardTestNotification(); const int kLotsOfToasts = 20; for (int id = 1; id <= kLotsOfToasts; ++id) { - EXPECT_TRUE(service_->ShowDesktopNotificationText( - GURL("http://www.google.com"), - GURL("/icon.png"), ASCIIToUTF16("Title"), ASCIIToUTF16("Text"), - process_id, route_id, - DesktopNotificationService::PageNotification, id)); + params.notification_id = id; + EXPECT_TRUE(service_->ShowDesktopNotification( + params, process_id, route_id, + DesktopNotificationService::PageNotification)); } MessageLoopForUI::current()->RunAllPending(); @@ -265,11 +295,11 @@ TEST_F(DesktopNotificationsTest, TestQueueing) { TEST_F(DesktopNotificationsTest, TestEarlyDestruction) { // Create some toasts and then prematurely delete the notification service, // just to make sure nothing crashes/leaks. + ViewHostMsg_ShowNotification_Params params = StandardTestNotification(); for (int id = 0; id <= 3; ++id) { - EXPECT_TRUE(service_->ShowDesktopNotificationText( - GURL("http://www.google.com"), - GURL("/icon.png"), ASCIIToUTF16("Title"), ASCIIToUTF16("Text"), - 0, 0, DesktopNotificationService::PageNotification, id)); + params.notification_id = id; + EXPECT_TRUE(service_->ShowDesktopNotification( + params, 0, 0, DesktopNotificationService::PageNotification)); } service_.reset(NULL); } @@ -277,12 +307,12 @@ TEST_F(DesktopNotificationsTest, TestEarlyDestruction) { TEST_F(DesktopNotificationsTest, TestUserInputEscaping) { // Create a test script with some HTML; assert that it doesn't get into the // data:// URL that's produced for the balloon. - EXPECT_TRUE(service_->ShowDesktopNotificationText( - GURL("http://www.google.com"), - GURL("/icon.png"), - ASCIIToUTF16("<script>window.alert('uh oh');</script>"), - ASCIIToUTF16("<i>this text is in italics</i>, as is %3ci%3ethis%3c/i%3e"), - 0, 0, DesktopNotificationService::PageNotification, 1)); + ViewHostMsg_ShowNotification_Params params = StandardTestNotification(); + params.title = ASCIIToUTF16("<script>window.alert('uh oh');</script>"); + params.body = ASCIIToUTF16("<i>this text is in italics</i>"); + params.notification_id = 1; + EXPECT_TRUE(service_->ShowDesktopNotification( + params, 0, 0, DesktopNotificationService::PageNotification)); MessageLoopForUI::current()->RunAllPending(); EXPECT_EQ(1, balloon_collection_->count()); diff --git a/chrome/browser/notifications/desktop_notifications_unittest.h b/chrome/browser/notifications/desktop_notifications_unittest.h index 46bfeb4e..56a2a6b 100644 --- a/chrome/browser/notifications/desktop_notifications_unittest.h +++ b/chrome/browser/notifications/desktop_notifications_unittest.h @@ -15,6 +15,7 @@ #include "chrome/browser/notifications/notification_test_util.h" #include "chrome/browser/notifications/notification_ui_manager.h" #include "chrome/browser/notifications/notifications_prefs_cache.h" +#include "chrome/common/render_messages.h" #include "chrome/test/testing_profile.h" #include "testing/gtest/include/gtest/gtest.h" @@ -91,6 +92,9 @@ class DesktopNotificationsTest : public testing::Test { return service_->prefs_cache()->HasPermission(origin); } + // Constructs a notification parameter structure for use in tests. + ViewHostMsg_ShowNotification_Params StandardTestNotification(); + // Create a message loop to allow notifications code to post tasks, // and a thread so that notifications code runs on the expected thread. MessageLoopForUI message_loop_; diff --git a/chrome/browser/notifications/notification.h b/chrome/browser/notifications/notification.h index a58c2ad..bdad291 100644 --- a/chrome/browser/notifications/notification.h +++ b/chrome/browser/notifications/notification.h @@ -16,12 +16,15 @@ class NotificationDelegate; // data: URLs representing simple text+icon notifications. class Notification { public: + // TODO: http://crbug.com/43899 Convert this class to string16. Notification(const GURL& origin_url, const GURL& content_url, const std::wstring& display_source, + const string16& replace_id, NotificationDelegate* delegate) : origin_url_(origin_url), content_url_(content_url), display_source_(display_source), + replace_id_(replace_id), delegate_(delegate) { } @@ -29,9 +32,18 @@ class Notification { : origin_url_(notification.origin_url()), content_url_(notification.content_url()), display_source_(notification.display_source()), + replace_id_(notification.replace_id()), delegate_(notification.delegate()) { } + void operator=(const Notification& notification) { + origin_url_ = notification.origin_url(); + content_url_ = notification.content_url(); + display_source_ = notification.display_source(); + replace_id_ = notification.replace_id(); + delegate_ = notification.delegate(); + } + // The URL (may be data:) containing the contents for the notification. const GURL& content_url() const { return content_url_; } @@ -41,6 +53,8 @@ class Notification { // A display string for the source of the notification. const std::wstring& display_source() const { return display_source_; } + const string16& replace_id() const { return replace_id_; } + void Display() const { delegate()->Display(); } void Error() const { delegate()->Error(); } void Close(bool by_user) const { delegate()->Close(by_user); } @@ -63,12 +77,12 @@ class Notification { // the same as origin_url_, or the name of an extension. std::wstring display_source_; + // The replace ID for the notification. + string16 replace_id_; + // A proxy object that allows access back to the JavaScript object that // represents the notification, for firing events. scoped_refptr<NotificationDelegate> delegate_; - - // Disallow assign. Copy constructor written above. - void operator=(const Notification&); }; #endif // CHROME_BROWSER_NOTIFICATIONS_NOTIFICATION_H_ diff --git a/chrome/browser/notifications/notification_ui_manager.cc b/chrome/browser/notifications/notification_ui_manager.cc index e5ae3bd..42b9c5e 100644 --- a/chrome/browser/notifications/notification_ui_manager.cc +++ b/chrome/browser/notifications/notification_ui_manager.cc @@ -22,6 +22,10 @@ class QueuedNotification { const Notification& notification() const { return notification_; } Profile* profile() const { return profile_; } + void Replace(const Notification& new_notification) { + notification_ = new_notification; + } + private: // The notification to be shown. Notification notification_; @@ -51,6 +55,10 @@ NotificationUIManager* NotificationUIManager::Create() { void NotificationUIManager::Add(const Notification& notification, Profile* profile) { + if (TryReplacement(notification)) { + return; + } + LOG(INFO) << "Added notification. URL: " << notification.content_url().spec().c_str(); show_queue_.push_back( @@ -88,3 +96,37 @@ void NotificationUIManager::ShowNotifications() { void NotificationUIManager::OnBalloonSpaceChanged() { CheckAndShowNotifications(); } + +bool NotificationUIManager::TryReplacement(const Notification& notification) { + const GURL& origin = notification.origin_url(); + const string16& replace_id = notification.replace_id(); + + if (replace_id.empty()) + return false; + + // First check the queue of pending notifications for replacement. + // Then check the list of notifications already being shown. + NotificationDeque::iterator iter; + for (iter = show_queue_.begin(); iter != show_queue_.end(); ++iter) { + if (origin == (*iter)->notification().origin_url() && + replace_id == (*iter)->notification().replace_id()) { + (*iter)->Replace(notification); + return true; + } + } + + BalloonCollection::Balloons::iterator balloon_iter; + BalloonCollection::Balloons balloons = + balloon_collection_->GetActiveBalloons(); + for (balloon_iter = balloons.begin(); + balloon_iter != balloons.end(); + ++balloon_iter) { + if (origin == (*balloon_iter)->notification().origin_url() && + replace_id == (*balloon_iter)->notification().replace_id()) { + (*balloon_iter)->Update(notification); + return true; + } + } + + return false; +} diff --git a/chrome/browser/notifications/notification_ui_manager.h b/chrome/browser/notifications/notification_ui_manager.h index 2eaedd3..e510e7f 100644 --- a/chrome/browser/notifications/notification_ui_manager.h +++ b/chrome/browser/notifications/notification_ui_manager.h @@ -61,6 +61,10 @@ class NotificationUIManager // BalloonCollectionObserver implementation. virtual void OnBalloonSpaceChanged(); + // Replace an existing notification with this one if applicable; + // returns true if the replacement happened. + bool TryReplacement(const Notification& notification); + // An owned pointer to the collection of active balloons. scoped_ptr<BalloonCollection> balloon_collection_; |