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