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