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/browser/extensions/state_store.h" 18 #include "chrome/common/extensions/api/alarms.h" 19 #include "extensions/browser/event_router.h" 20 #include "extensions/browser/extension_registry.h" 21 #include "extensions/browser/extension_system.h" 22 23 namespace extensions { 24 25 namespace alarms = api::alarms; 26 27 namespace { 28 29 // A list of alarms that this extension has set. 30 const char kRegisteredAlarms[] = "alarms"; 31 const char kAlarmGranularity[] = "granularity"; 32 33 // The minimum period between polling for alarms to run. 34 const base::TimeDelta kDefaultMinPollPeriod() { 35 return base::TimeDelta::FromDays(1); 36 } 37 38 class 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. 59 base::TimeDelta TimeDeltaFromDelay(double delay_in_minutes) { 60 return base::TimeDelta::FromMicroseconds( 61 delay_in_minutes * base::Time::kMicrosecondsPerMinute); 62 } 63 64 std::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 80 scoped_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 96 AlarmManager::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 108 AlarmManager::~AlarmManager() { 109 } 110 111 void 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 118 void 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 125 void AlarmManager::GetAllAlarms( 126 const std::string& extension_id, const GetAllAlarmsCallback& callback) { 127 RunWhenReady(extension_id, base::Bind( 128 &AlarmManager::GetAllAlarmsWhenReady, AsWeakPtr(), callback)); 129 } 130 131 void 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 138 void AlarmManager::RemoveAllAlarms(const std::string& extension_id, 139 const RemoveAllAlarmsCallback& callback) { 140 RunWhenReady(extension_id, base::Bind( 141 &AlarmManager::RemoveAllAlarmsWhenReady, AsWeakPtr(), callback)); 142 } 143 144 void 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 152 void 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 159 void 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 165 void 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 179 void 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 194 AlarmManager::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 209 void AlarmManager::SetClockForTesting(base::Clock* clock) { 210 clock_.reset(clock); 211 } 212 213 static base::LazyInstance<BrowserContextKeyedAPIFactory<AlarmManager> > 214 g_factory = LAZY_INSTANCE_INITIALIZER; 215 216 // static 217 BrowserContextKeyedAPIFactory<AlarmManager>* 218 AlarmManager::GetFactoryInstance() { 219 return g_factory.Pointer(); 220 } 221 222 // static 223 AlarmManager* AlarmManager::Get(content::BrowserContext* browser_context) { 224 return BrowserContextKeyedAPIFactory<AlarmManager>::Get(browser_context); 225 } 226 227 void 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 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); 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 271 void 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 286 void 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 300 void 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 317 void 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 325 void 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 369 void 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 395 static void RemoveAllOnUninstallCallback() {} 396 397 void 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 407 void 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 420 void AlarmManager::OnExtensionUninstalled( 421 content::BrowserContext* browser_context, 422 const Extension* extension) { 423 RemoveAllAlarms(extension->id(), base::Bind(RemoveAllOnUninstallCallback)); 424 } 425 426 // AlarmManager::Alarm 427 428 Alarm::Alarm() 429 : js_alarm(new api::alarms::Alarm()) { 430 } 431 432 Alarm::Alarm(const std::string& name, 433 const api::alarms::AlarmCreateInfo& create_info, 434 base::TimeDelta min_granularity, 435 base::Time now) 436 : js_alarm(new api::alarms::Alarm()) { 437 js_alarm->name = name; 438 minimum_granularity = min_granularity; 439 440 if (create_info.when.get()) { 441 // Absolute scheduling. 442 js_alarm->scheduled_time = *create_info.when; 443 granularity = base::Time::FromJsTime(js_alarm->scheduled_time) - now; 444 } else { 445 // Relative scheduling. 446 double* delay_in_minutes = create_info.delay_in_minutes.get(); 447 if (delay_in_minutes == NULL) 448 delay_in_minutes = create_info.period_in_minutes.get(); 449 CHECK(delay_in_minutes != NULL) 450 << "ValidateAlarmCreateInfo in alarms_api.cc should have " 451 << "prevented this call."; 452 base::TimeDelta delay = TimeDeltaFromDelay(*delay_in_minutes); 453 js_alarm->scheduled_time = (now + delay).ToJsTime(); 454 granularity = delay; 455 } 456 457 if (granularity < min_granularity) 458 granularity = min_granularity; 459 460 // Check for repetition. 461 if (create_info.period_in_minutes.get()) { 462 js_alarm->period_in_minutes.reset( 463 new double(*create_info.period_in_minutes)); 464 } 465 } 466 467 Alarm::~Alarm() { 468 } 469 470 } // namespace extensions 471