- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / api / alarms / alarm_manager.cc
1 // Copyright (c) 2012 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/extensions/api/alarms/alarm_manager.h"
6
7 #include "base/bind.h"
8 #include "base/json/json_writer.h"
9 #include "base/lazy_instance.h"
10 #include "base/message_loop/message_loop.h"
11 #include "base/time/clock.h"
12 #include "base/time/default_clock.h"
13 #include "base/time/time.h"
14 #include "base/value_conversions.h"
15 #include "base/values.h"
16 #include "chrome/browser/chrome_notification_types.h"
17 #include "chrome/browser/extensions/event_router.h"
18 #include "chrome/browser/extensions/extension_service.h"
19 #include "chrome/browser/extensions/extension_system.h"
20 #include "chrome/browser/extensions/state_store.h"
21 #include "chrome/browser/profiles/profile.h"
22 #include "chrome/common/extensions/api/alarms.h"
23 #include "content/public/browser/notification_service.h"
24
25 namespace extensions {
26
27 namespace alarms = api::alarms;
28
29 namespace {
30
31 // A list of alarms that this extension has set.
32 const char kRegisteredAlarms[] = "alarms";
33 const char kAlarmGranularity[] = "granularity";
34
35 // The minimum period between polling for alarms to run.
36 const base::TimeDelta kDefaultMinPollPeriod = base::TimeDelta::FromDays(1);
37
38 class DefaultAlarmDelegate : public AlarmManager::Delegate {
39  public:
40   explicit DefaultAlarmDelegate(Profile* profile) : profile_(profile) {}
41   virtual ~DefaultAlarmDelegate() {}
42
43   virtual void OnAlarm(const std::string& extension_id,
44                        const Alarm& alarm) OVERRIDE {
45     scoped_ptr<base::ListValue> args(new base::ListValue());
46     args->Append(alarm.js_alarm->ToValue().release());
47     scoped_ptr<Event> event(new Event(alarms::OnAlarm::kEventName,
48                                       args.Pass()));
49     ExtensionSystem::Get(profile_)->event_router()->DispatchEventToExtension(
50         extension_id, event.Pass());
51   }
52
53  private:
54   Profile* profile_;
55 };
56
57 // Creates a TimeDelta from a delay as specified in the API.
58 base::TimeDelta TimeDeltaFromDelay(double delay_in_minutes) {
59   return base::TimeDelta::FromMicroseconds(
60       delay_in_minutes * base::Time::kMicrosecondsPerMinute);
61 }
62
63 std::vector<Alarm> AlarmsFromValue(const base::ListValue* list) {
64   std::vector<Alarm> alarms;
65   for (size_t i = 0; i < list->GetSize(); ++i) {
66     const base::DictionaryValue* alarm_dict = NULL;
67     Alarm alarm;
68     if (list->GetDictionary(i, &alarm_dict) &&
69         api::alarms::Alarm::Populate(*alarm_dict, alarm.js_alarm.get())) {
70       const base::Value* time_value = NULL;
71       if (alarm_dict->Get(kAlarmGranularity, &time_value))
72         base::GetValueAsTimeDelta(*time_value, &alarm.granularity);
73       alarms.push_back(alarm);
74     }
75   }
76   return alarms;
77 }
78
79 scoped_ptr<base::ListValue> AlarmsToValue(const std::vector<Alarm>& alarms) {
80   scoped_ptr<base::ListValue> list(new base::ListValue());
81   for (size_t i = 0; i < alarms.size(); ++i) {
82     scoped_ptr<base::DictionaryValue> alarm =
83         alarms[i].js_alarm->ToValue().Pass();
84     alarm->Set(kAlarmGranularity,
85                base::CreateTimeDeltaValue(alarms[i].granularity));
86     list->Append(alarm.release());
87   }
88   return list.Pass();
89 }
90
91
92 }  // namespace
93
94 // AlarmManager
95
96 AlarmManager::AlarmManager(Profile* profile)
97     : profile_(profile),
98       clock_(new base::DefaultClock()),
99       delegate_(new DefaultAlarmDelegate(profile)) {
100   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED,
101                  content::Source<Profile>(profile_));
102   registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNINSTALLED,
103                  content::Source<Profile>(profile_));
104
105   StateStore* storage = ExtensionSystem::Get(profile_)->state_store();
106   if (storage)
107     storage->RegisterKey(kRegisteredAlarms);
108 }
109
110 AlarmManager::~AlarmManager() {
111 }
112
113 void AlarmManager::AddAlarm(const std::string& extension_id,
114                             const Alarm& alarm,
115                             const AddAlarmCallback& callback) {
116   RunWhenReady(extension_id, base::Bind(
117       &AlarmManager::AddAlarmWhenReady, AsWeakPtr(), alarm, callback));
118 }
119
120 void AlarmManager::GetAlarm(const std::string& extension_id,
121                             const std::string& name,
122                             const GetAlarmCallback& callback) {
123   RunWhenReady(extension_id, base::Bind(
124       &AlarmManager::GetAlarmWhenReady, AsWeakPtr(), name, callback));
125 }
126
127 void AlarmManager::GetAllAlarms(
128     const std::string& extension_id, const GetAllAlarmsCallback& callback) {
129   RunWhenReady(extension_id, base::Bind(
130       &AlarmManager::GetAllAlarmsWhenReady, AsWeakPtr(), callback));
131 }
132
133 void AlarmManager::RemoveAlarm(const std::string& extension_id,
134                                const std::string& name,
135                                const RemoveAlarmCallback& callback) {
136   RunWhenReady(extension_id, base::Bind(
137       &AlarmManager::RemoveAlarmWhenReady, AsWeakPtr(), name, callback));
138 }
139
140 void AlarmManager::RemoveAllAlarms(const std::string& extension_id,
141                                    const RemoveAllAlarmsCallback& callback) {
142   RunWhenReady(extension_id, base::Bind(
143       &AlarmManager::RemoveAllAlarmsWhenReady, AsWeakPtr(), callback));
144 }
145
146 void AlarmManager::AddAlarmWhenReady(const Alarm& alarm,
147                                      const AddAlarmCallback& callback,
148                                      const std::string& extension_id) {
149   AddAlarmImpl(extension_id, alarm);
150   WriteToStorage(extension_id);
151   callback.Run();
152 }
153
154 void AlarmManager::GetAlarmWhenReady(const std::string& name,
155                                      const GetAlarmCallback& callback,
156                                      const std::string& extension_id) {
157   AlarmIterator it = GetAlarmIterator(extension_id, name);
158   callback.Run(it.first != alarms_.end() ? &*it.second : NULL);
159 }
160
161 void AlarmManager::GetAllAlarmsWhenReady(const GetAllAlarmsCallback& callback,
162                                          const std::string& extension_id) {
163   AlarmMap::iterator list = alarms_.find(extension_id);
164   callback.Run(list != alarms_.end() ? &list->second : NULL);
165 }
166
167 void AlarmManager::RemoveAlarmWhenReady(const std::string& name,
168                                         const RemoveAlarmCallback& callback,
169                                         const std::string& extension_id) {
170   AlarmIterator it = GetAlarmIterator(extension_id, name);
171   if (it.first == alarms_.end()) {
172     callback.Run(false);
173     return;
174   }
175
176   RemoveAlarmIterator(it);
177   WriteToStorage(extension_id);
178   callback.Run(true);
179 }
180
181 void AlarmManager::RemoveAllAlarmsWhenReady(
182     const RemoveAllAlarmsCallback& callback, const std::string& extension_id) {
183   AlarmMap::iterator list = alarms_.find(extension_id);
184   if (list != alarms_.end()) {
185     // Note: I'm using indices rather than iterators here because
186     // RemoveAlarmIterator will delete the list when it becomes empty.
187     for (size_t i = 0, size = list->second.size(); i < size; ++i)
188       RemoveAlarmIterator(AlarmIterator(list, list->second.begin()));
189
190     CHECK(alarms_.find(extension_id) == alarms_.end());
191     WriteToStorage(extension_id);
192   }
193   callback.Run();
194 }
195
196 AlarmManager::AlarmIterator AlarmManager::GetAlarmIterator(
197     const std::string& extension_id, const std::string& name) {
198   AlarmMap::iterator list = alarms_.find(extension_id);
199   if (list == alarms_.end())
200     return make_pair(alarms_.end(), AlarmList::iterator());
201
202   for (AlarmList::iterator it = list->second.begin();
203        it != list->second.end(); ++it) {
204     if (it->js_alarm->name == name)
205       return make_pair(list, it);
206   }
207
208   return make_pair(alarms_.end(), AlarmList::iterator());
209 }
210
211 void AlarmManager::SetClockForTesting(base::Clock* clock) {
212   clock_.reset(clock);
213 }
214
215 static base::LazyInstance<ProfileKeyedAPIFactory<AlarmManager> >
216 g_factory = LAZY_INSTANCE_INITIALIZER;
217
218 // static
219 ProfileKeyedAPIFactory<AlarmManager>* AlarmManager::GetFactoryInstance() {
220   return &g_factory.Get();
221 }
222
223 // static
224 AlarmManager* AlarmManager::Get(Profile* profile) {
225   return ProfileKeyedAPIFactory<AlarmManager>::GetForProfile(profile);
226 }
227
228 void AlarmManager::RemoveAlarmIterator(const AlarmIterator& iter) {
229   AlarmList& list = iter.first->second;
230   list.erase(iter.second);
231   if (list.empty())
232     alarms_.erase(iter.first);
233
234   // Cancel the timer if there are no more alarms.
235   // We don't need to reschedule the poll otherwise, because in
236   // the worst case we would just poll one extra time.
237   if (alarms_.empty())
238     timer_.Stop();
239 }
240
241 void AlarmManager::OnAlarm(AlarmIterator it) {
242   CHECK(it.first != alarms_.end());
243   Alarm& alarm = *it.second;
244   std::string extension_id_copy(it.first->first);
245   delegate_->OnAlarm(extension_id_copy, alarm);
246
247   // Update our scheduled time for the next alarm.
248   if (double* period_in_minutes =
249       alarm.js_alarm->period_in_minutes.get()) {
250     alarm.js_alarm->scheduled_time =
251         (last_poll_time_ +
252          TimeDeltaFromDelay(*period_in_minutes)).ToJsTime();
253   } else {
254     RemoveAlarmIterator(it);
255   }
256   WriteToStorage(extension_id_copy);
257 }
258
259 void AlarmManager::AddAlarmImpl(const std::string& extension_id,
260                                 const Alarm& alarm) {
261   // Override any old alarm with the same name.
262   AlarmIterator old_alarm = GetAlarmIterator(extension_id,
263                                              alarm.js_alarm->name);
264   if (old_alarm.first != alarms_.end())
265     RemoveAlarmIterator(old_alarm);
266
267   alarms_[extension_id].push_back(alarm);
268
269   ScheduleNextPoll();
270 }
271
272 void AlarmManager::WriteToStorage(const std::string& extension_id) {
273   StateStore* storage = ExtensionSystem::Get(profile_)->state_store();
274   if (!storage)
275     return;
276
277   scoped_ptr<base::Value> alarms;
278   AlarmMap::iterator list = alarms_.find(extension_id);
279   if (list != alarms_.end())
280     alarms.reset(AlarmsToValue(list->second).release());
281   else
282     alarms.reset(AlarmsToValue(std::vector<Alarm>()).release());
283   storage->SetExtensionValue(extension_id, kRegisteredAlarms, alarms.Pass());
284 }
285
286 void AlarmManager::ReadFromStorage(const std::string& extension_id,
287                                    scoped_ptr<base::Value> value) {
288   base::ListValue* list = NULL;
289   if (value.get() && value->GetAsList(&list)) {
290     std::vector<Alarm> alarm_states = AlarmsFromValue(list);
291     for (size_t i = 0; i < alarm_states.size(); ++i)
292       AddAlarmImpl(extension_id, alarm_states[i]);
293   }
294
295   ReadyQueue& extension_ready_queue = ready_actions_[extension_id];
296   while (!extension_ready_queue.empty()) {
297     extension_ready_queue.front().Run(extension_id);
298     extension_ready_queue.pop();
299   }
300   ready_actions_.erase(extension_id);
301 }
302
303 void AlarmManager::ScheduleNextPoll() {
304   // If there are no alarms, stop the timer.
305   if (alarms_.empty()) {
306     timer_.Stop();
307     return;
308   }
309
310   // TODO(yoz): Try not to reschedule every single time if we're adding
311   // a lot of alarms.
312
313   // Find the soonest alarm that is scheduled to run and the smallest
314   // granularity of any alarm.
315   // alarms_ guarantees that none of its contained lists are empty.
316   base::Time soonest_alarm_time = base::Time::FromJsTime(
317       alarms_.begin()->second.begin()->js_alarm->scheduled_time);
318   base::TimeDelta min_granularity = kDefaultMinPollPeriod;
319   for (AlarmMap::const_iterator m_it = alarms_.begin(), m_end = alarms_.end();
320        m_it != m_end; ++m_it) {
321     for (AlarmList::const_iterator l_it = m_it->second.begin();
322          l_it != m_it->second.end(); ++l_it) {
323       base::Time cur_alarm_time =
324           base::Time::FromJsTime(l_it->js_alarm->scheduled_time);
325       if (cur_alarm_time < soonest_alarm_time)
326         soonest_alarm_time = cur_alarm_time;
327       if (l_it->granularity < min_granularity)
328         min_granularity = l_it->granularity;
329       base::TimeDelta cur_alarm_delta = cur_alarm_time - clock_->Now();
330       if (cur_alarm_delta < min_granularity)
331         min_granularity = cur_alarm_delta;
332       if (min_granularity < l_it->minimum_granularity)
333         min_granularity = l_it->minimum_granularity;
334     }
335   }
336
337   base::Time next_poll(last_poll_time_ + min_granularity);
338   // If the next alarm is more than min_granularity in the future, wait for it.
339   // Otherwise, only poll as often as min_granularity.
340   // As a special case, if we've never checked for an alarm before
341   // (e.g. during startup), let alarms fire asap.
342   if (last_poll_time_.is_null() || next_poll < soonest_alarm_time)
343     next_poll = soonest_alarm_time;
344
345   // Schedule the poll.
346   test_next_poll_time_ = next_poll;
347   base::TimeDelta delay = std::max(base::TimeDelta::FromSeconds(0),
348                                    next_poll - clock_->Now());
349   timer_.Start(FROM_HERE,
350                delay,
351                this,
352                &AlarmManager::PollAlarms);
353 }
354
355 void AlarmManager::PollAlarms() {
356   last_poll_time_ = clock_->Now();
357
358   // Run any alarms scheduled in the past. OnAlarm uses vector::erase to remove
359   // elements from the AlarmList, and map::erase to remove AlarmLists from the
360   // AlarmMap.
361   for (AlarmMap::iterator m_it = alarms_.begin(), m_end = alarms_.end();
362        m_it != m_end;) {
363     AlarmMap::iterator cur_extension = m_it++;
364
365     // Iterate (a) backwards so that removing elements doesn't affect
366     // upcoming iterations, and (b) with indices so that if the last
367     // iteration destroys the AlarmList, I'm not about to use the end
368     // iterator that the destruction invalidates.
369     for (size_t i = cur_extension->second.size(); i > 0; --i) {
370       AlarmList::iterator cur_alarm = cur_extension->second.begin() + i - 1;
371       if (base::Time::FromJsTime(cur_alarm->js_alarm->scheduled_time) <=
372           last_poll_time_) {
373         OnAlarm(make_pair(cur_extension, cur_alarm));
374       }
375     }
376   }
377
378   ScheduleNextPoll();
379 }
380
381 static void RemoveAllOnUninstallCallback() {}
382
383 void AlarmManager::RunWhenReady(
384     const std::string& extension_id, const ReadyAction& action) {
385   ReadyMap::iterator it = ready_actions_.find(extension_id);
386
387   if (it == ready_actions_.end())
388     action.Run(extension_id);
389   else
390     it->second.push(action);
391 }
392
393 void AlarmManager::Observe(
394     int type,
395     const content::NotificationSource& source,
396     const content::NotificationDetails& details) {
397   switch (type) {
398     case chrome::NOTIFICATION_EXTENSION_LOADED: {
399       const Extension* extension =
400           content::Details<const Extension>(details).ptr();
401       StateStore* storage = ExtensionSystem::Get(profile_)->state_store();
402       if (storage) {
403         ready_actions_.insert(
404             ReadyMap::value_type(extension->id(), ReadyQueue()));
405         storage->GetExtensionValue(extension->id(), kRegisteredAlarms,
406             base::Bind(&AlarmManager::ReadFromStorage,
407                        AsWeakPtr(), extension->id()));
408       }
409       break;
410     }
411     case chrome::NOTIFICATION_EXTENSION_UNINSTALLED: {
412       const Extension* extension =
413           content::Details<const Extension>(details).ptr();
414       RemoveAllAlarms(
415           extension->id(), base::Bind(RemoveAllOnUninstallCallback));
416       break;
417     }
418     default:
419       NOTREACHED();
420       break;
421   }
422 }
423
424 // AlarmManager::Alarm
425
426 Alarm::Alarm()
427     : js_alarm(new api::alarms::Alarm()) {
428 }
429
430 Alarm::Alarm(const std::string& name,
431              const api::alarms::AlarmCreateInfo& create_info,
432              base::TimeDelta min_granularity,
433              base::Time now)
434     : js_alarm(new api::alarms::Alarm()) {
435   js_alarm->name = name;
436   minimum_granularity = min_granularity;
437
438   if (create_info.when.get()) {
439     // Absolute scheduling.
440     js_alarm->scheduled_time = *create_info.when;
441     granularity = base::Time::FromJsTime(js_alarm->scheduled_time) - now;
442   } else {
443     // Relative scheduling.
444     double* delay_in_minutes = create_info.delay_in_minutes.get();
445     if (delay_in_minutes == NULL)
446       delay_in_minutes = create_info.period_in_minutes.get();
447     CHECK(delay_in_minutes != NULL)
448         << "ValidateAlarmCreateInfo in alarms_api.cc should have "
449         << "prevented this call.";
450     base::TimeDelta delay = TimeDeltaFromDelay(*delay_in_minutes);
451     js_alarm->scheduled_time = (now + delay).ToJsTime();
452     granularity = delay;
453   }
454
455   if (granularity < min_granularity)
456     granularity = min_granularity;
457
458   // Check for repetition.
459   if (create_info.period_in_minutes.get()) {
460     js_alarm->period_in_minutes.reset(
461         new double(*create_info.period_in_minutes));
462   }
463 }
464
465 Alarm::~Alarm() {
466 }
467
468 }  // namespace extensions