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