drive_api_service.cc revision eb525c5499e34cc9c4b825d6d9e75bb07cc06ace
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/drive/drive_api_service.h"
6
7#include <string>
8#include <vector>
9
10#include "base/bind.h"
11#include "base/strings/stringprintf.h"
12#include "base/task_runner_util.h"
13#include "base/values.h"
14#include "chrome/browser/drive/drive_api_util.h"
15#include "chrome/browser/google_apis/auth_service.h"
16#include "chrome/browser/google_apis/drive_api_parser.h"
17#include "chrome/browser/google_apis/drive_api_requests.h"
18#include "chrome/browser/google_apis/gdata_wapi_parser.h"
19#include "chrome/browser/google_apis/request_sender.h"
20#include "content/public/browser/browser_thread.h"
21
22using content::BrowserThread;
23using google_apis::AppList;
24using google_apis::AuthorizeAppCallback;
25using google_apis::CancelCallback;
26using google_apis::ChangeList;
27using google_apis::DownloadActionCallback;
28using google_apis::EntryActionCallback;
29using google_apis::FileList;
30using google_apis::FileResource;
31using google_apis::GDATA_OTHER_ERROR;
32using google_apis::GDATA_PARSE_ERROR;
33using google_apis::GDataErrorCode;
34using google_apis::GetAboutRequest;
35using google_apis::GetAboutResourceCallback;
36using google_apis::GetAppListCallback;
37using google_apis::GetApplistRequest;
38using google_apis::GetChangelistRequest;
39using google_apis::GetContentCallback;
40using google_apis::GetFileRequest;
41using google_apis::GetFilelistRequest;
42using google_apis::GetResourceEntryCallback;
43using google_apis::GetResourceListCallback;
44using google_apis::HTTP_SUCCESS;
45using google_apis::InitiateUploadCallback;
46using google_apis::ProgressCallback;
47using google_apis::RequestSender;
48using google_apis::ResourceEntry;
49using google_apis::ResourceList;
50using google_apis::UploadRangeCallback;
51using google_apis::UploadRangeResponse;
52using google_apis::drive::ContinueGetFileListRequest;
53using google_apis::drive::CopyResourceRequest;
54using google_apis::drive::CreateDirectoryRequest;
55using google_apis::drive::DeleteResourceRequest;
56using google_apis::drive::DownloadFileRequest;
57using google_apis::drive::GetUploadStatusRequest;
58using google_apis::drive::InitiateUploadExistingFileRequest;
59using google_apis::drive::InitiateUploadNewFileRequest;
60using google_apis::drive::InsertResourceRequest;
61using google_apis::drive::RenameResourceRequest;
62using google_apis::drive::ResumeUploadRequest;
63using google_apis::drive::TouchResourceRequest;
64using google_apis::drive::TrashResourceRequest;
65
66namespace drive {
67
68namespace {
69
70// OAuth2 scopes for Drive API.
71const char kDriveScope[] = "https://www.googleapis.com/auth/drive";
72const char kDriveAppsReadonlyScope[] =
73    "https://www.googleapis.com/auth/drive.apps.readonly";
74
75// Expected max number of files resources in a http request.
76// Be careful not to use something too small because it might overload the
77// server. Be careful not to use something too large because it takes longer
78// time to fetch the result without UI response.
79const int kMaxNumFilesResourcePerRequest = 500;
80const int kMaxNumFilesResourcePerRequestForSearch = 50;
81
82scoped_ptr<ResourceList> ParseChangeListJsonToResourceList(
83    scoped_ptr<base::Value> value) {
84  scoped_ptr<ChangeList> change_list(ChangeList::CreateFrom(*value));
85  if (!change_list) {
86    return scoped_ptr<ResourceList>();
87  }
88
89  return ResourceList::CreateFromChangeList(*change_list);
90}
91
92scoped_ptr<ResourceList> ParseFileListJsonToResourceList(
93    scoped_ptr<base::Value> value) {
94  scoped_ptr<FileList> file_list(FileList::CreateFrom(*value));
95  if (!file_list) {
96    return scoped_ptr<ResourceList>();
97  }
98
99  return ResourceList::CreateFromFileList(*file_list);
100}
101
102// Parses JSON value representing either ChangeList or FileList into
103// ResourceList.
104scoped_ptr<ResourceList> ParseResourceListOnBlockingPool(
105    scoped_ptr<base::Value> value) {
106  DCHECK(value);
107
108  // Dispatch the parsing based on kind field.
109  if (ChangeList::HasChangeListKind(*value)) {
110    return ParseChangeListJsonToResourceList(value.Pass());
111  }
112  if (FileList::HasFileListKind(*value)) {
113    return ParseFileListJsonToResourceList(value.Pass());
114  }
115
116  // The value type is unknown, so give up to parse and return an error.
117  return scoped_ptr<ResourceList>();
118}
119
120// Callback invoked when the parsing of resource list is completed,
121// regardless whether it is succeeded or not.
122void DidParseResourceListOnBlockingPool(
123    const GetResourceListCallback& callback,
124    scoped_ptr<ResourceList> resource_list) {
125  GDataErrorCode error = resource_list ? HTTP_SUCCESS : GDATA_PARSE_ERROR;
126  callback.Run(error, resource_list.Pass());
127}
128
129// Sends a task to parse the JSON value into ResourceList on blocking pool,
130// with a callback which is called when the task is done.
131void ParseResourceListOnBlockingPoolAndRun(
132    scoped_refptr<base::TaskRunner> blocking_task_runner,
133    const GetResourceListCallback& callback,
134    GDataErrorCode error,
135    scoped_ptr<base::Value> value) {
136  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
137  DCHECK(!callback.is_null());
138
139  if (error != HTTP_SUCCESS) {
140    // An error occurs, so run callback immediately.
141    callback.Run(error, scoped_ptr<ResourceList>());
142    return;
143  }
144
145  PostTaskAndReplyWithResult(
146      blocking_task_runner.get(),
147      FROM_HERE,
148      base::Bind(&ParseResourceListOnBlockingPool, base::Passed(&value)),
149      base::Bind(&DidParseResourceListOnBlockingPool, callback));
150}
151
152// Parses the FileResource value to ResourceEntry and runs |callback| on the
153// UI thread.
154void ParseResourceEntryAndRun(
155    const GetResourceEntryCallback& callback,
156    GDataErrorCode error,
157    scoped_ptr<FileResource> value) {
158  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
159  DCHECK(!callback.is_null());
160
161  if (!value) {
162    callback.Run(error, scoped_ptr<ResourceEntry>());
163    return;
164  }
165
166  // Converting to ResourceEntry is cheap enough to do on UI thread.
167  scoped_ptr<ResourceEntry> entry =
168      ResourceEntry::CreateFromFileResource(*value);
169  if (!entry) {
170    callback.Run(GDATA_PARSE_ERROR, scoped_ptr<ResourceEntry>());
171    return;
172  }
173
174  callback.Run(error, entry.Pass());
175}
176
177// Parses the JSON value to AppList runs |callback| on the UI thread
178// once parsing is done.
179void ParseAppListAndRun(const google_apis::GetAppListCallback& callback,
180                        google_apis::GDataErrorCode error,
181                        scoped_ptr<base::Value> value) {
182  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
183  DCHECK(!callback.is_null());
184
185  if (!value) {
186    callback.Run(error, scoped_ptr<google_apis::AppList>());
187    return;
188  }
189
190  // Parsing AppList is cheap enough to do on UI thread.
191  scoped_ptr<google_apis::AppList> app_list =
192      google_apis::AppList::CreateFrom(*value);
193  if (!app_list) {
194    callback.Run(google_apis::GDATA_PARSE_ERROR,
195                 scoped_ptr<google_apis::AppList>());
196    return;
197  }
198
199  callback.Run(error, app_list.Pass());
200}
201
202// Parses the FileResource value to ResourceEntry for upload range request,
203// and runs |callback| on the UI thread.
204void ParseResourceEntryForUploadRangeAndRun(
205    const UploadRangeCallback& callback,
206    const UploadRangeResponse& response,
207    scoped_ptr<FileResource> value) {
208  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
209  DCHECK(!callback.is_null());
210
211  if (!value) {
212    callback.Run(response, scoped_ptr<ResourceEntry>());
213    return;
214  }
215
216  // Converting to ResourceEntry is cheap enough to do on UI thread.
217  scoped_ptr<ResourceEntry> entry =
218      ResourceEntry::CreateFromFileResource(*value);
219  if (!entry) {
220    callback.Run(UploadRangeResponse(GDATA_PARSE_ERROR,
221                                     response.start_position_received,
222                                     response.end_position_received),
223                 scoped_ptr<ResourceEntry>());
224    return;
225  }
226
227  callback.Run(response, entry.Pass());
228}
229
230void ExtractOpenUrlAndRun(const std::string& app_id,
231                          const AuthorizeAppCallback& callback,
232                          GDataErrorCode error,
233                          scoped_ptr<FileResource> value) {
234  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
235  DCHECK(!callback.is_null());
236
237  if (!value) {
238    callback.Run(error, GURL());
239    return;
240  }
241
242  const std::vector<FileResource::OpenWithLink>& open_with_links =
243      value->open_with_links();
244  for (size_t i = 0; i < open_with_links.size(); ++i) {
245    if (open_with_links[i].app_id == app_id) {
246      callback.Run(HTTP_SUCCESS, open_with_links[i].open_url);
247      return;
248    }
249  }
250
251  // Not found.
252  callback.Run(GDATA_OTHER_ERROR, GURL());
253}
254
255// The resource ID for the root directory for Drive API is defined in the spec:
256// https://developers.google.com/drive/folder
257const char kDriveApiRootDirectoryResourceId[] = "root";
258
259}  // namespace
260
261DriveAPIService::DriveAPIService(
262    net::URLRequestContextGetter* url_request_context_getter,
263    base::TaskRunner* blocking_task_runner,
264    const GURL& base_url,
265    const GURL& base_download_url,
266    const std::string& custom_user_agent)
267    : url_request_context_getter_(url_request_context_getter),
268      blocking_task_runner_(blocking_task_runner),
269      profile_(NULL),
270      url_generator_(base_url, base_download_url),
271      custom_user_agent_(custom_user_agent) {
272  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
273}
274
275DriveAPIService::~DriveAPIService() {
276  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
277  if (sender_.get())
278    sender_->auth_service()->RemoveObserver(this);
279}
280
281void DriveAPIService::Initialize(Profile* profile) {
282  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
283  profile_ = profile;
284
285  std::vector<std::string> scopes;
286  scopes.push_back(kDriveScope);
287  scopes.push_back(kDriveAppsReadonlyScope);
288  sender_.reset(new RequestSender(profile,
289                                  url_request_context_getter_,
290                                  scopes,
291                                  custom_user_agent_));
292  sender_->Initialize();
293
294  sender_->auth_service()->AddObserver(this);
295}
296
297void DriveAPIService::AddObserver(DriveServiceObserver* observer) {
298  observers_.AddObserver(observer);
299}
300
301void DriveAPIService::RemoveObserver(DriveServiceObserver* observer) {
302  observers_.RemoveObserver(observer);
303}
304
305bool DriveAPIService::CanSendRequest() const {
306  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
307
308  return HasRefreshToken();
309}
310
311std::string DriveAPIService::CanonicalizeResourceId(
312    const std::string& resource_id) const {
313  return drive::util::CanonicalizeResourceId(resource_id);
314}
315
316std::string DriveAPIService::GetRootResourceId() const {
317  return kDriveApiRootDirectoryResourceId;
318}
319
320CancelCallback DriveAPIService::GetAllResourceList(
321    const GetResourceListCallback& callback) {
322  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
323  DCHECK(!callback.is_null());
324
325  // The simplest way to fetch the all resources list looks files.list method,
326  // but it seems impossible to know the returned list's changestamp.
327  // Thus, instead, we use changes.list method with includeDeleted=false here.
328  // The returned list should contain only resources currently existing.
329  return sender_->StartRequestWithRetry(
330      new GetChangelistRequest(
331          sender_.get(),
332          url_generator_,
333          false,  // include deleted
334          0,
335          kMaxNumFilesResourcePerRequest,
336          base::Bind(&ParseResourceListOnBlockingPoolAndRun,
337                     blocking_task_runner_,
338                     callback)));
339}
340
341CancelCallback DriveAPIService::GetResourceListInDirectory(
342    const std::string& directory_resource_id,
343    const GetResourceListCallback& callback) {
344  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
345  DCHECK(!directory_resource_id.empty());
346  DCHECK(!callback.is_null());
347
348  // Because children.list method on Drive API v2 returns only the list of
349  // children's references, but we need all file resource list.
350  // So, here we use files.list method instead, with setting parents query.
351  // After the migration from GData WAPI to Drive API v2, we should clean the
352  // code up by moving the responsibility to include "parents" in the query
353  // to client side.
354  // We aren't interested in files in trash in this context, neither.
355  return sender_->StartRequestWithRetry(
356      new GetFilelistRequest(
357          sender_.get(),
358          url_generator_,
359          base::StringPrintf(
360              "'%s' in parents and trashed = false",
361              drive::util::EscapeQueryStringValue(
362                  directory_resource_id).c_str()),
363          kMaxNumFilesResourcePerRequest,
364          base::Bind(&ParseResourceListOnBlockingPoolAndRun,
365                     blocking_task_runner_,
366                     callback)));
367}
368
369CancelCallback DriveAPIService::Search(
370    const std::string& search_query,
371    const GetResourceListCallback& callback) {
372  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
373  DCHECK(!search_query.empty());
374  DCHECK(!callback.is_null());
375
376  return sender_->StartRequestWithRetry(
377      new GetFilelistRequest(
378          sender_.get(),
379          url_generator_,
380          drive::util::TranslateQuery(search_query),
381          kMaxNumFilesResourcePerRequestForSearch,
382          base::Bind(&ParseResourceListOnBlockingPoolAndRun,
383                     blocking_task_runner_,
384                     callback)));
385}
386
387CancelCallback DriveAPIService::SearchByTitle(
388    const std::string& title,
389    const std::string& directory_resource_id,
390    const GetResourceListCallback& callback) {
391  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
392  DCHECK(!title.empty());
393  DCHECK(!callback.is_null());
394
395  std::string query;
396  base::StringAppendF(&query, "title = '%s'",
397                      drive::util::EscapeQueryStringValue(title).c_str());
398  if (!directory_resource_id.empty()) {
399    base::StringAppendF(
400        &query, " and '%s' in parents",
401        drive::util::EscapeQueryStringValue(directory_resource_id).c_str());
402  }
403  query += " and trashed = false";
404
405  return sender_->StartRequestWithRetry(
406      new GetFilelistRequest(
407          sender_.get(),
408          url_generator_,
409          query,
410          kMaxNumFilesResourcePerRequest,
411          base::Bind(&ParseResourceListOnBlockingPoolAndRun,
412                     blocking_task_runner_,
413                     callback)));
414}
415
416CancelCallback DriveAPIService::GetChangeList(
417    int64 start_changestamp,
418    const GetResourceListCallback& callback) {
419  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
420  DCHECK(!callback.is_null());
421
422  return sender_->StartRequestWithRetry(
423      new GetChangelistRequest(
424          sender_.get(),
425          url_generator_,
426          true,  // include deleted
427          start_changestamp,
428          kMaxNumFilesResourcePerRequest,
429          base::Bind(&ParseResourceListOnBlockingPoolAndRun,
430                     blocking_task_runner_,
431                     callback)));
432}
433
434CancelCallback DriveAPIService::ContinueGetResourceList(
435    const GURL& override_url,
436    const GetResourceListCallback& callback) {
437  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
438  DCHECK(!callback.is_null());
439
440  return sender_->StartRequestWithRetry(
441      new ContinueGetFileListRequest(
442          sender_.get(),
443          override_url,
444          base::Bind(&ParseResourceListOnBlockingPoolAndRun,
445                     blocking_task_runner_,
446                     callback)));
447}
448
449CancelCallback DriveAPIService::GetResourceEntry(
450    const std::string& resource_id,
451    const GetResourceEntryCallback& callback) {
452  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
453  DCHECK(!callback.is_null());
454
455  return sender_->StartRequestWithRetry(new GetFileRequest(
456      sender_.get(),
457      url_generator_,
458      resource_id,
459      base::Bind(&ParseResourceEntryAndRun, callback)));
460}
461
462CancelCallback DriveAPIService::GetAboutResource(
463    const GetAboutResourceCallback& callback) {
464  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
465  DCHECK(!callback.is_null());
466
467  return sender_->StartRequestWithRetry(
468      new GetAboutRequest(
469          sender_.get(),
470          url_generator_,
471          callback));
472}
473
474CancelCallback DriveAPIService::GetAppList(const GetAppListCallback& callback) {
475  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
476  DCHECK(!callback.is_null());
477
478  return sender_->StartRequestWithRetry(new GetApplistRequest(
479      sender_.get(),
480      url_generator_,
481      base::Bind(&ParseAppListAndRun, callback)));
482}
483
484CancelCallback DriveAPIService::DownloadFile(
485    const base::FilePath& local_cache_path,
486    const std::string& resource_id,
487    const DownloadActionCallback& download_action_callback,
488    const GetContentCallback& get_content_callback,
489    const ProgressCallback& progress_callback) {
490  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
491  DCHECK(!download_action_callback.is_null());
492  // get_content_callback may be null.
493
494  return sender_->StartRequestWithRetry(
495      new DownloadFileRequest(sender_.get(),
496                              url_generator_,
497                              resource_id,
498                              local_cache_path,
499                              download_action_callback,
500                              get_content_callback,
501                              progress_callback));
502}
503
504CancelCallback DriveAPIService::DeleteResource(
505    const std::string& resource_id,
506    const std::string& etag,
507    const EntryActionCallback& callback) {
508  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
509  DCHECK(!callback.is_null());
510
511  return sender_->StartRequestWithRetry(new TrashResourceRequest(
512      sender_.get(),
513      url_generator_,
514      resource_id,
515      callback));
516}
517
518CancelCallback DriveAPIService::AddNewDirectory(
519    const std::string& parent_resource_id,
520    const std::string& directory_name,
521    const GetResourceEntryCallback& callback) {
522  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
523  DCHECK(!callback.is_null());
524
525  return sender_->StartRequestWithRetry(
526      new CreateDirectoryRequest(
527          sender_.get(),
528          url_generator_,
529          parent_resource_id,
530          directory_name,
531          base::Bind(&ParseResourceEntryAndRun, callback)));
532}
533
534CancelCallback DriveAPIService::CopyResource(
535    const std::string& resource_id,
536    const std::string& parent_resource_id,
537    const std::string& new_name,
538    const GetResourceEntryCallback& callback) {
539  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
540  DCHECK(!callback.is_null());
541
542  return sender_->StartRequestWithRetry(
543      new CopyResourceRequest(
544          sender_.get(),
545          url_generator_,
546          resource_id,
547          parent_resource_id,
548          new_name,
549          base::Bind(&ParseResourceEntryAndRun, callback)));
550}
551
552CancelCallback DriveAPIService::CopyHostedDocument(
553    const std::string& resource_id,
554    const std::string& new_name,
555    const GetResourceEntryCallback& callback) {
556  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
557  DCHECK(!callback.is_null());
558
559  return sender_->StartRequestWithRetry(
560      new CopyResourceRequest(
561          sender_.get(),
562          url_generator_,
563          resource_id,
564          std::string(),  // parent_resource_id.
565          new_name,
566          base::Bind(&ParseResourceEntryAndRun, callback)));
567}
568
569CancelCallback DriveAPIService::RenameResource(
570    const std::string& resource_id,
571    const std::string& new_name,
572    const EntryActionCallback& callback) {
573  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
574  DCHECK(!callback.is_null());
575
576  return sender_->StartRequestWithRetry(
577      new RenameResourceRequest(
578          sender_.get(),
579          url_generator_,
580          resource_id,
581          new_name,
582          callback));
583}
584
585CancelCallback DriveAPIService::TouchResource(
586    const std::string& resource_id,
587    const base::Time& modified_date,
588    const base::Time& last_viewed_by_me_date,
589    const GetResourceEntryCallback& callback) {
590  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
591  DCHECK(!modified_date.is_null());
592  DCHECK(!last_viewed_by_me_date.is_null());
593  DCHECK(!callback.is_null());
594
595  return sender_->StartRequestWithRetry(
596      new TouchResourceRequest(
597          sender_.get(),
598          url_generator_,
599          resource_id,
600          modified_date,
601          last_viewed_by_me_date,
602          base::Bind(&ParseResourceEntryAndRun, callback)));
603}
604
605CancelCallback DriveAPIService::AddResourceToDirectory(
606    const std::string& parent_resource_id,
607    const std::string& resource_id,
608    const EntryActionCallback& callback) {
609  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
610  DCHECK(!callback.is_null());
611
612  return sender_->StartRequestWithRetry(
613      new InsertResourceRequest(
614          sender_.get(),
615          url_generator_,
616          parent_resource_id,
617          resource_id,
618          callback));
619}
620
621CancelCallback DriveAPIService::RemoveResourceFromDirectory(
622    const std::string& parent_resource_id,
623    const std::string& resource_id,
624    const EntryActionCallback& callback) {
625  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
626  DCHECK(!callback.is_null());
627
628  return sender_->StartRequestWithRetry(
629      new DeleteResourceRequest(
630          sender_.get(),
631          url_generator_,
632          parent_resource_id,
633          resource_id,
634          callback));
635}
636
637CancelCallback DriveAPIService::InitiateUploadNewFile(
638    const std::string& content_type,
639    int64 content_length,
640    const std::string& parent_resource_id,
641    const std::string& title,
642    const InitiateUploadCallback& callback) {
643  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
644  DCHECK(!callback.is_null());
645
646  return sender_->StartRequestWithRetry(
647      new InitiateUploadNewFileRequest(
648          sender_.get(),
649          url_generator_,
650          content_type,
651          content_length,
652          parent_resource_id,
653          title,
654          callback));
655}
656
657CancelCallback DriveAPIService::InitiateUploadExistingFile(
658    const std::string& content_type,
659    int64 content_length,
660    const std::string& resource_id,
661    const std::string& etag,
662    const InitiateUploadCallback& callback) {
663  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
664  DCHECK(!callback.is_null());
665
666  return sender_->StartRequestWithRetry(
667      new InitiateUploadExistingFileRequest(
668          sender_.get(),
669          url_generator_,
670          content_type,
671          content_length,
672          resource_id,
673          etag,
674          callback));
675}
676
677CancelCallback DriveAPIService::ResumeUpload(
678    const GURL& upload_url,
679    int64 start_position,
680    int64 end_position,
681    int64 content_length,
682    const std::string& content_type,
683    const base::FilePath& local_file_path,
684    const UploadRangeCallback& callback,
685    const ProgressCallback& progress_callback) {
686  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
687  DCHECK(!callback.is_null());
688
689  return sender_->StartRequestWithRetry(
690      new ResumeUploadRequest(
691          sender_.get(),
692          upload_url,
693          start_position,
694          end_position,
695          content_length,
696          content_type,
697          local_file_path,
698          base::Bind(&ParseResourceEntryForUploadRangeAndRun, callback),
699          progress_callback));
700}
701
702CancelCallback DriveAPIService::GetUploadStatus(
703    const GURL& upload_url,
704    int64 content_length,
705    const UploadRangeCallback& callback) {
706  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
707  DCHECK(!callback.is_null());
708
709  return sender_->StartRequestWithRetry(new GetUploadStatusRequest(
710      sender_.get(),
711      upload_url,
712      content_length,
713      base::Bind(&ParseResourceEntryForUploadRangeAndRun, callback)));
714}
715
716CancelCallback DriveAPIService::AuthorizeApp(
717    const std::string& resource_id,
718    const std::string& app_id,
719    const AuthorizeAppCallback& callback) {
720  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
721  DCHECK(!callback.is_null());
722
723  return sender_->StartRequestWithRetry(new GetFileRequest(
724      sender_.get(),
725      url_generator_,
726      resource_id,
727      base::Bind(&ExtractOpenUrlAndRun, app_id, callback)));
728}
729
730bool DriveAPIService::HasAccessToken() const {
731  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
732
733  return sender_->auth_service()->HasAccessToken();
734}
735
736bool DriveAPIService::HasRefreshToken() const {
737  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
738
739  return sender_->auth_service()->HasRefreshToken();
740}
741
742void DriveAPIService::ClearAccessToken() {
743  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
744  return sender_->auth_service()->ClearAccessToken();
745}
746
747void DriveAPIService::ClearRefreshToken() {
748  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
749  return sender_->auth_service()->ClearRefreshToken();
750}
751
752void DriveAPIService::OnOAuth2RefreshTokenChanged() {
753  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
754  if (CanSendRequest()) {
755    FOR_EACH_OBSERVER(
756        DriveServiceObserver, observers_, OnReadyToSendRequests());
757  } else if (!HasRefreshToken()) {
758    FOR_EACH_OBSERVER(
759        DriveServiceObserver, observers_, OnRefreshTokenInvalid());
760  }
761}
762
763}  // namespace drive
764