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 "extensions/browser/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 "extensions/browser/event_router.h"
17 #include "extensions/browser/extension_registry.h"
18 #include "extensions/browser/extension_system.h"
19 #include "extensions/browser/state_store.h"
20 #include "extensions/common/api/alarms.h"
22 namespace extensions
{
24 namespace alarms
= api::alarms
;
28 // A list of alarms that this extension has set.
29 const char kRegisteredAlarms
[] = "alarms";
30 const char kAlarmGranularity
[] = "granularity";
32 // The minimum period between polling for alarms to run.
33 const base::TimeDelta
kDefaultMinPollPeriod() {
34 return base::TimeDelta::FromDays(1);
37 class DefaultAlarmDelegate
: public AlarmManager::Delegate
{
39 explicit DefaultAlarmDelegate(content::BrowserContext
* context
)
40 : browser_context_(context
) {}
41 ~DefaultAlarmDelegate() override
{}
43 void OnAlarm(const std::string
& extension_id
, const Alarm
& alarm
) override
{
44 scoped_ptr
<base::ListValue
> args(new base::ListValue());
45 args
->Append(alarm
.js_alarm
->ToValue().release());
46 scoped_ptr
<Event
> event(new Event(
47 events::ALARMS_ON_ALARM
, alarms::OnAlarm::kEventName
, args
.Pass()));
48 EventRouter::Get(browser_context_
)
49 ->DispatchEventToExtension(extension_id
, event
.Pass());
53 content::BrowserContext
* browser_context_
;
56 // Creates a TimeDelta from a delay as specified in the API.
57 base::TimeDelta
TimeDeltaFromDelay(double delay_in_minutes
) {
58 return base::TimeDelta::FromMicroseconds(delay_in_minutes
*
59 base::Time::kMicrosecondsPerMinute
);
62 std::vector
<Alarm
> AlarmsFromValue(const base::ListValue
* list
) {
63 std::vector
<Alarm
> alarms
;
64 for (size_t i
= 0; i
< list
->GetSize(); ++i
) {
65 const base::DictionaryValue
* alarm_dict
= NULL
;
67 if (list
->GetDictionary(i
, &alarm_dict
) &&
68 alarms::Alarm::Populate(*alarm_dict
, alarm
.js_alarm
.get())) {
69 const base::Value
* time_value
= NULL
;
70 if (alarm_dict
->Get(kAlarmGranularity
, &time_value
))
71 base::GetValueAsTimeDelta(*time_value
, &alarm
.granularity
);
72 alarms
.push_back(alarm
);
78 scoped_ptr
<base::ListValue
> AlarmsToValue(const std::vector
<Alarm
>& alarms
) {
79 scoped_ptr
<base::ListValue
> list(new base::ListValue());
80 for (size_t i
= 0; i
< alarms
.size(); ++i
) {
81 scoped_ptr
<base::DictionaryValue
> alarm
=
82 alarms
[i
].js_alarm
->ToValue().Pass();
83 alarm
->Set(kAlarmGranularity
,
84 base::CreateTimeDeltaValue(alarms
[i
].granularity
));
85 list
->Append(alarm
.release());
94 AlarmManager::AlarmManager(content::BrowserContext
* context
)
95 : browser_context_(context
),
96 clock_(new base::DefaultClock()),
97 delegate_(new DefaultAlarmDelegate(context
)),
98 extension_registry_observer_(this) {
99 extension_registry_observer_
.Add(ExtensionRegistry::Get(browser_context_
));
101 StateStore
* storage
= ExtensionSystem::Get(browser_context_
)->state_store();
103 storage
->RegisterKey(kRegisteredAlarms
);
106 AlarmManager::~AlarmManager() {
109 void AlarmManager::AddAlarm(const std::string
& extension_id
,
111 const AddAlarmCallback
& callback
) {
112 RunWhenReady(extension_id
, base::Bind(&AlarmManager::AddAlarmWhenReady
,
113 AsWeakPtr(), alarm
, callback
));
116 void AlarmManager::GetAlarm(const std::string
& extension_id
,
117 const std::string
& name
,
118 const GetAlarmCallback
& callback
) {
119 RunWhenReady(extension_id
, base::Bind(&AlarmManager::GetAlarmWhenReady
,
120 AsWeakPtr(), name
, callback
));
123 void AlarmManager::GetAllAlarms(const std::string
& extension_id
,
124 const GetAllAlarmsCallback
& callback
) {
125 RunWhenReady(extension_id
, base::Bind(&AlarmManager::GetAllAlarmsWhenReady
,
126 AsWeakPtr(), callback
));
129 void AlarmManager::RemoveAlarm(const std::string
& extension_id
,
130 const std::string
& name
,
131 const RemoveAlarmCallback
& callback
) {
132 RunWhenReady(extension_id
, base::Bind(&AlarmManager::RemoveAlarmWhenReady
,
133 AsWeakPtr(), name
, callback
));
136 void AlarmManager::RemoveAllAlarms(const std::string
& extension_id
,
137 const RemoveAllAlarmsCallback
& callback
) {
138 RunWhenReady(extension_id
, base::Bind(&AlarmManager::RemoveAllAlarmsWhenReady
,
139 AsWeakPtr(), callback
));
142 void AlarmManager::AddAlarmWhenReady(const Alarm
& alarm
,
143 const AddAlarmCallback
& callback
,
144 const std::string
& extension_id
) {
145 AddAlarmImpl(extension_id
, alarm
);
146 WriteToStorage(extension_id
);
150 void AlarmManager::GetAlarmWhenReady(const std::string
& name
,
151 const GetAlarmCallback
& callback
,
152 const std::string
& extension_id
) {
153 AlarmIterator it
= GetAlarmIterator(extension_id
, name
);
154 callback
.Run(it
.first
!= alarms_
.end() ? &*it
.second
: NULL
);
157 void AlarmManager::GetAllAlarmsWhenReady(const GetAllAlarmsCallback
& callback
,
158 const std::string
& extension_id
) {
159 AlarmMap::iterator list
= alarms_
.find(extension_id
);
160 callback
.Run(list
!= alarms_
.end() ? &list
->second
: NULL
);
163 void AlarmManager::RemoveAlarmWhenReady(const std::string
& name
,
164 const RemoveAlarmCallback
& callback
,
165 const std::string
& extension_id
) {
166 AlarmIterator it
= GetAlarmIterator(extension_id
, name
);
167 if (it
.first
== alarms_
.end()) {
172 RemoveAlarmIterator(it
);
173 WriteToStorage(extension_id
);
177 void AlarmManager::RemoveAllAlarmsWhenReady(
178 const RemoveAllAlarmsCallback
& callback
,
179 const std::string
& extension_id
) {
180 AlarmMap::iterator list
= alarms_
.find(extension_id
);
181 if (list
!= alarms_
.end()) {
182 // Note: I'm using indices rather than iterators here because
183 // RemoveAlarmIterator will delete the list when it becomes empty.
184 for (size_t i
= 0, size
= list
->second
.size(); i
< size
; ++i
)
185 RemoveAlarmIterator(AlarmIterator(list
, list
->second
.begin()));
187 CHECK(alarms_
.find(extension_id
) == alarms_
.end());
188 WriteToStorage(extension_id
);
193 AlarmManager::AlarmIterator
AlarmManager::GetAlarmIterator(
194 const std::string
& extension_id
,
195 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());
200 for (AlarmList::iterator it
= list
->second
.begin(); it
!= list
->second
.end();
202 if (it
->js_alarm
->name
== name
)
203 return make_pair(list
, it
);
206 return make_pair(alarms_
.end(), AlarmList::iterator());
209 void AlarmManager::SetClockForTesting(base::Clock
* clock
) {
213 static base::LazyInstance
<BrowserContextKeyedAPIFactory
<AlarmManager
>>
214 g_factory
= LAZY_INSTANCE_INITIALIZER
;
217 BrowserContextKeyedAPIFactory
<AlarmManager
>*
218 AlarmManager::GetFactoryInstance() {
219 return g_factory
.Pointer();
223 AlarmManager
* AlarmManager::Get(content::BrowserContext
* browser_context
) {
224 return BrowserContextKeyedAPIFactory
<AlarmManager
>::Get(browser_context
);
227 void AlarmManager::RemoveAlarmIterator(const AlarmIterator
& iter
) {
228 AlarmList
& list
= iter
.first
->second
;
229 list
.erase(iter
.second
);
231 alarms_
.erase(iter
.first
);
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()) {
238 next_poll_time_
= base::Time();
242 void 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
);
248 // Update our scheduled time for the next alarm.
249 if (double* period_in_minutes
= alarm
.js_alarm
->period_in_minutes
.get()) {
250 // Get the timer's delay in JS time (i.e., convert it from minutes to
252 double period_in_js_time
= *period_in_minutes
*
253 base::Time::kMicrosecondsPerMinute
/
254 base::Time::kMicrosecondsPerMillisecond
;
255 // Find out how many periods have transpired since the alarm last went off
256 // (it's possible that we missed some).
257 int transpired_periods
=
258 (last_poll_time_
.ToJsTime() - alarm
.js_alarm
->scheduled_time
) /
260 // Schedule the alarm for the next period that is in-line with the original
262 alarm
.js_alarm
->scheduled_time
+=
263 period_in_js_time
* (transpired_periods
+ 1);
265 RemoveAlarmIterator(it
);
267 WriteToStorage(extension_id_copy
);
270 void AlarmManager::AddAlarmImpl(const std::string
& extension_id
,
271 const Alarm
& alarm
) {
272 // Override any old alarm with the same name.
273 AlarmIterator old_alarm
=
274 GetAlarmIterator(extension_id
, alarm
.js_alarm
->name
);
275 if (old_alarm
.first
!= alarms_
.end())
276 RemoveAlarmIterator(old_alarm
);
278 alarms_
[extension_id
].push_back(alarm
);
279 base::Time alarm_time
=
280 base::Time::FromJsTime(alarm
.js_alarm
->scheduled_time
);
281 if (next_poll_time_
.is_null() || alarm_time
< next_poll_time_
)
282 SetNextPollTime(alarm_time
);
285 void AlarmManager::WriteToStorage(const std::string
& extension_id
) {
286 StateStore
* storage
= ExtensionSystem::Get(browser_context_
)->state_store();
290 scoped_ptr
<base::Value
> alarms
;
291 AlarmMap::iterator list
= alarms_
.find(extension_id
);
292 if (list
!= alarms_
.end())
293 alarms
.reset(AlarmsToValue(list
->second
).release());
295 alarms
.reset(AlarmsToValue(std::vector
<Alarm
>()).release());
296 storage
->SetExtensionValue(extension_id
, kRegisteredAlarms
, alarms
.Pass());
299 void AlarmManager::ReadFromStorage(const std::string
& extension_id
,
300 scoped_ptr
<base::Value
> value
) {
301 base::ListValue
* list
= NULL
;
302 if (value
.get() && value
->GetAsList(&list
)) {
303 std::vector
<Alarm
> alarm_states
= AlarmsFromValue(list
);
304 for (size_t i
= 0; i
< alarm_states
.size(); ++i
)
305 AddAlarmImpl(extension_id
, alarm_states
[i
]);
308 ReadyQueue
& extension_ready_queue
= ready_actions_
[extension_id
];
309 while (!extension_ready_queue
.empty()) {
310 extension_ready_queue
.front().Run(extension_id
);
311 extension_ready_queue
.pop();
313 ready_actions_
.erase(extension_id
);
316 void AlarmManager::SetNextPollTime(const base::Time
& time
) {
317 next_poll_time_
= time
;
318 timer_
.Start(FROM_HERE
,
319 std::max(base::TimeDelta::FromSeconds(0), time
- clock_
->Now()),
320 this, &AlarmManager::PollAlarms
);
323 void AlarmManager::ScheduleNextPoll() {
324 // If there are no alarms, stop the timer.
325 if (alarms_
.empty()) {
327 next_poll_time_
= base::Time();
331 // Find the soonest alarm that is scheduled to run and the smallest
332 // granularity of any alarm.
333 // alarms_ guarantees that none of its contained lists are empty.
334 base::Time soonest_alarm_time
= base::Time::FromJsTime(
335 alarms_
.begin()->second
.begin()->js_alarm
->scheduled_time
);
336 base::TimeDelta min_granularity
= kDefaultMinPollPeriod();
337 for (AlarmMap::const_iterator m_it
= alarms_
.begin(), m_end
= alarms_
.end();
338 m_it
!= m_end
; ++m_it
) {
339 for (AlarmList::const_iterator l_it
= m_it
->second
.begin();
340 l_it
!= m_it
->second
.end(); ++l_it
) {
341 base::Time cur_alarm_time
=
342 base::Time::FromJsTime(l_it
->js_alarm
->scheduled_time
);
343 if (cur_alarm_time
< soonest_alarm_time
)
344 soonest_alarm_time
= cur_alarm_time
;
345 if (l_it
->granularity
< min_granularity
)
346 min_granularity
= l_it
->granularity
;
347 base::TimeDelta cur_alarm_delta
= cur_alarm_time
- last_poll_time_
;
348 if (cur_alarm_delta
< l_it
->minimum_granularity
)
349 cur_alarm_delta
= l_it
->minimum_granularity
;
350 if (cur_alarm_delta
< min_granularity
)
351 min_granularity
= cur_alarm_delta
;
355 base::Time
next_poll(last_poll_time_
+ min_granularity
);
356 // If the next alarm is more than min_granularity in the future, wait for it.
357 // Otherwise, only poll as often as min_granularity.
358 // As a special case, if we've never checked for an alarm before
359 // (e.g. during startup), let alarms fire asap.
360 if (last_poll_time_
.is_null() || next_poll
< soonest_alarm_time
)
361 next_poll
= soonest_alarm_time
;
363 // Schedule the poll.
364 SetNextPollTime(next_poll
);
367 void AlarmManager::PollAlarms() {
368 last_poll_time_
= clock_
->Now();
370 // Run any alarms scheduled in the past. OnAlarm uses vector::erase to remove
371 // elements from the AlarmList, and map::erase to remove AlarmLists from the
373 for (AlarmMap::iterator m_it
= alarms_
.begin(), m_end
= alarms_
.end();
375 AlarmMap::iterator cur_extension
= m_it
++;
377 // Iterate (a) backwards so that removing elements doesn't affect
378 // upcoming iterations, and (b) with indices so that if the last
379 // iteration destroys the AlarmList, I'm not about to use the end
380 // iterator that the destruction invalidates.
381 for (size_t i
= cur_extension
->second
.size(); i
> 0; --i
) {
382 AlarmList::iterator cur_alarm
= cur_extension
->second
.begin() + i
- 1;
383 if (base::Time::FromJsTime(cur_alarm
->js_alarm
->scheduled_time
) <=
385 OnAlarm(make_pair(cur_extension
, cur_alarm
));
393 static void RemoveAllOnUninstallCallback() {
396 void AlarmManager::RunWhenReady(const std::string
& extension_id
,
397 const ReadyAction
& action
) {
398 ReadyMap::iterator it
= ready_actions_
.find(extension_id
);
400 if (it
== ready_actions_
.end())
401 action
.Run(extension_id
);
403 it
->second
.push(action
);
406 void AlarmManager::OnExtensionLoaded(content::BrowserContext
* browser_context
,
407 const Extension
* extension
) {
408 StateStore
* storage
= ExtensionSystem::Get(browser_context_
)->state_store();
410 ready_actions_
.insert(ReadyMap::value_type(extension
->id(), ReadyQueue()));
411 storage
->GetExtensionValue(extension
->id(), kRegisteredAlarms
,
412 base::Bind(&AlarmManager::ReadFromStorage
,
413 AsWeakPtr(), extension
->id()));
417 void AlarmManager::OnExtensionUninstalled(
418 content::BrowserContext
* browser_context
,
419 const Extension
* extension
,
420 extensions::UninstallReason reason
) {
421 RemoveAllAlarms(extension
->id(), base::Bind(RemoveAllOnUninstallCallback
));
424 // AlarmManager::Alarm
426 Alarm::Alarm() : js_alarm(new alarms::Alarm()) {
429 Alarm::Alarm(const std::string
& name
,
430 const alarms::AlarmCreateInfo
& create_info
,
431 base::TimeDelta min_granularity
,
433 : js_alarm(new alarms::Alarm()) {
434 js_alarm
->name
= name
;
435 minimum_granularity
= min_granularity
;
437 if (create_info
.when
.get()) {
438 // Absolute scheduling.
439 js_alarm
->scheduled_time
= *create_info
.when
;
440 granularity
= base::Time::FromJsTime(js_alarm
->scheduled_time
) - now
;
442 // Relative scheduling.
443 double* delay_in_minutes
= create_info
.delay_in_minutes
.get();
444 if (delay_in_minutes
== NULL
)
445 delay_in_minutes
= create_info
.period_in_minutes
.get();
446 CHECK(delay_in_minutes
!= NULL
)
447 << "ValidateAlarmCreateInfo in alarms_api.cc should have "
448 << "prevented this call.";
449 base::TimeDelta delay
= TimeDeltaFromDelay(*delay_in_minutes
);
450 js_alarm
->scheduled_time
= (now
+ delay
).ToJsTime();
454 if (granularity
< min_granularity
)
455 granularity
= min_granularity
;
457 // Check for repetition.
458 if (create_info
.period_in_minutes
.get()) {
459 js_alarm
->period_in_minutes
.reset(
460 new double(*create_info
.period_in_minutes
));
467 } // namespace extensions