1 // Copyright (c) 2016 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/web_page_notifier_controller.h"
6 
7 #include "ash/public/cpp/notifier_metadata.h"
8 #include "base/bind.h"
9 #include "base/logging.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "base/task/cancelable_task_tracker.h"
12 #include "chrome/browser/content_settings/host_content_settings_map_factory.h"
13 #include "chrome/browser/favicon/favicon_service_factory.h"
14 #include "chrome/browser/notifications/notification_permission_context.h"
15 #include "chrome/browser/notifications/notifier_state_tracker.h"
16 #include "chrome/browser/notifications/notifier_state_tracker_factory.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "components/content_settings/core/browser/host_content_settings_map.h"
19 #include "components/content_settings/core/common/content_settings.h"
20 #include "components/favicon/core/favicon_service.h"
21 
WebPageNotifierController(Observer * observer)22 WebPageNotifierController::WebPageNotifierController(Observer* observer)
23     : observer_(observer) {}
24 
~WebPageNotifierController()25 WebPageNotifierController::~WebPageNotifierController() {}
26 
GetNotifierList(Profile * profile)27 std::vector<ash::NotifierMetadata> WebPageNotifierController::GetNotifierList(
28     Profile* profile) {
29   std::vector<ash::NotifierMetadata> notifiers;
30 
31   ContentSettingsForOneType settings;
32   HostContentSettingsMapFactory::GetForProfile(profile)->GetSettingsForOneType(
33       ContentSettingsType::NOTIFICATIONS, &settings);
34 
35   favicon::FaviconService* const favicon_service =
36       FaviconServiceFactory::GetForProfile(profile,
37                                            ServiceAccessType::EXPLICIT_ACCESS);
38   favicon_tracker_.reset(new base::CancelableTaskTracker());
39   patterns_.clear();
40   for (ContentSettingsForOneType::const_iterator iter = settings.begin();
41        iter != settings.end(); ++iter) {
42     if (iter->primary_pattern == ContentSettingsPattern::Wildcard() &&
43         iter->secondary_pattern == ContentSettingsPattern::Wildcard() &&
44         iter->source != "preference") {
45       continue;
46     }
47 
48     std::string url_pattern = iter->primary_pattern.ToString();
49     base::string16 name = base::UTF8ToUTF16(url_pattern);
50     GURL url(url_pattern);
51     message_center::NotifierId notifier_id(url);
52     NotifierStateTracker* const notifier_state_tracker =
53         NotifierStateTrackerFactory::GetForProfile(profile);
54     content_settings::SettingInfo info;
55     HostContentSettingsMapFactory::GetForProfile(profile)->GetWebsiteSetting(
56         url, GURL(), ContentSettingsType::NOTIFICATIONS, &info);
57     notifiers.emplace_back(
58         notifier_id, name,
59         notifier_state_tracker->IsNotifierEnabled(notifier_id),
60         info.source == content_settings::SETTING_SOURCE_POLICY,
61         gfx::ImageSkia());
62     patterns_[url_pattern] = iter->primary_pattern;
63     // Note that favicon service obtains the favicon from history. This means
64     // that it will fail to obtain the image if there are no history data for
65     // that URL.
66     favicon_service->GetFaviconImageForPageURL(
67         url,
68         base::BindOnce(&WebPageNotifierController::OnFaviconLoaded,
69                        base::Unretained(this), url),
70         favicon_tracker_.get());
71   }
72 
73   return notifiers;
74 }
75 
SetNotifierEnabled(Profile * profile,const message_center::NotifierId & notifier_id,bool enabled)76 void WebPageNotifierController::SetNotifierEnabled(
77     Profile* profile,
78     const message_center::NotifierId& notifier_id,
79     bool enabled) {
80   // WEB_PAGE notifier cannot handle in DesktopNotificationService
81   // since it has the exact URL pattern.
82   // TODO(mukai): fix this.
83   ContentSetting default_setting =
84       HostContentSettingsMapFactory::GetForProfile(profile)
85           ->GetDefaultContentSetting(ContentSettingsType::NOTIFICATIONS, NULL);
86 
87   DCHECK(default_setting == CONTENT_SETTING_ALLOW ||
88          default_setting == CONTENT_SETTING_BLOCK ||
89          default_setting == CONTENT_SETTING_ASK);
90 
91   // The content setting for notifications needs to clear when it changes to
92   // the default value or get explicitly set when it differs from the
93   // default.
94   bool differs_from_default_value =
95       (default_setting != CONTENT_SETTING_ALLOW && enabled) ||
96       (default_setting == CONTENT_SETTING_ALLOW && !enabled);
97 
98   if (differs_from_default_value) {
99     if (notifier_id.url.is_valid()) {
100       NotificationPermissionContext::UpdatePermission(
101           profile, notifier_id.url,
102           enabled ? CONTENT_SETTING_ALLOW : CONTENT_SETTING_BLOCK);
103     } else {
104       LOG(ERROR) << "Invalid url pattern: "
105                  << notifier_id.url.possibly_invalid_spec();
106     }
107   } else {
108     ContentSettingsPattern pattern;
109 
110     const auto& iter = patterns_.find(notifier_id.url.possibly_invalid_spec());
111     if (iter != patterns_.end()) {
112       pattern = iter->second;
113     } else if (notifier_id.url.is_valid()) {
114       pattern = ContentSettingsPattern::FromURLNoWildcard(notifier_id.url);
115     } else {
116       LOG(ERROR) << "Invalid url pattern: "
117                  << notifier_id.url.possibly_invalid_spec();
118     }
119 
120     if (pattern.IsValid()) {
121       // Note that we don't use
122       // NotificationPermissionContext::UpdatePermission()
123       // here because pattern might be from user manual input and not match
124       // the default one used by ClearSetting().
125       HostContentSettingsMapFactory::GetForProfile(profile)
126           ->SetContentSettingCustomScope(
127               pattern, ContentSettingsPattern::Wildcard(),
128               ContentSettingsType::NOTIFICATIONS, CONTENT_SETTING_DEFAULT);
129     }
130   }
131 
132   observer_->OnNotifierEnabledChanged(notifier_id, enabled);
133 }
134 
OnFaviconLoaded(const GURL & url,const favicon_base::FaviconImageResult & favicon_result)135 void WebPageNotifierController::OnFaviconLoaded(
136     const GURL& url,
137     const favicon_base::FaviconImageResult& favicon_result) {
138   observer_->OnIconImageUpdated(message_center::NotifierId(url),
139                                 favicon_result.image.AsImageSkia());
140 }
141