Fix build break
[chromium-blink-merge.git] / chrome / browser / google_apis / operation_registry.cc
blobe11a120b039b171c390432d42515492eb3af35bf
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.
5 #include "chrome/browser/google_apis/operation_registry.h"
7 #include "base/strings/string_number_conversions.h"
8 #include "content/public/browser/browser_thread.h"
10 using content::BrowserThread;
12 namespace {
14 const int64 kNotificationFrequencyInMilliseconds = 1000;
16 } // namespace
18 namespace google_apis {
20 std::string OperationTypeToString(OperationType type) {
21 switch (type) {
22 case OPERATION_UPLOAD: return "upload";
23 case OPERATION_DOWNLOAD: return "download";
24 case OPERATION_OTHER: return "other";
26 NOTREACHED();
27 return "unknown_transfer_state";
30 std::string OperationTransferStateToString(OperationTransferState state) {
31 switch (state) {
32 case OPERATION_NOT_STARTED: return "not_started";
33 case OPERATION_STARTED: return "started";
34 case OPERATION_IN_PROGRESS: return "in_progress";
35 case OPERATION_COMPLETED: return "completed";
36 case OPERATION_FAILED: return "failed";
37 // Suspended state is opaque to users and looks as same as "in_progress".
38 case OPERATION_SUSPENDED: return "in_progress";
40 NOTREACHED();
41 return "unknown_transfer_state";
44 OperationProgressStatus::OperationProgressStatus(OperationType type,
45 const base::FilePath& path)
46 : operation_id(-1),
47 operation_type(type),
48 file_path(path),
49 transfer_state(OPERATION_NOT_STARTED),
50 progress_current(0),
51 progress_total(-1) {
54 std::string OperationProgressStatus::DebugString() const {
55 std::string str;
56 str += "id=";
57 str += base::IntToString(operation_id);
58 str += " type=";
59 str += OperationTypeToString(operation_type);
60 str += " path=";
61 str += file_path.AsUTF8Unsafe();
62 str += " state=";
63 str += OperationTransferStateToString(transfer_state);
64 str += " progress=";
65 str += base::Int64ToString(progress_current);
66 str += "/";
67 str += base::Int64ToString(progress_total);
68 return str;
71 OperationRegistry::Operation::Operation(OperationRegistry* registry)
72 : registry_(registry),
73 progress_status_(OPERATION_OTHER, base::FilePath()) {
76 OperationRegistry::Operation::Operation(OperationRegistry* registry,
77 OperationType type,
78 const base::FilePath& path)
79 : registry_(registry),
80 progress_status_(type, path) {
83 OperationRegistry::Operation::~Operation() {
84 DCHECK(progress_status_.transfer_state == OPERATION_COMPLETED ||
85 progress_status_.transfer_state == OPERATION_SUSPENDED ||
86 progress_status_.transfer_state == OPERATION_FAILED);
89 void OperationRegistry::Operation::Cancel() {
90 DoCancel();
91 NotifyFinish(OPERATION_FAILED);
94 void OperationRegistry::Operation::NotifyStart() {
95 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
96 // Some operations may be restarted. Report only the first "start".
97 if (progress_status_.transfer_state == OPERATION_NOT_STARTED) {
98 progress_status_.transfer_state = OPERATION_STARTED;
99 progress_status_.start_time = base::Time::Now();
100 registry_->OnOperationStart(this, &progress_status_.operation_id);
104 void OperationRegistry::Operation::NotifyProgress(
105 int64 current, int64 total) {
106 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
107 DCHECK(progress_status_.transfer_state >= OPERATION_STARTED);
108 progress_status_.transfer_state = OPERATION_IN_PROGRESS;
109 progress_status_.progress_current = current;
110 progress_status_.progress_total = total;
111 registry_->OnOperationProgress(progress_status().operation_id);
114 void OperationRegistry::Operation::NotifyFinish(
115 OperationTransferState status) {
116 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
117 DCHECK(progress_status_.transfer_state >= OPERATION_STARTED);
118 DCHECK(status == OPERATION_COMPLETED || status == OPERATION_FAILED);
119 progress_status_.transfer_state = status;
120 registry_->OnOperationFinish(progress_status().operation_id);
123 void OperationRegistry::Operation::NotifySuspend() {
124 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
125 DCHECK(progress_status_.transfer_state >= OPERATION_STARTED);
126 progress_status_.transfer_state = OPERATION_SUSPENDED;
127 registry_->OnOperationSuspend(progress_status().operation_id);
130 void OperationRegistry::Operation::NotifyResume() {
131 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
132 if (progress_status_.transfer_state == OPERATION_NOT_STARTED) {
133 progress_status_.transfer_state = OPERATION_IN_PROGRESS;
134 registry_->OnOperationResume(this, &progress_status_);
138 OperationRegistry::OperationRegistry()
139 : do_notification_frequency_control_(true) {
140 in_flight_operations_.set_check_on_null_data(true);
143 OperationRegistry::~OperationRegistry() {
144 DCHECK(in_flight_operations_.IsEmpty());
147 void OperationRegistry::AddObserver(OperationRegistryObserver* observer) {
148 observer_list_.AddObserver(observer);
151 void OperationRegistry::RemoveObserver(OperationRegistryObserver* observer) {
152 observer_list_.RemoveObserver(observer);
155 void OperationRegistry::DisableNotificationFrequencyControlForTest() {
156 do_notification_frequency_control_ = false;
159 void OperationRegistry::CancelAll() {
160 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
162 for (OperationIDMap::iterator iter(&in_flight_operations_);
163 !iter.IsAtEnd();
164 iter.Advance()) {
165 Operation* operation = iter.GetCurrentValue();
166 CancelOperation(operation);
167 // CancelOperation may immediately trigger OnOperationFinish and remove the
168 // operation from the map, but IDMap is designed to be safe on such remove
169 // while iteration.
173 bool OperationRegistry::CancelForFilePath(const base::FilePath& file_path) {
174 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
176 for (OperationIDMap::iterator iter(&in_flight_operations_);
177 !iter.IsAtEnd();
178 iter.Advance()) {
179 Operation* operation = iter.GetCurrentValue();
180 if (operation->progress_status().file_path == file_path) {
181 CancelOperation(operation);
182 return true;
185 return false;
188 void OperationRegistry::CancelOperation(Operation* operation) {
189 if (operation->progress_status().transfer_state == OPERATION_SUSPENDED) {
190 // SUSPENDED operation already completed its job (like calling back to
191 // its client code). Invoking operation->Cancel() again on it is a kind of
192 // 'double deletion'. So here we directly call OnOperationFinish and just
193 // unregister the operation from the registry.
194 // TODO(kinaba): http://crbug.com/164098 Get rid of the hack.
195 OnOperationFinish(operation->progress_status().operation_id);
196 } else {
197 operation->Cancel();
201 void OperationRegistry::OnOperationStart(
202 OperationRegistry::Operation* operation,
203 OperationID* id) {
204 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
206 *id = in_flight_operations_.Add(operation);
207 DVLOG(1) << "GDataOperation[" << *id << "] started.";
208 if (IsFileTransferOperation(operation))
209 NotifyStatusToObservers();
212 void OperationRegistry::OnOperationProgress(OperationID id) {
213 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
215 Operation* operation = in_flight_operations_.Lookup(id);
216 DCHECK(operation);
218 DVLOG(1) << "GDataOperation[" << id << "] "
219 << operation->progress_status().DebugString();
220 if (IsFileTransferOperation(operation))
221 NotifyStatusToObservers();
224 void OperationRegistry::OnOperationFinish(OperationID id) {
225 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
227 Operation* operation = in_flight_operations_.Lookup(id);
228 DCHECK(operation);
230 DVLOG(1) << "GDataOperation[" << id << "] finished.";
231 if (IsFileTransferOperation(operation))
232 NotifyStatusToObservers();
233 in_flight_operations_.Remove(id);
236 void OperationRegistry::OnOperationResume(
237 OperationRegistry::Operation* operation,
238 OperationProgressStatus* new_status) {
239 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
241 // Find the corresponding suspended task.
242 Operation* suspended = NULL;
243 for (OperationIDMap::iterator iter(&in_flight_operations_);
244 !iter.IsAtEnd();
245 iter.Advance()) {
246 Operation* in_flight_operation = iter.GetCurrentValue();
247 const OperationProgressStatus& status =
248 in_flight_operation->progress_status();
249 if (status.transfer_state == OPERATION_SUSPENDED &&
250 status.file_path == operation->progress_status().file_path) {
251 suspended = in_flight_operation;
252 break;
256 if (!suspended) {
257 // Preceding suspended operations was not found. Assume it was canceled.
259 // operation->Cancel() needs to be called to properly shut down the
260 // current operation, but operation->Cancel() tries to unregister itself
261 // from the registry. So, as a hack, temporarily assign it an ID.
262 // TODO(kinaba): http://crbug.com/164098 Get rid of it.
263 new_status->operation_id = in_flight_operations_.Add(operation);
264 CancelOperation(operation);
265 return;
268 // Copy the progress status.
269 const OperationProgressStatus& old_status = suspended->progress_status();
270 OperationID old_id = old_status.operation_id;
272 new_status->progress_current = old_status.progress_current;
273 new_status->progress_total = old_status.progress_total;
274 new_status->start_time = old_status.start_time;
276 // Remove the old one and initiate the new operation.
277 in_flight_operations_.Remove(old_id);
278 new_status->operation_id = in_flight_operations_.Add(operation);
279 DVLOG(1) << "GDataOperation[" << old_id << " -> " <<
280 new_status->operation_id << "] resumed.";
281 if (IsFileTransferOperation(operation))
282 NotifyStatusToObservers();
285 void OperationRegistry::OnOperationSuspend(OperationID id) {
286 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
288 Operation* operation = in_flight_operations_.Lookup(id);
289 DCHECK(operation);
291 DVLOG(1) << "GDataOperation[" << id << "] suspended.";
292 if (IsFileTransferOperation(operation))
293 NotifyStatusToObservers();
296 bool OperationRegistry::IsFileTransferOperation(
297 const Operation* operation) const {
298 OperationType type = operation->progress_status().operation_type;
299 return type == OPERATION_UPLOAD || type == OPERATION_DOWNLOAD;
302 OperationProgressStatusList OperationRegistry::GetProgressStatusList() {
303 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
305 OperationProgressStatusList status_list;
306 for (OperationIDMap::const_iterator iter(&in_flight_operations_);
307 !iter.IsAtEnd();
308 iter.Advance()) {
309 const Operation* operation = iter.GetCurrentValue();
310 if (IsFileTransferOperation(operation))
311 status_list.push_back(operation->progress_status());
313 return status_list;
316 bool OperationRegistry::ShouldNotifyStatusNow(
317 const OperationProgressStatusList& list) {
318 if (!do_notification_frequency_control_)
319 return true;
321 base::Time now = base::Time::Now();
323 // If it is a first event, or some time abnormality is detected, we should
324 // not skip this notification.
325 if (last_notification_.is_null() || now < last_notification_) {
326 last_notification_ = now;
327 return true;
330 // If sufficiently long time has elapsed since the previous event, we should
331 // not skip this notification.
332 if ((now - last_notification_).InMilliseconds() >=
333 kNotificationFrequencyInMilliseconds) {
334 last_notification_ = now;
335 return true;
338 // If important events (OPERATION_STARTED, COMPLETED, or FAILED) are there,
339 // we should not skip this notification.
340 for (size_t i = 0; i < list.size(); ++i) {
341 if (list[i].transfer_state != OPERATION_IN_PROGRESS) {
342 last_notification_ = now;
343 return true;
347 // Otherwise we can skip it.
348 return false;
351 void OperationRegistry::NotifyStatusToObservers() {
352 OperationProgressStatusList list(GetProgressStatusList());
353 if (ShouldNotifyStatusNow(list))
354 FOR_EACH_OBSERVER(OperationRegistryObserver,
355 observer_list_,
356 OnProgressUpdate(list));
359 } // namespace google_apis