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

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

Issue 10545104: Refactor chrome.alarms interface to support absolute alarm deadlines. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fix Matt's comments, and re-merge create() Created 8 years, 6 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 | Annotate | Revision Log
OLDNEW
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 14 matching lines...) Expand all
25 25
26 // The minimum period between polling for alarms to run. 26 // The minimum period between polling for alarms to run.
27 const base::TimeDelta kDefaultMinPollPeriod = base::TimeDelta::FromMinutes(5); 27 const base::TimeDelta kDefaultMinPollPeriod = base::TimeDelta::FromMinutes(5);
28 28
29 class DefaultAlarmDelegate : public AlarmManager::Delegate { 29 class DefaultAlarmDelegate : public AlarmManager::Delegate {
30 public: 30 public:
31 explicit DefaultAlarmDelegate(Profile* profile) : profile_(profile) {} 31 explicit DefaultAlarmDelegate(Profile* profile) : profile_(profile) {}
32 virtual ~DefaultAlarmDelegate() {} 32 virtual ~DefaultAlarmDelegate() {}
33 33
34 virtual void OnAlarm(const std::string& extension_id, 34 virtual void OnAlarm(const std::string& extension_id,
35 const AlarmManager::Alarm& alarm) { 35 const Alarm& alarm) {
36 ListValue args; 36 ListValue args;
37 std::string json_args; 37 std::string json_args;
38 args.Append(alarm.ToValue().release()); 38 args.Append(alarm.js_alarm->ToValue().release());
39 base::JSONWriter::Write(&args, &json_args); 39 base::JSONWriter::Write(&args, &json_args);
40 ExtensionSystem::Get(profile_)->event_router()->DispatchEventToExtension( 40 ExtensionSystem::Get(profile_)->event_router()->DispatchEventToExtension(
41 extension_id, kOnAlarmEvent, json_args, NULL, GURL()); 41 extension_id, kOnAlarmEvent, json_args, NULL, GURL());
42 } 42 }
43 43
44 private: 44 private:
45 Profile* profile_; 45 Profile* profile_;
46 }; 46 };
47 47
48 // Creates a TimeDelta from a delay as specified in the API. 48 // Creates a TimeDelta from a delay as specified in the API.
49 base::TimeDelta TimeDeltaFromDelay(double delay_in_minutes) { 49 base::TimeDelta TimeDeltaFromDelay(double delay_in_minutes) {
50 return base::TimeDelta::FromMicroseconds( 50 return base::TimeDelta::FromMicroseconds(
51 delay_in_minutes * base::Time::kMicrosecondsPerMinute); 51 delay_in_minutes * base::Time::kMicrosecondsPerMinute);
52 } 52 }
53 53
54 } // namespace 54 } // namespace
55 55
56 // AlarmManager 56 // AlarmManager
57 57
58 AlarmManager::AlarmManager(Profile* profile) 58 AlarmManager::AlarmManager(Profile* profile, TimeProvider now)
59 : profile_(profile), 59 : profile_(profile),
60 now_(now),
60 delegate_(new DefaultAlarmDelegate(profile)), 61 delegate_(new DefaultAlarmDelegate(profile)),
61 last_poll_time_(base::Time()) { 62 last_poll_time_(base::Time()) {
62 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED, 63 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED,
63 content::Source<Profile>(profile_)); 64 content::Source<Profile>(profile_));
64 } 65 }
65 66
66 AlarmManager::~AlarmManager() { 67 AlarmManager::~AlarmManager() {
67 } 68 }
68 69
69 void AlarmManager::AddAlarm(const std::string& extension_id, 70 void AlarmManager::AddAlarm(const std::string& extension_id,
70 const linked_ptr<Alarm>& alarm) { 71 const Alarm& alarm) {
71 base::TimeDelta alarm_time = TimeDeltaFromDelay(alarm->delay_in_minutes); 72 AddAlarmImpl(extension_id, alarm);
72 AddAlarmImpl(extension_id, alarm, alarm_time);
73 WriteToPrefs(extension_id); 73 WriteToPrefs(extension_id);
74 } 74 }
75 75
76 const AlarmManager::Alarm* AlarmManager::GetAlarm( 76 const Alarm* AlarmManager::GetAlarm(
77 const std::string& extension_id, const std::string& name) { 77 const std::string& extension_id, const std::string& name) {
78 AlarmIterator it = GetAlarmIterator(extension_id, name); 78 AlarmIterator it = GetAlarmIterator(extension_id, name);
79 if (it.first == alarms_.end()) 79 if (it.first == alarms_.end())
80 return NULL; 80 return NULL;
81 return it.second->get(); 81 return &*it.second;
82 } 82 }
83 83
84 const AlarmManager::AlarmList* AlarmManager::GetAllAlarms( 84 const AlarmManager::AlarmList* AlarmManager::GetAllAlarms(
85 const std::string& extension_id) { 85 const std::string& extension_id) {
86 AlarmMap::iterator list = alarms_.find(extension_id); 86 AlarmMap::iterator list = alarms_.find(extension_id);
87 if (list == alarms_.end()) 87 if (list == alarms_.end())
88 return NULL; 88 return NULL;
89 return &list->second; 89 return &list->second;
90 } 90 }
91 91
92 AlarmManager::AlarmIterator AlarmManager::GetAlarmIterator( 92 AlarmManager::AlarmIterator AlarmManager::GetAlarmIterator(
93 const std::string& extension_id, const std::string& name) { 93 const std::string& extension_id, const std::string& name) {
94 AlarmMap::iterator list = alarms_.find(extension_id); 94 AlarmMap::iterator list = alarms_.find(extension_id);
95 if (list == alarms_.end()) 95 if (list == alarms_.end())
96 return make_pair(alarms_.end(), AlarmList::iterator()); 96 return make_pair(alarms_.end(), AlarmList::iterator());
97 97
98 for (AlarmList::iterator it = list->second.begin(); 98 for (AlarmList::iterator it = list->second.begin();
99 it != list->second.end(); ++it) { 99 it != list->second.end(); ++it) {
100 if ((*it)->name == name) 100 if (it->js_alarm->name == name)
101 return make_pair(list, it); 101 return make_pair(list, it);
102 } 102 }
103 103
104 return make_pair(alarms_.end(), AlarmList::iterator()); 104 return make_pair(alarms_.end(), AlarmList::iterator());
105 } 105 }
106 106
107 bool AlarmManager::RemoveAlarm(const std::string& extension_id, 107 bool AlarmManager::RemoveAlarm(const std::string& extension_id,
108 const std::string& name) { 108 const std::string& name) {
109 AlarmIterator it = GetAlarmIterator(extension_id, name); 109 AlarmIterator it = GetAlarmIterator(extension_id, name);
110 if (it.first == alarms_.end()) 110 if (it.first == alarms_.end())
(...skipping 12 matching lines...) Expand all
123 // Note: I'm using indices rather than iterators here because 123 // Note: I'm using indices rather than iterators here because
124 // RemoveAlarmIterator will delete the list when it becomes empty. 124 // RemoveAlarmIterator will delete the list when it becomes empty.
125 for (size_t i = 0, size = list->second.size(); i < size; ++i) 125 for (size_t i = 0, size = list->second.size(); i < size; ++i)
126 RemoveAlarmIterator(AlarmIterator(list, list->second.begin())); 126 RemoveAlarmIterator(AlarmIterator(list, list->second.begin()));
127 127
128 CHECK(alarms_.find(extension_id) == alarms_.end()); 128 CHECK(alarms_.find(extension_id) == alarms_.end());
129 WriteToPrefs(extension_id); 129 WriteToPrefs(extension_id);
130 } 130 }
131 131
132 void AlarmManager::RemoveAlarmIterator(const AlarmIterator& iter) { 132 void AlarmManager::RemoveAlarmIterator(const AlarmIterator& iter) {
133 // Cancel the timer if there are no more alarms.
134 // We don't need to reschedule the poll otherwise, because in
135 // the worst case we would just poll one extra time.
136 scheduled_times_.erase(iter.second->get());
137 if (scheduled_times_.empty())
138 timer_.Stop();
139
140 // Clean up our alarm list.
141 AlarmList& list = iter.first->second; 133 AlarmList& list = iter.first->second;
142 list.erase(iter.second); 134 list.erase(iter.second);
143 if (list.empty()) 135 if (list.empty())
144 alarms_.erase(iter.first); 136 alarms_.erase(iter.first);
137
138 // Cancel the timer if there are no more alarms.
139 // We don't need to reschedule the poll otherwise, because in
140 // the worst case we would just poll one extra time.
141 if (alarms_.empty())
142 timer_.Stop();
145 } 143 }
146 144
147 void AlarmManager::OnAlarm(const std::string& extension_id, 145 void AlarmManager::OnAlarm(AlarmIterator it) {
148 const std::string& name) {
149 AlarmIterator it = GetAlarmIterator(extension_id, name);
150 CHECK(it.first != alarms_.end()); 146 CHECK(it.first != alarms_.end());
151 const Alarm* alarm = it.second->get(); 147 Alarm& alarm = *it.second;
152 delegate_->OnAlarm(extension_id, *alarm); 148 std::string extension_id_copy(it.first->first);
149 delegate_->OnAlarm(extension_id_copy, alarm);
153 150
154 std::string extension_id_copy(extension_id); 151 // Update our scheduled time for the next alarm.
155 if (!alarm->repeating) { 152 if (double* period_in_minutes =
153 alarm.js_alarm->period_in_minutes.get()) {
154 alarm.js_alarm->scheduled_time =
155 (last_poll_time_ +
156 TimeDeltaFromDelay(*period_in_minutes)).ToJsTime();
157 } else {
156 RemoveAlarmIterator(it); 158 RemoveAlarmIterator(it);
157 } else {
158 // Update our scheduled time for the next alarm.
159 scheduled_times_[alarm].time =
160 last_poll_time_ + TimeDeltaFromDelay(alarm->delay_in_minutes);
161 } 159 }
162 WriteToPrefs(extension_id_copy); 160 WriteToPrefs(extension_id_copy);
163 } 161 }
164 162
165 void AlarmManager::AddAlarmImpl(const std::string& extension_id, 163 void AlarmManager::AddAlarmImpl(const std::string& extension_id,
166 const linked_ptr<Alarm>& alarm, 164 const Alarm& alarm) {
167 base::TimeDelta time_delay) {
168 // Override any old alarm with the same name. 165 // Override any old alarm with the same name.
169 AlarmIterator old_alarm = GetAlarmIterator(extension_id, alarm->name); 166 AlarmIterator old_alarm = GetAlarmIterator(extension_id,
167 alarm.js_alarm->name);
170 if (old_alarm.first != alarms_.end()) 168 if (old_alarm.first != alarms_.end())
171 RemoveAlarmIterator(old_alarm); 169 RemoveAlarmIterator(old_alarm);
172 170
173 alarms_[extension_id].push_back(alarm); 171 alarms_[extension_id].push_back(alarm);
174 AlarmRuntimeInfo info;
175 info.extension_id = extension_id;
176 info.time = base::Time::Now() + time_delay;
177 scheduled_times_[alarm.get()] = info;
178 172
179 // TODO(yoz): Is 0 really sane? There could be thrashing. 173 // TODO(yoz): Is 0 really sane? There could be thrashing.
180 ScheduleNextPoll(base::TimeDelta::FromMinutes(0)); 174 ScheduleNextPoll(base::TimeDelta::FromMinutes(0));
181 } 175 }
182 176
183 void AlarmManager::WriteToPrefs(const std::string& extension_id) { 177 void AlarmManager::WriteToPrefs(const std::string& extension_id) {
184 ExtensionService* service = 178 ExtensionService* service =
185 ExtensionSystem::Get(profile_)->extension_service(); 179 ExtensionSystem::Get(profile_)->extension_service();
186 if (!service || !service->extension_prefs()) 180 if (!service || !service->extension_prefs())
187 return; 181 return;
188 182
189 std::vector<AlarmPref> alarm_prefs;
190
191 AlarmMap::iterator list = alarms_.find(extension_id); 183 AlarmMap::iterator list = alarms_.find(extension_id);
192 if (list != alarms_.end()) { 184 if (list != alarms_.end())
193 for (AlarmList::iterator it = list->second.begin(); 185 service->extension_prefs()->SetRegisteredAlarms(
194 it != list->second.end(); ++it) { 186 extension_id, list->second);
195 AlarmPref pref; 187 else
196 pref.alarm = *it; 188 service->extension_prefs()->SetRegisteredAlarms(
197 pref.scheduled_run_time = scheduled_times_[it->get()].time; 189 extension_id, std::vector<Alarm>());
198 alarm_prefs.push_back(pref);
199 }
200 }
201
202 service->extension_prefs()->SetRegisteredAlarms(extension_id, alarm_prefs);
203 } 190 }
204 191
205 void AlarmManager::ReadFromPrefs(const std::string& extension_id) { 192 void AlarmManager::ReadFromPrefs(const std::string& extension_id) {
206 ExtensionService* service = 193 ExtensionService* service =
207 ExtensionSystem::Get(profile_)->extension_service(); 194 ExtensionSystem::Get(profile_)->extension_service();
208 if (!service || !service->extension_prefs()) 195 if (!service || !service->extension_prefs())
209 return; 196 return;
210 197
211 std::vector<AlarmPref> alarm_prefs = 198 std::vector<Alarm> alarm_prefs =
212 service->extension_prefs()->GetRegisteredAlarms(extension_id); 199 service->extension_prefs()->GetRegisteredAlarms(extension_id);
213 for (size_t i = 0; i < alarm_prefs.size(); ++i) { 200 for (size_t i = 0; i < alarm_prefs.size(); ++i) {
214 base::TimeDelta delay = 201 AddAlarmImpl(extension_id, alarm_prefs[i]);
215 alarm_prefs[i].scheduled_run_time - base::Time::Now();
216 if (delay < base::TimeDelta::FromSeconds(0))
217 delay = base::TimeDelta::FromSeconds(0);
218
219 AddAlarmImpl(extension_id, alarm_prefs[i].alarm, delay);
220 } 202 }
221 } 203 }
222 204
223 void AlarmManager::ScheduleNextPoll(base::TimeDelta min_period) { 205 void AlarmManager::ScheduleNextPoll(base::TimeDelta min_period) {
224 // 0. If there are no alarms, stop the timer. 206 // 0. If there are no alarms, stop the timer.
225 if (scheduled_times_.empty()) { 207 if (alarms_.empty()) {
226 timer_.Stop(); 208 timer_.Stop();
227 return; 209 return;
228 } 210 }
229 211
230 // TODO(yoz): Try not to reschedule every single time if we're adding 212 // TODO(yoz): Try not to reschedule every single time if we're adding
231 // a lot of alarms. 213 // a lot of alarms.
232 214
233 base::Time next_poll(last_poll_time_ + min_period); 215 base::Time next_poll(last_poll_time_ + min_period);
234 216
235 // Find the soonest alarm that is scheduled to run. 217 // Find the soonest alarm that is scheduled to run.
236 AlarmRuntimeInfoMap::iterator min_it = scheduled_times_.begin(); 218 // alarms_ guarantees that none of its contained lists are empty.
237 for (AlarmRuntimeInfoMap::iterator it = min_it; 219 base::Time soonest_alarm_time = base::Time::FromJsTime(
238 it != scheduled_times_.end(); ++it) { 220 alarms_.begin()->second.begin()->js_alarm->scheduled_time);
239 if (it->second.time < min_it->second.time) 221 for (AlarmMap::const_iterator m_it = alarms_.begin(), m_end = alarms_.end();
240 min_it = it; 222 m_it != m_end; ++m_it) {
223 for (AlarmList::const_iterator l_it = m_it->second.begin();
224 l_it != m_it->second.end(); ++l_it) {
225 base::Time cur_alarm_time =
226 base::Time::FromJsTime(l_it->js_alarm->scheduled_time);
227 if (cur_alarm_time < soonest_alarm_time)
228 soonest_alarm_time = cur_alarm_time;
229 }
241 } 230 }
242 base::Time soonest_alarm_time(min_it->second.time);
243 231
244 // If the next alarm is more than min_period in the future, wait for it. 232 // If the next alarm is more than min_period in the future, wait for it.
245 // Otherwise, only poll as often as min_period. 233 // Otherwise, only poll as often as min_period.
246 if (last_poll_time_.is_null() || next_poll < soonest_alarm_time) { 234 if (last_poll_time_.is_null() || next_poll < soonest_alarm_time) {
247 next_poll = soonest_alarm_time; 235 next_poll = soonest_alarm_time;
248 } 236 }
249 237
250 // Schedule the poll. 238 // Schedule the poll.
251 next_poll_time_ = next_poll; 239 next_poll_time_ = next_poll;
252 base::TimeDelta delay = std::max(base::TimeDelta::FromSeconds(0), 240 base::TimeDelta delay = std::max(base::TimeDelta::FromSeconds(0),
253 next_poll - base::Time::Now()); 241 next_poll - now_());
254 timer_.Start(FROM_HERE, 242 timer_.Start(FROM_HERE,
255 delay, 243 delay,
256 this, 244 this,
257 &AlarmManager::PollAlarms); 245 &AlarmManager::PollAlarms);
258 } 246 }
259 247
260 void AlarmManager::PollAlarms() { 248 void AlarmManager::PollAlarms() {
261 last_poll_time_ = base::Time::Now(); 249 last_poll_time_ = now_();
262 250
263 // Run any alarms scheduled in the past. Note that we could remove alarms 251 // Run any alarms scheduled in the past. OnAlarm uses vector::erase to remove
264 // during iteration if they are non-repeating. 252 // elements from the AlarmList, and map::erase to remove AlarmLists from the
265 AlarmRuntimeInfoMap::iterator iter = scheduled_times_.begin(); 253 // AlarmMap.
266 while (iter != scheduled_times_.end()) { 254 for (AlarmMap::iterator m_it = alarms_.begin(), m_end = alarms_.end();
267 AlarmRuntimeInfoMap::iterator it = iter; 255 m_it != m_end;) {
268 ++iter; 256 AlarmMap::iterator cur_extension = m_it++;
269 if (it->second.time <= next_poll_time_) { 257
270 OnAlarm(it->second.extension_id, it->first->name); 258 // Iterate (a) backwards so that removing elements doesn't affect
259 // upcoming iterations, and (b) with indices so that if the last
260 // iteration destroys the AlarmList, I'm not about to use the end
261 // iterator that the destruction invalidates.
262 for (size_t i = cur_extension->second.size(); i > 0; --i) {
263 AlarmList::iterator cur_alarm = cur_extension->second.begin() + i - 1;
264 if (base::Time::FromJsTime(cur_alarm->js_alarm->scheduled_time) <=
265 next_poll_time_) {
266 OnAlarm(make_pair(cur_extension, cur_alarm));
267 }
271 } 268 }
272 } 269 }
273 270
274 // Schedule the next poll. The soonest it may happen is after 271 // Schedule the next poll. The soonest it may happen is after
275 // kDefaultMinPollPeriod or after the shortest scheduled delay of any alarm, 272 // kDefaultMinPollPeriod or after the shortest granularity of any alarm,
276 // whichever comes sooner. 273 // whichever comes sooner.
277 base::TimeDelta min_poll_period = kDefaultMinPollPeriod; 274 base::TimeDelta min_poll_period = kDefaultMinPollPeriod;
278 for (AlarmRuntimeInfoMap::iterator it = scheduled_times_.begin(); 275 for (AlarmMap::const_iterator m_it = alarms_.begin(), m_end = alarms_.end();
279 it != scheduled_times_.end(); ++it) { 276 m_it != m_end; ++m_it) {
280 min_poll_period = std::min(TimeDeltaFromDelay(it->first->delay_in_minutes), 277 for (AlarmList::const_iterator
281 min_poll_period); 278 l_it = m_it->second.begin(), l_end = m_it->second.end();
279 l_it != l_end; ++l_it) {
280 if (l_it->granularity < min_poll_period)
281 min_poll_period = l_it->granularity;
282 }
282 } 283 }
283 ScheduleNextPoll(min_poll_period); 284 ScheduleNextPoll(min_poll_period);
284 } 285 }
285 286
286 void AlarmManager::Observe( 287 void AlarmManager::Observe(
287 int type, 288 int type,
288 const content::NotificationSource& source, 289 const content::NotificationSource& source,
289 const content::NotificationDetails& details) { 290 const content::NotificationDetails& details) {
290 switch (type) { 291 switch (type) {
291 case chrome::NOTIFICATION_EXTENSION_LOADED: { 292 case chrome::NOTIFICATION_EXTENSION_LOADED: {
292 const Extension* extension = 293 const Extension* extension =
293 content::Details<const Extension>(details).ptr(); 294 content::Details<const Extension>(details).ptr();
294 ReadFromPrefs(extension->id()); 295 ReadFromPrefs(extension->id());
295 break; 296 break;
296 } 297 }
297 default: 298 default:
298 NOTREACHED(); 299 NOTREACHED();
299 break; 300 break;
300 } 301 }
301 } 302 }
302 303
303 AlarmPref::AlarmPref() { 304 // AlarmManager::Alarm
305
306 Alarm::Alarm()
307 : js_alarm(new api::alarms::Alarm()) {
304 } 308 }
305 309
306 AlarmPref::~AlarmPref() { 310 Alarm::Alarm(const std::string& name,
311 const api::alarms::AlarmCreateInfo& create_info,
312 base::TimeDelta min_granularity,
313 TimeProvider now)
314 : js_alarm(new api::alarms::Alarm()) {
315 js_alarm->name = name;
316
317 if (create_info.when.get()) {
318 // Absolute scheduling.
319 js_alarm->scheduled_time = *create_info.when;
320 granularity = base::Time::FromJsTime(js_alarm->scheduled_time) - now();
321 } else {
322 // Relative scheduling.
323 double* delay_in_minutes = create_info.delay_in_minutes.get();
324 if (delay_in_minutes == NULL)
325 delay_in_minutes = create_info.period_in_minutes.get();
326 CHECK(delay_in_minutes != NULL)
327 << "ValidateAlarmCreateInfo in alarms_api.cc should have "
328 << "prevented this call.";
329 base::TimeDelta delay = TimeDeltaFromDelay(*delay_in_minutes);
330 js_alarm->scheduled_time = (now() + delay).ToJsTime();
331 granularity = delay;
332 }
333
334 if (granularity < min_granularity)
335 granularity = min_granularity;
336
337 // Check for repetition.
338 if (create_info.period_in_minutes.get()) {
339 js_alarm->period_in_minutes.reset(
340 new double(*create_info.period_in_minutes));
341 }
342 }
343
344 Alarm::~Alarm() {
307 } 345 }
308 346
309 } // namespace extensions 347 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698