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