desktop_notification_service.cc revision 7d4cd473f85ac64c3747c96c277f9e506a0d2246
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/metrics/histogram.h"
8#include "base/strings/utf_string_conversions.h"
9#include "base/threading/thread.h"
10#include "chrome/browser/browser_process.h"
11#include "chrome/browser/content_settings/content_settings_details.h"
12#include "chrome/browser/content_settings/content_settings_provider.h"
13#include "chrome/browser/content_settings/host_content_settings_map.h"
14#include "chrome/browser/extensions/extension_info_map.h"
15#include "chrome/browser/extensions/extension_service.h"
16#include "chrome/browser/extensions/extension_system.h"
17#include "chrome/browser/infobars/confirm_infobar_delegate.h"
18#include "chrome/browser/infobars/infobar_service.h"
19#include "chrome/browser/notifications/desktop_notification_service_factory.h"
20#include "chrome/browser/notifications/notification.h"
21#include "chrome/browser/notifications/notification_object_proxy.h"
22#include "chrome/browser/notifications/notification_ui_manager.h"
23#include "chrome/browser/prefs/scoped_user_pref_update.h"
24#include "chrome/browser/profiles/profile.h"
25#include "chrome/browser/ui/browser.h"
26#include "chrome/common/chrome_notification_types.h"
27#include "chrome/common/content_settings.h"
28#include "chrome/common/content_settings_pattern.h"
29#include "chrome/common/pref_names.h"
30#include "chrome/common/url_constants.h"
31#include "components/user_prefs/pref_registry_syncable.h"
32#include "content/public/browser/browser_thread.h"
33#include "content/public/browser/notification_service.h"
34#include "content/public/browser/render_view_host.h"
35#include "content/public/browser/web_contents.h"
36#include "content/public/common/show_desktop_notification_params.h"
37#include "extensions/common/constants.h"
38#include "grit/browser_resources.h"
39#include "grit/chromium_strings.h"
40#include "grit/generated_resources.h"
41#include "grit/theme_resources.h"
42#include "net/base/escape.h"
43#include "third_party/WebKit/public/web/WebSecurityOrigin.h"
44#include "ui/base/l10n/l10n_util.h"
45#include "ui/base/resource/resource_bundle.h"
46#include "ui/message_center/message_center_util.h"
47#include "ui/message_center/notifier_settings.h"
48#include "ui/webui/web_ui_util.h"
49
50using content::BrowserThread;
51using content::RenderViewHost;
52using content::WebContents;
53using WebKit::WebNotificationPresenter;
54using WebKit::WebTextDirection;
55using WebKit::WebSecurityOrigin;
56
57const ContentSetting kDefaultSetting = CONTENT_SETTING_ASK;
58
59namespace {
60
61void ToggleListPrefItem(PrefService* prefs, const char* key,
62                        const std::string& item, bool flag) {
63  ListPrefUpdate update(prefs, key);
64  base::ListValue* const list = update.Get();
65  if (flag) {
66    // AppendIfNotPresent will delete |adding_value| when the same value
67    // already exists.
68    base::StringValue* const adding_value = new base::StringValue(item);
69    list->AppendIfNotPresent(adding_value);
70  } else {
71    base::StringValue removed_value(item);
72    list->Remove(removed_value, NULL);
73  }
74}
75
76void CopySetFromPrefToMemory(PrefService* prefs, const char* key,
77                             std::set<std::string>* dst) {
78  dst->clear();
79  const base::ListValue* pref_list = prefs->GetList(key);
80  for (size_t i = 0; i < pref_list->GetSize(); ++i) {
81    std::string element;
82    if (!pref_list->GetString(i, &element) && element.empty()) {
83      LOG(WARNING) << i << "-th element is not a string for "
84                   << key;
85      continue;
86    }
87    dst->insert(element);
88  }
89}
90
91}  // namespace
92
93// NotificationPermissionInfoBarDelegate --------------------------------------
94
95// The delegate for the infobar shown when an origin requests notification
96// permissions.
97class NotificationPermissionInfoBarDelegate : public ConfirmInfoBarDelegate {
98 public:
99  // Creates a notification permission delegate and adds it to
100  // |infobar_service|.
101  static void Create(InfoBarService* infobar_service,
102                     DesktopNotificationService* notification_service,
103                     const GURL& origin,
104                     const string16& display_name,
105                     int process_id,
106                     int route_id,
107                     int callback_context);
108
109 private:
110  NotificationPermissionInfoBarDelegate(
111      InfoBarService* infobar_service,
112      DesktopNotificationService* notification_service,
113      const GURL& origin,
114      const string16& display_name,
115      int process_id,
116      int route_id,
117      int callback_context);
118  virtual ~NotificationPermissionInfoBarDelegate();
119
120  // ConfirmInfoBarDelegate:
121  virtual int GetIconID() const OVERRIDE;
122  virtual Type GetInfoBarType() const OVERRIDE;
123  virtual string16 GetMessageText() const OVERRIDE;
124  virtual string16 GetButtonLabel(InfoBarButton button) const OVERRIDE;
125  virtual bool Accept() OVERRIDE;
126  virtual bool Cancel() OVERRIDE;
127
128  // The origin we are asking for permissions on.
129  GURL origin_;
130
131  // The display name for the origin to be displayed.  Will be different from
132  // origin_ for extensions.
133  string16 display_name_;
134
135  // The notification service to be used.
136  DesktopNotificationService* notification_service_;
137
138  // The callback information that tells us how to respond to javascript via
139  // the correct RenderView.
140  int process_id_;
141  int route_id_;
142  int callback_context_;
143
144  // Whether the user clicked one of the buttons.
145  bool action_taken_;
146
147  DISALLOW_COPY_AND_ASSIGN(NotificationPermissionInfoBarDelegate);
148};
149
150// static
151void NotificationPermissionInfoBarDelegate::Create(
152    InfoBarService* infobar_service,
153    DesktopNotificationService* notification_service,
154    const GURL& origin,
155    const string16& display_name,
156    int process_id,
157    int route_id,
158    int callback_context) {
159  infobar_service->AddInfoBar(scoped_ptr<InfoBarDelegate>(
160      new NotificationPermissionInfoBarDelegate(
161          infobar_service, notification_service, origin, display_name,
162          process_id, route_id, callback_context)));
163}
164
165NotificationPermissionInfoBarDelegate::NotificationPermissionInfoBarDelegate(
166    InfoBarService* infobar_service,
167    DesktopNotificationService* notification_service,
168    const GURL& origin,
169    const string16& display_name,
170    int process_id,
171    int route_id,
172    int callback_context)
173    : ConfirmInfoBarDelegate(infobar_service),
174      origin_(origin),
175      display_name_(display_name),
176      notification_service_(notification_service),
177      process_id_(process_id),
178      route_id_(route_id),
179      callback_context_(callback_context),
180      action_taken_(false) {
181}
182
183NotificationPermissionInfoBarDelegate::
184    ~NotificationPermissionInfoBarDelegate() {
185  if (!action_taken_)
186    UMA_HISTOGRAM_COUNTS("NotificationPermissionRequest.Ignored", 1);
187
188  RenderViewHost* host = RenderViewHost::FromID(process_id_, route_id_);
189  if (host)
190    host->DesktopNotificationPermissionRequestDone(callback_context_);
191}
192
193int NotificationPermissionInfoBarDelegate::GetIconID() const {
194  return IDR_INFOBAR_DESKTOP_NOTIFICATIONS;
195}
196
197InfoBarDelegate::Type
198    NotificationPermissionInfoBarDelegate::GetInfoBarType() const {
199  return PAGE_ACTION_TYPE;
200}
201
202string16 NotificationPermissionInfoBarDelegate::GetMessageText() const {
203  return l10n_util::GetStringFUTF16(IDS_NOTIFICATION_PERMISSIONS,
204                                    display_name_);
205}
206
207string16 NotificationPermissionInfoBarDelegate::GetButtonLabel(
208    InfoBarButton button) const {
209  return l10n_util::GetStringUTF16((button == BUTTON_OK) ?
210      IDS_NOTIFICATION_PERMISSION_YES : IDS_NOTIFICATION_PERMISSION_NO);
211}
212
213bool NotificationPermissionInfoBarDelegate::Accept() {
214  UMA_HISTOGRAM_COUNTS("NotificationPermissionRequest.Allowed", 1);
215  notification_service_->GrantPermission(origin_);
216  action_taken_ = true;
217  return true;
218}
219
220bool NotificationPermissionInfoBarDelegate::Cancel() {
221  UMA_HISTOGRAM_COUNTS("NotificationPermissionRequest.Denied", 1);
222  notification_service_->DenyPermission(origin_);
223  action_taken_ = true;
224  return true;
225}
226
227
228// DesktopNotificationService -------------------------------------------------
229
230// static
231void DesktopNotificationService::RegisterUserPrefs(
232    user_prefs::PrefRegistrySyncable* registry) {
233  registry->RegisterListPref(prefs::kMessageCenterDisabledExtensionIds,
234                             user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
235  registry->RegisterListPref(prefs::kMessageCenterDisabledSystemComponentIds,
236                             user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
237}
238
239// static
240string16 DesktopNotificationService::CreateDataUrl(
241    const GURL& icon_url, const string16& title, const string16& body,
242    WebTextDirection dir) {
243  int resource;
244  std::vector<std::string> subst;
245  if (icon_url.is_valid()) {
246    resource = IDR_NOTIFICATION_ICON_HTML;
247    subst.push_back(icon_url.spec());
248    subst.push_back(net::EscapeForHTML(UTF16ToUTF8(title)));
249    subst.push_back(net::EscapeForHTML(UTF16ToUTF8(body)));
250    // icon float position
251    subst.push_back(dir == WebKit::WebTextDirectionRightToLeft ?
252                    "right" : "left");
253  } else if (title.empty() || body.empty()) {
254    resource = IDR_NOTIFICATION_1LINE_HTML;
255    string16 line = title.empty() ? body : title;
256    // Strings are div names in the template file.
257    string16 line_name = title.empty() ? ASCIIToUTF16("description")
258                                       : ASCIIToUTF16("title");
259    subst.push_back(net::EscapeForHTML(UTF16ToUTF8(line_name)));
260    subst.push_back(net::EscapeForHTML(UTF16ToUTF8(line)));
261  } else {
262    resource = IDR_NOTIFICATION_2LINE_HTML;
263    subst.push_back(net::EscapeForHTML(UTF16ToUTF8(title)));
264    subst.push_back(net::EscapeForHTML(UTF16ToUTF8(body)));
265  }
266  // body text direction
267  subst.push_back(dir == WebKit::WebTextDirectionRightToLeft ?
268                  "rtl" : "ltr");
269
270  return CreateDataUrl(resource, subst);
271}
272
273// static
274string16 DesktopNotificationService::CreateDataUrl(
275    int resource, const std::vector<std::string>& subst) {
276  const base::StringPiece template_html(
277      ResourceBundle::GetSharedInstance().GetRawDataResource(
278          resource));
279
280  if (template_html.empty()) {
281    NOTREACHED() << "unable to load template. ID: " << resource;
282    return string16();
283  }
284
285  std::string data = ReplaceStringPlaceholders(template_html, subst, NULL);
286  return UTF8ToUTF16("data:text/html;charset=utf-8," +
287                      net::EscapeQueryParamValue(data, false));
288}
289
290// static
291std::string DesktopNotificationService::AddNotification(
292    const GURL& origin_url,
293    const string16& title,
294    const string16& message,
295    const GURL& icon_url,
296    const string16& replace_id,
297    NotificationDelegate* delegate,
298    Profile* profile) {
299  if (message_center::IsRichNotificationEnabled()) {
300    // For message center create a non-HTML notification with |icon_url|.
301    Notification notification(origin_url, icon_url, title, message,
302                              WebKit::WebTextDirectionDefault,
303                              string16(), replace_id, delegate);
304    g_browser_process->notification_ui_manager()->Add(notification, profile);
305    return notification.notification_id();
306  }
307
308  // Generate a data URL embedding the icon URL, title, and message.
309  GURL content_url(CreateDataUrl(
310      icon_url, title, message, WebKit::WebTextDirectionDefault));
311  Notification notification(
312      GURL(), content_url, string16(), replace_id, delegate);
313  g_browser_process->notification_ui_manager()->Add(notification, profile);
314  return notification.notification_id();
315}
316
317// static
318std::string DesktopNotificationService::AddIconNotification(
319    const GURL& origin_url,
320    const string16& title,
321    const string16& message,
322    const gfx::Image& icon,
323    const string16& replace_id,
324    NotificationDelegate* delegate,
325    Profile* profile) {
326
327  if (message_center::IsRichNotificationEnabled()) {
328    // For message center create a non-HTML notification with |icon|.
329    Notification notification(origin_url, icon, title, message,
330                              WebKit::WebTextDirectionDefault,
331                              string16(), replace_id, delegate);
332    g_browser_process->notification_ui_manager()->Add(notification, profile);
333    return notification.notification_id();
334  }
335
336  GURL icon_url;
337  if (!icon.IsEmpty())
338    icon_url = GURL(webui::GetBitmapDataUrl(*icon.ToSkBitmap()));
339  return AddNotification(
340      origin_url, title, message, icon_url, replace_id, delegate, profile);
341}
342
343// static
344void DesktopNotificationService::RemoveNotification(
345    const std::string& notification_id) {
346    g_browser_process->notification_ui_manager()->CancelById(notification_id);
347}
348
349DesktopNotificationService::DesktopNotificationService(
350    Profile* profile,
351    NotificationUIManager* ui_manager)
352    : profile_(profile),
353      ui_manager_(ui_manager) {
354  OnDisabledExtensionIdsChanged();
355  OnDisabledSystemComponentIdsChanged();
356  disabled_extension_id_pref_.Init(
357      prefs::kMessageCenterDisabledExtensionIds,
358      profile_->GetPrefs(),
359      base::Bind(
360          &DesktopNotificationService::OnDisabledExtensionIdsChanged,
361          base::Unretained(this)));
362  disabled_system_component_id_pref_.Init(
363      prefs::kMessageCenterDisabledSystemComponentIds,
364      profile_->GetPrefs(),
365      base::Bind(
366          &DesktopNotificationService::OnDisabledSystemComponentIdsChanged,
367          base::Unretained(this)));
368  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNINSTALLED,
369                 content::Source<Profile>(profile_));
370}
371
372DesktopNotificationService::~DesktopNotificationService() {
373}
374
375void DesktopNotificationService::GrantPermission(const GURL& origin) {
376  ContentSettingsPattern primary_pattern =
377      ContentSettingsPattern::FromURLNoWildcard(origin);
378  profile_->GetHostContentSettingsMap()->SetContentSetting(
379      primary_pattern,
380      ContentSettingsPattern::Wildcard(),
381      CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
382      NO_RESOURCE_IDENTIFIER,
383      CONTENT_SETTING_ALLOW);
384}
385
386void DesktopNotificationService::DenyPermission(const GURL& origin) {
387  ContentSettingsPattern primary_pattern =
388      ContentSettingsPattern::FromURLNoWildcard(origin);
389  profile_->GetHostContentSettingsMap()->SetContentSetting(
390      primary_pattern,
391      ContentSettingsPattern::Wildcard(),
392      CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
393      NO_RESOURCE_IDENTIFIER,
394      CONTENT_SETTING_BLOCK);
395}
396
397ContentSetting DesktopNotificationService::GetDefaultContentSetting(
398    std::string* provider_id) {
399  return profile_->GetHostContentSettingsMap()->GetDefaultContentSetting(
400      CONTENT_SETTINGS_TYPE_NOTIFICATIONS, provider_id);
401}
402
403void DesktopNotificationService::SetDefaultContentSetting(
404    ContentSetting setting) {
405  profile_->GetHostContentSettingsMap()->SetDefaultContentSetting(
406      CONTENT_SETTINGS_TYPE_NOTIFICATIONS, setting);
407}
408
409void DesktopNotificationService::ResetToDefaultContentSetting() {
410  profile_->GetHostContentSettingsMap()->SetDefaultContentSetting(
411      CONTENT_SETTINGS_TYPE_NOTIFICATIONS, CONTENT_SETTING_DEFAULT);
412}
413
414void DesktopNotificationService::GetNotificationsSettings(
415    ContentSettingsForOneType* settings) {
416  profile_->GetHostContentSettingsMap()->GetSettingsForOneType(
417      CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
418      NO_RESOURCE_IDENTIFIER,
419      settings);
420}
421
422void DesktopNotificationService::ClearSetting(
423    const ContentSettingsPattern& pattern) {
424  profile_->GetHostContentSettingsMap()->SetContentSetting(
425      pattern,
426      ContentSettingsPattern::Wildcard(),
427      CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
428      NO_RESOURCE_IDENTIFIER,
429      CONTENT_SETTING_DEFAULT);
430}
431
432void DesktopNotificationService::ResetAllOrigins() {
433  profile_->GetHostContentSettingsMap()->ClearSettingsForOneType(
434      CONTENT_SETTINGS_TYPE_NOTIFICATIONS);
435}
436
437ContentSetting DesktopNotificationService::GetContentSetting(
438    const GURL& origin) {
439  return profile_->GetHostContentSettingsMap()->GetContentSetting(
440      origin,
441      origin,
442      CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
443      NO_RESOURCE_IDENTIFIER);
444}
445
446void DesktopNotificationService::RequestPermission(
447    const GURL& origin, int process_id, int route_id, int callback_context,
448    WebContents* contents) {
449  // If |origin| hasn't been seen before and the default content setting for
450  // notifications is "ask", show an infobar.
451  // The cache can only answer queries on the IO thread once it's initialized,
452  // so don't ask the cache.
453  ContentSetting setting = GetContentSetting(origin);
454  if (setting == CONTENT_SETTING_ASK) {
455    // Show an info bar requesting permission.
456    InfoBarService* infobar_service =
457        InfoBarService::FromWebContents(contents);
458    // |infobar_service| may be NULL, e.g., if this request originated in a
459    // browser action popup, extension background page, or any HTML that runs
460    // outside of a tab.
461    if (infobar_service) {
462      NotificationPermissionInfoBarDelegate::Create(
463          infobar_service,
464          DesktopNotificationServiceFactory::GetForProfile(
465              Profile::FromBrowserContext(contents->GetBrowserContext())),
466          origin,
467          DisplayNameForOriginInProcessId(origin, process_id),
468          process_id,
469          route_id,
470          callback_context);
471      return;
472    }
473  }
474
475  // Notify renderer immediately.
476  RenderViewHost* host = RenderViewHost::FromID(process_id, route_id);
477  if (host)
478    host->DesktopNotificationPermissionRequestDone(callback_context);
479}
480
481#if !defined(OS_WIN)
482void DesktopNotificationService::ShowNotification(
483    const Notification& notification) {
484  GetUIManager()->Add(notification, profile_);
485}
486
487bool DesktopNotificationService::CancelDesktopNotification(
488    int process_id, int route_id, int notification_id) {
489  scoped_refptr<NotificationObjectProxy> proxy(
490      new NotificationObjectProxy(process_id, route_id, notification_id,
491                                  false));
492  return GetUIManager()->CancelById(proxy->id());
493}
494#endif  // OS_WIN
495
496bool DesktopNotificationService::ShowDesktopNotification(
497    const content::ShowDesktopNotificationHostMsgParams& params,
498    int process_id, int route_id, DesktopNotificationSource source) {
499  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
500  const GURL& origin = params.origin;
501  NotificationObjectProxy* proxy =
502      new NotificationObjectProxy(process_id, route_id,
503                                  params.notification_id,
504                                  source == WorkerNotification);
505
506  string16 display_source = DisplayNameForOriginInProcessId(origin, process_id);
507  if (params.is_html) {
508    ShowNotification(Notification(origin, params.contents_url, display_source,
509        params.replace_id, proxy));
510  } else {
511    Notification notification(origin, params.icon_url, params.title,
512        params.body, params.direction, display_source, params.replace_id,
513        proxy);
514    // The webkit notification doesn't timeout.
515    notification.set_never_timeout(true);
516    ShowNotification(notification);
517  }
518  return true;
519}
520
521string16 DesktopNotificationService::DisplayNameForOriginInProcessId(
522    const GURL& origin, int process_id) {
523  // If the source is an extension, lookup the display name.
524  // Message center prefers to use extension name if the notification
525  // is allowed by an extension.
526  if (NotificationUIManager::DelegatesToMessageCenter() ||
527      origin.SchemeIs(extensions::kExtensionScheme)) {
528    ExtensionInfoMap* extension_info_map =
529        extensions::ExtensionSystem::Get(profile_)->info_map();
530    if (extension_info_map) {
531      ExtensionSet extensions;
532      extension_info_map->GetExtensionsWithAPIPermissionForSecurityOrigin(
533          origin, process_id, extensions::APIPermission::kNotification,
534          &extensions);
535      for (ExtensionSet::const_iterator iter = extensions.begin();
536           iter != extensions.end(); ++iter) {
537        if (IsExtensionEnabled((*iter)->id()))
538          return UTF8ToUTF16((*iter)->name());
539      }
540    }
541  }
542  return UTF8ToUTF16(origin.host());
543}
544
545void DesktopNotificationService::NotifySettingsChange() {
546  content::NotificationService::current()->Notify(
547      chrome::NOTIFICATION_DESKTOP_NOTIFICATION_SETTINGS_CHANGED,
548      content::Source<DesktopNotificationService>(this),
549      content::NotificationService::NoDetails());
550}
551
552NotificationUIManager* DesktopNotificationService::GetUIManager() {
553  // We defer setting ui_manager_ to the global singleton until we need it
554  // in order to avoid UI dependent construction during startup.
555  if (!ui_manager_)
556    ui_manager_ = g_browser_process->notification_ui_manager();
557  return ui_manager_;
558}
559
560bool DesktopNotificationService::IsExtensionEnabled(const std::string& id) {
561  return disabled_extension_ids_.find(id) == disabled_extension_ids_.end();
562}
563
564void DesktopNotificationService::SetExtensionEnabled(
565    const std::string& id, bool enabled) {
566  // Do not touch |disabled_extension_ids_|. It will be updated at
567  // OnDisabledExtensionIdsChanged() which will be called when the pref changes.
568  ToggleListPrefItem(
569      profile_->GetPrefs(),
570      prefs::kMessageCenterDisabledExtensionIds,
571      id,
572      !enabled);
573}
574
575void DesktopNotificationService::OnDisabledExtensionIdsChanged() {
576  CopySetFromPrefToMemory(profile_->GetPrefs(),
577                          prefs::kMessageCenterDisabledExtensionIds,
578                          &disabled_extension_ids_);
579}
580
581bool DesktopNotificationService::IsSystemComponentEnabled(
582    message_center::Notifier::SystemComponentNotifierType type) {
583  return disabled_system_component_ids_.find(message_center::ToString(type)) ==
584         disabled_system_component_ids_.end();
585}
586
587void DesktopNotificationService::SetSystemComponentEnabled(
588    message_center::Notifier::SystemComponentNotifierType type, bool enabled) {
589  // Do not touch |disabled_extension_ids_|. It will be updated at
590  // OnDisabledExtensionIdsChanged() which will be called when the pref changes.
591  ToggleListPrefItem(
592      profile_->GetPrefs(),
593      prefs::kMessageCenterDisabledSystemComponentIds,
594      message_center::ToString(type),
595      !enabled);
596}
597
598void DesktopNotificationService::OnDisabledSystemComponentIdsChanged() {
599  disabled_system_component_ids_.clear();
600  CopySetFromPrefToMemory(profile_->GetPrefs(),
601                          prefs::kMessageCenterDisabledSystemComponentIds,
602                          &disabled_system_component_ids_);
603}
604
605WebKit::WebNotificationPresenter::Permission
606    DesktopNotificationService::HasPermission(const GURL& origin) {
607  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
608  HostContentSettingsMap* host_content_settings_map =
609      profile_->GetHostContentSettingsMap();
610  ContentSetting setting = host_content_settings_map->GetContentSetting(
611      origin,
612      origin,
613      CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
614      NO_RESOURCE_IDENTIFIER);
615
616  if (setting == CONTENT_SETTING_ALLOW)
617    return WebKit::WebNotificationPresenter::PermissionAllowed;
618  if (setting == CONTENT_SETTING_BLOCK)
619    return WebKit::WebNotificationPresenter::PermissionDenied;
620  if (setting == CONTENT_SETTING_ASK)
621    return WebKit::WebNotificationPresenter::PermissionNotAllowed;
622  NOTREACHED() << "Invalid notifications settings value: " << setting;
623  return WebKit::WebNotificationPresenter::PermissionNotAllowed;
624}
625
626void DesktopNotificationService::Observe(
627    int type,
628    const content::NotificationSource& source,
629    const content::NotificationDetails& details) {
630  DCHECK_EQ(chrome::NOTIFICATION_EXTENSION_UNINSTALLED, type);
631
632  extensions::Extension* extension =
633      content::Details<extensions::Extension>(details).ptr();
634  if (IsExtensionEnabled(extension->id()))
635    return;
636
637  SetExtensionEnabled(extension->id(), true);
638}
639