blob: ef91c33e1e5a78e8781d91b51d04cef4c42af216 [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"
Ben Murdochbb1529c2013-08-08 10:24:53 +010012#include "chrome/browser/browser_process.h"
Ben Murdocha3f7b4e2013-07-24 10:36:34 +010013#include "chrome/browser/chrome_notification_types.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000014#include "chrome/browser/extensions/app_icon_loader_impl.h"
15#include "chrome/browser/extensions/extension_service.h"
16#include "chrome/browser/favicon/favicon_service.h"
17#include "chrome/browser/favicon/favicon_service_factory.h"
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +010018#include "chrome/browser/history/history_types.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000019#include "chrome/browser/notifications/desktop_notification_service.h"
20#include "chrome/browser/notifications/desktop_notification_service_factory.h"
Ben Murdocheb525c52013-07-10 11:40:50 +010021#include "chrome/browser/notifications/sync_notifier/chrome_notifier_service.h"
22#include "chrome/browser/notifications/sync_notifier/chrome_notifier_service_factory.h"
Ben Murdochbb1529c2013-08-08 10:24:53 +010023#include "chrome/browser/profiles/profile.h"
24#include "chrome/browser/profiles/profile_info_cache.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000025#include "chrome/browser/profiles/profile_manager.h"
26#include "chrome/common/cancelable_task_tracker.h"
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010027#include "chrome/common/extensions/extension_constants.h"
Ben Murdoch9ab55632013-07-18 11:57:30 +010028#include "chrome/common/favicon/favicon_types.h"
Ben Murdocha3f7b4e2013-07-24 10:36:34 +010029#include "content/public/browser/notification_service.h"
30#include "content/public/browser/notification_source.h"
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010031#include "grit/theme_resources.h"
32#include "grit/ui_strings.h"
33#include "ui/base/l10n/l10n_util.h"
34#include "ui/base/resource/resource_bundle.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000035#include "ui/gfx/image/image.h"
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +010036#include "ui/message_center/message_center_style.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000037
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
Ben Murdochbb1529c2013-08-08 10:24:53 +010041namespace message_center {
42class ProfileNotifierGroup : public message_center::NotifierGroup {
43 public:
44 ProfileNotifierGroup(const gfx::Image& icon,
45 const string16& display_name,
46 const string16& login_info,
47 size_t index,
48 const base::FilePath& profile_path);
49 virtual ~ProfileNotifierGroup() {}
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010050
Ben Murdochbb1529c2013-08-08 10:24:53 +010051 Profile* profile() const { return profile_; }
52
53 private:
54 Profile* profile_;
55};
56
57ProfileNotifierGroup::ProfileNotifierGroup(const gfx::Image& icon,
58 const string16& display_name,
59 const string16& login_info,
60 size_t index,
61 const base::FilePath& profile_path)
62 : message_center::NotifierGroup(icon, display_name, login_info, index),
63 profile_(NULL) {
64 // Try to get the profile
65 profile_ =
66 g_browser_process->profile_manager()->GetProfileByPath(profile_path);
67}
68} // namespace message_center
69
70namespace {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010071class NotifierComparator {
72 public:
73 explicit NotifierComparator(icu::Collator* collator) : collator_(collator) {}
74
75 bool operator() (Notifier* n1, Notifier* n2) {
76 return base::i18n::CompareString16WithCollator(
77 collator_, n1->name, n2->name) == UCOL_LESS;
78 }
79
80 private:
81 icu::Collator* collator_;
82};
83
84bool SimpleCompareNotifiers(Notifier* n1, Notifier* n2) {
85 return n1->name < n2->name;
86}
87
88} // namespace
89
Ben Murdochbb1529c2013-08-08 10:24:53 +010090MessageCenterSettingsController::MessageCenterSettingsController(
91 ProfileInfoCache* profile_info_cache)
92 : current_notifier_group_(0), profile_info_cache_(profile_info_cache) {
93 DCHECK(profile_info_cache_);
94 // The following events all represent changes that may need to be reflected in
95 // the profile selector context menu, so listen for them all. We'll just
96 // rebuild the list when we get any of them.
97 registrar_.Add(this,
98 chrome::NOTIFICATION_PROFILE_CREATED,
99 content::NotificationService::AllBrowserContextsAndSources());
100 registrar_.Add(this,
101 chrome::NOTIFICATION_PROFILE_ADDED,
102 content::NotificationService::AllBrowserContextsAndSources());
103 registrar_.Add(this,
104 chrome::NOTIFICATION_PROFILE_DESTROYED,
105 content::NotificationService::AllBrowserContextsAndSources());
106 registrar_.Add(this,
107 chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED,
108 content::NotificationService::AllBrowserContextsAndSources());
109 RebuildNotifierGroups();
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000110}
111
112MessageCenterSettingsController::~MessageCenterSettingsController() {
113}
114
Ben Murdocheb525c52013-07-10 11:40:50 +0100115void MessageCenterSettingsController::AddObserver(
116 message_center::NotifierSettingsObserver* observer) {
117 observers_.AddObserver(observer);
118}
119
120void MessageCenterSettingsController::RemoveObserver(
121 message_center::NotifierSettingsObserver* observer) {
122 observers_.RemoveObserver(observer);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000123}
124
Ben Murdochbb1529c2013-08-08 10:24:53 +0100125size_t MessageCenterSettingsController::GetNotifierGroupCount() const {
126 return notifier_groups_.size();
127}
128
129const message_center::NotifierGroup&
130MessageCenterSettingsController::GetNotifierGroupAt(size_t index) const {
131 DCHECK_LT(index, notifier_groups_.size());
132 return *(notifier_groups_[index]);
133}
134
135const message_center::NotifierGroup&
136MessageCenterSettingsController::GetActiveNotifierGroup() const {
137 DCHECK_LT(current_notifier_group_, notifier_groups_.size());
138 return *(notifier_groups_[current_notifier_group_]);
139}
140
141void MessageCenterSettingsController::SwitchToNotifierGroup(size_t index) {
142 DCHECK_LT(index, notifier_groups_.size());
143 if (current_notifier_group_ == index)
144 return;
145
146 current_notifier_group_ = index;
147 FOR_EACH_OBSERVER(message_center::NotifierSettingsObserver,
148 observers_,
149 NotifierGroupChanged());
150}
151
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000152void MessageCenterSettingsController::GetNotifierList(
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100153 std::vector<Notifier*>* notifiers) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000154 DCHECK(notifiers);
155 // TODO(mukai): Fix this for multi-profile.
Ben Murdocheb525c52013-07-10 11:40:50 +0100156 // Temporarily use the last used profile to prevent chrome from crashing when
157 // the default profile is not loaded.
Ben Murdochbb1529c2013-08-08 10:24:53 +0100158 message_center::ProfileNotifierGroup* group =
159 notifier_groups_[current_notifier_group_];
160 Profile* profile = group->profile();
161 if (!profile)
162 return;
163
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000164 DesktopNotificationService* notification_service =
Ben Murdochbb1529c2013-08-08 10:24:53 +0100165 DesktopNotificationServiceFactory::GetForProfile(profile);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000166
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100167 UErrorCode error;
168 scoped_ptr<icu::Collator> collator(icu::Collator::createInstance(error));
169 scoped_ptr<NotifierComparator> comparator;
170 if (!U_FAILURE(error))
171 comparator.reset(new NotifierComparator(collator.get()));
172
Ben Murdochbb1529c2013-08-08 10:24:53 +0100173 ExtensionService* extension_service = profile->GetExtensionService();
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000174 const ExtensionSet* extension_set = extension_service->extensions();
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100175 // The extension icon size has to be 32x32 at least to load bigger icons if
176 // the icon doesn't exist for the specified size, and in that case it falls
177 // back to the default icon. The fetched icon will be resized in the settings
178 // dialog. See chrome/browser/extensions/extension_icon_image.cc and
179 // crbug.com/222931
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000180 app_icon_loader_.reset(new extensions::AppIconLoaderImpl(
Ben Murdochbb1529c2013-08-08 10:24:53 +0100181 profile, extension_misc::EXTENSION_ICON_SMALL, this));
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000182 for (ExtensionSet::const_iterator iter = extension_set->begin();
Ben Murdochbb1529c2013-08-08 10:24:53 +0100183 iter != extension_set->end();
184 ++iter) {
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100185 const extensions::Extension* extension = iter->get();
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000186 if (!extension->HasAPIPermission(
Torne (Richard Coles)7d4cd472013-06-19 11:58:07 +0100187 extensions::APIPermission::kNotification)) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000188 continue;
189 }
190
Ben Murdocheb525c52013-07-10 11:40:50 +0100191 NotifierId notifier_id(NotifierId::APPLICATION, extension->id());
192 notifiers->push_back(new Notifier(
193 notifier_id,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000194 UTF8ToUTF16(extension->name()),
Ben Murdocheb525c52013-07-10 11:40:50 +0100195 notification_service->IsNotifierEnabled(notifier_id)));
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000196 app_icon_loader_->FetchImage(extension->id());
197 }
Ben Murdocheb525c52013-07-10 11:40:50 +0100198
199 if (notifier::ChromeNotifierServiceFactory::UseSyncedNotifications(
200 CommandLine::ForCurrentProcess())) {
201 notifier::ChromeNotifierService* sync_notifier_service =
202 notifier::ChromeNotifierServiceFactory::GetInstance()->GetForProfile(
Ben Murdochbb1529c2013-08-08 10:24:53 +0100203 profile, Profile::EXPLICIT_ACCESS);
Ben Murdocheb525c52013-07-10 11:40:50 +0100204 sync_notifier_service->GetSyncedNotificationServices(notifiers);
205
206 if (comparator)
207 std::sort(notifiers->begin(), notifiers->end(), *comparator);
208 else
209 std::sort(notifiers->begin(), notifiers->end(), SimpleCompareNotifiers);
210 }
211
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100212 int app_count = notifiers->size();
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000213
214 ContentSettingsForOneType settings;
215 notification_service->GetNotificationsSettings(&settings);
Ben Murdochbb1529c2013-08-08 10:24:53 +0100216 FaviconService* favicon_service =
217 FaviconServiceFactory::GetForProfile(profile, Profile::EXPLICIT_ACCESS);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000218 favicon_tracker_.reset(new CancelableTaskTracker());
219 patterns_.clear();
220 for (ContentSettingsForOneType::const_iterator iter = settings.begin();
221 iter != settings.end(); ++iter) {
222 if (iter->primary_pattern == ContentSettingsPattern::Wildcard() &&
223 iter->secondary_pattern == ContentSettingsPattern::Wildcard() &&
224 iter->source != "preference") {
225 continue;
226 }
227
228 std::string url_pattern = iter->primary_pattern.ToString();
229 string16 name = UTF8ToUTF16(url_pattern);
230 GURL url(url_pattern);
Ben Murdocheb525c52013-07-10 11:40:50 +0100231 NotifierId notifier_id(url);
232 notifiers->push_back(new Notifier(
233 notifier_id,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000234 name,
Ben Murdocheb525c52013-07-10 11:40:50 +0100235 notification_service->IsNotifierEnabled(notifier_id)));
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000236 patterns_[name] = iter->primary_pattern;
237 FaviconService::FaviconForURLParams favicon_params(
Ben Murdochbb1529c2013-08-08 10:24:53 +0100238 profile,
239 url,
240 chrome::FAVICON | chrome::TOUCH_ICON,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000241 message_center::kSettingsIconSize);
242 // Note that favicon service obtains the favicon from history. This means
243 // that it will fail to obtain the image if there are no history data for
244 // that URL.
245 favicon_service->GetFaviconImageForURL(
246 favicon_params,
247 base::Bind(&MessageCenterSettingsController::OnFaviconLoaded,
248 base::Unretained(this), url),
249 favicon_tracker_.get());
250 }
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100251
252 // Screenshot notification feature is only for ChromeOS. See crbug.com/238358
253#if defined(OS_CHROMEOS)
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100254 const string16 screenshot_name =
255 l10n_util::GetStringUTF16(IDS_MESSAGE_CENTER_NOTIFIER_SCREENSHOT_NAME);
Ben Murdocheb525c52013-07-10 11:40:50 +0100256 NotifierId screenshot_notifier_id(NotifierId::SCREENSHOT);
257 Notifier* const screenshot_notifier = new Notifier(
258 screenshot_notifier_id,
259 screenshot_name,
260 notification_service->IsNotifierEnabled(screenshot_notifier_id));
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100261 screenshot_notifier->icon =
262 ui::ResourceBundle::GetSharedInstance().GetImageNamed(
263 IDR_SCREENSHOT_NOTIFICATION_ICON);
264 notifiers->push_back(screenshot_notifier);
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100265#endif
266
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100267 if (comparator) {
268 std::sort(notifiers->begin() + app_count, notifiers->end(), *comparator);
269 } else {
270 std::sort(notifiers->begin() + app_count, notifiers->end(),
271 SimpleCompareNotifiers);
272 }
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000273}
274
275void MessageCenterSettingsController::SetNotifierEnabled(
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100276 const Notifier& notifier,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000277 bool enabled) {
Ben Murdochbb1529c2013-08-08 10:24:53 +0100278 Profile* profile = notifier_groups_[current_notifier_group_]->profile();
279 DCHECK(profile);
280
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000281 DesktopNotificationService* notification_service =
Ben Murdochbb1529c2013-08-08 10:24:53 +0100282 DesktopNotificationServiceFactory::GetForProfile(profile);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000283
Ben Murdocheb525c52013-07-10 11:40:50 +0100284 if (notifier.notifier_id.type == NotifierId::WEB_PAGE) {
285 // WEB_PAGE notifier cannot handle in DesktopNotificationService
286 // since it has the exact URL pattern.
287 // TODO(mukai): fix this.
288 ContentSetting default_setting =
289 notification_service->GetDefaultContentSetting(NULL);
290 DCHECK(default_setting == CONTENT_SETTING_ALLOW ||
291 default_setting == CONTENT_SETTING_BLOCK ||
292 default_setting == CONTENT_SETTING_ASK);
293 if ((enabled && default_setting != CONTENT_SETTING_ALLOW) ||
294 (!enabled && default_setting == CONTENT_SETTING_ALLOW)) {
295 if (notifier.notifier_id.url.is_valid()) {
296 if (enabled)
297 notification_service->GrantPermission(notifier.notifier_id.url);
298 else
299 notification_service->DenyPermission(notifier.notifier_id.url);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000300 } else {
Ben Murdocheb525c52013-07-10 11:40:50 +0100301 LOG(ERROR) << "Invalid url pattern: "
302 << notifier.notifier_id.url.spec();
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000303 }
Ben Murdocheb525c52013-07-10 11:40:50 +0100304 } else {
305 std::map<string16, ContentSettingsPattern>::const_iterator iter =
306 patterns_.find(notifier.name);
307 if (iter != patterns_.end()) {
308 notification_service->ClearSetting(iter->second);
309 } else {
310 LOG(ERROR) << "Invalid url pattern: "
311 << notifier.notifier_id.url.spec();
312 }
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000313 }
Ben Murdocheb525c52013-07-10 11:40:50 +0100314 } else {
315 notification_service->SetNotifierEnabled(notifier.notifier_id, enabled);
316 if (notifier.notifier_id.type == NotifierId::SYNCED_NOTIFICATION_SERVICE) {
317 notifier::ChromeNotifierService* notifier_service =
318 notifier::ChromeNotifierServiceFactory::GetInstance()->GetForProfile(
Ben Murdochbb1529c2013-08-08 10:24:53 +0100319 profile, Profile::EXPLICIT_ACCESS);
Ben Murdocheb525c52013-07-10 11:40:50 +0100320 notifier_service->OnSyncedNotificationServiceEnabled(
321 notifier.notifier_id.id, enabled);
322 }
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000323 }
324}
325
326void MessageCenterSettingsController::OnNotifierSettingsClosing() {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000327 DCHECK(favicon_tracker_.get());
328 favicon_tracker_->TryCancelAll();
329 patterns_.clear();
330}
331
332void MessageCenterSettingsController::OnFaviconLoaded(
333 const GURL& url,
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100334 const chrome::FaviconImageResult& favicon_result) {
Ben Murdocheb525c52013-07-10 11:40:50 +0100335 FOR_EACH_OBSERVER(message_center::NotifierSettingsObserver,
336 observers_,
337 UpdateIconImage(NotifierId(url), favicon_result.image));
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000338}
339
340
341void MessageCenterSettingsController::SetAppImage(const std::string& id,
342 const gfx::ImageSkia& image) {
Ben Murdocheb525c52013-07-10 11:40:50 +0100343 FOR_EACH_OBSERVER(message_center::NotifierSettingsObserver,
344 observers_,
345 UpdateIconImage(NotifierId(NotifierId::APPLICATION, id),
346 gfx::Image(image)));
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000347}
Ben Murdochbb1529c2013-08-08 10:24:53 +0100348
349void MessageCenterSettingsController::Observe(
350 int type,
351 const content::NotificationSource& source,
352 const content::NotificationDetails& details) {
353 RebuildNotifierGroups();
354 FOR_EACH_OBSERVER(message_center::NotifierSettingsObserver,
355 observers_,
356 NotifierGroupChanged());
357}
358
359void MessageCenterSettingsController::RebuildNotifierGroups() {
360 notifier_groups_.clear();
361 current_notifier_group_ = 0;
362
363 const size_t count = profile_info_cache_->GetNumberOfProfiles();
364 for (size_t i = 0; i < count; ++i) {
365 message_center::ProfileNotifierGroup* group =
366 new message_center::ProfileNotifierGroup(
367 profile_info_cache_->GetAvatarIconOfProfileAtIndex(i),
368 profile_info_cache_->GetNameOfProfileAtIndex(i),
369 profile_info_cache_->GetUserNameOfProfileAtIndex(i),
370 i,
371 profile_info_cache_->GetPathOfProfileAtIndex(i));
372 if (group->profile() != NULL) {
373 notifier_groups_.push_back(group);
374 }
375 }
376}