// 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/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/browser/ui/website_settings/permission_bubble_manager.h" #include "chrome/browser/ui/website_settings/permission_bubble_request.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/extension_system.h" #include "extensions/browser/info_map.h" #include "extensions/common/constants.h" #include "extensions/common/extension.h" #include "extensions/common/extension_set.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; const char kChromeNowExtensionID[] = "pafkbggdmjlpgkdkcbjmhmfcdpncadgh"; // NotificationPermissionRequest --------------------------------------- class NotificationPermissionRequest : public PermissionBubbleRequest { public: NotificationPermissionRequest( DesktopNotificationService* notification_service, const GURL& origin, base::string16 display_name, int process_id, int route_id, int callback_context); virtual ~NotificationPermissionRequest(); // PermissionBubbleDelegate: virtual int GetIconID() const OVERRIDE; virtual base::string16 GetMessageText() const OVERRIDE; virtual base::string16 GetMessageTextFragment() const OVERRIDE; virtual bool HasUserGesture() const OVERRIDE; virtual GURL GetRequestingHostname() const OVERRIDE; virtual void PermissionGranted() OVERRIDE; virtual void PermissionDenied() OVERRIDE; virtual void Cancelled() OVERRIDE; virtual void RequestFinished() OVERRIDE; private: // The notification service to be used. DesktopNotificationService* notification_service_; // 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 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(NotificationPermissionRequest); }; NotificationPermissionRequest::NotificationPermissionRequest( DesktopNotificationService* notification_service, const GURL& origin, base::string16 display_name, int process_id, int route_id, int callback_context) : notification_service_(notification_service), origin_(origin), display_name_(display_name), process_id_(process_id), route_id_(route_id), callback_context_(callback_context), action_taken_(false) {} NotificationPermissionRequest::~NotificationPermissionRequest() {} int NotificationPermissionRequest::GetIconID() const { return IDR_INFOBAR_DESKTOP_NOTIFICATIONS; } base::string16 NotificationPermissionRequest::GetMessageText() const { return l10n_util::GetStringFUTF16(IDS_NOTIFICATION_PERMISSIONS, display_name_); } base::string16 NotificationPermissionRequest::GetMessageTextFragment() const { return l10n_util::GetStringUTF16(IDS_NOTIFICATION_PERMISSIONS_FRAGMENT); } bool NotificationPermissionRequest::HasUserGesture() const { // Currently notification permission requests are only issued on // user gesture. return true; } GURL NotificationPermissionRequest::GetRequestingHostname() const { return origin_; } void NotificationPermissionRequest::PermissionGranted() { action_taken_ = true; UMA_HISTOGRAM_COUNTS("NotificationPermissionRequest.Allowed", 1); notification_service_->GrantPermission(origin_); } void NotificationPermissionRequest::PermissionDenied() { action_taken_ = true; UMA_HISTOGRAM_COUNTS("NotificationPermissionRequest.Denied", 1); notification_service_->DenyPermission(origin_); } void NotificationPermissionRequest::Cancelled() { } void NotificationPermissionRequest::RequestFinished() { if (!action_taken_) UMA_HISTOGRAM_COUNTS("NotificationPermissionRequest.Ignored", 1); RenderViewHost* host = RenderViewHost::FromID(process_id_, route_id_); if (host) host->DesktopNotificationPermissionRequestDone(callback_context_); delete this; } // 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( 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); ExtensionWelcomeNotification::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 subst; if (icon_url.is_valid()) { resource = IDR_NOTIFICATION_ICON_HTML; subst.push_back(icon_url.spec()); subst.push_back(net::EscapeForHTML(base::UTF16ToUTF8(title))); subst.push_back(net::EscapeForHTML(base::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() ? base::ASCIIToUTF16("description") : base::ASCIIToUTF16("title"); subst.push_back(net::EscapeForHTML(base::UTF16ToUTF8(line_name))); subst.push_back(net::EscapeForHTML(base::UTF16ToUTF8(line))); } else { resource = IDR_NOTIFICATION_2LINE_HTML; subst.push_back(net::EscapeForHTML(base::UTF16ToUTF8(title))); subst.push_back(net::EscapeForHTML(base::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& 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 base::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_)); } 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) { if (PermissionBubbleManager::Enabled()) { PermissionBubbleManager* bubble_manager = PermissionBubbleManager::FromWebContents(contents); bubble_manager->AddRequest(new NotificationPermissionRequest(this, origin, DisplayNameForOriginInProcessId(origin, process_id), process_id, route_id, callback_context)); return; } // 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, this, 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 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) { extensions::ExtensionSet extensions; extension_info_map->GetExtensionsWithAPIPermissionForSecurityOrigin( origin, process_id, extensions::APIPermission::kNotification, &extensions); for (extensions::ExtensionSet::const_iterator iter = extensions.begin(); iter != extensions.end(); ++iter) { NotifierId notifier_id(NotifierId::APPLICATION, (*iter)->id()); if (IsNotifierEnabled(notifier_id)) return base::UTF8ToUTF16((*iter)->name()); } } } return base::UTF8ToUTF16(origin.host()); } void DesktopNotificationService::NotifySettingsChange() { content::NotificationService::current()->Notify( chrome::NOTIFICATION_DESKTOP_NOTIFICATION_SETTINGS_CHANGED, content::Source(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 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 (!chrome_now_welcome_notification_ && message_center::IsRichNotificationEnabled()) { chrome_now_welcome_notification_ = ExtensionWelcomeNotification::Create(kChromeNowExtensionID, profile_); } if (chrome_now_welcome_notification_) { chrome_now_welcome_notification_->ShowWelcomeNotificationIfNecessary( notification); } } void DesktopNotificationService::OnStringListPrefChanged( const char* pref_name, std::set* ids_field) { ids_field->clear(); // Separate GetPrefs()->GetList() to analyze the crash. See crbug.com/322320 const PrefService* pref_service = profile_->GetPrefs(); CHECK(pref_service); const base::ListValue* pref_list = pref_service->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(details).ptr(); NotifierId notifier_id(NotifierId::APPLICATION, extension->id()); if (IsNotifierEnabled(notifier_id)) return; // The settings for ephemeral apps will be persisted across cache evictions. if (extension->is_ephemeral()) 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 args(new base::ListValue()); args->Append(new base::StringValue( extensions::api::notifications::ToString(permission))); scoped_ptr 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)); }