blob: 9acefdf1a8505890fa2297fcc7896d8f908c87a1 [file] [log] [blame]
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001// Copyright (c) 2013 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/message_center_settings_controller.h"
6
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +01007#include <algorithm>
8
Ben Murdocheb525c52013-07-10 11:40:50 +01009#include "base/command_line.h"
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010010#include "base/i18n/string_compare.h"
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010011#include "base/strings/utf_string_conversions.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000012#include "chrome/browser/extensions/app_icon_loader_impl.h"
13#include "chrome/browser/extensions/extension_service.h"
14#include "chrome/browser/favicon/favicon_service.h"
15#include "chrome/browser/favicon/favicon_service_factory.h"
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +010016#include "chrome/browser/favicon/favicon_types.h"
17#include "chrome/browser/history/history_types.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000018#include "chrome/browser/notifications/desktop_notification_service.h"
19#include "chrome/browser/notifications/desktop_notification_service_factory.h"
Ben Murdocheb525c52013-07-10 11:40:50 +010020#include "chrome/browser/notifications/sync_notifier/chrome_notifier_service.h"
21#include "chrome/browser/notifications/sync_notifier/chrome_notifier_service_factory.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000022#include "chrome/browser/profiles/profile.h"
23#include "chrome/browser/profiles/profile_manager.h"
24#include "chrome/common/cancelable_task_tracker.h"
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010025#include "chrome/common/extensions/extension_constants.h"
26#include "grit/theme_resources.h"
27#include "grit/ui_strings.h"
28#include "ui/base/l10n/l10n_util.h"
29#include "ui/base/resource/resource_bundle.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000030#include "ui/gfx/image/image.h"
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +010031#include "ui/message_center/message_center_style.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000032
Ben Murdocheb525c52013-07-10 11:40:50 +010033#if defined(USE_ASH)
34#include "ash/shell.h"
35#include "ash/system/web_notification/web_notification_tray.h"
36#endif
37
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010038using message_center::Notifier;
Ben Murdocheb525c52013-07-10 11:40:50 +010039using message_center::NotifierId;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010040
41namespace {
42
43class NotifierComparator {
44 public:
45 explicit NotifierComparator(icu::Collator* collator) : collator_(collator) {}
46
47 bool operator() (Notifier* n1, Notifier* n2) {
48 return base::i18n::CompareString16WithCollator(
49 collator_, n1->name, n2->name) == UCOL_LESS;
50 }
51
52 private:
53 icu::Collator* collator_;
54};
55
56bool SimpleCompareNotifiers(Notifier* n1, Notifier* n2) {
57 return n1->name < n2->name;
58}
59
60} // namespace
61
Ben Murdocheb525c52013-07-10 11:40:50 +010062MessageCenterSettingsController::MessageCenterSettingsController() {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000063}
64
65MessageCenterSettingsController::~MessageCenterSettingsController() {
66}
67
Ben Murdocheb525c52013-07-10 11:40:50 +010068void MessageCenterSettingsController::AddObserver(
69 message_center::NotifierSettingsObserver* observer) {
70 observers_.AddObserver(observer);
71}
72
73void MessageCenterSettingsController::RemoveObserver(
74 message_center::NotifierSettingsObserver* observer) {
75 observers_.RemoveObserver(observer);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000076}
77
78void MessageCenterSettingsController::GetNotifierList(
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010079 std::vector<Notifier*>* notifiers) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000080 DCHECK(notifiers);
81 // TODO(mukai): Fix this for multi-profile.
Ben Murdocheb525c52013-07-10 11:40:50 +010082 // Temporarily use the last used profile to prevent chrome from crashing when
83 // the default profile is not loaded.
84 Profile* profile = ProfileManager::GetLastUsedProfileAllowedByPolicy();
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000085 DesktopNotificationService* notification_service =
86 DesktopNotificationServiceFactory::GetForProfile(profile);
87
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010088 UErrorCode error;
89 scoped_ptr<icu::Collator> collator(icu::Collator::createInstance(error));
90 scoped_ptr<NotifierComparator> comparator;
91 if (!U_FAILURE(error))
92 comparator.reset(new NotifierComparator(collator.get()));
93
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000094 ExtensionService* extension_service = profile->GetExtensionService();
95 const ExtensionSet* extension_set = extension_service->extensions();
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010096 // The extension icon size has to be 32x32 at least to load bigger icons if
97 // the icon doesn't exist for the specified size, and in that case it falls
98 // back to the default icon. The fetched icon will be resized in the settings
99 // dialog. See chrome/browser/extensions/extension_icon_image.cc and
100 // crbug.com/222931
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000101 app_icon_loader_.reset(new extensions::AppIconLoaderImpl(
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100102 profile, extension_misc::EXTENSION_ICON_SMALL, this));
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000103 for (ExtensionSet::const_iterator iter = extension_set->begin();
104 iter != extension_set->end(); ++iter) {
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100105 const extensions::Extension* extension = iter->get();
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000106 if (!extension->HasAPIPermission(
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100107 extensions::APIPermission::kNotification)) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000108 continue;
109 }
110
Ben Murdocheb525c52013-07-10 11:40:50 +0100111 NotifierId notifier_id(NotifierId::APPLICATION, extension->id());
112 notifiers->push_back(new Notifier(
113 notifier_id,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000114 UTF8ToUTF16(extension->name()),
Ben Murdocheb525c52013-07-10 11:40:50 +0100115 notification_service->IsNotifierEnabled(notifier_id)));
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000116 app_icon_loader_->FetchImage(extension->id());
117 }
Ben Murdocheb525c52013-07-10 11:40:50 +0100118
119 if (notifier::ChromeNotifierServiceFactory::UseSyncedNotifications(
120 CommandLine::ForCurrentProcess())) {
121 notifier::ChromeNotifierService* sync_notifier_service =
122 notifier::ChromeNotifierServiceFactory::GetInstance()->GetForProfile(
123 profile, Profile::EXPLICIT_ACCESS);
124 sync_notifier_service->GetSyncedNotificationServices(notifiers);
125
126 if (comparator)
127 std::sort(notifiers->begin(), notifiers->end(), *comparator);
128 else
129 std::sort(notifiers->begin(), notifiers->end(), SimpleCompareNotifiers);
130 }
131
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100132 int app_count = notifiers->size();
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000133
134 ContentSettingsForOneType settings;
135 notification_service->GetNotificationsSettings(&settings);
136 FaviconService* favicon_service = FaviconServiceFactory::GetForProfile(
137 profile, Profile::EXPLICIT_ACCESS);
138 favicon_tracker_.reset(new CancelableTaskTracker());
139 patterns_.clear();
140 for (ContentSettingsForOneType::const_iterator iter = settings.begin();
141 iter != settings.end(); ++iter) {
142 if (iter->primary_pattern == ContentSettingsPattern::Wildcard() &&
143 iter->secondary_pattern == ContentSettingsPattern::Wildcard() &&
144 iter->source != "preference") {
145 continue;
146 }
147
148 std::string url_pattern = iter->primary_pattern.ToString();
149 string16 name = UTF8ToUTF16(url_pattern);
150 GURL url(url_pattern);
Ben Murdocheb525c52013-07-10 11:40:50 +0100151 NotifierId notifier_id(url);
152 notifiers->push_back(new Notifier(
153 notifier_id,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000154 name,
Ben Murdocheb525c52013-07-10 11:40:50 +0100155 notification_service->IsNotifierEnabled(notifier_id)));
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000156 patterns_[name] = iter->primary_pattern;
157 FaviconService::FaviconForURLParams favicon_params(
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100158 profile, url, chrome::FAVICON | chrome::TOUCH_ICON,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000159 message_center::kSettingsIconSize);
160 // Note that favicon service obtains the favicon from history. This means
161 // that it will fail to obtain the image if there are no history data for
162 // that URL.
163 favicon_service->GetFaviconImageForURL(
164 favicon_params,
165 base::Bind(&MessageCenterSettingsController::OnFaviconLoaded,
166 base::Unretained(this), url),
167 favicon_tracker_.get());
168 }
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100169
170 // Screenshot notification feature is only for ChromeOS. See crbug.com/238358
171#if defined(OS_CHROMEOS)
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100172 const string16 screenshot_name =
173 l10n_util::GetStringUTF16(IDS_MESSAGE_CENTER_NOTIFIER_SCREENSHOT_NAME);
Ben Murdocheb525c52013-07-10 11:40:50 +0100174 NotifierId screenshot_notifier_id(NotifierId::SCREENSHOT);
175 Notifier* const screenshot_notifier = new Notifier(
176 screenshot_notifier_id,
177 screenshot_name,
178 notification_service->IsNotifierEnabled(screenshot_notifier_id));
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100179 screenshot_notifier->icon =
180 ui::ResourceBundle::GetSharedInstance().GetImageNamed(
181 IDR_SCREENSHOT_NOTIFICATION_ICON);
182 notifiers->push_back(screenshot_notifier);
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100183#endif
184
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100185 if (comparator) {
186 std::sort(notifiers->begin() + app_count, notifiers->end(), *comparator);
187 } else {
188 std::sort(notifiers->begin() + app_count, notifiers->end(),
189 SimpleCompareNotifiers);
190 }
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000191}
192
193void MessageCenterSettingsController::SetNotifierEnabled(
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100194 const Notifier& notifier,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000195 bool enabled) {
196 // TODO(mukai): Fix this for multi-profile.
197 Profile* profile = ProfileManager::GetDefaultProfile();
198 DesktopNotificationService* notification_service =
199 DesktopNotificationServiceFactory::GetForProfile(profile);
200
Ben Murdocheb525c52013-07-10 11:40:50 +0100201 if (notifier.notifier_id.type == NotifierId::WEB_PAGE) {
202 // WEB_PAGE notifier cannot handle in DesktopNotificationService
203 // since it has the exact URL pattern.
204 // TODO(mukai): fix this.
205 ContentSetting default_setting =
206 notification_service->GetDefaultContentSetting(NULL);
207 DCHECK(default_setting == CONTENT_SETTING_ALLOW ||
208 default_setting == CONTENT_SETTING_BLOCK ||
209 default_setting == CONTENT_SETTING_ASK);
210 if ((enabled && default_setting != CONTENT_SETTING_ALLOW) ||
211 (!enabled && default_setting == CONTENT_SETTING_ALLOW)) {
212 if (notifier.notifier_id.url.is_valid()) {
213 if (enabled)
214 notification_service->GrantPermission(notifier.notifier_id.url);
215 else
216 notification_service->DenyPermission(notifier.notifier_id.url);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000217 } else {
Ben Murdocheb525c52013-07-10 11:40:50 +0100218 LOG(ERROR) << "Invalid url pattern: "
219 << notifier.notifier_id.url.spec();
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000220 }
Ben Murdocheb525c52013-07-10 11:40:50 +0100221 } else {
222 std::map<string16, ContentSettingsPattern>::const_iterator iter =
223 patterns_.find(notifier.name);
224 if (iter != patterns_.end()) {
225 notification_service->ClearSetting(iter->second);
226 } else {
227 LOG(ERROR) << "Invalid url pattern: "
228 << notifier.notifier_id.url.spec();
229 }
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000230 }
Ben Murdocheb525c52013-07-10 11:40:50 +0100231 } else {
232 notification_service->SetNotifierEnabled(notifier.notifier_id, enabled);
233 if (notifier.notifier_id.type == NotifierId::SYNCED_NOTIFICATION_SERVICE) {
234 notifier::ChromeNotifierService* notifier_service =
235 notifier::ChromeNotifierServiceFactory::GetInstance()->GetForProfile(
236 profile, Profile::EXPLICIT_ACCESS);
237 notifier_service->OnSyncedNotificationServiceEnabled(
238 notifier.notifier_id.id, enabled);
239 }
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000240 }
241}
242
243void MessageCenterSettingsController::OnNotifierSettingsClosing() {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000244 DCHECK(favicon_tracker_.get());
245 favicon_tracker_->TryCancelAll();
246 patterns_.clear();
247}
248
249void MessageCenterSettingsController::OnFaviconLoaded(
250 const GURL& url,
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100251 const chrome::FaviconImageResult& favicon_result) {
Ben Murdocheb525c52013-07-10 11:40:50 +0100252 FOR_EACH_OBSERVER(message_center::NotifierSettingsObserver,
253 observers_,
254 UpdateIconImage(NotifierId(url), favicon_result.image));
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000255}
256
257
258void MessageCenterSettingsController::SetAppImage(const std::string& id,
259 const gfx::ImageSkia& image) {
Ben Murdocheb525c52013-07-10 11:40:50 +0100260 FOR_EACH_OBSERVER(message_center::NotifierSettingsObserver,
261 observers_,
262 UpdateIconImage(NotifierId(NotifierId::APPLICATION, id),
263 gfx::Image(image)));
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000264}