OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/extensions/api/alarms/alarm_manager.h" | 5 #include "chrome/browser/extensions/api/alarms/alarm_manager.h" |
6 | 6 |
7 #include "base/bind.h" | 7 #include "base/bind.h" |
8 #include "base/json/json_writer.h" | 8 #include "base/json/json_writer.h" |
9 #include "base/message_loop.h" | 9 #include "base/message_loop.h" |
10 #include "base/time.h" | 10 #include "base/time.h" |
(...skipping 11 matching lines...) Expand all Loading... |
22 | 22 |
23 namespace { | 23 namespace { |
24 | 24 |
25 const char kOnAlarmEvent[] = "alarms.onAlarm"; | 25 const char kOnAlarmEvent[] = "alarms.onAlarm"; |
26 | 26 |
27 // A list of alarms that this extension has set. | 27 // A list of alarms that this extension has set. |
28 const char kRegisteredAlarms[] = "alarms"; | 28 const char kRegisteredAlarms[] = "alarms"; |
29 const char kAlarmGranularity[] = "granularity"; | 29 const char kAlarmGranularity[] = "granularity"; |
30 | 30 |
31 // The minimum period between polling for alarms to run. | 31 // The minimum period between polling for alarms to run. |
32 const base::TimeDelta kDefaultMinPollPeriod = base::TimeDelta::FromMinutes(1); | 32 const base::TimeDelta kDefaultMinPollPeriod = base::TimeDelta::FromDays(1); |
33 | 33 |
34 class DefaultAlarmDelegate : public AlarmManager::Delegate { | 34 class DefaultAlarmDelegate : public AlarmManager::Delegate { |
35 public: | 35 public: |
36 explicit DefaultAlarmDelegate(Profile* profile) : profile_(profile) {} | 36 explicit DefaultAlarmDelegate(Profile* profile) : profile_(profile) {} |
37 virtual ~DefaultAlarmDelegate() {} | 37 virtual ~DefaultAlarmDelegate() {} |
38 | 38 |
39 virtual void OnAlarm(const std::string& extension_id, | 39 virtual void OnAlarm(const std::string& extension_id, |
40 const Alarm& alarm) { | 40 const Alarm& alarm) { |
41 scoped_ptr<ListValue> args(new ListValue()); | 41 scoped_ptr<ListValue> args(new ListValue()); |
42 args->Append(alarm.js_alarm->ToValue().release()); | 42 args->Append(alarm.js_alarm->ToValue().release()); |
(...skipping 157 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
200 void AlarmManager::AddAlarmImpl(const std::string& extension_id, | 200 void AlarmManager::AddAlarmImpl(const std::string& extension_id, |
201 const Alarm& alarm) { | 201 const Alarm& alarm) { |
202 // Override any old alarm with the same name. | 202 // Override any old alarm with the same name. |
203 AlarmIterator old_alarm = GetAlarmIterator(extension_id, | 203 AlarmIterator old_alarm = GetAlarmIterator(extension_id, |
204 alarm.js_alarm->name); | 204 alarm.js_alarm->name); |
205 if (old_alarm.first != alarms_.end()) | 205 if (old_alarm.first != alarms_.end()) |
206 RemoveAlarmIterator(old_alarm); | 206 RemoveAlarmIterator(old_alarm); |
207 | 207 |
208 alarms_[extension_id].push_back(alarm); | 208 alarms_[extension_id].push_back(alarm); |
209 | 209 |
210 // TODO(yoz): Is 0 really sane? There could be thrashing. | 210 ScheduleNextPoll(); |
211 ScheduleNextPoll(base::TimeDelta::FromMinutes(0)); | |
212 } | 211 } |
213 | 212 |
214 void AlarmManager::WriteToStorage(const std::string& extension_id) { | 213 void AlarmManager::WriteToStorage(const std::string& extension_id) { |
215 StateStore* storage = ExtensionSystem::Get(profile_)->state_store(); | 214 StateStore* storage = ExtensionSystem::Get(profile_)->state_store(); |
216 if (!storage) | 215 if (!storage) |
217 return; | 216 return; |
218 | 217 |
219 scoped_ptr<Value> alarms; | 218 scoped_ptr<Value> alarms; |
220 AlarmMap::iterator list = alarms_.find(extension_id); | 219 AlarmMap::iterator list = alarms_.find(extension_id); |
221 if (list != alarms_.end()) | 220 if (list != alarms_.end()) |
222 alarms.reset(AlarmsToValue(list->second).release()); | 221 alarms.reset(AlarmsToValue(list->second).release()); |
223 else | 222 else |
224 alarms.reset(AlarmsToValue(std::vector<Alarm>()).release()); | 223 alarms.reset(AlarmsToValue(std::vector<Alarm>()).release()); |
225 storage->SetExtensionValue(extension_id, kRegisteredAlarms, alarms.Pass()); | 224 storage->SetExtensionValue(extension_id, kRegisteredAlarms, alarms.Pass()); |
226 } | 225 } |
227 | 226 |
228 void AlarmManager::ReadFromStorage(const std::string& extension_id, | 227 void AlarmManager::ReadFromStorage(const std::string& extension_id, |
229 scoped_ptr<base::Value> value) { | 228 scoped_ptr<base::Value> value) { |
230 base::ListValue* list = NULL; | 229 base::ListValue* list = NULL; |
231 if (!value.get() || !value->GetAsList(&list)) | 230 if (!value.get() || !value->GetAsList(&list)) |
232 return; | 231 return; |
233 | 232 |
234 std::vector<Alarm> alarm_states = AlarmsFromValue(list); | 233 std::vector<Alarm> alarm_states = AlarmsFromValue(list); |
235 for (size_t i = 0; i < alarm_states.size(); ++i) { | 234 for (size_t i = 0; i < alarm_states.size(); ++i) { |
236 AddAlarmImpl(extension_id, alarm_states[i]); | 235 AddAlarmImpl(extension_id, alarm_states[i]); |
237 } | 236 } |
238 } | 237 } |
239 | 238 |
240 void AlarmManager::ScheduleNextPoll(base::TimeDelta min_period) { | 239 void AlarmManager::ScheduleNextPoll() { |
241 // 0. If there are no alarms, stop the timer. | 240 // 0. If there are no alarms, stop the timer. |
242 if (alarms_.empty()) { | 241 if (alarms_.empty()) { |
243 timer_.Stop(); | 242 timer_.Stop(); |
244 return; | 243 return; |
245 } | 244 } |
246 | 245 |
247 // TODO(yoz): Try not to reschedule every single time if we're adding | 246 // TODO(yoz): Try not to reschedule every single time if we're adding |
248 // a lot of alarms. | 247 // a lot of alarms. |
249 | 248 |
250 base::Time next_poll(last_poll_time_ + min_period); | 249 // Find the soonest alarm that is scheduled to run and the smallest |
251 | 250 // granularity of any alarm. |
252 // Find the soonest alarm that is scheduled to run. | |
253 // alarms_ guarantees that none of its contained lists are empty. | 251 // alarms_ guarantees that none of its contained lists are empty. |
254 base::Time soonest_alarm_time = base::Time::FromJsTime( | 252 base::Time soonest_alarm_time = base::Time::FromJsTime( |
255 alarms_.begin()->second.begin()->js_alarm->scheduled_time); | 253 alarms_.begin()->second.begin()->js_alarm->scheduled_time); |
| 254 base::TimeDelta min_granularity = kDefaultMinPollPeriod; |
256 for (AlarmMap::const_iterator m_it = alarms_.begin(), m_end = alarms_.end(); | 255 for (AlarmMap::const_iterator m_it = alarms_.begin(), m_end = alarms_.end(); |
257 m_it != m_end; ++m_it) { | 256 m_it != m_end; ++m_it) { |
258 for (AlarmList::const_iterator l_it = m_it->second.begin(); | 257 for (AlarmList::const_iterator l_it = m_it->second.begin(); |
259 l_it != m_it->second.end(); ++l_it) { | 258 l_it != m_it->second.end(); ++l_it) { |
260 base::Time cur_alarm_time = | 259 base::Time cur_alarm_time = |
261 base::Time::FromJsTime(l_it->js_alarm->scheduled_time); | 260 base::Time::FromJsTime(l_it->js_alarm->scheduled_time); |
262 if (cur_alarm_time < soonest_alarm_time) | 261 if (cur_alarm_time < soonest_alarm_time) |
263 soonest_alarm_time = cur_alarm_time; | 262 soonest_alarm_time = cur_alarm_time; |
| 263 if (l_it->granularity < min_granularity) |
| 264 min_granularity = l_it->granularity; |
264 } | 265 } |
265 } | 266 } |
266 | 267 |
267 // If the next alarm is more than min_period in the future, wait for it. | 268 base::Time next_poll(last_poll_time_ + min_granularity); |
268 // Otherwise, only poll as often as min_period. | 269 // If the next alarm is more than min_granularity in the future, wait for it. |
269 if (last_poll_time_.is_null() || next_poll < soonest_alarm_time) { | 270 // Otherwise, only poll as often as min_granularity. |
| 271 // As a special case, if we've never checked for an alarm before |
| 272 // (e.g. during startup), let alarms fire asap. |
| 273 if (last_poll_time_.is_null() || next_poll < soonest_alarm_time) |
270 next_poll = soonest_alarm_time; | 274 next_poll = soonest_alarm_time; |
271 } | |
272 | 275 |
273 // Schedule the poll. | 276 // Schedule the poll. |
274 next_poll_time_ = next_poll; | 277 next_poll_time_ = next_poll; |
275 base::TimeDelta delay = std::max(base::TimeDelta::FromSeconds(0), | 278 base::TimeDelta delay = std::max(base::TimeDelta::FromSeconds(0), |
276 next_poll - now_()); | 279 next_poll - now_()); |
277 timer_.Start(FROM_HERE, | 280 timer_.Start(FROM_HERE, |
278 delay, | 281 delay, |
279 this, | 282 this, |
280 &AlarmManager::PollAlarms); | 283 &AlarmManager::PollAlarms); |
281 } | 284 } |
(...skipping 14 matching lines...) Expand all Loading... |
296 // iterator that the destruction invalidates. | 299 // iterator that the destruction invalidates. |
297 for (size_t i = cur_extension->second.size(); i > 0; --i) { | 300 for (size_t i = cur_extension->second.size(); i > 0; --i) { |
298 AlarmList::iterator cur_alarm = cur_extension->second.begin() + i - 1; | 301 AlarmList::iterator cur_alarm = cur_extension->second.begin() + i - 1; |
299 if (base::Time::FromJsTime(cur_alarm->js_alarm->scheduled_time) <= | 302 if (base::Time::FromJsTime(cur_alarm->js_alarm->scheduled_time) <= |
300 next_poll_time_) { | 303 next_poll_time_) { |
301 OnAlarm(make_pair(cur_extension, cur_alarm)); | 304 OnAlarm(make_pair(cur_extension, cur_alarm)); |
302 } | 305 } |
303 } | 306 } |
304 } | 307 } |
305 | 308 |
306 // Schedule the next poll. The soonest it may happen is after | 309 ScheduleNextPoll(); |
307 // kDefaultMinPollPeriod or after the shortest granularity of any alarm, | |
308 // whichever comes sooner. | |
309 base::TimeDelta min_poll_period = kDefaultMinPollPeriod; | |
310 for (AlarmMap::const_iterator m_it = alarms_.begin(), m_end = alarms_.end(); | |
311 m_it != m_end; ++m_it) { | |
312 for (AlarmList::const_iterator l_it = m_it->second.begin(); | |
313 l_it != m_it->second.end(); ++l_it) { | |
314 if (l_it->granularity < min_poll_period) | |
315 min_poll_period = l_it->granularity; | |
316 } | |
317 } | |
318 ScheduleNextPoll(min_poll_period); | |
319 } | 310 } |
320 | 311 |
321 void AlarmManager::Observe( | 312 void AlarmManager::Observe( |
322 int type, | 313 int type, |
323 const content::NotificationSource& source, | 314 const content::NotificationSource& source, |
324 const content::NotificationDetails& details) { | 315 const content::NotificationDetails& details) { |
325 switch (type) { | 316 switch (type) { |
326 case chrome::NOTIFICATION_EXTENSION_LOADED: { | 317 case chrome::NOTIFICATION_EXTENSION_LOADED: { |
327 const Extension* extension = | 318 const Extension* extension = |
328 content::Details<const Extension>(details).ptr(); | 319 content::Details<const Extension>(details).ptr(); |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
377 if (create_info.period_in_minutes.get()) { | 368 if (create_info.period_in_minutes.get()) { |
378 js_alarm->period_in_minutes.reset( | 369 js_alarm->period_in_minutes.reset( |
379 new double(*create_info.period_in_minutes)); | 370 new double(*create_info.period_in_minutes)); |
380 } | 371 } |
381 } | 372 } |
382 | 373 |
383 Alarm::~Alarm() { | 374 Alarm::~Alarm() { |
384 } | 375 } |
385 | 376 |
386 } // namespace extensions | 377 } // namespace extensions |
OLD | NEW |