普通文本  |  683行  |  25.21 KB

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/notifications/desktop_notification_service.h"

#include "base/metrics/histogram.h"
#include "base/prefs/scoped_user_pref_update.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/content_settings/content_settings_details.h"
#include "chrome/browser/content_settings/content_settings_provider.h"
#include "chrome/browser/content_settings/host_content_settings_map.h"
#include "chrome/browser/extensions/api/notifications/notifications_api.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/extension_system.h"
#include "chrome/browser/infobars/confirm_infobar_delegate.h"
#include "chrome/browser/infobars/infobar.h"
#include "chrome/browser/infobars/infobar_service.h"
#include "chrome/browser/notifications/desktop_notification_service_factory.h"
#include "chrome/browser/notifications/notification.h"
#include "chrome/browser/notifications/notification_object_proxy.h"
#include "chrome/browser/notifications/notification_ui_manager.h"
#include "chrome/browser/notifications/sync_notifier/chrome_notifier_service.h"
#include "chrome/browser/notifications/sync_notifier/chrome_notifier_service_factory.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/common/content_settings.h"
#include "chrome/common/content_settings_pattern.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/url_constants.h"
#include "components/user_prefs/pref_registry_syncable.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/show_desktop_notification_params.h"
#include "extensions/browser/event_router.h"
#include "extensions/browser/info_map.h"
#include "extensions/common/constants.h"
#include "grit/browser_resources.h"
#include "grit/chromium_strings.h"
#include "grit/generated_resources.h"
#include "grit/theme_resources.h"
#include "net/base/escape.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/base/webui/web_ui_util.h"
#include "ui/message_center/message_center_util.h"
#include "ui/message_center/notifier_settings.h"

using content::BrowserThread;
using content::RenderViewHost;
using content::WebContents;
using message_center::NotifierId;
using blink::WebTextDirection;


// NotificationPermissionInfoBarDelegate --------------------------------------

// The delegate for the infobar shown when an origin requests notification
// permissions.
class NotificationPermissionInfoBarDelegate : public ConfirmInfoBarDelegate {
 public:
  // Creates a notification permission infobar and delegate and adds the infobar
  // to |infobar_service|.
  static void Create(InfoBarService* infobar_service,
                     DesktopNotificationService* notification_service,
                     const GURL& origin,
                     const base::string16& display_name,
                     int process_id,
                     int route_id,
                     int callback_context);

 private:
  NotificationPermissionInfoBarDelegate(
      DesktopNotificationService* notification_service,
      const GURL& origin,
      const base::string16& display_name,
      int process_id,
      int route_id,
      int callback_context);
  virtual ~NotificationPermissionInfoBarDelegate();

  // ConfirmInfoBarDelegate:
  virtual int GetIconID() const OVERRIDE;
  virtual Type GetInfoBarType() const OVERRIDE;
  virtual base::string16 GetMessageText() const OVERRIDE;
  virtual base::string16 GetButtonLabel(InfoBarButton button) const OVERRIDE;
  virtual bool Accept() OVERRIDE;
  virtual bool Cancel() OVERRIDE;

  // The origin we are asking for permissions on.
  GURL origin_;

  // The display name for the origin to be displayed.  Will be different from
  // origin_ for extensions.
  base::string16 display_name_;

  // The notification service to be used.
  DesktopNotificationService* notification_service_;

  // The callback information that tells us how to respond to javascript via
  // the correct RenderView.
  int process_id_;
  int route_id_;
  int callback_context_;

  // Whether the user clicked one of the buttons.
  bool action_taken_;

  DISALLOW_COPY_AND_ASSIGN(NotificationPermissionInfoBarDelegate);
};

// static
void NotificationPermissionInfoBarDelegate::Create(
    InfoBarService* infobar_service,
    DesktopNotificationService* notification_service,
    const GURL& origin,
    const base::string16& display_name,
    int process_id,
    int route_id,
    int callback_context) {
  infobar_service->AddInfoBar(ConfirmInfoBarDelegate::CreateInfoBar(
      scoped_ptr<ConfirmInfoBarDelegate>(
          new NotificationPermissionInfoBarDelegate(
              notification_service, origin, display_name, process_id, route_id,
              callback_context))));
}

NotificationPermissionInfoBarDelegate::NotificationPermissionInfoBarDelegate(
    DesktopNotificationService* notification_service,
    const GURL& origin,
    const base::string16& display_name,
    int process_id,
    int route_id,
    int callback_context)
    : ConfirmInfoBarDelegate(),
      origin_(origin),
      display_name_(display_name),
      notification_service_(notification_service),
      process_id_(process_id),
      route_id_(route_id),
      callback_context_(callback_context),
      action_taken_(false) {
}

NotificationPermissionInfoBarDelegate::
    ~NotificationPermissionInfoBarDelegate() {
  if (!action_taken_)
    UMA_HISTOGRAM_COUNTS("NotificationPermissionRequest.Ignored", 1);

  RenderViewHost* host = RenderViewHost::FromID(process_id_, route_id_);
  if (host)
    host->DesktopNotificationPermissionRequestDone(callback_context_);
}

int NotificationPermissionInfoBarDelegate::GetIconID() const {
  return IDR_INFOBAR_DESKTOP_NOTIFICATIONS;
}

InfoBarDelegate::Type
    NotificationPermissionInfoBarDelegate::GetInfoBarType() const {
  return PAGE_ACTION_TYPE;
}

base::string16 NotificationPermissionInfoBarDelegate::GetMessageText() const {
  return l10n_util::GetStringFUTF16(IDS_NOTIFICATION_PERMISSIONS,
                                    display_name_);
}

base::string16 NotificationPermissionInfoBarDelegate::GetButtonLabel(
    InfoBarButton button) const {
  return l10n_util::GetStringUTF16((button == BUTTON_OK) ?
      IDS_NOTIFICATION_PERMISSION_YES : IDS_NOTIFICATION_PERMISSION_NO);
}

bool NotificationPermissionInfoBarDelegate::Accept() {
  UMA_HISTOGRAM_COUNTS("NotificationPermissionRequest.Allowed", 1);
  notification_service_->GrantPermission(origin_);
  action_taken_ = true;
  return true;
}

bool NotificationPermissionInfoBarDelegate::Cancel() {
  UMA_HISTOGRAM_COUNTS("NotificationPermissionRequest.Denied", 1);
  notification_service_->DenyPermission(origin_);
  action_taken_ = true;
  return true;
}


// DesktopNotificationService -------------------------------------------------

// static
void DesktopNotificationService::RegisterProfilePrefs(
    user_prefs::PrefRegistrySyncable* registry) {
  registry->RegisterListPref(
      prefs::kMessageCenterDisabledExtensionIds,
      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
  registry->RegisterListPref(
      prefs::kMessageCenterDisabledSystemComponentIds,
      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
  registry->RegisterListPref(
      prefs::kMessageCenterEnabledSyncNotifierIds,
      user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
  WelcomeNotification::RegisterProfilePrefs(registry);
}

// static
base::string16 DesktopNotificationService::CreateDataUrl(
    const GURL& icon_url,
    const base::string16& title,
    const base::string16& body,
    WebTextDirection dir) {
  int resource;
  std::vector<std::string> subst;
  if (icon_url.is_valid()) {
    resource = IDR_NOTIFICATION_ICON_HTML;
    subst.push_back(icon_url.spec());
    subst.push_back(net::EscapeForHTML(UTF16ToUTF8(title)));
    subst.push_back(net::EscapeForHTML(UTF16ToUTF8(body)));
    // icon float position
    subst.push_back(dir == blink::WebTextDirectionRightToLeft ?
                    "right" : "left");
  } else if (title.empty() || body.empty()) {
    resource = IDR_NOTIFICATION_1LINE_HTML;
    base::string16 line = title.empty() ? body : title;
    // Strings are div names in the template file.
    base::string16 line_name = title.empty() ? ASCIIToUTF16("description")
                                       : ASCIIToUTF16("title");
    subst.push_back(net::EscapeForHTML(UTF16ToUTF8(line_name)));
    subst.push_back(net::EscapeForHTML(UTF16ToUTF8(line)));
  } else {
    resource = IDR_NOTIFICATION_2LINE_HTML;
    subst.push_back(net::EscapeForHTML(UTF16ToUTF8(title)));
    subst.push_back(net::EscapeForHTML(UTF16ToUTF8(body)));
  }
  // body text direction
  subst.push_back(dir == blink::WebTextDirectionRightToLeft ?
                  "rtl" : "ltr");

  return CreateDataUrl(resource, subst);
}

// static
base::string16 DesktopNotificationService::CreateDataUrl(
    int resource, const std::vector<std::string>& subst) {
  const base::StringPiece template_html(
      ResourceBundle::GetSharedInstance().GetRawDataResource(
          resource));

  if (template_html.empty()) {
    NOTREACHED() << "unable to load template. ID: " << resource;
    return base::string16();
  }

  std::string data = ReplaceStringPlaceholders(template_html, subst, NULL);
  return UTF8ToUTF16("data:text/html;charset=utf-8," +
                      net::EscapeQueryParamValue(data, false));
}

// static
std::string DesktopNotificationService::AddNotification(
    const GURL& origin_url,
    const base::string16& title,
    const base::string16& message,
    const GURL& icon_url,
    const base::string16& replace_id,
    NotificationDelegate* delegate,
    Profile* profile) {
  if (message_center::IsRichNotificationEnabled()) {
    // For message center create a non-HTML notification with |icon_url|.
    Notification notification(origin_url, icon_url, title, message,
                              blink::WebTextDirectionDefault,
                              base::string16(), replace_id, delegate);
    g_browser_process->notification_ui_manager()->Add(notification, profile);
    return notification.notification_id();
  }

  // Generate a data URL embedding the icon URL, title, and message.
  GURL content_url(CreateDataUrl(
      icon_url, title, message, blink::WebTextDirectionDefault));
  Notification notification(
      GURL(), content_url, base::string16(), replace_id, delegate);
  g_browser_process->notification_ui_manager()->Add(notification, profile);
  return notification.notification_id();
}

// static
std::string DesktopNotificationService::AddIconNotification(
    const GURL& origin_url,
    const base::string16& title,
    const base::string16& message,
    const gfx::Image& icon,
    const base::string16& replace_id,
    NotificationDelegate* delegate,
    Profile* profile) {
  if (message_center::IsRichNotificationEnabled()) {
    // For message center create a non-HTML notification with |icon|.
    Notification notification(origin_url, icon, title, message,
                              blink::WebTextDirectionDefault,
                              base::string16(), replace_id, delegate);
    g_browser_process->notification_ui_manager()->Add(notification, profile);
    return notification.notification_id();
  }

  GURL icon_url;
  if (!icon.IsEmpty())
    icon_url = GURL(webui::GetBitmapDataUrl(*icon.ToSkBitmap()));
  return AddNotification(
      origin_url, title, message, icon_url, replace_id, delegate, profile);
}

// static
void DesktopNotificationService::RemoveNotification(
    const std::string& notification_id) {
    g_browser_process->notification_ui_manager()->CancelById(notification_id);
}

DesktopNotificationService::DesktopNotificationService(
    Profile* profile,
    NotificationUIManager* ui_manager)
    : profile_(profile),
      ui_manager_(ui_manager) {
  OnStringListPrefChanged(
      prefs::kMessageCenterDisabledExtensionIds, &disabled_extension_ids_);
  OnStringListPrefChanged(
      prefs::kMessageCenterDisabledSystemComponentIds,
      &disabled_system_component_ids_);
  OnStringListPrefChanged(
      prefs::kMessageCenterEnabledSyncNotifierIds, &enabled_sync_notifier_ids_);
  disabled_extension_id_pref_.Init(
      prefs::kMessageCenterDisabledExtensionIds,
      profile_->GetPrefs(),
      base::Bind(
          &DesktopNotificationService::OnStringListPrefChanged,
          base::Unretained(this),
          base::Unretained(prefs::kMessageCenterDisabledExtensionIds),
          base::Unretained(&disabled_extension_ids_)));
  disabled_system_component_id_pref_.Init(
      prefs::kMessageCenterDisabledSystemComponentIds,
      profile_->GetPrefs(),
      base::Bind(
          &DesktopNotificationService::OnStringListPrefChanged,
          base::Unretained(this),
          base::Unretained(prefs::kMessageCenterDisabledSystemComponentIds),
          base::Unretained(&disabled_system_component_ids_)));
  enabled_sync_notifier_id_pref_.Init(
      prefs::kMessageCenterEnabledSyncNotifierIds,
      profile_->GetPrefs(),
      base::Bind(
          &DesktopNotificationService::OnStringListPrefChanged,
          base::Unretained(this),
          base::Unretained(prefs::kMessageCenterEnabledSyncNotifierIds),
          base::Unretained(&enabled_sync_notifier_ids_)));
  registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNINSTALLED,
                 content::Source<Profile>(profile_));
}

DesktopNotificationService::~DesktopNotificationService() {
}

void DesktopNotificationService::GrantPermission(const GURL& origin) {
  ContentSettingsPattern primary_pattern =
      ContentSettingsPattern::FromURLNoWildcard(origin);
  profile_->GetHostContentSettingsMap()->SetContentSetting(
      primary_pattern,
      ContentSettingsPattern::Wildcard(),
      CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
      NO_RESOURCE_IDENTIFIER,
      CONTENT_SETTING_ALLOW);
}

void DesktopNotificationService::DenyPermission(const GURL& origin) {
  ContentSettingsPattern primary_pattern =
      ContentSettingsPattern::FromURLNoWildcard(origin);
  profile_->GetHostContentSettingsMap()->SetContentSetting(
      primary_pattern,
      ContentSettingsPattern::Wildcard(),
      CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
      NO_RESOURCE_IDENTIFIER,
      CONTENT_SETTING_BLOCK);
}

ContentSetting DesktopNotificationService::GetDefaultContentSetting(
    std::string* provider_id) {
  return profile_->GetHostContentSettingsMap()->GetDefaultContentSetting(
      CONTENT_SETTINGS_TYPE_NOTIFICATIONS, provider_id);
}

void DesktopNotificationService::SetDefaultContentSetting(
    ContentSetting setting) {
  profile_->GetHostContentSettingsMap()->SetDefaultContentSetting(
      CONTENT_SETTINGS_TYPE_NOTIFICATIONS, setting);
}

void DesktopNotificationService::ResetToDefaultContentSetting() {
  profile_->GetHostContentSettingsMap()->SetDefaultContentSetting(
      CONTENT_SETTINGS_TYPE_NOTIFICATIONS, CONTENT_SETTING_DEFAULT);
}

void DesktopNotificationService::GetNotificationsSettings(
    ContentSettingsForOneType* settings) {
  profile_->GetHostContentSettingsMap()->GetSettingsForOneType(
      CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
      NO_RESOURCE_IDENTIFIER,
      settings);
}

void DesktopNotificationService::ClearSetting(
    const ContentSettingsPattern& pattern) {
  profile_->GetHostContentSettingsMap()->SetContentSetting(
      pattern,
      ContentSettingsPattern::Wildcard(),
      CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
      NO_RESOURCE_IDENTIFIER,
      CONTENT_SETTING_DEFAULT);
}

void DesktopNotificationService::ResetAllOrigins() {
  profile_->GetHostContentSettingsMap()->ClearSettingsForOneType(
      CONTENT_SETTINGS_TYPE_NOTIFICATIONS);
}

ContentSetting DesktopNotificationService::GetContentSetting(
    const GURL& origin) {
  return profile_->GetHostContentSettingsMap()->GetContentSetting(
      origin,
      origin,
      CONTENT_SETTINGS_TYPE_NOTIFICATIONS,
      NO_RESOURCE_IDENTIFIER);
}

void DesktopNotificationService::RequestPermission(
    const GURL& origin, int process_id, int route_id, int callback_context,
    WebContents* contents) {
  // If |origin| hasn't been seen before and the default content setting for
  // notifications is "ask", show an infobar.
  // The cache can only answer queries on the IO thread once it's initialized,
  // so don't ask the cache.
  ContentSetting setting = GetContentSetting(origin);
  if (setting == CONTENT_SETTING_ASK) {
    // Show an info bar requesting permission.
    InfoBarService* infobar_service =
        InfoBarService::FromWebContents(contents);
    // |infobar_service| may be NULL, e.g., if this request originated in a
    // browser action popup, extension background page, or any HTML that runs
    // outside of a tab.
    if (infobar_service) {
      NotificationPermissionInfoBarDelegate::Create(
          infobar_service,
          DesktopNotificationServiceFactory::GetForProfile(
              Profile::FromBrowserContext(contents->GetBrowserContext())),
          origin, DisplayNameForOriginInProcessId(origin, process_id),
          process_id, route_id, callback_context);
      return;
    }
  }

  // Notify renderer immediately.
  RenderViewHost* host = RenderViewHost::FromID(process_id, route_id);
  if (host)
    host->DesktopNotificationPermissionRequestDone(callback_context);
}

#if !defined(OS_WIN)
void DesktopNotificationService::ShowNotification(
    const Notification& notification) {
  GetUIManager()->Add(notification, profile_);
}

bool DesktopNotificationService::CancelDesktopNotification(
    int process_id, int route_id, int notification_id) {
  scoped_refptr<NotificationObjectProxy> proxy(
      new NotificationObjectProxy(process_id, route_id, notification_id,
                                  false));
  return GetUIManager()->CancelById(proxy->id());
}
#endif  // OS_WIN

bool DesktopNotificationService::ShowDesktopNotification(
    const content::ShowDesktopNotificationHostMsgParams& params,
    int process_id, int route_id, DesktopNotificationSource source) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  const GURL& origin = params.origin;
  NotificationObjectProxy* proxy =
      new NotificationObjectProxy(process_id, route_id,
                                  params.notification_id,
                                  source == WorkerNotification);

  base::string16 display_source =
      DisplayNameForOriginInProcessId(origin, process_id);
  Notification notification(origin, params.icon_url, params.title,
      params.body, params.direction, display_source, params.replace_id,
      proxy);

  // The webkit notification doesn't timeout.
  notification.set_never_timeout(true);

  ShowNotification(notification);
  return true;
}

base::string16 DesktopNotificationService::DisplayNameForOriginInProcessId(
    const GURL& origin, int process_id) {
  // If the source is an extension, lookup the display name.
  // Message center prefers to use extension name if the notification
  // is allowed by an extension.
  if (NotificationUIManager::DelegatesToMessageCenter() ||
      origin.SchemeIs(extensions::kExtensionScheme)) {
    extensions::InfoMap* extension_info_map =
        extensions::ExtensionSystem::Get(profile_)->info_map();
    if (extension_info_map) {
      ExtensionSet extensions;
      extension_info_map->GetExtensionsWithAPIPermissionForSecurityOrigin(
          origin, process_id, extensions::APIPermission::kNotification,
          &extensions);
      for (ExtensionSet::const_iterator iter = extensions.begin();
           iter != extensions.end(); ++iter) {
        NotifierId notifier_id(NotifierId::APPLICATION, (*iter)->id());
        if (IsNotifierEnabled(notifier_id))
          return UTF8ToUTF16((*iter)->name());
      }
    }
  }
  return UTF8ToUTF16(origin.host());
}

void DesktopNotificationService::NotifySettingsChange() {
  content::NotificationService::current()->Notify(
      chrome::NOTIFICATION_DESKTOP_NOTIFICATION_SETTINGS_CHANGED,
      content::Source<DesktopNotificationService>(this),
      content::NotificationService::NoDetails());
}

NotificationUIManager* DesktopNotificationService::GetUIManager() {
  // We defer setting ui_manager_ to the global singleton until we need it
  // in order to avoid UI dependent construction during startup.
  if (!ui_manager_)
    ui_manager_ = g_browser_process->notification_ui_manager();
  return ui_manager_;
}

bool DesktopNotificationService::IsNotifierEnabled(
    const NotifierId& notifier_id) {
  switch (notifier_id.type) {
    case NotifierId::APPLICATION:
      return disabled_extension_ids_.find(notifier_id.id) ==
          disabled_extension_ids_.end();
    case NotifierId::WEB_PAGE:
      return GetContentSetting(notifier_id.url) == CONTENT_SETTING_ALLOW;
    case NotifierId::SYSTEM_COMPONENT:
#if defined(OS_CHROMEOS)
      return disabled_system_component_ids_.find(notifier_id.id) ==
          disabled_system_component_ids_.end();
#else
      // We do not disable system component notifications.
      return true;
#endif
    case NotifierId::SYNCED_NOTIFICATION_SERVICE:
      return enabled_sync_notifier_ids_.find(notifier_id.id) !=
          enabled_sync_notifier_ids_.end();
  }

  NOTREACHED();
  return false;
}

void DesktopNotificationService::SetNotifierEnabled(
    const NotifierId& notifier_id,
    bool enabled) {
  DCHECK_NE(NotifierId::WEB_PAGE, notifier_id.type);

  bool add_new_item = false;
  const char* pref_name = NULL;
  scoped_ptr<base::StringValue> id;
  switch (notifier_id.type) {
    case NotifierId::APPLICATION:
      pref_name = prefs::kMessageCenterDisabledExtensionIds;
      add_new_item = !enabled;
      id.reset(new base::StringValue(notifier_id.id));
      FirePermissionLevelChangedEvent(notifier_id, enabled);
      break;
    case NotifierId::SYSTEM_COMPONENT:
#if defined(OS_CHROMEOS)
      pref_name = prefs::kMessageCenterDisabledSystemComponentIds;
      add_new_item = !enabled;
      id.reset(new base::StringValue(notifier_id.id));
#else
      return;
#endif
      break;
    case NotifierId::SYNCED_NOTIFICATION_SERVICE:
      pref_name = prefs::kMessageCenterEnabledSyncNotifierIds;
      // Adding a new item if |enabled| == true, since synced notification
      // services are opt-in.
      add_new_item = enabled;
      id.reset(new base::StringValue(notifier_id.id));
      break;
    default:
      NOTREACHED();
  }
  DCHECK(pref_name != NULL);

  ListPrefUpdate update(profile_->GetPrefs(), pref_name);
  base::ListValue* const list = update.Get();
  if (add_new_item) {
    // AppendIfNotPresent will delete |adding_value| when the same value
    // already exists.
    list->AppendIfNotPresent(id.release());
  } else {
    list->Remove(*id, NULL);
  }
}

void DesktopNotificationService::ShowWelcomeNotificationIfNecessary(
    const Notification& notification) {
  if (!welcome_notification && message_center::IsRichNotificationEnabled()) {
    welcome_notification.reset(
        new WelcomeNotification(profile_, g_browser_process->message_center()));
  }

  if (welcome_notification)
    welcome_notification->ShowWelcomeNotificationIfNecessary(notification);
}

void DesktopNotificationService::OnStringListPrefChanged(
    const char* pref_name, std::set<std::string>* ids_field) {
  ids_field->clear();
  const base::ListValue* pref_list = profile_->GetPrefs()->GetList(pref_name);
  for (size_t i = 0; i < pref_list->GetSize(); ++i) {
    std::string element;
    if (pref_list->GetString(i, &element) && !element.empty())
      ids_field->insert(element);
    else
      LOG(WARNING) << i << "-th element is not a string for " << pref_name;
  }
}

void DesktopNotificationService::Observe(
    int type,
    const content::NotificationSource& source,
    const content::NotificationDetails& details) {
  DCHECK_EQ(chrome::NOTIFICATION_EXTENSION_UNINSTALLED, type);

  extensions::Extension* extension =
      content::Details<extensions::Extension>(details).ptr();
  NotifierId notifier_id(NotifierId::APPLICATION, extension->id());
  if (IsNotifierEnabled(notifier_id))
    return;

  SetNotifierEnabled(notifier_id, true);
}

void DesktopNotificationService::FirePermissionLevelChangedEvent(
    const NotifierId& notifier_id, bool enabled) {
  DCHECK_EQ(NotifierId::APPLICATION, notifier_id.type);
  extensions::api::notifications::PermissionLevel permission =
      enabled ? extensions::api::notifications::PERMISSION_LEVEL_GRANTED
              : extensions::api::notifications::PERMISSION_LEVEL_DENIED;
  scoped_ptr<base::ListValue> args(new base::ListValue());
  args->Append(new base::StringValue(
      extensions::api::notifications::ToString(permission)));
  scoped_ptr<extensions::Event> event(new extensions::Event(
      extensions::api::notifications::OnPermissionLevelChanged::kEventName,
      args.Pass()));
  extensions::ExtensionSystem::Get(profile_)->event_router()->
      DispatchEventToExtension(notifier_id.id, event.Pass());

  // Tell the IO thread that this extension's permission for notifications
  // has changed.
  extensions::InfoMap* extension_info_map =
      extensions::ExtensionSystem::Get(profile_)->info_map();
  BrowserThread::PostTask(
      BrowserThread::IO, FROM_HERE,
      base::Bind(&extensions::InfoMap::SetNotificationsDisabled,
                 extension_info_map, notifier_id.id, !enabled));

}