Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(296)

Side by Side Diff: chrome/browser/extensions/api/alarms/alarm_manager.cc

Issue 789643004: Move chrome.alarms API from chrome/ to extensions/. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebased Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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
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 ~DefaultAlarmDelegate() override {}
43
44 void OnAlarm(const std::string& extension_id, const Alarm& alarm) override {
45 scoped_ptr<base::ListValue> args(new base::ListValue());
46 args->Append(alarm.js_alarm->ToValue().release());
47 scoped_ptr<Event> event(new Event(alarms::OnAlarm::kEventName,
48 args.Pass()));
49 EventRouter::Get(browser_context_)
50 ->DispatchEventToExtension(extension_id, event.Pass());
51 }
52
53 private:
54 content::BrowserContext* browser_context_;
55 };
56
57 // Creates a TimeDelta from a delay as specified in the API.
58 base::TimeDelta TimeDeltaFromDelay(double delay_in_minutes) {
59 return base::TimeDelta::FromMicroseconds(
60 delay_in_minutes * base::Time::kMicrosecondsPerMinute);
61 }
62
63 std::vector<Alarm> AlarmsFromValue(const base::ListValue* list) {
64 std::vector<Alarm> alarms;
65 for (size_t i = 0; i < list->GetSize(); ++i) {
66 const base::DictionaryValue* alarm_dict = NULL;
67 Alarm alarm;
68 if (list->GetDictionary(i, &alarm_dict) &&
69 api::alarms::Alarm::Populate(*alarm_dict, alarm.js_alarm.get())) {
70 const base::Value* time_value = NULL;
71 if (alarm_dict->Get(kAlarmGranularity, &time_value))
72 base::GetValueAsTimeDelta(*time_value, &alarm.granularity);
73 alarms.push_back(alarm);
74 }
75 }
76 return alarms;
77 }
78
79 scoped_ptr<base::ListValue> AlarmsToValue(const std::vector<Alarm>& alarms) {
80 scoped_ptr<base::ListValue> list(new base::ListValue());
81 for (size_t i = 0; i < alarms.size(); ++i) {
82 scoped_ptr<base::DictionaryValue> alarm =
83 alarms[i].js_alarm->ToValue().Pass();
84 alarm->Set(kAlarmGranularity,
85 base::CreateTimeDeltaValue(alarms[i].granularity));
86 list->Append(alarm.release());
87 }
88 return list.Pass();
89 }
90
91 } // namespace
92
93 // AlarmManager
94
95 AlarmManager::AlarmManager(content::BrowserContext* context)
96 : browser_context_(context),
97 clock_(new base::DefaultClock()),
98 delegate_(new DefaultAlarmDelegate(context)),
99 extension_registry_observer_(this) {
100 extension_registry_observer_.Add(ExtensionRegistry::Get(browser_context_));
101
102 StateStore* storage = ExtensionSystem::Get(browser_context_)->state_store();
103 if (storage)
104 storage->RegisterKey(kRegisteredAlarms);
105 }
106
107 AlarmManager::~AlarmManager() {
108 }
109
110 void AlarmManager::AddAlarm(const std::string& extension_id,
111 const Alarm& alarm,
112 const AddAlarmCallback& callback) {
113 RunWhenReady(extension_id, base::Bind(
114 &AlarmManager::AddAlarmWhenReady, AsWeakPtr(), alarm, callback));
115 }
116
117 void AlarmManager::GetAlarm(const std::string& extension_id,
118 const std::string& name,
119 const GetAlarmCallback& callback) {
120 RunWhenReady(extension_id, base::Bind(
121 &AlarmManager::GetAlarmWhenReady, AsWeakPtr(), name, callback));
122 }
123
124 void AlarmManager::GetAllAlarms(
125 const std::string& extension_id, const GetAllAlarmsCallback& callback) {
126 RunWhenReady(extension_id, base::Bind(
127 &AlarmManager::GetAllAlarmsWhenReady, AsWeakPtr(), callback));
128 }
129
130 void AlarmManager::RemoveAlarm(const std::string& extension_id,
131 const std::string& name,
132 const RemoveAlarmCallback& callback) {
133 RunWhenReady(extension_id, base::Bind(
134 &AlarmManager::RemoveAlarmWhenReady, AsWeakPtr(), name, callback));
135 }
136
137 void AlarmManager::RemoveAllAlarms(const std::string& extension_id,
138 const RemoveAllAlarmsCallback& callback) {
139 RunWhenReady(extension_id, base::Bind(
140 &AlarmManager::RemoveAllAlarmsWhenReady, AsWeakPtr(), callback));
141 }
142
143 void AlarmManager::AddAlarmWhenReady(const Alarm& alarm,
144 const AddAlarmCallback& callback,
145 const std::string& extension_id) {
146 AddAlarmImpl(extension_id, alarm);
147 WriteToStorage(extension_id);
148 callback.Run();
149 }
150
151 void AlarmManager::GetAlarmWhenReady(const std::string& name,
152 const GetAlarmCallback& callback,
153 const std::string& extension_id) {
154 AlarmIterator it = GetAlarmIterator(extension_id, name);
155 callback.Run(it.first != alarms_.end() ? &*it.second : NULL);
156 }
157
158 void AlarmManager::GetAllAlarmsWhenReady(const GetAllAlarmsCallback& callback,
159 const std::string& extension_id) {
160 AlarmMap::iterator list = alarms_.find(extension_id);
161 callback.Run(list != alarms_.end() ? &list->second : NULL);
162 }
163
164 void AlarmManager::RemoveAlarmWhenReady(const std::string& name,
165 const RemoveAlarmCallback& callback,
166 const std::string& extension_id) {
167 AlarmIterator it = GetAlarmIterator(extension_id, name);
168 if (it.first == alarms_.end()) {
169 callback.Run(false);
170 return;
171 }
172
173 RemoveAlarmIterator(it);
174 WriteToStorage(extension_id);
175 callback.Run(true);
176 }
177
178 void AlarmManager::RemoveAllAlarmsWhenReady(
179 const RemoveAllAlarmsCallback& callback, 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()));
186
187 CHECK(alarms_.find(extension_id) == alarms_.end());
188 WriteToStorage(extension_id);
189 }
190 callback.Run();
191 }
192
193 AlarmManager::AlarmIterator AlarmManager::GetAlarmIterator(
194 const std::string& extension_id, const std::string& name) {
195 AlarmMap::iterator list = alarms_.find(extension_id);
196 if (list == alarms_.end())
197 return make_pair(alarms_.end(), AlarmList::iterator());
198
199 for (AlarmList::iterator it = list->second.begin();
200 it != list->second.end(); ++it) {
201 if (it->js_alarm->name == name)
202 return make_pair(list, it);
203 }
204
205 return make_pair(alarms_.end(), AlarmList::iterator());
206 }
207
208 void AlarmManager::SetClockForTesting(base::Clock* clock) {
209 clock_.reset(clock);
210 }
211
212 static base::LazyInstance<BrowserContextKeyedAPIFactory<AlarmManager> >
213 g_factory = LAZY_INSTANCE_INITIALIZER;
214
215 // static
216 BrowserContextKeyedAPIFactory<AlarmManager>*
217 AlarmManager::GetFactoryInstance() {
218 return g_factory.Pointer();
219 }
220
221 // static
222 AlarmManager* AlarmManager::Get(content::BrowserContext* browser_context) {
223 return BrowserContextKeyedAPIFactory<AlarmManager>::Get(browser_context);
224 }
225
226 void AlarmManager::RemoveAlarmIterator(const AlarmIterator& iter) {
227 AlarmList& list = iter.first->second;
228 list.erase(iter.second);
229 if (list.empty())
230 alarms_.erase(iter.first);
231
232 // Cancel the timer if there are no more alarms.
233 // We don't need to reschedule the poll otherwise, because in
234 // the worst case we would just poll one extra time.
235 if (alarms_.empty()) {
236 timer_.Stop();
237 next_poll_time_ = base::Time();
238 }
239 }
240
241 void AlarmManager::OnAlarm(AlarmIterator it) {
242 CHECK(it.first != alarms_.end());
243 Alarm& alarm = *it.second;
244 std::string extension_id_copy(it.first->first);
245 delegate_->OnAlarm(extension_id_copy, alarm);
246
247 // Update our scheduled time for the next alarm.
248 if (double* period_in_minutes =
249 alarm.js_alarm->period_in_minutes.get()) {
250 // Get the timer's delay in JS time (i.e., convert it from minutes to
251 // milliseconds).
252 double period_in_js_time =
253 *period_in_minutes * 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) /
259 period_in_js_time;
260 // Schedule the alarm for the next period that is in-line with the original
261 // scheduling.
262 alarm.js_alarm->scheduled_time +=
263 period_in_js_time * (transpired_periods + 1);
264 } else {
265 RemoveAlarmIterator(it);
266 }
267 WriteToStorage(extension_id_copy);
268 }
269
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 = GetAlarmIterator(extension_id,
274 alarm.js_alarm->name);
275 if (old_alarm.first != alarms_.end())
276 RemoveAlarmIterator(old_alarm);
277
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);
283 }
284
285 void AlarmManager::WriteToStorage(const std::string& extension_id) {
286 StateStore* storage = ExtensionSystem::Get(browser_context_)->state_store();
287 if (!storage)
288 return;
289
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());
294 else
295 alarms.reset(AlarmsToValue(std::vector<Alarm>()).release());
296 storage->SetExtensionValue(extension_id, kRegisteredAlarms, alarms.Pass());
297 }
298
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]);
306 }
307
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();
312 }
313 ready_actions_.erase(extension_id);
314 }
315
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,
321 &AlarmManager::PollAlarms);
322 }
323
324 void AlarmManager::ScheduleNextPoll() {
325 // If there are no alarms, stop the timer.
326 if (alarms_.empty()) {
327 timer_.Stop();
328 next_poll_time_ = base::Time();
329 return;
330 }
331
332 // Find the soonest alarm that is scheduled to run and the smallest
333 // granularity of any alarm.
334 // alarms_ guarantees that none of its contained lists are empty.
335 base::Time soonest_alarm_time = base::Time::FromJsTime(
336 alarms_.begin()->second.begin()->js_alarm->scheduled_time);
337 base::TimeDelta min_granularity = kDefaultMinPollPeriod();
338 for (AlarmMap::const_iterator m_it = alarms_.begin(), m_end = alarms_.end();
339 m_it != m_end; ++m_it) {
340 for (AlarmList::const_iterator l_it = m_it->second.begin();
341 l_it != m_it->second.end(); ++l_it) {
342 base::Time cur_alarm_time =
343 base::Time::FromJsTime(l_it->js_alarm->scheduled_time);
344 if (cur_alarm_time < soonest_alarm_time)
345 soonest_alarm_time = cur_alarm_time;
346 if (l_it->granularity < min_granularity)
347 min_granularity = l_it->granularity;
348 base::TimeDelta cur_alarm_delta = cur_alarm_time - last_poll_time_;
349 if (cur_alarm_delta < l_it->minimum_granularity)
350 cur_alarm_delta = l_it->minimum_granularity;
351 if (cur_alarm_delta < min_granularity)
352 min_granularity = cur_alarm_delta;
353 }
354 }
355
356 base::Time next_poll(last_poll_time_ + min_granularity);
357 // If the next alarm is more than min_granularity in the future, wait for it.
358 // Otherwise, only poll as often as min_granularity.
359 // As a special case, if we've never checked for an alarm before
360 // (e.g. during startup), let alarms fire asap.
361 if (last_poll_time_.is_null() || next_poll < soonest_alarm_time)
362 next_poll = soonest_alarm_time;
363
364 // Schedule the poll.
365 SetNextPollTime(next_poll);
366 }
367
368 void AlarmManager::PollAlarms() {
369 last_poll_time_ = clock_->Now();
370
371 // Run any alarms scheduled in the past. OnAlarm uses vector::erase to remove
372 // elements from the AlarmList, and map::erase to remove AlarmLists from the
373 // AlarmMap.
374 for (AlarmMap::iterator m_it = alarms_.begin(), m_end = alarms_.end();
375 m_it != m_end;) {
376 AlarmMap::iterator cur_extension = m_it++;
377
378 // Iterate (a) backwards so that removing elements doesn't affect
379 // upcoming iterations, and (b) with indices so that if the last
380 // iteration destroys the AlarmList, I'm not about to use the end
381 // iterator that the destruction invalidates.
382 for (size_t i = cur_extension->second.size(); i > 0; --i) {
383 AlarmList::iterator cur_alarm = cur_extension->second.begin() + i - 1;
384 if (base::Time::FromJsTime(cur_alarm->js_alarm->scheduled_time) <=
385 last_poll_time_) {
386 OnAlarm(make_pair(cur_extension, cur_alarm));
387 }
388 }
389 }
390
391 ScheduleNextPoll();
392 }
393
394 static void RemoveAllOnUninstallCallback() {}
395
396 void AlarmManager::RunWhenReady(
397 const std::string& extension_id, const ReadyAction& action) {
398 ReadyMap::iterator it = ready_actions_.find(extension_id);
399
400 if (it == ready_actions_.end())
401 action.Run(extension_id);
402 else
403 it->second.push(action);
404 }
405
406 void AlarmManager::OnExtensionLoaded(content::BrowserContext* browser_context,
407 const Extension* extension) {
408 StateStore* storage = ExtensionSystem::Get(browser_context_)->state_store();
409 if (storage) {
410 ready_actions_.insert(ReadyMap::value_type(extension->id(), ReadyQueue()));
411 storage->GetExtensionValue(
412 extension->id(),
413 kRegisteredAlarms,
414 base::Bind(
415 &AlarmManager::ReadFromStorage, AsWeakPtr(), extension->id()));
416 }
417 }
418
419 void AlarmManager::OnExtensionUninstalled(
420 content::BrowserContext* browser_context,
421 const Extension* extension,
422 extensions::UninstallReason reason) {
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
OLDNEW
« no previous file with comments | « chrome/browser/extensions/api/alarms/alarm_manager.h ('k') | chrome/browser/extensions/api/alarms/alarms_api.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698