Fix build break
[chromium-blink-merge.git] / chrome / browser / google_apis / drive_api_service.cc
blob20b48049e8a71ce79ea910f82aeb02506beeaca9
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/drive_api_service.h"
7 #include <string>
8 #include <vector>
10 #include "base/bind.h"
11 #include "base/message_loop_proxy.h"
12 #include "base/string_util.h"
13 #include "base/stringprintf.h"
14 #include "base/task_runner_util.h"
15 #include "base/threading/sequenced_worker_pool.h"
16 #include "base/values.h"
17 #include "chrome/browser/google_apis/auth_service.h"
18 #include "chrome/browser/google_apis/drive_api_operations.h"
19 #include "chrome/browser/google_apis/drive_api_parser.h"
20 #include "chrome/browser/google_apis/gdata_wapi_operations.h"
21 #include "chrome/browser/google_apis/gdata_wapi_parser.h"
22 #include "chrome/browser/google_apis/operation_runner.h"
23 #include "chrome/browser/google_apis/time_util.h"
24 #include "chrome/browser/profiles/profile.h"
25 #include "content/public/browser/browser_thread.h"
27 using content::BrowserThread;
29 namespace google_apis {
31 namespace {
33 // OAuth2 scopes for Drive API.
34 const char kDriveScope[] = "https://www.googleapis.com/auth/drive";
35 const char kDriveAppsReadonlyScope[] =
36 "https://www.googleapis.com/auth/drive.apps.readonly";
38 scoped_ptr<ResourceList> ParseChangeListJsonToResourceList(
39 scoped_ptr<base::Value> value) {
40 scoped_ptr<ChangeList> change_list(ChangeList::CreateFrom(*value));
41 if (!change_list) {
42 return scoped_ptr<ResourceList>();
45 return ResourceList::CreateFromChangeList(*change_list);
48 scoped_ptr<ResourceList> ParseFileListJsonToResourceList(
49 scoped_ptr<base::Value> value) {
50 scoped_ptr<FileList> file_list(FileList::CreateFrom(*value));
51 if (!file_list) {
52 return scoped_ptr<ResourceList>();
55 return ResourceList::CreateFromFileList(*file_list);
58 // Parses JSON value representing either ChangeList or FileList into
59 // ResourceList.
60 scoped_ptr<ResourceList> ParseResourceListOnBlockingPool(
61 scoped_ptr<base::Value> value) {
62 DCHECK(value);
64 // Dispatch the parsing based on kind field.
65 if (ChangeList::HasChangeListKind(*value)) {
66 return ParseChangeListJsonToResourceList(value.Pass());
68 if (FileList::HasFileListKind(*value)) {
69 return ParseFileListJsonToResourceList(value.Pass());
72 // The value type is unknown, so give up to parse and return an error.
73 return scoped_ptr<ResourceList>();
76 // Callback invoked when the parsing of resource list is completed,
77 // regardless whether it is succeeded or not.
78 void DidParseResourceListOnBlockingPool(
79 const GetResourceListCallback& callback,
80 scoped_ptr<ResourceList> resource_list) {
81 GDataErrorCode error = resource_list ? HTTP_SUCCESS : GDATA_PARSE_ERROR;
82 callback.Run(error, resource_list.Pass());
85 // Sends a task to parse the JSON value into ResourceList on blocking pool,
86 // with a callback which is called when the task is done.
87 void ParseResourceListOnBlockingPoolAndRun(
88 const GetResourceListCallback& callback,
89 GDataErrorCode error,
90 scoped_ptr<base::Value> value) {
91 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
92 DCHECK(!callback.is_null());
94 if (error != HTTP_SUCCESS) {
95 // An error occurs, so run callback immediately.
96 callback.Run(error, scoped_ptr<ResourceList>());
97 return;
100 PostTaskAndReplyWithResult(
101 BrowserThread::GetBlockingPool(),
102 FROM_HERE,
103 base::Bind(&ParseResourceListOnBlockingPool, base::Passed(&value)),
104 base::Bind(&DidParseResourceListOnBlockingPool, callback));
107 // Parses the FileResource value to ResourceEntry and runs |callback| on the
108 // UI thread.
109 void ParseResourceEntryAndRun(
110 const GetResourceEntryCallback& callback,
111 GDataErrorCode error,
112 scoped_ptr<FileResource> value) {
113 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
114 DCHECK(!callback.is_null());
116 if (!value) {
117 callback.Run(error, scoped_ptr<ResourceEntry>());
118 return;
121 // Converting to ResourceEntry is cheap enough to do on UI thread.
122 scoped_ptr<ResourceEntry> entry =
123 ResourceEntry::CreateFromFileResource(*value);
124 if (!entry) {
125 callback.Run(GDATA_PARSE_ERROR, scoped_ptr<ResourceEntry>());
126 return;
129 callback.Run(error, entry.Pass());
132 // Parses the AboutResource value to AccountMetadata and runs |callback|
133 // on the UI thread once parsing is done.
134 void ParseAccountMetadataAndRun(
135 const GetAccountMetadataCallback& callback,
136 GDataErrorCode error,
137 scoped_ptr<AboutResource> value) {
138 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
139 DCHECK(!callback.is_null());
141 if (!value) {
142 callback.Run(error, scoped_ptr<AccountMetadata>());
143 return;
146 // TODO(satorux): Convert AboutResource to AccountMetadata.
147 // For now just returning an error. crbug.com/165621
148 callback.Run(GDATA_PARSE_ERROR, scoped_ptr<AccountMetadata>());
151 // Parses the JSON value to AppList runs |callback| on the UI thread
152 // once parsing is done.
153 void ParseAppListAndRun(const google_apis::GetAppListCallback& callback,
154 google_apis::GDataErrorCode error,
155 scoped_ptr<base::Value> value) {
156 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
157 DCHECK(!callback.is_null());
159 if (!value) {
160 callback.Run(error, scoped_ptr<google_apis::AppList>());
161 return;
164 // Parsing AppList is cheap enough to do on UI thread.
165 scoped_ptr<google_apis::AppList> app_list =
166 google_apis::AppList::CreateFrom(*value);
167 if (!app_list) {
168 callback.Run(google_apis::GDATA_PARSE_ERROR,
169 scoped_ptr<google_apis::AppList>());
170 return;
173 callback.Run(error, app_list.Pass());
176 // Parses the FileResource value to ResourceEntry for upload range operation,
177 // and runs |callback| on the UI thread.
178 void ParseResourceEntryForUploadRangeAndRun(
179 const UploadRangeCallback& callback,
180 const UploadRangeResponse& response,
181 scoped_ptr<FileResource> value) {
182 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
183 DCHECK(!callback.is_null());
185 if (!value) {
186 callback.Run(response, scoped_ptr<ResourceEntry>());
187 return;
190 // Converting to ResourceEntry is cheap enough to do on UI thread.
191 scoped_ptr<ResourceEntry> entry =
192 ResourceEntry::CreateFromFileResource(*value);
193 if (!entry) {
194 callback.Run(UploadRangeResponse(GDATA_PARSE_ERROR,
195 response.start_position_received,
196 response.end_position_received),
197 scoped_ptr<ResourceEntry>());
198 return;
201 callback.Run(response, entry.Pass());
204 // It is necessary to escape ' to \' in the query's string value.
205 // See also: https://developers.google.com/drive/search-parameters
206 std::string EscapeQueryStringValue(const std::string& str) {
207 std::string result;
208 ReplaceChars(str, "'", "\\'", &result);
209 return result;
212 // The resource ID for the root directory for Drive API is defined in the spec:
213 // https://developers.google.com/drive/folder
214 const char kDriveApiRootDirectoryResourceId[] = "root";
216 } // namespace
218 DriveAPIService::DriveAPIService(
219 net::URLRequestContextGetter* url_request_context_getter,
220 const GURL& base_url,
221 const GURL& wapi_base_url,
222 const std::string& custom_user_agent)
223 : url_request_context_getter_(url_request_context_getter),
224 profile_(NULL),
225 runner_(NULL),
226 url_generator_(base_url),
227 wapi_url_generator_(wapi_base_url),
228 custom_user_agent_(custom_user_agent) {
229 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
232 DriveAPIService::~DriveAPIService() {
233 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
234 if (runner_.get()) {
235 runner_->operation_registry()->RemoveObserver(this);
236 runner_->auth_service()->RemoveObserver(this);
240 void DriveAPIService::Initialize(Profile* profile) {
241 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
242 profile_ = profile;
244 std::vector<std::string> scopes;
245 scopes.push_back(kDriveScope);
246 scopes.push_back(kDriveAppsReadonlyScope);
247 runner_.reset(new OperationRunner(profile,
248 url_request_context_getter_,
249 scopes,
250 custom_user_agent_));
251 runner_->Initialize();
253 runner_->auth_service()->AddObserver(this);
254 runner_->operation_registry()->AddObserver(this);
257 void DriveAPIService::AddObserver(DriveServiceObserver* observer) {
258 observers_.AddObserver(observer);
261 void DriveAPIService::RemoveObserver(DriveServiceObserver* observer) {
262 observers_.RemoveObserver(observer);
265 bool DriveAPIService::CanStartOperation() const {
266 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
268 return HasRefreshToken();
271 void DriveAPIService::CancelAll() {
272 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
273 runner_->CancelAll();
276 bool DriveAPIService::CancelForFilePath(const base::FilePath& file_path) {
277 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
278 return operation_registry()->CancelForFilePath(file_path);
281 OperationProgressStatusList DriveAPIService::GetProgressStatusList() const {
282 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
283 return operation_registry()->GetProgressStatusList();
286 std::string DriveAPIService::GetRootResourceId() const {
287 return kDriveApiRootDirectoryResourceId;
290 void DriveAPIService::GetAllResourceList(
291 const GetResourceListCallback& callback) {
292 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
293 DCHECK(!callback.is_null());
295 // The simplest way to fetch the all resources list looks files.list method,
296 // but it seems impossible to know the returned list's changestamp.
297 // Thus, instead, we use changes.list method with includeDeleted=false here.
298 // The returned list should contain only resources currently existing.
299 runner_->StartOperationWithRetry(
300 new GetChangelistOperation(
301 operation_registry(),
302 url_request_context_getter_,
303 url_generator_,
304 false, // include deleted
306 base::Bind(&ParseResourceListOnBlockingPoolAndRun, callback)));
309 void DriveAPIService::GetResourceListInDirectory(
310 const std::string& directory_resource_id,
311 const GetResourceListCallback& callback) {
312 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
313 DCHECK(!directory_resource_id.empty());
314 DCHECK(!callback.is_null());
316 // Because children.list method on Drive API v2 returns only the list of
317 // children's references, but we need all file resource list.
318 // So, here we use files.list method instead, with setting parents query.
319 // After the migration from GData WAPI to Drive API v2, we should clean the
320 // code up by moving the resposibility to include "parents" in the query
321 // to client side.
322 // We aren't interested in files in trash in this context, neither.
323 runner_->StartOperationWithRetry(
324 new GetFilelistOperation(
325 operation_registry(),
326 url_request_context_getter_,
327 url_generator_,
328 base::StringPrintf(
329 "'%s' in parents and trashed = false",
330 EscapeQueryStringValue(directory_resource_id).c_str()),
331 base::Bind(&ParseResourceListOnBlockingPoolAndRun, callback)));
334 void DriveAPIService::Search(const std::string& search_query,
335 const GetResourceListCallback& callback) {
336 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
337 DCHECK(!search_query.empty());
338 DCHECK(!callback.is_null());
340 runner_->StartOperationWithRetry(
341 new GetFilelistOperation(
342 operation_registry(),
343 url_request_context_getter_,
344 url_generator_,
345 search_query,
346 base::Bind(&ParseResourceListOnBlockingPoolAndRun, callback)));
349 void DriveAPIService::SearchInDirectory(
350 const std::string& search_query,
351 const std::string& directory_resource_id,
352 const GetResourceListCallback& callback) {
353 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
354 DCHECK(!search_query.empty());
355 DCHECK(!directory_resource_id.empty());
356 DCHECK(!callback.is_null());
358 runner_->StartOperationWithRetry(
359 new GetFilelistOperation(
360 operation_registry(),
361 url_request_context_getter_,
362 url_generator_,
363 base::StringPrintf(
364 "%s and '%s' in parents and trashed = false",
365 search_query.c_str(),
366 EscapeQueryStringValue(directory_resource_id).c_str()),
367 base::Bind(&ParseResourceListOnBlockingPoolAndRun, callback)));
370 void DriveAPIService::GetChangeList(int64 start_changestamp,
371 const GetResourceListCallback& callback) {
372 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
373 DCHECK(!callback.is_null());
375 runner_->StartOperationWithRetry(
376 new GetChangelistOperation(
377 operation_registry(),
378 url_request_context_getter_,
379 url_generator_,
380 true, // include deleted
381 start_changestamp,
382 base::Bind(&ParseResourceListOnBlockingPoolAndRun, callback)));
385 void DriveAPIService::ContinueGetResourceList(
386 const GURL& override_url,
387 const GetResourceListCallback& callback) {
388 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
389 DCHECK(!callback.is_null());
391 runner_->StartOperationWithRetry(
392 new drive::ContinueGetFileListOperation(
393 operation_registry(),
394 url_request_context_getter_,
395 override_url,
396 base::Bind(&ParseResourceListOnBlockingPoolAndRun, callback)));
399 void DriveAPIService::GetResourceEntry(
400 const std::string& resource_id,
401 const GetResourceEntryCallback& callback) {
402 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
403 DCHECK(!callback.is_null());
405 runner_->StartOperationWithRetry(new GetFileOperation(
406 operation_registry(),
407 url_request_context_getter_,
408 url_generator_,
409 resource_id,
410 base::Bind(&ParseResourceEntryAndRun, callback)));
413 void DriveAPIService::GetAccountMetadata(
414 const GetAccountMetadataCallback& callback) {
415 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
416 DCHECK(!callback.is_null());
418 runner_->StartOperationWithRetry(
419 new GetAboutOperation(
420 operation_registry(),
421 url_request_context_getter_,
422 url_generator_,
423 base::Bind(&ParseAccountMetadataAndRun, callback)));
426 void DriveAPIService::GetAboutResource(
427 const GetAboutResourceCallback& callback) {
428 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
429 DCHECK(!callback.is_null());
431 runner_->StartOperationWithRetry(
432 new GetAboutOperation(
433 operation_registry(),
434 url_request_context_getter_,
435 url_generator_,
436 callback));
439 void DriveAPIService::GetAppList(const GetAppListCallback& callback) {
440 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
441 DCHECK(!callback.is_null());
443 runner_->StartOperationWithRetry(new GetApplistOperation(
444 operation_registry(),
445 url_request_context_getter_,
446 url_generator_,
447 base::Bind(&ParseAppListAndRun, callback)));
450 void DriveAPIService::DownloadFile(
451 const base::FilePath& virtual_path,
452 const base::FilePath& local_cache_path,
453 const GURL& download_url,
454 const DownloadActionCallback& download_action_callback,
455 const GetContentCallback& get_content_callback,
456 const ProgressCallback& progress_callback) {
457 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
458 DCHECK(!download_action_callback.is_null());
459 // get_content_callback may be null.
461 runner_->StartOperationWithRetry(
462 new DownloadFileOperation(operation_registry(),
463 url_request_context_getter_,
464 download_action_callback,
465 get_content_callback,
466 progress_callback,
467 download_url,
468 virtual_path,
469 local_cache_path));
472 void DriveAPIService::DeleteResource(
473 const std::string& resource_id,
474 const std::string& etag,
475 const EntryActionCallback& callback) {
476 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
477 DCHECK(!callback.is_null());
479 runner_->StartOperationWithRetry(new drive::TrashResourceOperation(
480 operation_registry(),
481 url_request_context_getter_,
482 url_generator_,
483 resource_id,
484 callback));
487 void DriveAPIService::AddNewDirectory(
488 const std::string& parent_resource_id,
489 const std::string& directory_name,
490 const GetResourceEntryCallback& callback) {
491 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
492 DCHECK(!callback.is_null());
494 runner_->StartOperationWithRetry(
495 new drive::CreateDirectoryOperation(
496 operation_registry(),
497 url_request_context_getter_,
498 url_generator_,
499 parent_resource_id,
500 directory_name,
501 base::Bind(&ParseResourceEntryAndRun, callback)));
504 void DriveAPIService::CopyHostedDocument(
505 const std::string& resource_id,
506 const std::string& new_name,
507 const GetResourceEntryCallback& callback) {
508 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
509 DCHECK(!callback.is_null());
511 runner_->StartOperationWithRetry(
512 new drive::CopyResourceOperation(
513 operation_registry(),
514 url_request_context_getter_,
515 url_generator_,
516 resource_id,
517 new_name,
518 base::Bind(&ParseResourceEntryAndRun, callback)));
521 void DriveAPIService::RenameResource(
522 const std::string& resource_id,
523 const std::string& new_name,
524 const EntryActionCallback& callback) {
525 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
526 DCHECK(!callback.is_null());
528 runner_->StartOperationWithRetry(
529 new drive::RenameResourceOperation(
530 operation_registry(),
531 url_request_context_getter_,
532 url_generator_,
533 resource_id,
534 new_name,
535 callback));
538 void DriveAPIService::AddResourceToDirectory(
539 const std::string& parent_resource_id,
540 const std::string& resource_id,
541 const EntryActionCallback& callback) {
542 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
543 DCHECK(!callback.is_null());
545 runner_->StartOperationWithRetry(
546 new drive::InsertResourceOperation(
547 operation_registry(),
548 url_request_context_getter_,
549 url_generator_,
550 parent_resource_id,
551 resource_id,
552 callback));
555 void DriveAPIService::RemoveResourceFromDirectory(
556 const std::string& parent_resource_id,
557 const std::string& resource_id,
558 const EntryActionCallback& callback) {
559 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
560 DCHECK(!callback.is_null());
562 runner_->StartOperationWithRetry(
563 new drive::DeleteResourceOperation(
564 operation_registry(),
565 url_request_context_getter_,
566 url_generator_,
567 parent_resource_id,
568 resource_id,
569 callback));
572 void DriveAPIService::InitiateUploadNewFile(
573 const base::FilePath& drive_file_path,
574 const std::string& content_type,
575 int64 content_length,
576 const std::string& parent_resource_id,
577 const std::string& title,
578 const InitiateUploadCallback& callback) {
579 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
580 DCHECK(!callback.is_null());
582 runner_->StartOperationWithRetry(
583 new drive::InitiateUploadNewFileOperation(
584 operation_registry(),
585 url_request_context_getter_,
586 url_generator_,
587 drive_file_path,
588 content_type,
589 content_length,
590 parent_resource_id,
591 title,
592 callback));
595 void DriveAPIService::InitiateUploadExistingFile(
596 const base::FilePath& drive_file_path,
597 const std::string& content_type,
598 int64 content_length,
599 const std::string& resource_id,
600 const std::string& etag,
601 const InitiateUploadCallback& callback) {
602 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
603 DCHECK(!callback.is_null());
605 runner_->StartOperationWithRetry(
606 new drive::InitiateUploadExistingFileOperation(
607 operation_registry(),
608 url_request_context_getter_,
609 url_generator_,
610 drive_file_path,
611 content_type,
612 content_length,
613 resource_id,
614 etag,
615 callback));
618 void DriveAPIService::ResumeUpload(
619 UploadMode upload_mode,
620 const base::FilePath& drive_file_path,
621 const GURL& upload_url,
622 int64 start_position,
623 int64 end_position,
624 int64 content_length,
625 const std::string& content_type,
626 const scoped_refptr<net::IOBuffer>& buf,
627 const UploadRangeCallback& callback,
628 const ProgressCallback& progress_callback) {
629 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
630 DCHECK(!callback.is_null());
632 runner_->StartOperationWithRetry(
633 new drive::ResumeUploadOperation(
634 operation_registry(),
635 url_request_context_getter_,
636 upload_mode,
637 drive_file_path,
638 upload_url,
639 start_position,
640 end_position,
641 content_length,
642 content_type,
643 buf,
644 base::Bind(&ParseResourceEntryForUploadRangeAndRun, callback),
645 progress_callback));
648 void DriveAPIService::GetUploadStatus(
649 UploadMode upload_mode,
650 const base::FilePath& drive_file_path,
651 const GURL& upload_url,
652 int64 content_length,
653 const UploadRangeCallback& callback) {
654 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
655 DCHECK(!callback.is_null());
657 // TODO(hidehiko): Implement this.
658 NOTREACHED();
661 void DriveAPIService::AuthorizeApp(
662 const std::string& resource_id,
663 const std::string& app_id,
664 const AuthorizeAppCallback& callback) {
665 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
666 DCHECK(!callback.is_null());
668 // Unfortunately, there is no support of authorizing
669 // third party application on Drive API v2.
670 // As a temporary work around, we'll use the GData WAPI's api here.
671 // TODO(hidehiko): Get rid of this hack, and use the Drive API when it is
672 // supported.
673 runner_->StartOperationWithRetry(
674 new AuthorizeAppOperation(
675 operation_registry(),
676 url_request_context_getter_,
677 wapi_url_generator_,
678 callback,
679 resource_id,
680 app_id));
683 bool DriveAPIService::HasAccessToken() const {
684 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
686 return runner_->auth_service()->HasAccessToken();
689 bool DriveAPIService::HasRefreshToken() const {
690 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
692 return runner_->auth_service()->HasRefreshToken();
695 void DriveAPIService::ClearAccessToken() {
696 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
697 return runner_->auth_service()->ClearAccessToken();
700 void DriveAPIService::ClearRefreshToken() {
701 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
702 return runner_->auth_service()->ClearRefreshToken();
705 OperationRegistry* DriveAPIService::operation_registry() const {
706 return runner_->operation_registry();
709 void DriveAPIService::OnOAuth2RefreshTokenChanged() {
710 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
711 if (CanStartOperation()) {
712 FOR_EACH_OBSERVER(
713 DriveServiceObserver, observers_, OnReadyToPerformOperations());
714 } else if (!HasRefreshToken()) {
715 FOR_EACH_OBSERVER(
716 DriveServiceObserver, observers_, OnRefreshTokenInvalid());
720 void DriveAPIService::OnProgressUpdate(
721 const OperationProgressStatusList& list) {
722 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
723 FOR_EACH_OBSERVER(
724 DriveServiceObserver, observers_, OnProgressUpdate(list));
727 } // namespace google_apis