Adding instrumentation to locate the source of jankiness
[chromium-blink-merge.git] / chrome / browser / notifications / extension_welcome_notification.cc
blob1963c3b0fb925deb2b46789e8bc8aca83e9499ff
1 // Copyright 2014 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.
5 #include "chrome/browser/notifications/extension_welcome_notification.h"
7 #include "base/guid.h"
8 #include "base/lazy_instance.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/prefs/pref_service.h"
11 #include "base/strings/utf_string_conversions.h"
12 #include "chrome/browser/browser_process.h"
13 #include "chrome/browser/notifications/notification.h"
14 #include "chrome/browser/prefs/pref_service_syncable.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/browser/ui/browser_navigator.h"
17 #include "chrome/common/pref_names.h"
18 #include "chrome/common/url_constants.h"
19 #include "chrome/grit/generated_resources.h"
20 #include "components/pref_registry/pref_registry_syncable.h"
21 #include "grit/theme_resources.h"
22 #include "ui/base/l10n/l10n_util.h"
23 #include "ui/base/resource/resource_bundle.h"
24 #include "ui/message_center/message_center.h"
25 #include "ui/message_center/notification.h"
26 #include "ui/message_center/notification_delegate.h"
27 #include "ui/message_center/notification_types.h"
29 const int ExtensionWelcomeNotification::kRequestedShowTimeDays = 14;
30 const char ExtensionWelcomeNotification::kChromeNowExtensionID[] =
31 "pafkbggdmjlpgkdkcbjmhmfcdpncadgh";
33 namespace {
35 class NotificationCallbacks
36 : public message_center::NotificationDelegate {
37 public:
38 NotificationCallbacks(
39 Profile* profile,
40 const message_center::NotifierId notifier_id,
41 const std::string& welcome_notification_id,
42 ExtensionWelcomeNotification::Delegate* delegate)
43 : profile_(profile),
44 notifier_id_(notifier_id.type, notifier_id.id),
45 welcome_notification_id_(welcome_notification_id),
46 delegate_(delegate) {
49 // Overridden from NotificationDelegate:
50 virtual void Display() override {}
51 virtual void Error() override {}
53 virtual void Close(bool by_user) override {
54 if (by_user) {
55 // Setting the preference here may cause the notification erasing
56 // to reenter. Posting a task avoids this issue.
57 delegate_->PostTask(
58 FROM_HERE,
59 base::Bind(&NotificationCallbacks::MarkAsDismissed, this));
63 virtual void Click() override {}
64 virtual void ButtonClick(int index) override {
65 if (index == 0) {
66 OpenNotificationLearnMoreTab();
67 } else if (index == 1) {
68 DisableNotificationProvider();
69 Close(true);
70 } else {
71 NOTREACHED();
75 private:
76 void MarkAsDismissed() {
77 profile_->GetPrefs()->SetBoolean(prefs::kWelcomeNotificationDismissedLocal,
78 true);
81 void OpenNotificationLearnMoreTab() {
82 chrome::NavigateParams params(
83 profile_,
84 GURL(chrome::kNotificationWelcomeLearnMoreURL),
85 ui::PAGE_TRANSITION_LINK);
86 params.disposition = NEW_FOREGROUND_TAB;
87 params.window_action = chrome::NavigateParams::SHOW_WINDOW;
88 chrome::Navigate(&params);
91 void DisableNotificationProvider() {
92 message_center::Notifier notifier(notifier_id_, base::string16(), true);
93 message_center::MessageCenter* message_center =
94 delegate_->GetMessageCenter();
95 message_center->DisableNotificationsByNotifier(notifier_id_);
96 message_center->RemoveNotification(welcome_notification_id_, false);
97 message_center->GetNotifierSettingsProvider()->SetNotifierEnabled(
98 notifier, false);
101 virtual ~NotificationCallbacks() {}
103 Profile* const profile_;
105 const message_center::NotifierId notifier_id_;
107 std::string welcome_notification_id_;
109 // Weak ref owned by ExtensionWelcomeNotification.
110 ExtensionWelcomeNotification::Delegate* const delegate_;
112 DISALLOW_COPY_AND_ASSIGN(NotificationCallbacks);
115 class DefaultDelegate : public ExtensionWelcomeNotification::Delegate {
116 public:
117 DefaultDelegate() {}
119 virtual message_center::MessageCenter* GetMessageCenter() override {
120 return g_browser_process->message_center();
123 virtual base::Time GetCurrentTime() override {
124 return base::Time::Now();
127 virtual void PostTask(
128 const tracked_objects::Location& from_here,
129 const base::Closure& task) override {
130 base::MessageLoop::current()->PostTask(from_here, task);
133 private:
134 DISALLOW_COPY_AND_ASSIGN(DefaultDelegate);
137 } // namespace
139 ExtensionWelcomeNotification::ExtensionWelcomeNotification(
140 Profile* const profile,
141 ExtensionWelcomeNotification::Delegate* const delegate)
142 : notifier_id_(message_center::NotifierId::APPLICATION,
143 kChromeNowExtensionID),
144 profile_(profile),
145 delegate_(delegate) {
146 welcome_notification_dismissed_pref_.Init(
147 prefs::kWelcomeNotificationDismissed,
148 profile_->GetPrefs(),
149 base::Bind(
150 &ExtensionWelcomeNotification::OnWelcomeNotificationDismissedChanged,
151 base::Unretained(this)));
152 welcome_notification_dismissed_local_pref_.Init(
153 prefs::kWelcomeNotificationDismissedLocal,
154 profile_->GetPrefs());
157 // static
158 ExtensionWelcomeNotification* ExtensionWelcomeNotification::Create(
159 Profile* const profile) {
160 return Create(profile, new DefaultDelegate());
163 // static
164 ExtensionWelcomeNotification* ExtensionWelcomeNotification::Create(
165 Profile* const profile, Delegate* const delegate) {
166 return new ExtensionWelcomeNotification(profile, delegate);
169 ExtensionWelcomeNotification::~ExtensionWelcomeNotification() {
170 if (delayed_notification_) {
171 delayed_notification_.reset();
172 PrefServiceSyncable::FromProfile(profile_)->RemoveObserver(this);
173 } else {
174 HideWelcomeNotification();
178 void ExtensionWelcomeNotification::OnIsSyncingChanged() {
179 DCHECK(delayed_notification_);
180 PrefServiceSyncable* const pref_service_syncable =
181 PrefServiceSyncable::FromProfile(profile_);
182 if (pref_service_syncable->IsSyncing()) {
183 pref_service_syncable->RemoveObserver(this);
184 scoped_ptr<Notification> previous_notification(
185 delayed_notification_.release());
186 ShowWelcomeNotificationIfNecessary(*(previous_notification.get()));
190 void ExtensionWelcomeNotification::ShowWelcomeNotificationIfNecessary(
191 const Notification& notification) {
192 if ((notification.notifier_id() == notifier_id_) && !delayed_notification_) {
193 PrefServiceSyncable* const pref_service_syncable =
194 PrefServiceSyncable::FromProfile(profile_);
195 if (pref_service_syncable->IsSyncing()) {
196 PrefService* const pref_service = profile_->GetPrefs();
197 if (!UserHasDismissedWelcomeNotification()) {
198 const PopUpRequest pop_up_request =
199 pref_service->GetBoolean(
200 prefs::kWelcomeNotificationPreviouslyPoppedUp)
201 ? POP_UP_HIDDEN
202 : POP_UP_SHOWN;
203 if (pop_up_request == POP_UP_SHOWN) {
204 pref_service->SetBoolean(
205 prefs::kWelcomeNotificationPreviouslyPoppedUp, true);
208 if (IsWelcomeNotificationExpired()) {
209 ExpireWelcomeNotification();
210 } else {
211 ShowWelcomeNotification(
212 notification.display_source(), pop_up_request);
215 } else {
216 delayed_notification_.reset(new Notification(notification));
217 pref_service_syncable->AddObserver(this);
222 // static
223 void ExtensionWelcomeNotification::RegisterProfilePrefs(
224 user_prefs::PrefRegistrySyncable* prefs) {
225 prefs->RegisterBooleanPref(prefs::kWelcomeNotificationDismissed,
226 false,
227 user_prefs::PrefRegistrySyncable::SYNCABLE_PREF);
228 prefs->RegisterBooleanPref(prefs::kWelcomeNotificationDismissedLocal,
229 false,
230 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
231 prefs->RegisterBooleanPref(prefs::kWelcomeNotificationPreviouslyPoppedUp,
232 false,
233 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
234 prefs->RegisterInt64Pref(prefs::kWelcomeNotificationExpirationTimestamp,
236 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
239 message_center::MessageCenter*
240 ExtensionWelcomeNotification::GetMessageCenter() const {
241 return delegate_->GetMessageCenter();
244 void ExtensionWelcomeNotification::ShowWelcomeNotification(
245 const base::string16& display_source,
246 const PopUpRequest pop_up_request) {
247 message_center::ButtonInfo learn_more(
248 l10n_util::GetStringUTF16(IDS_NOTIFICATION_WELCOME_BUTTON_LEARN_MORE));
249 learn_more.icon = ui::ResourceBundle::GetSharedInstance().GetImageNamed(
250 IDR_NOTIFICATION_WELCOME_LEARN_MORE);
251 message_center::ButtonInfo disable(
252 l10n_util::GetStringUTF16(IDS_NOTIFIER_WELCOME_BUTTON));
253 disable.icon = ui::ResourceBundle::GetSharedInstance().GetImageNamed(
254 IDR_NOTIFIER_BLOCK_BUTTON);
256 message_center::RichNotificationData rich_notification_data;
257 rich_notification_data.priority = 2;
258 rich_notification_data.buttons.push_back(learn_more);
259 rich_notification_data.buttons.push_back(disable);
261 if (welcome_notification_id_.empty())
262 welcome_notification_id_ = base::GenerateGUID();
264 if (!welcome_notification_id_.empty()) {
265 scoped_ptr<message_center::Notification> message_center_notification(
266 new message_center::Notification(
267 message_center::NOTIFICATION_TYPE_BASE_FORMAT,
268 welcome_notification_id_,
269 l10n_util::GetStringUTF16(IDS_NOTIFICATION_WELCOME_TITLE),
270 l10n_util::GetStringUTF16(IDS_NOTIFICATION_WELCOME_BODY),
271 ui::ResourceBundle::GetSharedInstance().GetImageNamed(
272 IDR_NOTIFICATION_WELCOME_ICON),
273 display_source,
274 notifier_id_,
275 rich_notification_data,
276 new NotificationCallbacks(
277 profile_, notifier_id_, welcome_notification_id_,
278 delegate_.get())));
280 if (pop_up_request == POP_UP_HIDDEN)
281 message_center_notification->set_shown_as_popup(true);
283 GetMessageCenter()->AddNotification(message_center_notification.Pass());
284 StartExpirationTimer();
288 void ExtensionWelcomeNotification::HideWelcomeNotification() {
289 if (!welcome_notification_id_.empty() &&
290 GetMessageCenter()->FindVisibleNotificationById(
291 welcome_notification_id_) != NULL) {
292 GetMessageCenter()->RemoveNotification(welcome_notification_id_, false);
293 StopExpirationTimer();
297 bool ExtensionWelcomeNotification::UserHasDismissedWelcomeNotification() const {
298 // This was previously a syncable preference; now it's per-machine.
299 // Only the local pref will be written moving forward, but check for both so
300 // users won't be double-toasted.
301 bool shown_synced = profile_->GetPrefs()->GetBoolean(
302 prefs::kWelcomeNotificationDismissed);
303 bool shown_local = profile_->GetPrefs()->GetBoolean(
304 prefs::kWelcomeNotificationDismissedLocal);
305 return (shown_synced || shown_local);
308 void ExtensionWelcomeNotification::OnWelcomeNotificationDismissedChanged() {
309 if (UserHasDismissedWelcomeNotification()) {
310 HideWelcomeNotification();
314 void ExtensionWelcomeNotification::StartExpirationTimer() {
315 if (!expiration_timer_ && !IsWelcomeNotificationExpired()) {
316 base::Time expiration_timestamp = GetExpirationTimestamp();
317 if (expiration_timestamp.is_null()) {
318 SetExpirationTimestampFromNow();
319 expiration_timestamp = GetExpirationTimestamp();
320 DCHECK(!expiration_timestamp.is_null());
322 expiration_timer_.reset(
323 new base::OneShotTimer<ExtensionWelcomeNotification>());
324 expiration_timer_->Start(
325 FROM_HERE,
326 expiration_timestamp - delegate_->GetCurrentTime(),
327 this,
328 &ExtensionWelcomeNotification::ExpireWelcomeNotification);
332 void ExtensionWelcomeNotification::StopExpirationTimer() {
333 if (expiration_timer_) {
334 expiration_timer_->Stop();
335 expiration_timer_.reset();
339 void ExtensionWelcomeNotification::ExpireWelcomeNotification() {
340 DCHECK(IsWelcomeNotificationExpired());
341 profile_->GetPrefs()->SetBoolean(
342 prefs::kWelcomeNotificationDismissedLocal, true);
343 HideWelcomeNotification();
346 base::Time ExtensionWelcomeNotification::GetExpirationTimestamp() const {
347 PrefService* const pref_service = profile_->GetPrefs();
348 const int64 expiration_timestamp =
349 pref_service->GetInt64(prefs::kWelcomeNotificationExpirationTimestamp);
350 return (expiration_timestamp == 0)
351 ? base::Time()
352 : base::Time::FromInternalValue(expiration_timestamp);
355 void ExtensionWelcomeNotification::SetExpirationTimestampFromNow() {
356 PrefService* const pref_service = profile_->GetPrefs();
357 pref_service->SetInt64(
358 prefs::kWelcomeNotificationExpirationTimestamp,
359 (delegate_->GetCurrentTime() +
360 base::TimeDelta::FromDays(kRequestedShowTimeDays)).ToInternalValue());
363 bool ExtensionWelcomeNotification::IsWelcomeNotificationExpired() const {
364 const base::Time expiration_timestamp = GetExpirationTimestamp();
365 return !expiration_timestamp.is_null() &&
366 (expiration_timestamp <= delegate_->GetCurrentTime());