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.
5 #include "chrome/browser/extensions/api/alarms/alarm_manager.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/extension_service.h"
18 #include "chrome/browser/extensions/state_store.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/common/extensions/api/alarms.h"
21 #include "content/public/browser/notification_service.h"
22 #include "extensions/browser/event_router.h"
23 #include "extensions/browser/extension_system.h"
25 namespace extensions {
27 namespace alarms = api::alarms;
31 // A list of alarms that this extension has set.
32 const char kRegisteredAlarms[] = "alarms";
33 const char kAlarmGranularity[] = "granularity";
35 // The minimum period between polling for alarms to run.
36 const base::TimeDelta kDefaultMinPollPeriod() {
37 return base::TimeDelta::FromDays(1);
40 class DefaultAlarmDelegate : public AlarmManager::Delegate {
42 explicit DefaultAlarmDelegate(content::BrowserContext* context)
43 : browser_context_(context) {}
44 virtual ~DefaultAlarmDelegate() {}
46 virtual void OnAlarm(const std::string& extension_id,
47 const Alarm& alarm) OVERRIDE {
48 scoped_ptr<base::ListValue> args(new base::ListValue());
49 args->Append(alarm.js_alarm->ToValue().release());
50 scoped_ptr<Event> event(new Event(alarms::OnAlarm::kEventName,
52 ExtensionSystem::Get(browser_context_)
54 ->DispatchEventToExtension(extension_id, event.Pass());
58 content::BrowserContext* browser_context_;
61 // Creates a TimeDelta from a delay as specified in the API.
62 base::TimeDelta TimeDeltaFromDelay(double delay_in_minutes) {
63 return base::TimeDelta::FromMicroseconds(
64 delay_in_minutes * base::Time::kMicrosecondsPerMinute);
67 std::vector<Alarm> AlarmsFromValue(const base::ListValue* list) {
68 std::vector<Alarm> alarms;
69 for (size_t i = 0; i < list->GetSize(); ++i) {
70 const base::DictionaryValue* alarm_dict = NULL;
72 if (list->GetDictionary(i, &alarm_dict) &&
73 api::alarms::Alarm::Populate(*alarm_dict, alarm.js_alarm.get())) {
74 const base::Value* time_value = NULL;
75 if (alarm_dict->Get(kAlarmGranularity, &time_value))
76 base::GetValueAsTimeDelta(*time_value, &alarm.granularity);
77 alarms.push_back(alarm);
83 scoped_ptr<base::ListValue> AlarmsToValue(const std::vector<Alarm>& alarms) {
84 scoped_ptr<base::ListValue> list(new base::ListValue());
85 for (size_t i = 0; i < alarms.size(); ++i) {
86 scoped_ptr<base::DictionaryValue> alarm =
87 alarms[i].js_alarm->ToValue().Pass();
88 alarm->Set(kAlarmGranularity,
89 base::CreateTimeDeltaValue(alarms[i].granularity));
90 list->Append(alarm.release());
100 AlarmManager::AlarmManager(content::BrowserContext* context)
101 : profile_(Profile::FromBrowserContext(context)),
102 clock_(new base::DefaultClock()),
103 delegate_(new DefaultAlarmDelegate(context)) {
104 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED,
105 content::Source<Profile>(profile_));
106 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNINSTALLED,
107 content::Source<Profile>(profile_));
109 StateStore* storage = ExtensionSystem::Get(profile_)->state_store();
111 storage->RegisterKey(kRegisteredAlarms);
114 AlarmManager::~AlarmManager() {
117 void AlarmManager::AddAlarm(const std::string& extension_id,
119 const AddAlarmCallback& callback) {
120 RunWhenReady(extension_id, base::Bind(
121 &AlarmManager::AddAlarmWhenReady, AsWeakPtr(), alarm, callback));
124 void AlarmManager::GetAlarm(const std::string& extension_id,
125 const std::string& name,
126 const GetAlarmCallback& callback) {
127 RunWhenReady(extension_id, base::Bind(
128 &AlarmManager::GetAlarmWhenReady, AsWeakPtr(), name, callback));
131 void AlarmManager::GetAllAlarms(
132 const std::string& extension_id, const GetAllAlarmsCallback& callback) {
133 RunWhenReady(extension_id, base::Bind(
134 &AlarmManager::GetAllAlarmsWhenReady, AsWeakPtr(), callback));
137 void AlarmManager::RemoveAlarm(const std::string& extension_id,
138 const std::string& name,
139 const RemoveAlarmCallback& callback) {
140 RunWhenReady(extension_id, base::Bind(
141 &AlarmManager::RemoveAlarmWhenReady, AsWeakPtr(), name, callback));
144 void AlarmManager::RemoveAllAlarms(const std::string& extension_id,
145 const RemoveAllAlarmsCallback& callback) {
146 RunWhenReady(extension_id, base::Bind(
147 &AlarmManager::RemoveAllAlarmsWhenReady, AsWeakPtr(), callback));
150 void AlarmManager::AddAlarmWhenReady(const Alarm& alarm,
151 const AddAlarmCallback& callback,
152 const std::string& extension_id) {
153 AddAlarmImpl(extension_id, alarm);
154 WriteToStorage(extension_id);
158 void AlarmManager::GetAlarmWhenReady(const std::string& name,
159 const GetAlarmCallback& callback,
160 const std::string& extension_id) {
161 AlarmIterator it = GetAlarmIterator(extension_id, name);
162 callback.Run(it.first != alarms_.end() ? &*it.second : NULL);
165 void AlarmManager::GetAllAlarmsWhenReady(const GetAllAlarmsCallback& callback,
166 const std::string& extension_id) {
167 AlarmMap::iterator list = alarms_.find(extension_id);
168 callback.Run(list != alarms_.end() ? &list->second : NULL);
171 void AlarmManager::RemoveAlarmWhenReady(const std::string& name,
172 const RemoveAlarmCallback& callback,
173 const std::string& extension_id) {
174 AlarmIterator it = GetAlarmIterator(extension_id, name);
175 if (it.first == alarms_.end()) {
180 RemoveAlarmIterator(it);
181 WriteToStorage(extension_id);
185 void AlarmManager::RemoveAllAlarmsWhenReady(
186 const RemoveAllAlarmsCallback& callback, const std::string& extension_id) {
187 AlarmMap::iterator list = alarms_.find(extension_id);
188 if (list != alarms_.end()) {
189 // Note: I'm using indices rather than iterators here because
190 // RemoveAlarmIterator will delete the list when it becomes empty.
191 for (size_t i = 0, size = list->second.size(); i < size; ++i)
192 RemoveAlarmIterator(AlarmIterator(list, list->second.begin()));
194 CHECK(alarms_.find(extension_id) == alarms_.end());
195 WriteToStorage(extension_id);
200 AlarmManager::AlarmIterator AlarmManager::GetAlarmIterator(
201 const std::string& extension_id, const std::string& name) {
202 AlarmMap::iterator list = alarms_.find(extension_id);
203 if (list == alarms_.end())
204 return make_pair(alarms_.end(), AlarmList::iterator());
206 for (AlarmList::iterator it = list->second.begin();
207 it != list->second.end(); ++it) {
208 if (it->js_alarm->name == name)
209 return make_pair(list, it);
212 return make_pair(alarms_.end(), AlarmList::iterator());
215 void AlarmManager::SetClockForTesting(base::Clock* clock) {
219 static base::LazyInstance<BrowserContextKeyedAPIFactory<AlarmManager> >
220 g_factory = LAZY_INSTANCE_INITIALIZER;
223 BrowserContextKeyedAPIFactory<AlarmManager>*
224 AlarmManager::GetFactoryInstance() {
225 return g_factory.Pointer();
229 AlarmManager* AlarmManager::Get(Profile* profile) {
230 return BrowserContextKeyedAPIFactory<AlarmManager>::Get(profile);
233 void AlarmManager::RemoveAlarmIterator(const AlarmIterator& iter) {
234 AlarmList& list = iter.first->second;
235 list.erase(iter.second);
237 alarms_.erase(iter.first);
239 // Cancel the timer if there are no more alarms.
240 // We don't need to reschedule the poll otherwise, because in
241 // the worst case we would just poll one extra time.
242 if (alarms_.empty()) {
244 next_poll_time_ = base::Time();
248 void AlarmManager::OnAlarm(AlarmIterator it) {
249 CHECK(it.first != alarms_.end());
250 Alarm& alarm = *it.second;
251 std::string extension_id_copy(it.first->first);
252 delegate_->OnAlarm(extension_id_copy, alarm);
254 // Update our scheduled time for the next alarm.
255 if (double* period_in_minutes =
256 alarm.js_alarm->period_in_minutes.get()) {
257 // Get the timer's delay in JS time (i.e., convert it from minutes to
259 double period_in_js_time =
260 *period_in_minutes * base::Time::kMicrosecondsPerMinute /
261 base::Time::kMicrosecondsPerMillisecond;
262 // Find out how many periods have transpired since the alarm last went off
263 // (it's possible that we missed some).
264 int transpired_periods =
265 (last_poll_time_.ToJsTime() - alarm.js_alarm->scheduled_time) /
267 // Schedule the alarm for the next period that is in-line with the original
269 alarm.js_alarm->scheduled_time +=
270 period_in_js_time * (transpired_periods + 1);
272 RemoveAlarmIterator(it);
274 WriteToStorage(extension_id_copy);
277 void AlarmManager::AddAlarmImpl(const std::string& extension_id,
278 const Alarm& alarm) {
279 // Override any old alarm with the same name.
280 AlarmIterator old_alarm = GetAlarmIterator(extension_id,
281 alarm.js_alarm->name);
282 if (old_alarm.first != alarms_.end())
283 RemoveAlarmIterator(old_alarm);
285 alarms_[extension_id].push_back(alarm);
286 base::Time alarm_time =
287 base::Time::FromJsTime(alarm.js_alarm->scheduled_time);
288 if (next_poll_time_.is_null() || alarm_time < next_poll_time_)
289 SetNextPollTime(alarm_time);
292 void AlarmManager::WriteToStorage(const std::string& extension_id) {
293 StateStore* storage = ExtensionSystem::Get(profile_)->state_store();
297 scoped_ptr<base::Value> alarms;
298 AlarmMap::iterator list = alarms_.find(extension_id);
299 if (list != alarms_.end())
300 alarms.reset(AlarmsToValue(list->second).release());
302 alarms.reset(AlarmsToValue(std::vector<Alarm>()).release());
303 storage->SetExtensionValue(extension_id, kRegisteredAlarms, alarms.Pass());
306 void AlarmManager::ReadFromStorage(const std::string& extension_id,
307 scoped_ptr<base::Value> value) {
308 base::ListValue* list = NULL;
309 if (value.get() && value->GetAsList(&list)) {
310 std::vector<Alarm> alarm_states = AlarmsFromValue(list);
311 for (size_t i = 0; i < alarm_states.size(); ++i)
312 AddAlarmImpl(extension_id, alarm_states[i]);
315 ReadyQueue& extension_ready_queue = ready_actions_[extension_id];
316 while (!extension_ready_queue.empty()) {
317 extension_ready_queue.front().Run(extension_id);
318 extension_ready_queue.pop();
320 ready_actions_.erase(extension_id);
323 void AlarmManager::SetNextPollTime(const base::Time& time) {
324 next_poll_time_ = time;
325 timer_.Start(FROM_HERE,
326 std::max(base::TimeDelta::FromSeconds(0), time - clock_->Now()),
328 &AlarmManager::PollAlarms);
331 void AlarmManager::ScheduleNextPoll() {
332 // If there are no alarms, stop the timer.
333 if (alarms_.empty()) {
335 next_poll_time_ = base::Time();
339 // Find the soonest alarm that is scheduled to run and the smallest
340 // granularity of any alarm.
341 // alarms_ guarantees that none of its contained lists are empty.
342 base::Time soonest_alarm_time = base::Time::FromJsTime(
343 alarms_.begin()->second.begin()->js_alarm->scheduled_time);
344 base::TimeDelta min_granularity = kDefaultMinPollPeriod();
345 for (AlarmMap::const_iterator m_it = alarms_.begin(), m_end = alarms_.end();
346 m_it != m_end; ++m_it) {
347 for (AlarmList::const_iterator l_it = m_it->second.begin();
348 l_it != m_it->second.end(); ++l_it) {
349 base::Time cur_alarm_time =
350 base::Time::FromJsTime(l_it->js_alarm->scheduled_time);
351 if (cur_alarm_time < soonest_alarm_time)
352 soonest_alarm_time = cur_alarm_time;
353 if (l_it->granularity < min_granularity)
354 min_granularity = l_it->granularity;
355 base::TimeDelta cur_alarm_delta = cur_alarm_time - last_poll_time_;
356 if (cur_alarm_delta < l_it->minimum_granularity)
357 cur_alarm_delta = l_it->minimum_granularity;
358 if (cur_alarm_delta < min_granularity)
359 min_granularity = cur_alarm_delta;
363 base::Time next_poll(last_poll_time_ + min_granularity);
364 // If the next alarm is more than min_granularity in the future, wait for it.
365 // Otherwise, only poll as often as min_granularity.
366 // As a special case, if we've never checked for an alarm before
367 // (e.g. during startup), let alarms fire asap.
368 if (last_poll_time_.is_null() || next_poll < soonest_alarm_time)
369 next_poll = soonest_alarm_time;
371 // Schedule the poll.
372 SetNextPollTime(next_poll);
375 void AlarmManager::PollAlarms() {
376 last_poll_time_ = clock_->Now();
378 // Run any alarms scheduled in the past. OnAlarm uses vector::erase to remove
379 // elements from the AlarmList, and map::erase to remove AlarmLists from the
381 for (AlarmMap::iterator m_it = alarms_.begin(), m_end = alarms_.end();
383 AlarmMap::iterator cur_extension = m_it++;
385 // Iterate (a) backwards so that removing elements doesn't affect
386 // upcoming iterations, and (b) with indices so that if the last
387 // iteration destroys the AlarmList, I'm not about to use the end
388 // iterator that the destruction invalidates.
389 for (size_t i = cur_extension->second.size(); i > 0; --i) {
390 AlarmList::iterator cur_alarm = cur_extension->second.begin() + i - 1;
391 if (base::Time::FromJsTime(cur_alarm->js_alarm->scheduled_time) <=
393 OnAlarm(make_pair(cur_extension, cur_alarm));
401 static void RemoveAllOnUninstallCallback() {}
403 void AlarmManager::RunWhenReady(
404 const std::string& extension_id, const ReadyAction& action) {
405 ReadyMap::iterator it = ready_actions_.find(extension_id);
407 if (it == ready_actions_.end())
408 action.Run(extension_id);
410 it->second.push(action);
413 void AlarmManager::Observe(
415 const content::NotificationSource& source,
416 const content::NotificationDetails& details) {
418 case chrome::NOTIFICATION_EXTENSION_LOADED: {
419 const Extension* extension =
420 content::Details<const Extension>(details).ptr();
421 StateStore* storage = ExtensionSystem::Get(profile_)->state_store();
423 ready_actions_.insert(
424 ReadyMap::value_type(extension->id(), ReadyQueue()));
425 storage->GetExtensionValue(extension->id(), kRegisteredAlarms,
426 base::Bind(&AlarmManager::ReadFromStorage,
427 AsWeakPtr(), extension->id()));
431 case chrome::NOTIFICATION_EXTENSION_UNINSTALLED: {
432 const Extension* extension =
433 content::Details<const Extension>(details).ptr();
435 extension->id(), base::Bind(RemoveAllOnUninstallCallback));
444 // AlarmManager::Alarm
447 : js_alarm(new api::alarms::Alarm()) {
450 Alarm::Alarm(const std::string& name,
451 const api::alarms::AlarmCreateInfo& create_info,
452 base::TimeDelta min_granularity,
454 : js_alarm(new api::alarms::Alarm()) {
455 js_alarm->name = name;
456 minimum_granularity = min_granularity;
458 if (create_info.when.get()) {
459 // Absolute scheduling.
460 js_alarm->scheduled_time = *create_info.when;
461 granularity = base::Time::FromJsTime(js_alarm->scheduled_time) - now;
463 // Relative scheduling.
464 double* delay_in_minutes = create_info.delay_in_minutes.get();
465 if (delay_in_minutes == NULL)
466 delay_in_minutes = create_info.period_in_minutes.get();
467 CHECK(delay_in_minutes != NULL)
468 << "ValidateAlarmCreateInfo in alarms_api.cc should have "
469 << "prevented this call.";
470 base::TimeDelta delay = TimeDeltaFromDelay(*delay_in_minutes);
471 js_alarm->scheduled_time = (now + delay).ToJsTime();
475 if (granularity < min_granularity)
476 granularity = min_granularity;
478 // Check for repetition.
479 if (create_info.period_in_minutes.get()) {
480 js_alarm->period_in_minutes.reset(
481 new double(*create_info.period_in_minutes));
488 } // namespace extensions