Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / notifications / desktop_notification_service.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/desktop_notification_service.h"
6
7 #include "base/bind.h"
8 #include "base/metrics/histogram.h"
9 #include "base/prefs/scoped_user_pref_update.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "chrome/browser/browser_process.h"
12 #include "chrome/browser/notifications/desktop_notification_profile_util.h"
13 #include "chrome/browser/notifications/notification.h"
14 #include "chrome/browser/notifications/notification_object_proxy.h"
15 #include "chrome/browser/notifications/notification_ui_manager.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/browser/ui/browser.h"
18 #include "chrome/common/pref_names.h"
19 #include "components/content_settings/core/common/permission_request_id.h"
20 #include "components/pref_registry/pref_registry_syncable.h"
21 #include "content/public/browser/browser_thread.h"
22 #include "content/public/browser/desktop_notification_delegate.h"
23 #include "content/public/common/show_desktop_notification_params.h"
24 #include "ui/base/webui/web_ui_util.h"
25 #include "ui/message_center/notifier_settings.h"
26
27 #if defined(ENABLE_EXTENSIONS)
28 #include "chrome/browser/extensions/api/notifications/notifications_api.h"
29 #include "chrome/browser/extensions/extension_service.h"
30 #include "extensions/browser/event_router.h"
31 #include "extensions/browser/extension_registry.h"
32 #include "extensions/browser/extension_system.h"
33 #include "extensions/browser/extension_util.h"
34 #include "extensions/browser/info_map.h"
35 #include "extensions/browser/suggest_permission_util.h"
36 #include "extensions/common/constants.h"
37 #include "extensions/common/extension.h"
38 #include "extensions/common/extension_set.h"
39 #endif
40
41 using content::BrowserThread;
42 using message_center::NotifierId;
43
44 namespace {
45
46 void CancelNotification(const std::string& id, ProfileID profile_id) {
47   g_browser_process->notification_ui_manager()->CancelById(id, profile_id);
48 }
49
50 }  // namespace
51
52 // DesktopNotificationService -------------------------------------------------
53
54 // static
55 void DesktopNotificationService::RegisterProfilePrefs(
56     user_prefs::PrefRegistrySyncable* registry) {
57   registry->RegisterListPref(
58       prefs::kMessageCenterDisabledExtensionIds,
59       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
60   registry->RegisterListPref(
61       prefs::kMessageCenterDisabledSystemComponentIds,
62       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
63 }
64
65 // static
66 std::string DesktopNotificationService::AddIconNotification(
67     const GURL& origin_url,
68     const base::string16& title,
69     const base::string16& message,
70     const gfx::Image& icon,
71     const base::string16& replace_id,
72     NotificationDelegate* delegate,
73     Profile* profile) {
74   Notification notification(message_center::NOTIFICATION_TYPE_SIMPLE,
75                             origin_url,
76                             title,
77                             message,
78                             icon,
79                             blink::WebTextDirectionDefault,
80                             NotifierId(origin_url),
81                             base::string16(),
82                             replace_id,
83                             message_center::RichNotificationData(),
84                             delegate);
85   g_browser_process->notification_ui_manager()->Add(notification, profile);
86   return notification.delegate_id();
87 }
88
89 DesktopNotificationService::DesktopNotificationService(Profile* profile)
90     : PermissionContextBase(profile, CONTENT_SETTINGS_TYPE_NOTIFICATIONS),
91       profile_(profile)
92 #if defined(ENABLE_EXTENSIONS)
93       ,
94       extension_registry_observer_(this)
95 #endif
96 {
97   OnStringListPrefChanged(
98       prefs::kMessageCenterDisabledExtensionIds, &disabled_extension_ids_);
99   OnStringListPrefChanged(
100       prefs::kMessageCenterDisabledSystemComponentIds,
101       &disabled_system_component_ids_);
102   disabled_extension_id_pref_.Init(
103       prefs::kMessageCenterDisabledExtensionIds,
104       profile_->GetPrefs(),
105       base::Bind(
106           &DesktopNotificationService::OnStringListPrefChanged,
107           base::Unretained(this),
108           base::Unretained(prefs::kMessageCenterDisabledExtensionIds),
109           base::Unretained(&disabled_extension_ids_)));
110   disabled_system_component_id_pref_.Init(
111       prefs::kMessageCenterDisabledSystemComponentIds,
112       profile_->GetPrefs(),
113       base::Bind(
114           &DesktopNotificationService::OnStringListPrefChanged,
115           base::Unretained(this),
116           base::Unretained(prefs::kMessageCenterDisabledSystemComponentIds),
117           base::Unretained(&disabled_system_component_ids_)));
118 #if defined(ENABLE_EXTENSIONS)
119   extension_registry_observer_.Add(
120       extensions::ExtensionRegistry::Get(profile_));
121 #endif
122 }
123
124 DesktopNotificationService::~DesktopNotificationService() {
125 }
126
127 void DesktopNotificationService::RequestNotificationPermission(
128     content::WebContents* web_contents,
129     const PermissionRequestID& request_id,
130     const GURL& requesting_origin,
131     bool user_gesture,
132     const base::Callback<void(bool)>& result_callback) {
133   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
134
135 #if defined(ENABLE_EXTENSIONS)
136   extensions::InfoMap* extension_info_map =
137       extensions::ExtensionSystem::Get(profile_)->info_map();
138   const extensions::Extension* extension = NULL;
139   if (extension_info_map) {
140     extensions::ExtensionSet extensions;
141     extension_info_map->GetExtensionsWithAPIPermissionForSecurityOrigin(
142         requesting_origin,
143         request_id.render_process_id(),
144         extensions::APIPermission::kNotifications,
145         &extensions);
146     for (extensions::ExtensionSet::const_iterator iter = extensions.begin();
147          iter != extensions.end(); ++iter) {
148       if (IsNotifierEnabled(NotifierId(
149               NotifierId::APPLICATION, (*iter)->id()))) {
150         extension = iter->get();
151         break;
152       }
153     }
154   }
155   if (IsExtensionWithPermissionOrSuggestInConsole(
156           extensions::APIPermission::kNotifications,
157           extension,
158           web_contents->GetRenderViewHost())) {
159     result_callback.Run(true);
160     return;
161   }
162 #endif
163
164   RequestPermission(web_contents,
165                     request_id,
166                     requesting_origin,
167                     user_gesture,
168                     result_callback);
169 }
170
171 void DesktopNotificationService::ShowDesktopNotification(
172     const content::ShowDesktopNotificationHostMsgParams& params,
173     int render_process_id,
174     scoped_ptr<content::DesktopNotificationDelegate> delegate,
175     base::Closure* cancel_callback) {
176   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
177   const GURL& origin = params.origin;
178   NotificationObjectProxy* proxy = new NotificationObjectProxy(delegate.Pass());
179
180   base::string16 display_source = DisplayNameForOriginInProcessId(
181       origin, render_process_id);
182
183   // TODO(peter): Icons for Web Notifications are currently always requested for
184   // 1x scale, whereas the displays on which they can be displayed can have a
185   // different pixel density. Be smarter about this when the API gets updated
186   // with a way for developers to specify images of different resolutions.
187   Notification notification(origin, params.title, params.body,
188       gfx::Image::CreateFrom1xBitmap(params.icon),
189       display_source, params.replace_id, proxy);
190
191   // The webkit notification doesn't timeout.
192   notification.set_never_timeout(true);
193
194   g_browser_process->notification_ui_manager()->Add(notification, profile_);
195   if (cancel_callback)
196     *cancel_callback =
197         base::Bind(&CancelNotification,
198                    proxy->id(),
199                    NotificationUIManager::GetProfileID(profile_));
200
201   DesktopNotificationProfileUtil::UsePermission(profile_, origin);
202 }
203
204 base::string16 DesktopNotificationService::DisplayNameForOriginInProcessId(
205     const GURL& origin, int process_id) {
206 #if defined(ENABLE_EXTENSIONS)
207   // If the source is an extension, lookup the display name.
208   if (origin.SchemeIs(extensions::kExtensionScheme)) {
209     extensions::InfoMap* extension_info_map =
210         extensions::ExtensionSystem::Get(profile_)->info_map();
211     if (extension_info_map) {
212       extensions::ExtensionSet extensions;
213       extension_info_map->GetExtensionsWithAPIPermissionForSecurityOrigin(
214           origin,
215           process_id,
216           extensions::APIPermission::kNotifications,
217           &extensions);
218       for (extensions::ExtensionSet::const_iterator iter = extensions.begin();
219            iter != extensions.end(); ++iter) {
220         NotifierId notifier_id(NotifierId::APPLICATION, (*iter)->id());
221         if (IsNotifierEnabled(notifier_id))
222           return base::UTF8ToUTF16((*iter)->name());
223       }
224     }
225   }
226 #endif
227
228   return base::UTF8ToUTF16(origin.host());
229 }
230
231 bool DesktopNotificationService::IsNotifierEnabled(
232     const NotifierId& notifier_id) {
233   switch (notifier_id.type) {
234     case NotifierId::APPLICATION:
235       return disabled_extension_ids_.find(notifier_id.id) ==
236           disabled_extension_ids_.end();
237     case NotifierId::WEB_PAGE:
238       return DesktopNotificationProfileUtil::GetContentSetting(
239           profile_, notifier_id.url) == CONTENT_SETTING_ALLOW;
240     case NotifierId::SYSTEM_COMPONENT:
241 #if defined(OS_CHROMEOS)
242       return disabled_system_component_ids_.find(notifier_id.id) ==
243           disabled_system_component_ids_.end();
244 #else
245       // We do not disable system component notifications.
246       return true;
247 #endif
248   }
249
250   NOTREACHED();
251   return false;
252 }
253
254 void DesktopNotificationService::SetNotifierEnabled(
255     const NotifierId& notifier_id,
256     bool enabled) {
257   DCHECK_NE(NotifierId::WEB_PAGE, notifier_id.type);
258
259   bool add_new_item = false;
260   const char* pref_name = NULL;
261   scoped_ptr<base::StringValue> id;
262   switch (notifier_id.type) {
263     case NotifierId::APPLICATION:
264       pref_name = prefs::kMessageCenterDisabledExtensionIds;
265       add_new_item = !enabled;
266       id.reset(new base::StringValue(notifier_id.id));
267       FirePermissionLevelChangedEvent(notifier_id, enabled);
268       break;
269     case NotifierId::SYSTEM_COMPONENT:
270 #if defined(OS_CHROMEOS)
271       pref_name = prefs::kMessageCenterDisabledSystemComponentIds;
272       add_new_item = !enabled;
273       id.reset(new base::StringValue(notifier_id.id));
274 #else
275       return;
276 #endif
277       break;
278     default:
279       NOTREACHED();
280   }
281   DCHECK(pref_name != NULL);
282
283   ListPrefUpdate update(profile_->GetPrefs(), pref_name);
284   base::ListValue* const list = update.Get();
285   if (add_new_item) {
286     // AppendIfNotPresent will delete |adding_value| when the same value
287     // already exists.
288     list->AppendIfNotPresent(id.release());
289   } else {
290     list->Remove(*id, NULL);
291   }
292 }
293
294 void DesktopNotificationService::OnStringListPrefChanged(
295     const char* pref_name, std::set<std::string>* ids_field) {
296   ids_field->clear();
297   // Separate GetPrefs()->GetList() to analyze the crash. See crbug.com/322320
298   const PrefService* pref_service = profile_->GetPrefs();
299   CHECK(pref_service);
300   const base::ListValue* pref_list = pref_service->GetList(pref_name);
301   for (size_t i = 0; i < pref_list->GetSize(); ++i) {
302     std::string element;
303     if (pref_list->GetString(i, &element) && !element.empty())
304       ids_field->insert(element);
305     else
306       LOG(WARNING) << i << "-th element is not a string for " << pref_name;
307   }
308 }
309
310 #if defined(ENABLE_EXTENSIONS)
311 void DesktopNotificationService::OnExtensionUninstalled(
312     content::BrowserContext* browser_context,
313     const extensions::Extension* extension,
314     extensions::UninstallReason reason) {
315   NotifierId notifier_id(NotifierId::APPLICATION, extension->id());
316   if (IsNotifierEnabled(notifier_id))
317     return;
318
319   // The settings for ephemeral apps will be persisted across cache evictions.
320   if (extensions::util::IsEphemeralApp(extension->id(), profile_))
321     return;
322
323   SetNotifierEnabled(notifier_id, true);
324 }
325 #endif
326
327 // Unlike other permission types, granting a notification for a given origin
328 // will not take into account the |embedder_origin|, it will only be based
329 // on the requesting iframe origin.
330 // TODO(mukai) Consider why notifications behave differently than
331 // other permissions. crbug.com/416894
332 void DesktopNotificationService::UpdateContentSetting(
333     const GURL& requesting_origin,
334     const GURL& embedder_origin,
335     bool allowed) {
336   if (allowed) {
337     DesktopNotificationProfileUtil::GrantPermission(
338         profile_, requesting_origin);
339   } else {
340     DesktopNotificationProfileUtil::DenyPermission(profile_, requesting_origin);
341   }
342 }
343
344 void DesktopNotificationService::FirePermissionLevelChangedEvent(
345     const NotifierId& notifier_id, bool enabled) {
346 #if defined(ENABLE_EXTENSIONS)
347   DCHECK_EQ(NotifierId::APPLICATION, notifier_id.type);
348   extensions::api::notifications::PermissionLevel permission =
349       enabled ? extensions::api::notifications::PERMISSION_LEVEL_GRANTED
350               : extensions::api::notifications::PERMISSION_LEVEL_DENIED;
351   scoped_ptr<base::ListValue> args(new base::ListValue());
352   args->Append(new base::StringValue(
353       extensions::api::notifications::ToString(permission)));
354   scoped_ptr<extensions::Event> event(new extensions::Event(
355       extensions::api::notifications::OnPermissionLevelChanged::kEventName,
356       args.Pass()));
357   extensions::EventRouter::Get(profile_)
358       ->DispatchEventToExtension(notifier_id.id, event.Pass());
359
360   // Tell the IO thread that this extension's permission for notifications
361   // has changed.
362   extensions::InfoMap* extension_info_map =
363       extensions::ExtensionSystem::Get(profile_)->info_map();
364   BrowserThread::PostTask(
365       BrowserThread::IO, FROM_HERE,
366       base::Bind(&extensions::InfoMap::SetNotificationsDisabled,
367                  extension_info_map, notifier_id.id, !enabled));
368 #endif
369 }