Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / notifications / extension_welcome_notification.cc
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.
4
5 #include "chrome/browser/notifications/extension_welcome_notification.h"
6
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"
28
29 const int ExtensionWelcomeNotification::kRequestedShowTimeDays = 14;
30 const char ExtensionWelcomeNotification::kChromeNowExtensionID[] =
31     "pafkbggdmjlpgkdkcbjmhmfcdpncadgh";
32
33 namespace {
34
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) {
47   }
48
49   // Overridden from NotificationDelegate:
50   virtual void Display() OVERRIDE {}
51   virtual void Error() OVERRIDE {}
52
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));
60     }
61   }
62
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();
72     }
73   }
74
75  private:
76   void MarkAsDismissed() {
77     profile_->GetPrefs()->SetBoolean(prefs::kWelcomeNotificationDismissedLocal,
78                                      true);
79   }
80
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);
89   }
90
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);
99   }
100
101   virtual ~NotificationCallbacks() {}
102
103   Profile* const profile_;
104
105   const message_center::NotifierId notifier_id_;
106
107   std::string welcome_notification_id_;
108
109   // Weak ref owned by ExtensionWelcomeNotification.
110   ExtensionWelcomeNotification::Delegate* const delegate_;
111
112   DISALLOW_COPY_AND_ASSIGN(NotificationCallbacks);
113 };
114
115 class DefaultDelegate : public ExtensionWelcomeNotification::Delegate {
116  public:
117   DefaultDelegate() {}
118
119   virtual message_center::MessageCenter* GetMessageCenter() OVERRIDE {
120     return g_browser_process->message_center();
121   }
122
123   virtual base::Time GetCurrentTime() OVERRIDE {
124     return base::Time::Now();
125   }
126
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);
131   }
132
133  private:
134   DISALLOW_COPY_AND_ASSIGN(DefaultDelegate);
135 };
136
137 }  // namespace
138
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());
155 }
156
157 // static
158 ExtensionWelcomeNotification* ExtensionWelcomeNotification::Create(
159     Profile* const profile) {
160   return Create(profile, new DefaultDelegate());
161 }
162
163 // static
164 ExtensionWelcomeNotification* ExtensionWelcomeNotification::Create(
165     Profile* const profile, Delegate* const delegate) {
166   return new ExtensionWelcomeNotification(profile, delegate);
167 }
168
169 ExtensionWelcomeNotification::~ExtensionWelcomeNotification() {
170   if (delayed_notification_) {
171     delayed_notification_.reset();
172     PrefServiceSyncable::FromProfile(profile_)->RemoveObserver(this);
173   } else {
174     HideWelcomeNotification();
175   }
176 }
177
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()));
187   }
188 }
189
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);
206         }
207
208         if (IsWelcomeNotificationExpired()) {
209           ExpireWelcomeNotification();
210         } else {
211           ShowWelcomeNotification(
212               notification.display_source(), pop_up_request);
213         }
214       }
215     } else {
216       delayed_notification_.reset(new Notification(notification));
217       pref_service_syncable->AddObserver(this);
218     }
219   }
220 }
221
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,
235                            0,
236                            user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
237 }
238
239 message_center::MessageCenter*
240 ExtensionWelcomeNotification::GetMessageCenter() const {
241   return delegate_->GetMessageCenter();
242 }
243
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);
255
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);
260
261   if (welcome_notification_id_.empty())
262     welcome_notification_id_ = base::GenerateGUID();
263
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())));
279
280     if (pop_up_request == POP_UP_HIDDEN)
281       message_center_notification->set_shown_as_popup(true);
282
283     GetMessageCenter()->AddNotification(message_center_notification.Pass());
284     StartExpirationTimer();
285   }
286 }
287
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();
294   }
295 }
296
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);
306 }
307
308 void ExtensionWelcomeNotification::OnWelcomeNotificationDismissedChanged() {
309   if (UserHasDismissedWelcomeNotification()) {
310     HideWelcomeNotification();
311   }
312 }
313
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());
321     }
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);
329   }
330 }
331
332 void ExtensionWelcomeNotification::StopExpirationTimer() {
333   if (expiration_timer_) {
334     expiration_timer_->Stop();
335     expiration_timer_.reset();
336   }
337 }
338
339 void ExtensionWelcomeNotification::ExpireWelcomeNotification() {
340   DCHECK(IsWelcomeNotificationExpired());
341   profile_->GetPrefs()->SetBoolean(
342       prefs::kWelcomeNotificationDismissedLocal, true);
343   HideWelcomeNotification();
344 }
345
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);
353 }
354
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());
361 }
362
363 bool ExtensionWelcomeNotification::IsWelcomeNotificationExpired() const {
364   const base::Time expiration_timestamp = GetExpirationTimestamp();
365   return !expiration_timestamp.is_null() &&
366          (expiration_timestamp <= delegate_->GetCurrentTime());
367 }