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