Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / notifications / message_center_notification_manager.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/notifications/message_center_notification_manager.h"
6
7 #include "base/logging.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/prefs/pref_registry_simple.h"
10 #include "base/prefs/pref_service.h"
11 #include "base/stl_util.h"
12 #include "chrome/browser/extensions/api/notification_provider/notification_provider_api.h"
13 #include "chrome/browser/notifications/desktop_notification_service.h"
14 #include "chrome/browser/notifications/desktop_notification_service_factory.h"
15 #include "chrome/browser/notifications/extension_welcome_notification.h"
16 #include "chrome/browser/notifications/extension_welcome_notification_factory.h"
17 #include "chrome/browser/notifications/fullscreen_notification_blocker.h"
18 #include "chrome/browser/notifications/message_center_settings_controller.h"
19 #include "chrome/browser/notifications/notification.h"
20 #include "chrome/browser/notifications/notification_conversion_helper.h"
21 #include "chrome/browser/notifications/screen_lock_notification_blocker.h"
22 #include "chrome/browser/profiles/profile.h"
23 #include "chrome/common/extensions/api/notification_provider.h"
24 #include "chrome/common/pref_names.h"
25 #include "content/public/browser/web_contents.h"
26 #include "content/public/common/url_constants.h"
27 #include "extensions/browser/extension_registry.h"
28 #include "extensions/common/extension_set.h"
29 #include "extensions/common/permissions/permissions_data.h"
30 #include "ui/gfx/image/image_skia.h"
31 #include "ui/message_center/message_center_style.h"
32 #include "ui/message_center/message_center_tray.h"
33 #include "ui/message_center/message_center_types.h"
34 #include "ui/message_center/notifier_settings.h"
35
36 #if defined(OS_CHROMEOS)
37 #include "chrome/browser/notifications/login_state_notification_blocker_chromeos.h"
38 #include "chrome/browser/ui/ash/multi_user/multi_user_util.h"
39 #endif
40
41 #if defined(USE_ASH)
42 #include "ash/shell.h"
43 #include "ash/system/web_notification/web_notification_tray.h"
44 #endif
45
46 #if defined(OS_WIN)
47 // The first-run balloon will be shown |kFirstRunIdleDelaySeconds| after all
48 // popups go away and the user has notifications in the message center.
49 const int kFirstRunIdleDelaySeconds = 1;
50 #endif
51
52 MessageCenterNotificationManager::MessageCenterNotificationManager(
53     message_center::MessageCenter* message_center,
54     PrefService* local_state,
55     scoped_ptr<message_center::NotifierSettingsProvider> settings_provider)
56     : message_center_(message_center),
57 #if defined(OS_WIN)
58       first_run_idle_timeout_(
59           base::TimeDelta::FromSeconds(kFirstRunIdleDelaySeconds)),
60       weak_factory_(this),
61 #endif
62       settings_provider_(settings_provider.Pass()),
63       system_observer_(this),
64       stats_collector_(message_center),
65       google_now_stats_collector_(message_center) {
66 #if defined(OS_WIN)
67   first_run_pref_.Init(prefs::kMessageCenterShowedFirstRunBalloon, local_state);
68 #endif
69
70   message_center_->AddObserver(this);
71   message_center_->SetNotifierSettingsProvider(settings_provider_.get());
72
73 #if defined(OS_CHROMEOS)
74 #if !defined(USE_ATHENA)
75   // TODO(oshima|hashimoto): Support notification on athena. crbug.com/408755.
76   blockers_.push_back(
77       new LoginStateNotificationBlockerChromeOS(message_center));
78 #endif
79 #else
80   blockers_.push_back(new ScreenLockNotificationBlocker(message_center));
81 #endif
82   blockers_.push_back(new FullscreenNotificationBlocker(message_center));
83
84 #if defined(OS_WIN) || defined(OS_MACOSX) \
85   || (defined(OS_LINUX) && !defined(OS_CHROMEOS))
86   // On Windows, Linux and Mac, the notification manager owns the tray icon and
87   // views.Other platforms have global ownership and Create will return NULL.
88   tray_.reset(message_center::CreateMessageCenterTray());
89 #endif
90 }
91
92 MessageCenterNotificationManager::~MessageCenterNotificationManager() {
93   message_center_->SetNotifierSettingsProvider(NULL);
94   message_center_->RemoveObserver(this);
95
96   STLDeleteContainerPairSecondPointers(profile_notifications_.begin(),
97                                        profile_notifications_.end());
98   profile_notifications_.clear();
99 }
100
101 void MessageCenterNotificationManager::RegisterPrefs(
102     PrefRegistrySimple* registry) {
103   registry->RegisterBooleanPref(prefs::kMessageCenterShowedFirstRunBalloon,
104                                 false);
105   registry->RegisterBooleanPref(prefs::kMessageCenterShowIcon, true);
106   registry->RegisterBooleanPref(prefs::kMessageCenterForcedOnTaskbar, false);
107 }
108
109 ////////////////////////////////////////////////////////////////////////////////
110 // NotificationUIManager
111
112 void MessageCenterNotificationManager::Add(const Notification& notification,
113                                            Profile* profile) {
114   if (Update(notification, profile))
115     return;
116
117   ExtensionWelcomeNotificationFactory::GetForBrowserContext(profile)->
118       ShowWelcomeNotificationIfNecessary(notification);
119
120   // WARNING: You MUST update the message center via the notification within a
121   // ProfileNotification object or the profile ID will not be correctly set for
122   // ChromeOS.
123   ProfileNotification* profile_notification(
124       new ProfileNotification(profile, notification, message_center_));
125   AddProfileNotification(profile_notification);
126
127   // TODO(liyanhou): Change the logic to only send notifications to one party.
128   // Currently, if there is an app with notificationProvider permission,
129   // notifications will go to both message center and the app.
130   // Change it to send notifications to message center only when the user chose
131   // default message center (extension_id.empty()).
132
133   // If there exist apps/extensions that have notificationProvider permission,
134   // route notifications to one of the apps/extensions.
135   std::string extension_id = GetExtensionTakingOverNotifications(profile);
136   if (!extension_id.empty())
137     profile_notification->AddToAlternateProvider(extension_id);
138
139   message_center_->AddNotification(make_scoped_ptr(
140       new message_center::Notification(profile_notification->notification())));
141   profile_notification->StartDownloads();
142 }
143
144 bool MessageCenterNotificationManager::Update(const Notification& notification,
145                                               Profile* profile) {
146   const base::string16& replace_id = notification.replace_id();
147   if (replace_id.empty())
148     return false;
149
150   const GURL origin_url = notification.origin_url();
151   DCHECK(origin_url.is_valid());
152
153   // Since replace_id is provided by arbitrary JS, we need to use origin_url
154   // (which is an app url in case of app/extension) to scope the replace ids
155   // in the given profile.
156   for (NotificationMap::iterator iter = profile_notifications_.begin();
157        iter != profile_notifications_.end(); ++iter) {
158     ProfileNotification* old_notification = (*iter).second;
159     if (old_notification->notification().replace_id() == replace_id &&
160         old_notification->notification().origin_url() == origin_url &&
161         old_notification->profile() == profile) {
162       // Changing the type from non-progress to progress does not count towards
163       // the immediate update allowed in the message center.
164       std::string old_id =
165           old_notification->notification().delegate_id();
166
167       // Add/remove notification in the local list but just update the same
168       // one in MessageCenter.
169       delete old_notification;
170       profile_notifications_.erase(old_id);
171       ProfileNotification* new_notification =
172           new ProfileNotification(profile, notification, message_center_);
173       profile_notifications_[notification.delegate_id()] = new_notification;
174
175       // TODO(liyanhou): Add routing updated notifications to alternative
176       // providers.
177
178       // WARNING: You MUST use AddProfileNotification or update the message
179       // center via the notification within a ProfileNotification object or the
180       // profile ID will not be correctly set for ChromeOS.
181       message_center_->UpdateNotification(
182           old_id,
183           make_scoped_ptr(new message_center::Notification(
184               new_notification->notification())));
185
186       new_notification->StartDownloads();
187       return true;
188     }
189   }
190   return false;
191 }
192
193 const Notification* MessageCenterNotificationManager::FindById(
194     const std::string& id) const {
195   NotificationMap::const_iterator iter = profile_notifications_.find(id);
196   if (iter == profile_notifications_.end())
197     return NULL;
198   return &(iter->second->notification());
199 }
200
201 bool MessageCenterNotificationManager::CancelById(const std::string& id) {
202   // See if this ID hasn't been shown yet.
203   // If it has been shown, remove it.
204   NotificationMap::iterator iter = profile_notifications_.find(id);
205   if (iter == profile_notifications_.end())
206     return false;
207
208   RemoveProfileNotification(iter->second);
209   message_center_->RemoveNotification(id, /* by_user */ false);
210   return true;
211 }
212
213 std::set<std::string>
214 MessageCenterNotificationManager::GetAllIdsByProfileAndSourceOrigin(
215     Profile* profile,
216     const GURL& source) {
217
218   std::set<std::string> notification_ids;
219   for (NotificationMap::iterator iter = profile_notifications_.begin();
220        iter != profile_notifications_.end(); iter++) {
221     if ((*iter).second->notification().origin_url() == source &&
222         profile == (*iter).second->profile()) {
223       notification_ids.insert(iter->first);
224     }
225   }
226   return notification_ids;
227 }
228
229 bool MessageCenterNotificationManager::CancelAllBySourceOrigin(
230     const GURL& source) {
231   // Same pattern as CancelById, but more complicated than the above
232   // because there may be multiple notifications from the same source.
233   bool removed = false;
234
235   for (NotificationMap::iterator loopiter = profile_notifications_.begin();
236        loopiter != profile_notifications_.end(); ) {
237     NotificationMap::iterator curiter = loopiter++;
238     if ((*curiter).second->notification().origin_url() == source) {
239       const std::string id = curiter->first;
240       RemoveProfileNotification(curiter->second);
241       message_center_->RemoveNotification(id, /* by_user */ false);
242       removed = true;
243     }
244   }
245   return removed;
246 }
247
248 bool MessageCenterNotificationManager::CancelAllByProfile(Profile* profile) {
249   // Same pattern as CancelAllBySourceOrigin.
250   bool removed = false;
251
252   for (NotificationMap::iterator loopiter = profile_notifications_.begin();
253        loopiter != profile_notifications_.end(); ) {
254     NotificationMap::iterator curiter = loopiter++;
255     if (profile == (*curiter).second->profile()) {
256       const std::string id = curiter->first;
257       RemoveProfileNotification(curiter->second);
258       message_center_->RemoveNotification(id, /* by_user */ false);
259       removed = true;
260     }
261   }
262   return removed;
263 }
264
265 void MessageCenterNotificationManager::CancelAll() {
266   message_center_->RemoveAllNotifications(/* by_user */ false);
267 }
268
269 ////////////////////////////////////////////////////////////////////////////////
270 // MessageCenter::Observer
271 void MessageCenterNotificationManager::OnNotificationRemoved(
272     const std::string& notification_id,
273     bool by_user) {
274   NotificationMap::const_iterator iter =
275       profile_notifications_.find(notification_id);
276   if (iter != profile_notifications_.end())
277     RemoveProfileNotification(iter->second);
278
279 #if defined(OS_WIN)
280   CheckFirstRunTimer();
281 #endif
282 }
283
284 void MessageCenterNotificationManager::OnCenterVisibilityChanged(
285     message_center::Visibility visibility) {
286 #if defined(OS_WIN)
287   if (visibility == message_center::VISIBILITY_TRANSIENT)
288     CheckFirstRunTimer();
289 #endif
290 }
291
292 void MessageCenterNotificationManager::OnNotificationUpdated(
293     const std::string& notification_id) {
294 #if defined(OS_WIN)
295   CheckFirstRunTimer();
296 #endif
297 }
298
299 void MessageCenterNotificationManager::EnsureMessageCenterClosed() {
300   if (tray_.get())
301     tray_->GetMessageCenterTray()->HideMessageCenterBubble();
302
303 #if defined(USE_ASH)
304   if (ash::Shell::HasInstance()) {
305     ash::WebNotificationTray* tray =
306         ash::Shell::GetInstance()->GetWebNotificationTray();
307     if (tray)
308       tray->GetMessageCenterTray()->HideMessageCenterBubble();
309   }
310 #endif
311 }
312
313 void MessageCenterNotificationManager::SetMessageCenterTrayDelegateForTest(
314     message_center::MessageCenterTrayDelegate* delegate) {
315   tray_.reset(delegate);
316 }
317
318 ////////////////////////////////////////////////////////////////////////////////
319 // ImageDownloads
320
321 MessageCenterNotificationManager::ImageDownloads::ImageDownloads(
322     message_center::MessageCenter* message_center,
323     ImageDownloadsObserver* observer)
324     : message_center_(message_center),
325       pending_downloads_(0),
326       observer_(observer) {
327 }
328
329 MessageCenterNotificationManager::ImageDownloads::~ImageDownloads() { }
330
331 void MessageCenterNotificationManager::ImageDownloads::StartDownloads(
332     const Notification& notification) {
333   // In case all downloads are synchronous, assume a pending download.
334   AddPendingDownload();
335
336   // Notification primary icon.
337   StartDownloadWithImage(
338       notification,
339       &notification.icon(),
340       notification.icon_url(),
341       base::Bind(&message_center::MessageCenter::SetNotificationIcon,
342                  base::Unretained(message_center_),
343                  notification.delegate_id()));
344
345   // Notification image.
346   StartDownloadWithImage(
347       notification,
348       NULL,
349       notification.image_url(),
350       base::Bind(&message_center::MessageCenter::SetNotificationImage,
351                  base::Unretained(message_center_),
352                  notification.delegate_id()));
353
354   // Notification button icons.
355   StartDownloadWithImage(
356       notification,
357       NULL,
358       notification.button_one_icon_url(),
359       base::Bind(&message_center::MessageCenter::SetNotificationButtonIcon,
360                  base::Unretained(message_center_),
361                  notification.delegate_id(),
362                  0));
363   StartDownloadWithImage(
364       notification,
365       NULL,
366       notification.button_two_icon_url(),
367       base::Bind(&message_center::MessageCenter::SetNotificationButtonIcon,
368                  base::Unretained(message_center_),
369                  notification.delegate_id(),
370                  1));
371
372   // This should tell the observer we're done if everything was synchronous.
373   PendingDownloadCompleted();
374 }
375
376 void MessageCenterNotificationManager::ImageDownloads::StartDownloadWithImage(
377     const Notification& notification,
378     const gfx::Image* image,
379     const GURL& url,
380     const SetImageCallback& callback) {
381   // Set the image directly if we have it.
382   if (image && !image->IsEmpty()) {
383     callback.Run(*image);
384     return;
385   }
386
387   // Leave the image null if there's no URL.
388   if (url.is_empty())
389     return;
390
391   content::WebContents* contents = notification.delegate()->GetWebContents();
392   if (!contents) {
393     LOG(WARNING) << "Notification needs an image but has no WebContents";
394     return;
395   }
396
397   AddPendingDownload();
398
399   contents->DownloadImage(
400       url,
401       false,  // Not a favicon
402       0,  // No maximum size
403       base::Bind(
404           &MessageCenterNotificationManager::ImageDownloads::DownloadComplete,
405           AsWeakPtr(),
406           callback));
407 }
408
409 void MessageCenterNotificationManager::ImageDownloads::DownloadComplete(
410     const SetImageCallback& callback,
411     int download_id,
412     int http_status_code,
413     const GURL& image_url,
414     const std::vector<SkBitmap>& bitmaps,
415     const std::vector<gfx::Size>& original_bitmap_sizes) {
416   PendingDownloadCompleted();
417
418   if (bitmaps.empty())
419     return;
420   gfx::Image image = gfx::Image::CreateFrom1xBitmap(bitmaps[0]);
421   callback.Run(image);
422 }
423
424 // Private methods.
425
426 void MessageCenterNotificationManager::ImageDownloads::AddPendingDownload() {
427   ++pending_downloads_;
428 }
429
430 void
431 MessageCenterNotificationManager::ImageDownloads::PendingDownloadCompleted() {
432   DCHECK(pending_downloads_ > 0);
433   if (--pending_downloads_ == 0 && observer_)
434     observer_->OnDownloadsCompleted();
435 }
436
437 ////////////////////////////////////////////////////////////////////////////////
438 // ProfileNotification
439
440 MessageCenterNotificationManager::ProfileNotification::ProfileNotification(
441     Profile* profile,
442     const Notification& notification,
443     message_center::MessageCenter* message_center)
444     : profile_(profile),
445       notification_(notification),
446       downloads_(new ImageDownloads(message_center, this)) {
447   DCHECK(profile);
448 #if defined(OS_CHROMEOS)
449   notification_.set_profile_id(multi_user_util::GetUserIDFromProfile(profile));
450 #endif
451 }
452
453 MessageCenterNotificationManager::ProfileNotification::~ProfileNotification() {
454 }
455
456 void MessageCenterNotificationManager::ProfileNotification::StartDownloads() {
457   downloads_->StartDownloads(notification_);
458 }
459
460 void
461 MessageCenterNotificationManager::ProfileNotification::OnDownloadsCompleted() {
462   notification_.delegate()->ReleaseRenderViewHost();
463 }
464
465 void
466 MessageCenterNotificationManager::ProfileNotification::AddToAlternateProvider(
467     const std::string extension_id) {
468   // Convert data from Notification type to NotificationOptions type.
469   extensions::api::notifications::NotificationOptions options;
470   NotificationConversionHelper::NotificationToNotificationOptions(notification_,
471                                                                   &options);
472
473   // Send the notification to the alternate provider extension/app.
474   extensions::NotificationProviderEventRouter event_router(profile_);
475   event_router.CreateNotification(extension_id,
476                                   notification_.notifier_id().id,
477                                   notification_.delegate_id(),
478                                   options);
479 }
480
481 ////////////////////////////////////////////////////////////////////////////////
482 // private
483
484 void MessageCenterNotificationManager::AddProfileNotification(
485     ProfileNotification* profile_notification) {
486   std::string id = profile_notification->notification().delegate_id();
487   // Notification ids should be unique.
488   DCHECK(profile_notifications_.find(id) == profile_notifications_.end());
489   profile_notifications_[id] = profile_notification;
490 }
491
492 void MessageCenterNotificationManager::RemoveProfileNotification(
493     ProfileNotification* profile_notification) {
494   std::string id = profile_notification->notification().delegate_id();
495   profile_notifications_.erase(id);
496   delete profile_notification;
497 }
498
499 MessageCenterNotificationManager::ProfileNotification*
500     MessageCenterNotificationManager::FindProfileNotification(
501         const std::string& id) const {
502   NotificationMap::const_iterator iter = profile_notifications_.find(id);
503   if (iter == profile_notifications_.end())
504     return NULL;
505
506   return (*iter).second;
507 }
508
509 std::string
510 MessageCenterNotificationManager::GetExtensionTakingOverNotifications(
511     Profile* profile) {
512   // TODO(liyanhou): When additional settings in Chrome Settings is implemented,
513   // change choosing the last app with permission to a user selected app.
514   extensions::ExtensionRegistry* registry =
515       extensions::ExtensionRegistry::Get(profile);
516   DCHECK(registry);
517   std::string extension_id;
518   for (extensions::ExtensionSet::const_iterator it =
519            registry->enabled_extensions().begin();
520        it != registry->enabled_extensions().end();
521        ++it) {
522     if ((*it->get()).permissions_data()->HasAPIPermission(
523             extensions::APIPermission::ID::kNotificationProvider)) {
524       extension_id = (*it->get()).id();
525       return extension_id;
526     }
527   }
528   return extension_id;
529 }