Update mojo surfaces bindings and mojo/cc/ glue
[chromium-blink-merge.git] / chrome / browser / notifications / desktop_notification_service.cc
bloba3d68dbcaf84cb7ed256a9ad954b3bdae6f39794
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.
5 #include "chrome/browser/notifications/desktop_notification_service.h"
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 "base/threading/thread.h"
12 #include "chrome/browser/browser_process.h"
13 #include "chrome/browser/chrome_notification_types.h"
14 #include "chrome/browser/content_settings/content_settings_details.h"
15 #include "chrome/browser/content_settings/content_settings_provider.h"
16 #include "chrome/browser/notifications/desktop_notification_profile_util.h"
17 #include "chrome/browser/notifications/desktop_notification_service_factory.h"
18 #include "chrome/browser/notifications/notification.h"
19 #include "chrome/browser/notifications/notification_object_proxy.h"
20 #include "chrome/browser/notifications/notification_ui_manager.h"
21 #include "chrome/browser/notifications/sync_notifier/chrome_notifier_service.h"
22 #include "chrome/browser/notifications/sync_notifier/chrome_notifier_service_factory.h"
23 #include "chrome/browser/profiles/profile.h"
24 #include "chrome/browser/ui/browser.h"
25 #include "chrome/common/pref_names.h"
26 #include "chrome/common/url_constants.h"
27 #include "components/pref_registry/pref_registry_syncable.h"
28 #include "content/public/browser/browser_thread.h"
29 #include "content/public/browser/desktop_notification_delegate.h"
30 #include "content/public/browser/notification_service.h"
31 #include "content/public/browser/render_frame_host.h"
32 #include "content/public/browser/render_process_host.h"
33 #include "content/public/browser/render_view_host.h"
34 #include "content/public/browser/web_contents.h"
35 #include "content/public/common/show_desktop_notification_params.h"
36 #include "grit/browser_resources.h"
37 #include "grit/theme_resources.h"
38 #include "net/base/escape.h"
39 #include "ui/base/resource/resource_bundle.h"
40 #include "ui/base/webui/web_ui_util.h"
41 #include "ui/message_center/notifier_settings.h"
43 #if defined(ENABLE_EXTENSIONS)
44 #include "chrome/browser/extensions/api/notifications/notifications_api.h"
45 #include "chrome/browser/extensions/extension_service.h"
46 #include "extensions/browser/event_router.h"
47 #include "extensions/browser/extension_registry.h"
48 #include "extensions/browser/extension_system.h"
49 #include "extensions/browser/extension_util.h"
50 #include "extensions/browser/info_map.h"
51 #include "extensions/common/constants.h"
52 #include "extensions/common/extension.h"
53 #include "extensions/common/extension_set.h"
54 #endif
56 using blink::WebTextDirection;
57 using content::BrowserThread;
58 using content::RenderViewHost;
59 using content::WebContents;
60 using message_center::NotifierId;
62 namespace {
64 const char kChromeNowExtensionID[] = "pafkbggdmjlpgkdkcbjmhmfcdpncadgh";
66 void CancelNotification(const std::string& id) {
67 g_browser_process->notification_ui_manager()->CancelById(id);
70 } // namespace
73 // DesktopNotificationService -------------------------------------------------
75 // static
76 void DesktopNotificationService::RegisterProfilePrefs(
77 user_prefs::PrefRegistrySyncable* registry) {
78 registry->RegisterListPref(
79 prefs::kMessageCenterDisabledExtensionIds,
80 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
81 registry->RegisterListPref(
82 prefs::kMessageCenterDisabledSystemComponentIds,
83 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
84 ExtensionWelcomeNotification::RegisterProfilePrefs(registry);
87 // static
88 base::string16 DesktopNotificationService::CreateDataUrl(
89 const GURL& icon_url,
90 const base::string16& title,
91 const base::string16& body,
92 WebTextDirection dir) {
93 int resource;
94 std::vector<std::string> subst;
95 if (icon_url.is_valid()) {
96 resource = IDR_NOTIFICATION_ICON_HTML;
97 subst.push_back(icon_url.spec());
98 subst.push_back(net::EscapeForHTML(base::UTF16ToUTF8(title)));
99 subst.push_back(net::EscapeForHTML(base::UTF16ToUTF8(body)));
100 // icon float position
101 subst.push_back(dir == blink::WebTextDirectionRightToLeft ?
102 "right" : "left");
103 } else if (title.empty() || body.empty()) {
104 resource = IDR_NOTIFICATION_1LINE_HTML;
105 base::string16 line = title.empty() ? body : title;
106 // Strings are div names in the template file.
107 base::string16 line_name =
108 title.empty() ? base::ASCIIToUTF16("description")
109 : base::ASCIIToUTF16("title");
110 subst.push_back(net::EscapeForHTML(base::UTF16ToUTF8(line_name)));
111 subst.push_back(net::EscapeForHTML(base::UTF16ToUTF8(line)));
112 } else {
113 resource = IDR_NOTIFICATION_2LINE_HTML;
114 subst.push_back(net::EscapeForHTML(base::UTF16ToUTF8(title)));
115 subst.push_back(net::EscapeForHTML(base::UTF16ToUTF8(body)));
117 // body text direction
118 subst.push_back(dir == blink::WebTextDirectionRightToLeft ?
119 "rtl" : "ltr");
121 return CreateDataUrl(resource, subst);
124 // static
125 base::string16 DesktopNotificationService::CreateDataUrl(
126 int resource, const std::vector<std::string>& subst) {
127 const base::StringPiece template_html(
128 ResourceBundle::GetSharedInstance().GetRawDataResource(
129 resource));
131 if (template_html.empty()) {
132 NOTREACHED() << "unable to load template. ID: " << resource;
133 return base::string16();
136 std::string data = ReplaceStringPlaceholders(template_html, subst, NULL);
137 return base::UTF8ToUTF16("data:text/html;charset=utf-8," +
138 net::EscapeQueryParamValue(data, false));
141 // static
142 std::string DesktopNotificationService::AddIconNotification(
143 const GURL& origin_url,
144 const base::string16& title,
145 const base::string16& message,
146 const gfx::Image& icon,
147 const base::string16& replace_id,
148 NotificationDelegate* delegate,
149 Profile* profile) {
150 Notification notification(origin_url, icon, title, message,
151 blink::WebTextDirectionDefault,
152 base::string16(), replace_id, delegate);
153 g_browser_process->notification_ui_manager()->Add(notification, profile);
154 return notification.delegate_id();
157 DesktopNotificationService::DesktopNotificationService(
158 Profile* profile,
159 NotificationUIManager* ui_manager)
160 : PermissionContextBase(profile, CONTENT_SETTINGS_TYPE_NOTIFICATIONS),
161 profile_(profile),
162 ui_manager_(ui_manager),
163 extension_registry_observer_(this),
164 weak_factory_(this) {
165 OnStringListPrefChanged(
166 prefs::kMessageCenterDisabledExtensionIds, &disabled_extension_ids_);
167 OnStringListPrefChanged(
168 prefs::kMessageCenterDisabledSystemComponentIds,
169 &disabled_system_component_ids_);
170 disabled_extension_id_pref_.Init(
171 prefs::kMessageCenterDisabledExtensionIds,
172 profile_->GetPrefs(),
173 base::Bind(
174 &DesktopNotificationService::OnStringListPrefChanged,
175 base::Unretained(this),
176 base::Unretained(prefs::kMessageCenterDisabledExtensionIds),
177 base::Unretained(&disabled_extension_ids_)));
178 disabled_system_component_id_pref_.Init(
179 prefs::kMessageCenterDisabledSystemComponentIds,
180 profile_->GetPrefs(),
181 base::Bind(
182 &DesktopNotificationService::OnStringListPrefChanged,
183 base::Unretained(this),
184 base::Unretained(prefs::kMessageCenterDisabledSystemComponentIds),
185 base::Unretained(&disabled_system_component_ids_)));
186 extension_registry_observer_.Add(
187 extensions::ExtensionRegistry::Get(profile_));
190 DesktopNotificationService::~DesktopNotificationService() {
193 void DesktopNotificationService::RequestNotificationPermission(
194 content::WebContents* web_contents,
195 const PermissionRequestID& request_id,
196 const GURL& requesting_frame,
197 bool user_gesture,
198 const NotificationPermissionCallback& callback) {
199 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
200 RequestPermission(
201 web_contents,
202 request_id,
203 requesting_frame,
204 user_gesture,
205 base::Bind(&DesktopNotificationService::OnNotificationPermissionRequested,
206 weak_factory_.GetWeakPtr(),
207 callback));
210 void DesktopNotificationService::ShowDesktopNotification(
211 const content::ShowDesktopNotificationHostMsgParams& params,
212 content::RenderFrameHost* render_frame_host,
213 scoped_ptr<content::DesktopNotificationDelegate> delegate,
214 base::Closure* cancel_callback) {
215 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
216 const GURL& origin = params.origin;
217 NotificationObjectProxy* proxy =
218 new NotificationObjectProxy(render_frame_host, delegate.Pass());
220 base::string16 display_source = DisplayNameForOriginInProcessId(
221 origin, render_frame_host->GetProcess()->GetID());
222 Notification notification(origin, params.icon_url, params.title,
223 params.body, params.direction, display_source, params.replace_id,
224 proxy);
226 // The webkit notification doesn't timeout.
227 notification.set_never_timeout(true);
229 GetUIManager()->Add(notification, profile_);
230 if (cancel_callback)
231 *cancel_callback = base::Bind(&CancelNotification, proxy->id());
233 DesktopNotificationProfileUtil::UsePermission(profile_, origin);
236 base::string16 DesktopNotificationService::DisplayNameForOriginInProcessId(
237 const GURL& origin, int process_id) {
238 #if defined(ENABLE_EXTENSIONS)
239 // If the source is an extension, lookup the display name.
240 if (origin.SchemeIs(extensions::kExtensionScheme)) {
241 extensions::InfoMap* extension_info_map =
242 extensions::ExtensionSystem::Get(profile_)->info_map();
243 if (extension_info_map) {
244 extensions::ExtensionSet extensions;
245 extension_info_map->GetExtensionsWithAPIPermissionForSecurityOrigin(
246 origin,
247 process_id,
248 extensions::APIPermission::kNotifications,
249 &extensions);
250 for (extensions::ExtensionSet::const_iterator iter = extensions.begin();
251 iter != extensions.end(); ++iter) {
252 NotifierId notifier_id(NotifierId::APPLICATION, (*iter)->id());
253 if (IsNotifierEnabled(notifier_id))
254 return base::UTF8ToUTF16((*iter)->name());
258 #endif
260 return base::UTF8ToUTF16(origin.host());
263 NotificationUIManager* DesktopNotificationService::GetUIManager() {
264 // We defer setting ui_manager_ to the global singleton until we need it
265 // in order to avoid UI dependent construction during startup.
266 if (!ui_manager_)
267 ui_manager_ = g_browser_process->notification_ui_manager();
268 return ui_manager_;
271 bool DesktopNotificationService::IsNotifierEnabled(
272 const NotifierId& notifier_id) {
273 switch (notifier_id.type) {
274 case NotifierId::APPLICATION:
275 return disabled_extension_ids_.find(notifier_id.id) ==
276 disabled_extension_ids_.end();
277 case NotifierId::WEB_PAGE:
278 return DesktopNotificationProfileUtil::GetContentSetting(
279 profile_, notifier_id.url) == CONTENT_SETTING_ALLOW;
280 case NotifierId::SYSTEM_COMPONENT:
281 #if defined(OS_CHROMEOS)
282 return disabled_system_component_ids_.find(notifier_id.id) ==
283 disabled_system_component_ids_.end();
284 #else
285 // We do not disable system component notifications.
286 return true;
287 #endif
290 NOTREACHED();
291 return false;
294 void DesktopNotificationService::SetNotifierEnabled(
295 const NotifierId& notifier_id,
296 bool enabled) {
297 DCHECK_NE(NotifierId::WEB_PAGE, notifier_id.type);
299 bool add_new_item = false;
300 const char* pref_name = NULL;
301 scoped_ptr<base::StringValue> id;
302 switch (notifier_id.type) {
303 case NotifierId::APPLICATION:
304 pref_name = prefs::kMessageCenterDisabledExtensionIds;
305 add_new_item = !enabled;
306 id.reset(new base::StringValue(notifier_id.id));
307 FirePermissionLevelChangedEvent(notifier_id, enabled);
308 break;
309 case NotifierId::SYSTEM_COMPONENT:
310 #if defined(OS_CHROMEOS)
311 pref_name = prefs::kMessageCenterDisabledSystemComponentIds;
312 add_new_item = !enabled;
313 id.reset(new base::StringValue(notifier_id.id));
314 #else
315 return;
316 #endif
317 break;
318 default:
319 NOTREACHED();
321 DCHECK(pref_name != NULL);
323 ListPrefUpdate update(profile_->GetPrefs(), pref_name);
324 base::ListValue* const list = update.Get();
325 if (add_new_item) {
326 // AppendIfNotPresent will delete |adding_value| when the same value
327 // already exists.
328 list->AppendIfNotPresent(id.release());
329 } else {
330 list->Remove(*id, NULL);
334 void DesktopNotificationService::ShowWelcomeNotificationIfNecessary(
335 const Notification& notification) {
336 if (!chrome_now_welcome_notification_) {
337 chrome_now_welcome_notification_ =
338 ExtensionWelcomeNotification::Create(kChromeNowExtensionID, profile_);
341 if (chrome_now_welcome_notification_) {
342 chrome_now_welcome_notification_->ShowWelcomeNotificationIfNecessary(
343 notification);
347 void DesktopNotificationService::OnStringListPrefChanged(
348 const char* pref_name, std::set<std::string>* ids_field) {
349 ids_field->clear();
350 // Separate GetPrefs()->GetList() to analyze the crash. See crbug.com/322320
351 const PrefService* pref_service = profile_->GetPrefs();
352 CHECK(pref_service);
353 const base::ListValue* pref_list = pref_service->GetList(pref_name);
354 for (size_t i = 0; i < pref_list->GetSize(); ++i) {
355 std::string element;
356 if (pref_list->GetString(i, &element) && !element.empty())
357 ids_field->insert(element);
358 else
359 LOG(WARNING) << i << "-th element is not a string for " << pref_name;
363 void DesktopNotificationService::OnExtensionUninstalled(
364 content::BrowserContext* browser_context,
365 const extensions::Extension* extension,
366 extensions::UninstallReason reason) {
367 #if defined(ENABLE_EXTENSIONS)
368 NotifierId notifier_id(NotifierId::APPLICATION, extension->id());
369 if (IsNotifierEnabled(notifier_id))
370 return;
372 // The settings for ephemeral apps will be persisted across cache evictions.
373 if (extensions::util::IsEphemeralApp(extension->id(), profile_))
374 return;
376 SetNotifierEnabled(notifier_id, true);
377 #endif
380 void DesktopNotificationService::OnNotificationPermissionRequested(
381 const NotificationPermissionCallback& callback, bool allowed) {
382 blink::WebNotificationPermission permission = allowed ?
383 blink::WebNotificationPermissionAllowed :
384 blink::WebNotificationPermissionDenied;
386 callback.Run(permission);
389 void DesktopNotificationService::FirePermissionLevelChangedEvent(
390 const NotifierId& notifier_id, bool enabled) {
391 #if defined(ENABLE_EXTENSIONS)
392 DCHECK_EQ(NotifierId::APPLICATION, notifier_id.type);
393 extensions::api::notifications::PermissionLevel permission =
394 enabled ? extensions::api::notifications::PERMISSION_LEVEL_GRANTED
395 : extensions::api::notifications::PERMISSION_LEVEL_DENIED;
396 scoped_ptr<base::ListValue> args(new base::ListValue());
397 args->Append(new base::StringValue(
398 extensions::api::notifications::ToString(permission)));
399 scoped_ptr<extensions::Event> event(new extensions::Event(
400 extensions::api::notifications::OnPermissionLevelChanged::kEventName,
401 args.Pass()));
402 extensions::EventRouter::Get(profile_)
403 ->DispatchEventToExtension(notifier_id.id, event.Pass());
405 // Tell the IO thread that this extension's permission for notifications
406 // has changed.
407 extensions::InfoMap* extension_info_map =
408 extensions::ExtensionSystem::Get(profile_)->info_map();
409 BrowserThread::PostTask(
410 BrowserThread::IO, FROM_HERE,
411 base::Bind(&extensions::InfoMap::SetNotificationsDisabled,
412 extension_info_map, notifier_id.id, !enabled));
413 #endif