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