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