Home | History | Annotate | Download | only in drive
      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/sequenced_task_runner.h"
     12 #include "base/strings/stringprintf.h"
     13 #include "base/task_runner_util.h"
     14 #include "base/values.h"
     15 #include "chrome/browser/drive/drive_api_util.h"
     16 #include "content/public/browser/browser_thread.h"
     17 #include "google_apis/drive/auth_service.h"
     18 #include "google_apis/drive/drive_api_parser.h"
     19 #include "google_apis/drive/drive_api_requests.h"
     20 #include "google_apis/drive/gdata_errorcode.h"
     21 #include "google_apis/drive/gdata_wapi_parser.h"
     22 #include "google_apis/drive/gdata_wapi_requests.h"
     23 #include "google_apis/drive/request_sender.h"
     24 #include "net/url_request/url_request_context_getter.h"
     25 
     26 using content::BrowserThread;
     27 using google_apis::AppList;
     28 using google_apis::AppListCallback;
     29 using google_apis::AuthStatusCallback;
     30 using google_apis::AuthorizeAppCallback;
     31 using google_apis::CancelCallback;
     32 using google_apis::ChangeList;
     33 using google_apis::DownloadActionCallback;
     34 using google_apis::EntryActionCallback;
     35 using google_apis::FileList;
     36 using google_apis::FileResource;
     37 using google_apis::GDATA_OTHER_ERROR;
     38 using google_apis::GDATA_PARSE_ERROR;
     39 using google_apis::GDataErrorCode;
     40 using google_apis::AboutResourceCallback;
     41 using google_apis::GetContentCallback;
     42 using google_apis::GetResourceEntryCallback;
     43 using google_apis::GetResourceEntryRequest;
     44 using google_apis::GetResourceListCallback;
     45 using google_apis::GetResourceListRequest;
     46 using google_apis::GetShareUrlCallback;
     47 using google_apis::HTTP_NOT_IMPLEMENTED;
     48 using google_apis::HTTP_SUCCESS;
     49 using google_apis::InitiateUploadCallback;
     50 using google_apis::Link;
     51 using google_apis::ProgressCallback;
     52 using google_apis::RequestSender;
     53 using google_apis::ResourceEntry;
     54 using google_apis::ResourceList;
     55 using google_apis::UploadRangeCallback;
     56 using google_apis::UploadRangeResponse;
     57 using google_apis::drive::AboutGetRequest;
     58 using google_apis::drive::AppsListRequest;
     59 using google_apis::drive::ChangesListRequest;
     60 using google_apis::drive::ChangesListNextPageRequest;
     61 using google_apis::drive::ChildrenDeleteRequest;
     62 using google_apis::drive::ChildrenInsertRequest;
     63 using google_apis::drive::DownloadFileRequest;
     64 using google_apis::drive::FilesCopyRequest;
     65 using google_apis::drive::FilesGetRequest;
     66 using google_apis::drive::FilesInsertRequest;
     67 using google_apis::drive::FilesPatchRequest;
     68 using google_apis::drive::FilesListRequest;
     69 using google_apis::drive::FilesListNextPageRequest;
     70 using google_apis::drive::FilesDeleteRequest;
     71 using google_apis::drive::FilesTrashRequest;
     72 using google_apis::drive::GetUploadStatusRequest;
     73 using google_apis::drive::InitiateUploadExistingFileRequest;
     74 using google_apis::drive::InitiateUploadNewFileRequest;
     75 using google_apis::drive::ResumeUploadRequest;
     76 
     77 namespace drive {
     78 
     79 namespace {
     80 
     81 // OAuth2 scopes for Drive API.
     82 const char kDriveScope[] = "https://www.googleapis.com/auth/drive";
     83 const char kDriveAppsReadonlyScope[] =
     84     "https://www.googleapis.com/auth/drive.apps.readonly";
     85 
     86 // Mime type to create a directory.
     87 const char kFolderMimeType[] = "application/vnd.google-apps.folder";
     88 
     89 // Max number of file entries to be fetched in a single http request.
     90 //
     91 // The larger the number is,
     92 // - The total running time to fetch the whole file list will become shorter.
     93 // - The running time for a single request tends to become longer.
     94 // Since the file list fetching is a completely background task, for our side,
     95 // only the total time matters. However, the server seems to have a time limit
     96 // per single request, which disables us to set the largest value (1000).
     97 // TODO(kinaba): make it larger when the server gets faster.
     98 const int kMaxNumFilesResourcePerRequest = 250;
     99 const int kMaxNumFilesResourcePerRequestForSearch = 50;
    100 
    101 // For performance, we declare all fields we use.
    102 const char kAboutResourceFields[] =
    103     "kind,quotaBytesTotal,quotaBytesUsed,largestChangeId,rootFolderId";
    104 const char kFileResourceFields[] =
    105     "kind,id,title,createdDate,sharedWithMeDate,downloadUrl,mimeType,"
    106     "md5Checksum,fileSize,labels/trashed,imageMediaMetadata/width,"
    107     "imageMediaMetadata/height,imageMediaMetadata/rotation,etag,"
    108     "parents/parentLink,selfLink,thumbnailLink,alternateLink,embedLink,"
    109     "modifiedDate,lastViewedByMeDate,shared";
    110 const char kFileResourceOpenWithLinksFields[] =
    111     "kind,id,openWithLinks/*";
    112 const char kFileListFields[] =
    113     "kind,items(kind,id,title,createdDate,sharedWithMeDate,downloadUrl,"
    114     "mimeType,md5Checksum,fileSize,labels/trashed,imageMediaMetadata/width,"
    115     "imageMediaMetadata/height,imageMediaMetadata/rotation,etag,"
    116     "parents/parentLink,selfLink,thumbnailLink,alternateLink,embedLink,"
    117     "modifiedDate,lastViewedByMeDate,shared),nextLink";
    118 const char kChangeListFields[] =
    119     "kind,items(file(kind,id,title,createdDate,sharedWithMeDate,downloadUrl,"
    120     "mimeType,md5Checksum,fileSize,labels/trashed,imageMediaMetadata/width,"
    121     "imageMediaMetadata/height,imageMediaMetadata/rotation,etag,"
    122     "parents/parentLink,selfLink,thumbnailLink,alternateLink,embedLink,"
    123     "modifiedDate,lastViewedByMeDate,shared),deleted,id,fileId),nextLink,"
    124     "largestChangeId";
    125 
    126 // Callback invoked when the parsing of resource list is completed,
    127 // regardless whether it is succeeded or not.
    128 void DidConvertToResourceListOnBlockingPool(
    129     const GetResourceListCallback& callback,
    130     scoped_ptr<ResourceList> resource_list) {
    131   GDataErrorCode error = resource_list ? HTTP_SUCCESS : GDATA_PARSE_ERROR;
    132   callback.Run(error, resource_list.Pass());
    133 }
    134 
    135 // Converts the FileResource value to ResourceEntry and runs |callback| on the
    136 // UI thread.
    137 void ConvertFileEntryToResourceEntryAndRun(
    138     const GetResourceEntryCallback& callback,
    139     GDataErrorCode error,
    140     scoped_ptr<FileResource> value) {
    141   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    142   DCHECK(!callback.is_null());
    143 
    144   if (!value) {
    145     callback.Run(error, scoped_ptr<ResourceEntry>());
    146     return;
    147   }
    148 
    149   // Converting to ResourceEntry is cheap enough to do on UI thread.
    150   scoped_ptr<ResourceEntry> entry =
    151       util::ConvertFileResourceToResourceEntry(*value);
    152   if (!entry) {
    153     callback.Run(GDATA_PARSE_ERROR, scoped_ptr<ResourceEntry>());
    154     return;
    155   }
    156 
    157   callback.Run(error, entry.Pass());
    158 }
    159 
    160 // Thin adapter of ConvertFileListToResourceList.
    161 scoped_ptr<ResourceList> ConvertFileListToResourceList(
    162     scoped_ptr<FileList> file_list) {
    163   return util::ConvertFileListToResourceList(*file_list);
    164 }
    165 
    166 // Converts the FileList value to ResourceList on blocking pool and runs
    167 // |callback| on the UI thread.
    168 void ConvertFileListToResourceListOnBlockingPoolAndRun(
    169     scoped_refptr<base::TaskRunner> blocking_task_runner,
    170     const GetResourceListCallback& callback,
    171     GDataErrorCode error,
    172     scoped_ptr<FileList> value) {
    173   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    174   DCHECK(!callback.is_null());
    175 
    176   if (!value) {
    177     callback.Run(error, scoped_ptr<ResourceList>());
    178     return;
    179   }
    180 
    181   // Convert the value on blocking pool.
    182   base::PostTaskAndReplyWithResult(
    183       blocking_task_runner.get(),
    184       FROM_HERE,
    185       base::Bind(&ConvertFileListToResourceList, base::Passed(&value)),
    186       base::Bind(&DidConvertToResourceListOnBlockingPool, callback));
    187 }
    188 
    189 // Thin adapter of ConvertChangeListToResourceList.
    190 scoped_ptr<ResourceList> ConvertChangeListToResourceList(
    191     scoped_ptr<ChangeList> change_list) {
    192   return util::ConvertChangeListToResourceList(*change_list);
    193 }
    194 
    195 // Converts the FileList value to ResourceList on blocking pool and runs
    196 // |callback| on the UI thread.
    197 void ConvertChangeListToResourceListOnBlockingPoolAndRun(
    198     scoped_refptr<base::TaskRunner> blocking_task_runner,
    199     const GetResourceListCallback& callback,
    200     GDataErrorCode error,
    201     scoped_ptr<ChangeList> value) {
    202   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    203   DCHECK(!callback.is_null());
    204 
    205   if (!value) {
    206     callback.Run(error, scoped_ptr<ResourceList>());
    207     return;
    208   }
    209 
    210   // Convert the value on blocking pool.
    211   base::PostTaskAndReplyWithResult(
    212       blocking_task_runner.get(),
    213       FROM_HERE,
    214       base::Bind(&ConvertChangeListToResourceList, base::Passed(&value)),
    215       base::Bind(&DidConvertToResourceListOnBlockingPool, callback));
    216 }
    217 
    218 // Converts the FileResource value to ResourceEntry for upload range request,
    219 // and runs |callback| on the UI thread.
    220 void ConvertFileResourceToResourceEntryForUploadRangeAndRun(
    221     const UploadRangeCallback& callback,
    222     const UploadRangeResponse& response,
    223     scoped_ptr<FileResource> value) {
    224   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    225   DCHECK(!callback.is_null());
    226 
    227   if (!value) {
    228     callback.Run(response, scoped_ptr<ResourceEntry>());
    229     return;
    230   }
    231 
    232   // Converting to ResourceEntry is cheap enough to do on UI thread.
    233   scoped_ptr<ResourceEntry> entry =
    234       util::ConvertFileResourceToResourceEntry(*value);
    235   if (!entry) {
    236     callback.Run(UploadRangeResponse(GDATA_PARSE_ERROR,
    237                                      response.start_position_received,
    238                                      response.end_position_received),
    239                  scoped_ptr<ResourceEntry>());
    240     return;
    241   }
    242 
    243   callback.Run(response, entry.Pass());
    244 }
    245 
    246 void ExtractOpenUrlAndRun(const std::string& app_id,
    247                           const AuthorizeAppCallback& callback,
    248                           GDataErrorCode error,
    249                           scoped_ptr<FileResource> value) {
    250   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    251   DCHECK(!callback.is_null());
    252 
    253   if (!value) {
    254     callback.Run(error, GURL());
    255     return;
    256   }
    257 
    258   const std::vector<FileResource::OpenWithLink>& open_with_links =
    259       value->open_with_links();
    260   for (size_t i = 0; i < open_with_links.size(); ++i) {
    261     if (open_with_links[i].app_id == app_id) {
    262       callback.Run(HTTP_SUCCESS, open_with_links[i].open_url);
    263       return;
    264     }
    265   }
    266 
    267   // Not found.
    268   callback.Run(GDATA_OTHER_ERROR, GURL());
    269 }
    270 
    271 // Ignores the |entry|, and runs the |callback|.
    272 void EntryActionCallbackAdapter(
    273     const EntryActionCallback& callback,
    274     GDataErrorCode error, scoped_ptr<FileResource> entry) {
    275   callback.Run(error);
    276 }
    277 
    278 // The resource ID for the root directory for Drive API is defined in the spec:
    279 // https://developers.google.com/drive/folder
    280 const char kDriveApiRootDirectoryResourceId[] = "root";
    281 
    282 }  // namespace
    283 
    284 DriveAPIService::DriveAPIService(
    285     OAuth2TokenService* oauth2_token_service,
    286     net::URLRequestContextGetter* url_request_context_getter,
    287     base::SequencedTaskRunner* blocking_task_runner,
    288     const GURL& base_url,
    289     const GURL& base_download_url,
    290     const GURL& wapi_base_url,
    291     const std::string& custom_user_agent)
    292     : oauth2_token_service_(oauth2_token_service),
    293       url_request_context_getter_(url_request_context_getter),
    294       blocking_task_runner_(blocking_task_runner),
    295       url_generator_(base_url, base_download_url),
    296       wapi_url_generator_(wapi_base_url, base_download_url),
    297       custom_user_agent_(custom_user_agent) {
    298   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    299 }
    300 
    301 DriveAPIService::~DriveAPIService() {
    302   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    303   if (sender_.get())
    304     sender_->auth_service()->RemoveObserver(this);
    305 }
    306 
    307 void DriveAPIService::Initialize(const std::string& account_id) {
    308   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    309 
    310   std::vector<std::string> scopes;
    311   scopes.push_back(kDriveScope);
    312   scopes.push_back(kDriveAppsReadonlyScope);
    313 
    314   // GData WAPI token. These are for GetShareUrl().
    315   scopes.push_back(util::kDocsListScope);
    316   scopes.push_back(util::kDriveAppsScope);
    317 
    318   sender_.reset(new RequestSender(
    319       new google_apis::AuthService(oauth2_token_service_,
    320                                    account_id,
    321                                    url_request_context_getter_.get(),
    322                                    scopes),
    323       url_request_context_getter_.get(),
    324       blocking_task_runner_.get(),
    325       custom_user_agent_));
    326   sender_->auth_service()->AddObserver(this);
    327 }
    328 
    329 void DriveAPIService::AddObserver(DriveServiceObserver* observer) {
    330   observers_.AddObserver(observer);
    331 }
    332 
    333 void DriveAPIService::RemoveObserver(DriveServiceObserver* observer) {
    334   observers_.RemoveObserver(observer);
    335 }
    336 
    337 bool DriveAPIService::CanSendRequest() const {
    338   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    339 
    340   return HasRefreshToken();
    341 }
    342 
    343 ResourceIdCanonicalizer DriveAPIService::GetResourceIdCanonicalizer() const {
    344   return base::Bind(&drive::util::CanonicalizeResourceId);
    345 }
    346 
    347 std::string DriveAPIService::GetRootResourceId() const {
    348   return kDriveApiRootDirectoryResourceId;
    349 }
    350 
    351 CancelCallback DriveAPIService::GetAllResourceList(
    352     const GetResourceListCallback& callback) {
    353   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    354   DCHECK(!callback.is_null());
    355 
    356   FilesListRequest* request = new FilesListRequest(
    357       sender_.get(), url_generator_,
    358       base::Bind(&ConvertFileListToResourceListOnBlockingPoolAndRun,
    359                  blocking_task_runner_, callback));
    360   request->set_max_results(kMaxNumFilesResourcePerRequest);
    361   request->set_q("trashed = false");  // Exclude trashed files.
    362   request->set_fields(kFileListFields);
    363   return sender_->StartRequestWithRetry(request);
    364 }
    365 
    366 CancelCallback DriveAPIService::GetResourceListInDirectory(
    367     const std::string& directory_resource_id,
    368     const GetResourceListCallback& callback) {
    369   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    370   DCHECK(!directory_resource_id.empty());
    371   DCHECK(!callback.is_null());
    372 
    373   // Because children.list method on Drive API v2 returns only the list of
    374   // children's references, but we need all file resource list.
    375   // So, here we use files.list method instead, with setting parents query.
    376   // After the migration from GData WAPI to Drive API v2, we should clean the
    377   // code up by moving the responsibility to include "parents" in the query
    378   // to client side.
    379   // We aren't interested in files in trash in this context, neither.
    380   FilesListRequest* request = new FilesListRequest(
    381       sender_.get(), url_generator_,
    382       base::Bind(&ConvertFileListToResourceListOnBlockingPoolAndRun,
    383                  blocking_task_runner_, callback));
    384   request->set_max_results(kMaxNumFilesResourcePerRequest);
    385   request->set_q(base::StringPrintf(
    386       "'%s' in parents and trashed = false",
    387       drive::util::EscapeQueryStringValue(directory_resource_id).c_str()));
    388   request->set_fields(kFileListFields);
    389   return sender_->StartRequestWithRetry(request);
    390 }
    391 
    392 CancelCallback DriveAPIService::Search(
    393     const std::string& search_query,
    394     const GetResourceListCallback& callback) {
    395   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    396   DCHECK(!search_query.empty());
    397   DCHECK(!callback.is_null());
    398 
    399   FilesListRequest* request = new FilesListRequest(
    400       sender_.get(), url_generator_,
    401       base::Bind(&ConvertFileListToResourceListOnBlockingPoolAndRun,
    402                  blocking_task_runner_, callback));
    403   request->set_max_results(kMaxNumFilesResourcePerRequestForSearch);
    404   request->set_q(drive::util::TranslateQuery(search_query));
    405   request->set_fields(kFileListFields);
    406   return sender_->StartRequestWithRetry(request);
    407 }
    408 
    409 CancelCallback DriveAPIService::SearchByTitle(
    410     const std::string& title,
    411     const std::string& directory_resource_id,
    412     const GetResourceListCallback& callback) {
    413   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    414   DCHECK(!title.empty());
    415   DCHECK(!callback.is_null());
    416 
    417   std::string query;
    418   base::StringAppendF(&query, "title = '%s'",
    419                       drive::util::EscapeQueryStringValue(title).c_str());
    420   if (!directory_resource_id.empty()) {
    421     base::StringAppendF(
    422         &query, " and '%s' in parents",
    423         drive::util::EscapeQueryStringValue(directory_resource_id).c_str());
    424   }
    425   query += " and trashed = false";
    426 
    427   FilesListRequest* request = new FilesListRequest(
    428       sender_.get(), url_generator_,
    429       base::Bind(&ConvertFileListToResourceListOnBlockingPoolAndRun,
    430                  blocking_task_runner_, callback));
    431   request->set_max_results(kMaxNumFilesResourcePerRequest);
    432   request->set_q(query);
    433   request->set_fields(kFileListFields);
    434   return sender_->StartRequestWithRetry(request);
    435 }
    436 
    437 CancelCallback DriveAPIService::GetChangeList(
    438     int64 start_changestamp,
    439     const GetResourceListCallback& callback) {
    440   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    441   DCHECK(!callback.is_null());
    442 
    443   ChangesListRequest* request = new ChangesListRequest(
    444       sender_.get(), url_generator_,
    445       base::Bind(&ConvertChangeListToResourceListOnBlockingPoolAndRun,
    446                  blocking_task_runner_, callback));
    447   request->set_max_results(kMaxNumFilesResourcePerRequest);
    448   request->set_start_change_id(start_changestamp);
    449   request->set_fields(kChangeListFields);
    450   return sender_->StartRequestWithRetry(request);
    451 }
    452 
    453 CancelCallback DriveAPIService::GetRemainingChangeList(
    454     const GURL& next_link,
    455     const GetResourceListCallback& callback) {
    456   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    457   DCHECK(!next_link.is_empty());
    458   DCHECK(!callback.is_null());
    459 
    460   ChangesListNextPageRequest* request = new ChangesListNextPageRequest(
    461       sender_.get(),
    462       base::Bind(&ConvertChangeListToResourceListOnBlockingPoolAndRun,
    463                  blocking_task_runner_, callback));
    464   request->set_next_link(next_link);
    465   request->set_fields(kChangeListFields);
    466   return sender_->StartRequestWithRetry(request);
    467 }
    468 
    469 CancelCallback DriveAPIService::GetRemainingFileList(
    470     const GURL& next_link,
    471     const GetResourceListCallback& callback) {
    472   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    473   DCHECK(!next_link.is_empty());
    474   DCHECK(!callback.is_null());
    475 
    476   FilesListNextPageRequest* request = new FilesListNextPageRequest(
    477       sender_.get(),
    478       base::Bind(&ConvertFileListToResourceListOnBlockingPoolAndRun,
    479                  blocking_task_runner_, callback));
    480   request->set_next_link(next_link);
    481   request->set_fields(kFileListFields);
    482   return sender_->StartRequestWithRetry(request);
    483 }
    484 
    485 CancelCallback DriveAPIService::GetResourceEntry(
    486     const std::string& resource_id,
    487     const GetResourceEntryCallback& callback) {
    488   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    489   DCHECK(!callback.is_null());
    490 
    491   FilesGetRequest* request = new FilesGetRequest(
    492       sender_.get(), url_generator_,
    493       base::Bind(&ConvertFileEntryToResourceEntryAndRun, callback));
    494   request->set_file_id(resource_id);
    495   request->set_fields(kFileResourceFields);
    496   return sender_->StartRequestWithRetry(request);
    497 }
    498 
    499 CancelCallback DriveAPIService::GetShareUrl(
    500     const std::string& resource_id,
    501     const GURL& embed_origin,
    502     const GetShareUrlCallback& callback) {
    503   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    504   DCHECK(!callback.is_null());
    505 
    506   // Unfortunately "share url" is not yet supported on Drive API v2.
    507   // So, as a fallback, we use GData WAPI protocol for this method.
    508   // TODO(hidehiko): Get rid of this implementation when share url is
    509   // supported on Drive API v2.
    510   return sender_->StartRequestWithRetry(
    511       new GetResourceEntryRequest(sender_.get(),
    512                                   wapi_url_generator_,
    513                                   resource_id,
    514                                   embed_origin,
    515                                   base::Bind(&util::ParseShareUrlAndRun,
    516                                              callback)));
    517 }
    518 
    519 CancelCallback DriveAPIService::GetAboutResource(
    520     const AboutResourceCallback& callback) {
    521   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    522   DCHECK(!callback.is_null());
    523 
    524   AboutGetRequest* request =
    525       new AboutGetRequest(sender_.get(), url_generator_, callback);
    526   request->set_fields(kAboutResourceFields);
    527   return sender_->StartRequestWithRetry(request);
    528 }
    529 
    530 CancelCallback DriveAPIService::GetAppList(const AppListCallback& callback) {
    531   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    532   DCHECK(!callback.is_null());
    533 
    534   return sender_->StartRequestWithRetry(
    535       new AppsListRequest(sender_.get(), url_generator_, callback));
    536 }
    537 
    538 CancelCallback DriveAPIService::DownloadFile(
    539     const base::FilePath& local_cache_path,
    540     const std::string& resource_id,
    541     const DownloadActionCallback& download_action_callback,
    542     const GetContentCallback& get_content_callback,
    543     const ProgressCallback& progress_callback) {
    544   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    545   DCHECK(!download_action_callback.is_null());
    546   // get_content_callback may be null.
    547 
    548   return sender_->StartRequestWithRetry(
    549       new DownloadFileRequest(sender_.get(),
    550                               url_generator_,
    551                               resource_id,
    552                               local_cache_path,
    553                               download_action_callback,
    554                               get_content_callback,
    555                               progress_callback));
    556 }
    557 
    558 CancelCallback DriveAPIService::DeleteResource(
    559     const std::string& resource_id,
    560     const std::string& etag,
    561     const EntryActionCallback& callback) {
    562   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    563   DCHECK(!callback.is_null());
    564 
    565   FilesDeleteRequest* request = new FilesDeleteRequest(
    566       sender_.get(), url_generator_, callback);
    567   request->set_file_id(resource_id);
    568   request->set_etag(etag);
    569   return sender_->StartRequestWithRetry(request);
    570 }
    571 
    572 CancelCallback DriveAPIService::TrashResource(
    573     const std::string& resource_id,
    574     const EntryActionCallback& callback) {
    575   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    576   DCHECK(!callback.is_null());
    577 
    578   FilesTrashRequest* request = new FilesTrashRequest(
    579       sender_.get(), url_generator_,
    580       base::Bind(&EntryActionCallbackAdapter, callback));
    581   request->set_file_id(resource_id);
    582   request->set_fields(kFileResourceFields);
    583   return sender_->StartRequestWithRetry(request);
    584 }
    585 
    586 CancelCallback DriveAPIService::AddNewDirectory(
    587     const std::string& parent_resource_id,
    588     const std::string& directory_title,
    589     const GetResourceEntryCallback& callback) {
    590   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    591   DCHECK(!callback.is_null());
    592 
    593   FilesInsertRequest* request = new FilesInsertRequest(
    594       sender_.get(), url_generator_,
    595       base::Bind(&ConvertFileEntryToResourceEntryAndRun, callback));
    596   request->set_mime_type(kFolderMimeType);
    597   request->add_parent(parent_resource_id);
    598   request->set_title(directory_title);
    599   request->set_fields(kFileResourceFields);
    600   return sender_->StartRequestWithRetry(request);
    601 }
    602 
    603 CancelCallback DriveAPIService::CopyResource(
    604     const std::string& resource_id,
    605     const std::string& parent_resource_id,
    606     const std::string& new_title,
    607     const base::Time& last_modified,
    608     const GetResourceEntryCallback& callback) {
    609   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    610   DCHECK(!callback.is_null());
    611 
    612   FilesCopyRequest* request = new FilesCopyRequest(
    613       sender_.get(), url_generator_,
    614       base::Bind(&ConvertFileEntryToResourceEntryAndRun, callback));
    615   request->set_file_id(resource_id);
    616   request->add_parent(parent_resource_id);
    617   request->set_title(new_title);
    618   request->set_modified_date(last_modified);
    619   request->set_fields(kFileResourceFields);
    620   return sender_->StartRequestWithRetry(request);
    621 }
    622 
    623 CancelCallback DriveAPIService::UpdateResource(
    624     const std::string& resource_id,
    625     const std::string& parent_resource_id,
    626     const std::string& new_title,
    627     const base::Time& last_modified,
    628     const base::Time& last_viewed_by_me,
    629     const GetResourceEntryCallback& callback) {
    630   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    631   DCHECK(!callback.is_null());
    632 
    633   FilesPatchRequest* request = new FilesPatchRequest(
    634       sender_.get(), url_generator_,
    635       base::Bind(&ConvertFileEntryToResourceEntryAndRun, callback));
    636   request->set_file_id(resource_id);
    637   request->set_title(new_title);
    638   if (!parent_resource_id.empty())
    639     request->add_parent(parent_resource_id);
    640   if (!last_modified.is_null()) {
    641     // Need to set setModifiedDate to true to overwrite modifiedDate.
    642     request->set_set_modified_date(true);
    643     request->set_modified_date(last_modified);
    644   }
    645   if (!last_viewed_by_me.is_null()) {
    646     // Need to set updateViewedDate to false, otherwise the lastViewedByMeDate
    647     // will be set to the request time (not the specified time via request).
    648     request->set_update_viewed_date(false);
    649     request->set_last_viewed_by_me_date(last_viewed_by_me);
    650   }
    651   request->set_fields(kFileResourceFields);
    652   return sender_->StartRequestWithRetry(request);
    653 }
    654 
    655 CancelCallback DriveAPIService::RenameResource(
    656     const std::string& resource_id,
    657     const std::string& new_title,
    658     const EntryActionCallback& callback) {
    659   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    660   DCHECK(!callback.is_null());
    661 
    662   FilesPatchRequest* request = new FilesPatchRequest(
    663       sender_.get(), url_generator_,
    664       base::Bind(&EntryActionCallbackAdapter, callback));
    665   request->set_file_id(resource_id);
    666   request->set_title(new_title);
    667   request->set_fields(kFileResourceFields);
    668   return sender_->StartRequestWithRetry(request);
    669 }
    670 
    671 CancelCallback DriveAPIService::AddResourceToDirectory(
    672     const std::string& parent_resource_id,
    673     const std::string& resource_id,
    674     const EntryActionCallback& callback) {
    675   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    676   DCHECK(!callback.is_null());
    677 
    678   ChildrenInsertRequest* request =
    679       new ChildrenInsertRequest(sender_.get(), url_generator_, callback);
    680   request->set_folder_id(parent_resource_id);
    681   request->set_id(resource_id);
    682   return sender_->StartRequestWithRetry(request);
    683 }
    684 
    685 CancelCallback DriveAPIService::RemoveResourceFromDirectory(
    686     const std::string& parent_resource_id,
    687     const std::string& resource_id,
    688     const EntryActionCallback& callback) {
    689   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    690   DCHECK(!callback.is_null());
    691 
    692   ChildrenDeleteRequest* request =
    693       new ChildrenDeleteRequest(sender_.get(), url_generator_, callback);
    694   request->set_child_id(resource_id);
    695   request->set_folder_id(parent_resource_id);
    696   return sender_->StartRequestWithRetry(request);
    697 }
    698 
    699 CancelCallback DriveAPIService::InitiateUploadNewFile(
    700     const std::string& content_type,
    701     int64 content_length,
    702     const std::string& parent_resource_id,
    703     const std::string& title,
    704     const InitiateUploadCallback& callback) {
    705   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    706   DCHECK(!callback.is_null());
    707 
    708   return sender_->StartRequestWithRetry(
    709       new InitiateUploadNewFileRequest(
    710           sender_.get(),
    711           url_generator_,
    712           content_type,
    713           content_length,
    714           parent_resource_id,
    715           title,
    716           callback));
    717 }
    718 
    719 CancelCallback DriveAPIService::InitiateUploadExistingFile(
    720     const std::string& content_type,
    721     int64 content_length,
    722     const std::string& resource_id,
    723     const std::string& etag,
    724     const InitiateUploadCallback& callback) {
    725   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    726   DCHECK(!callback.is_null());
    727 
    728   return sender_->StartRequestWithRetry(
    729       new InitiateUploadExistingFileRequest(
    730           sender_.get(),
    731           url_generator_,
    732           content_type,
    733           content_length,
    734           resource_id,
    735           etag,
    736           callback));
    737 }
    738 
    739 CancelCallback DriveAPIService::ResumeUpload(
    740     const GURL& upload_url,
    741     int64 start_position,
    742     int64 end_position,
    743     int64 content_length,
    744     const std::string& content_type,
    745     const base::FilePath& local_file_path,
    746     const UploadRangeCallback& callback,
    747     const ProgressCallback& progress_callback) {
    748   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    749   DCHECK(!callback.is_null());
    750 
    751   return sender_->StartRequestWithRetry(
    752       new ResumeUploadRequest(
    753           sender_.get(),
    754           upload_url,
    755           start_position,
    756           end_position,
    757           content_length,
    758           content_type,
    759           local_file_path,
    760           base::Bind(&ConvertFileResourceToResourceEntryForUploadRangeAndRun,
    761                      callback),
    762           progress_callback));
    763 }
    764 
    765 CancelCallback DriveAPIService::GetUploadStatus(
    766     const GURL& upload_url,
    767     int64 content_length,
    768     const UploadRangeCallback& callback) {
    769   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    770   DCHECK(!callback.is_null());
    771 
    772   return sender_->StartRequestWithRetry(new GetUploadStatusRequest(
    773       sender_.get(),
    774       upload_url,
    775       content_length,
    776       base::Bind(&ConvertFileResourceToResourceEntryForUploadRangeAndRun,
    777                  callback)));
    778 }
    779 
    780 CancelCallback DriveAPIService::AuthorizeApp(
    781     const std::string& resource_id,
    782     const std::string& app_id,
    783     const AuthorizeAppCallback& callback) {
    784   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    785   DCHECK(!callback.is_null());
    786 
    787   FilesGetRequest* request = new FilesGetRequest(
    788       sender_.get(), url_generator_,
    789       base::Bind(&ExtractOpenUrlAndRun, app_id, callback));
    790   request->set_file_id(resource_id);
    791   request->set_fields(kFileResourceOpenWithLinksFields);
    792   return sender_->StartRequestWithRetry(request);
    793 }
    794 
    795 CancelCallback DriveAPIService::GetResourceListInDirectoryByWapi(
    796     const std::string& directory_resource_id,
    797     const google_apis::GetResourceListCallback& callback) {
    798   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    799   DCHECK(!directory_resource_id.empty());
    800   DCHECK(!callback.is_null());
    801 
    802   return sender_->StartRequestWithRetry(
    803       new GetResourceListRequest(sender_.get(),
    804                                  wapi_url_generator_,
    805                                  GURL(),         // No override url
    806                                  0,              // start changestamp
    807                                  std::string(),  // empty search query
    808                                  directory_resource_id,
    809                                  callback));
    810 }
    811 
    812 CancelCallback DriveAPIService::GetRemainingResourceList(
    813     const GURL& next_link,
    814     const google_apis::GetResourceListCallback& callback) {
    815   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    816   DCHECK(!next_link.is_empty());
    817   DCHECK(!callback.is_null());
    818 
    819   return sender_->StartRequestWithRetry(
    820       new GetResourceListRequest(sender_.get(),
    821                                  wapi_url_generator_,
    822                                  next_link,
    823                                  0,              // start changestamp
    824                                  std::string(),  // empty search query
    825                                  std::string(),  // no directory resource id
    826                                  callback));
    827 }
    828 
    829 bool DriveAPIService::HasAccessToken() const {
    830   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    831   return sender_->auth_service()->HasAccessToken();
    832 }
    833 
    834 void DriveAPIService::RequestAccessToken(const AuthStatusCallback& callback) {
    835   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    836   DCHECK(!callback.is_null());
    837 
    838   const std::string access_token = sender_->auth_service()->access_token();
    839   if (!access_token.empty()) {
    840     callback.Run(google_apis::HTTP_NOT_MODIFIED, access_token);
    841     return;
    842   }
    843 
    844   // Retrieve the new auth token.
    845   sender_->auth_service()->StartAuthentication(callback);
    846 }
    847 
    848 bool DriveAPIService::HasRefreshToken() const {
    849   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    850   return sender_->auth_service()->HasRefreshToken();
    851 }
    852 
    853 void DriveAPIService::ClearAccessToken() {
    854   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    855   sender_->auth_service()->ClearAccessToken();
    856 }
    857 
    858 void DriveAPIService::ClearRefreshToken() {
    859   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    860   sender_->auth_service()->ClearRefreshToken();
    861 }
    862 
    863 void DriveAPIService::OnOAuth2RefreshTokenChanged() {
    864   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    865   if (CanSendRequest()) {
    866     FOR_EACH_OBSERVER(
    867         DriveServiceObserver, observers_, OnReadyToSendRequests());
    868   } else if (!HasRefreshToken()) {
    869     FOR_EACH_OBSERVER(
    870         DriveServiceObserver, observers_, OnRefreshTokenInvalid());
    871   }
    872 }
    873 
    874 }  // namespace drive
    875