| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "google_apis/drive/gdata_wapi_requests.h" |
| |
| #include "base/location.h" |
| #include "base/sequenced_task_runner.h" |
| #include "base/task_runner_util.h" |
| #include "base/values.h" |
| #include "google_apis/drive/gdata_wapi_parser.h" |
| #include "google_apis/drive/gdata_wapi_url_generator.h" |
| #include "google_apis/drive/request_sender.h" |
| #include "google_apis/drive/request_util.h" |
| #include "third_party/libxml/chromium/libxml_utils.h" |
| |
| using net::URLFetcher; |
| |
| namespace google_apis { |
| |
| namespace { |
| |
| // Parses the JSON value to ResourceList. |
| scoped_ptr<ResourceList> ParseResourceListOnBlockingPool( |
| scoped_ptr<base::Value> value) { |
| DCHECK(value); |
| |
| return ResourceList::ExtractAndParse(*value); |
| } |
| |
| // Runs |callback| with |error| and |resource_list|, but replace the error code |
| // with GDATA_PARSE_ERROR, if there was a parsing error. |
| void DidParseResourceListOnBlockingPool( |
| const GetResourceListCallback& callback, |
| GDataErrorCode error, |
| scoped_ptr<ResourceList> resource_list) { |
| DCHECK(!callback.is_null()); |
| |
| // resource_list being NULL indicates there was a parsing error. |
| if (!resource_list) |
| error = GDATA_PARSE_ERROR; |
| |
| callback.Run(error, resource_list.Pass()); |
| } |
| |
| // Parses the JSON value to ResourceList on the blocking pool and runs |
| // |callback| on the UI thread once parsing is done. |
| void ParseResourceListAndRun( |
| scoped_refptr<base::TaskRunner> blocking_task_runner, |
| const GetResourceListCallback& callback, |
| GDataErrorCode error, |
| scoped_ptr<base::Value> value) { |
| DCHECK(!callback.is_null()); |
| |
| if (!value) { |
| callback.Run(error, scoped_ptr<ResourceList>()); |
| return; |
| } |
| |
| base::PostTaskAndReplyWithResult( |
| blocking_task_runner, |
| FROM_HERE, |
| base::Bind(&ParseResourceListOnBlockingPool, base::Passed(&value)), |
| base::Bind(&DidParseResourceListOnBlockingPool, callback, error)); |
| } |
| |
| // Parses the JSON value to AccountMetadata and runs |callback| on the UI |
| // thread once parsing is done. |
| void ParseAccounetMetadataAndRun(const GetAccountMetadataCallback& callback, |
| GDataErrorCode error, |
| scoped_ptr<base::Value> value) { |
| DCHECK(!callback.is_null()); |
| |
| if (!value) { |
| callback.Run(error, scoped_ptr<AccountMetadata>()); |
| return; |
| } |
| |
| // Parsing AccountMetadata is cheap enough to do on UI thread. |
| scoped_ptr<AccountMetadata> entry = |
| google_apis::AccountMetadata::CreateFrom(*value); |
| if (!entry) { |
| callback.Run(GDATA_PARSE_ERROR, scoped_ptr<AccountMetadata>()); |
| return; |
| } |
| |
| callback.Run(error, entry.Pass()); |
| } |
| |
| // Parses the |value| to ResourceEntry with error handling. |
| // This is designed to be used for ResumeUploadRequest and |
| // GetUploadStatusRequest. |
| scoped_ptr<ResourceEntry> ParseResourceEntry(scoped_ptr<base::Value> value) { |
| scoped_ptr<ResourceEntry> entry; |
| if (value.get()) { |
| entry = ResourceEntry::ExtractAndParse(*value); |
| |
| // Note: |value| may be NULL, in particular if the callback is for a |
| // failure. |
| if (!entry.get()) |
| LOG(WARNING) << "Invalid entry received on upload."; |
| } |
| |
| return entry.Pass(); |
| } |
| |
| // Extracts the open link url from the JSON Feed. Used by AuthorizeApp(). |
| void ParseOpenLinkAndRun(const std::string& app_id, |
| const AuthorizeAppCallback& callback, |
| GDataErrorCode error, |
| scoped_ptr<base::Value> value) { |
| DCHECK(!callback.is_null()); |
| |
| if (!value) { |
| callback.Run(error, GURL()); |
| return; |
| } |
| |
| // Parsing ResourceEntry is cheap enough to do on UI thread. |
| scoped_ptr<ResourceEntry> resource_entry = ParseResourceEntry(value.Pass()); |
| if (!resource_entry) { |
| callback.Run(GDATA_PARSE_ERROR, GURL()); |
| return; |
| } |
| |
| // Look for the link to open the file with the app with |app_id|. |
| const ScopedVector<Link>& resource_links = resource_entry->links(); |
| GURL open_link; |
| for (size_t i = 0; i < resource_links.size(); ++i) { |
| const Link& link = *resource_links[i]; |
| if (link.type() == Link::LINK_OPEN_WITH && link.app_id() == app_id) { |
| open_link = link.href(); |
| break; |
| } |
| } |
| |
| if (open_link.is_empty()) |
| error = GDATA_OTHER_ERROR; |
| |
| callback.Run(error, open_link); |
| } |
| |
| } // namespace |
| |
| //============================ GetResourceListRequest ======================== |
| |
| GetResourceListRequest::GetResourceListRequest( |
| RequestSender* sender, |
| const GDataWapiUrlGenerator& url_generator, |
| const GURL& override_url, |
| int64 start_changestamp, |
| const std::string& search_string, |
| const std::string& directory_resource_id, |
| const GetResourceListCallback& callback) |
| : GetDataRequest( |
| sender, |
| base::Bind(&ParseResourceListAndRun, |
| make_scoped_refptr(sender->blocking_task_runner()), |
| callback)), |
| url_generator_(url_generator), |
| override_url_(override_url), |
| start_changestamp_(start_changestamp), |
| search_string_(search_string), |
| directory_resource_id_(directory_resource_id) { |
| DCHECK(!callback.is_null()); |
| } |
| |
| GetResourceListRequest::~GetResourceListRequest() {} |
| |
| GURL GetResourceListRequest::GetURL() const { |
| return url_generator_.GenerateResourceListUrl(override_url_, |
| start_changestamp_, |
| search_string_, |
| directory_resource_id_); |
| } |
| |
| //============================ SearchByTitleRequest ========================== |
| |
| SearchByTitleRequest::SearchByTitleRequest( |
| RequestSender* sender, |
| const GDataWapiUrlGenerator& url_generator, |
| const std::string& title, |
| const std::string& directory_resource_id, |
| const GetResourceListCallback& callback) |
| : GetDataRequest( |
| sender, |
| base::Bind(&ParseResourceListAndRun, |
| make_scoped_refptr(sender->blocking_task_runner()), |
| callback)), |
| url_generator_(url_generator), |
| title_(title), |
| directory_resource_id_(directory_resource_id) { |
| DCHECK(!callback.is_null()); |
| } |
| |
| SearchByTitleRequest::~SearchByTitleRequest() {} |
| |
| GURL SearchByTitleRequest::GetURL() const { |
| return url_generator_.GenerateSearchByTitleUrl( |
| title_, directory_resource_id_); |
| } |
| |
| //============================ GetResourceEntryRequest ======================= |
| |
| GetResourceEntryRequest::GetResourceEntryRequest( |
| RequestSender* sender, |
| const GDataWapiUrlGenerator& url_generator, |
| const std::string& resource_id, |
| const GURL& embed_origin, |
| const GetDataCallback& callback) |
| : GetDataRequest(sender, callback), |
| url_generator_(url_generator), |
| resource_id_(resource_id), |
| embed_origin_(embed_origin) { |
| DCHECK(!callback.is_null()); |
| } |
| |
| GetResourceEntryRequest::~GetResourceEntryRequest() {} |
| |
| GURL GetResourceEntryRequest::GetURL() const { |
| return url_generator_.GenerateEditUrlWithEmbedOrigin( |
| resource_id_, embed_origin_); |
| } |
| |
| //========================= GetAccountMetadataRequest ======================== |
| |
| GetAccountMetadataRequest::GetAccountMetadataRequest( |
| RequestSender* sender, |
| const GDataWapiUrlGenerator& url_generator, |
| const GetAccountMetadataCallback& callback, |
| bool include_installed_apps) |
| : GetDataRequest(sender, |
| base::Bind(&ParseAccounetMetadataAndRun, callback)), |
| url_generator_(url_generator), |
| include_installed_apps_(include_installed_apps) { |
| DCHECK(!callback.is_null()); |
| } |
| |
| GetAccountMetadataRequest::~GetAccountMetadataRequest() {} |
| |
| GURL GetAccountMetadataRequest::GetURL() const { |
| return url_generator_.GenerateAccountMetadataUrl(include_installed_apps_); |
| } |
| |
| //=========================== DeleteResourceRequest ========================== |
| |
| DeleteResourceRequest::DeleteResourceRequest( |
| RequestSender* sender, |
| const GDataWapiUrlGenerator& url_generator, |
| const EntryActionCallback& callback, |
| const std::string& resource_id, |
| const std::string& etag) |
| : EntryActionRequest(sender, callback), |
| url_generator_(url_generator), |
| resource_id_(resource_id), |
| etag_(etag) { |
| DCHECK(!callback.is_null()); |
| } |
| |
| DeleteResourceRequest::~DeleteResourceRequest() {} |
| |
| GURL DeleteResourceRequest::GetURL() const { |
| return url_generator_.GenerateEditUrl(resource_id_); |
| } |
| |
| URLFetcher::RequestType DeleteResourceRequest::GetRequestType() const { |
| return URLFetcher::DELETE_REQUEST; |
| } |
| |
| std::vector<std::string> |
| DeleteResourceRequest::GetExtraRequestHeaders() const { |
| std::vector<std::string> headers; |
| headers.push_back(util::GenerateIfMatchHeader(etag_)); |
| return headers; |
| } |
| |
| //========================== CreateDirectoryRequest ========================== |
| |
| CreateDirectoryRequest::CreateDirectoryRequest( |
| RequestSender* sender, |
| const GDataWapiUrlGenerator& url_generator, |
| const GetDataCallback& callback, |
| const std::string& parent_resource_id, |
| const std::string& directory_title) |
| : GetDataRequest(sender, callback), |
| url_generator_(url_generator), |
| parent_resource_id_(parent_resource_id), |
| directory_title_(directory_title) { |
| DCHECK(!callback.is_null()); |
| } |
| |
| CreateDirectoryRequest::~CreateDirectoryRequest() {} |
| |
| GURL CreateDirectoryRequest::GetURL() const { |
| return url_generator_.GenerateContentUrl(parent_resource_id_); |
| } |
| |
| URLFetcher::RequestType |
| CreateDirectoryRequest::GetRequestType() const { |
| return URLFetcher::POST; |
| } |
| |
| bool CreateDirectoryRequest::GetContentData(std::string* upload_content_type, |
| std::string* upload_content) { |
| upload_content_type->assign("application/atom+xml"); |
| XmlWriter xml_writer; |
| xml_writer.StartWriting(); |
| xml_writer.StartElement("entry"); |
| xml_writer.AddAttribute("xmlns", "http://www.w3.org/2005/Atom"); |
| |
| xml_writer.StartElement("category"); |
| xml_writer.AddAttribute("scheme", |
| "http://schemas.google.com/g/2005#kind"); |
| xml_writer.AddAttribute("term", |
| "http://schemas.google.com/docs/2007#folder"); |
| xml_writer.EndElement(); // Ends "category" element. |
| |
| xml_writer.WriteElement("title", directory_title_); |
| |
| xml_writer.EndElement(); // Ends "entry" element. |
| xml_writer.StopWriting(); |
| upload_content->assign(xml_writer.GetWrittenString()); |
| DVLOG(1) << "CreateDirectory data: " << *upload_content_type << ", [" |
| << *upload_content << "]"; |
| return true; |
| } |
| |
| //=========================== RenameResourceRequest ========================== |
| |
| RenameResourceRequest::RenameResourceRequest( |
| RequestSender* sender, |
| const GDataWapiUrlGenerator& url_generator, |
| const EntryActionCallback& callback, |
| const std::string& resource_id, |
| const std::string& new_title) |
| : EntryActionRequest(sender, callback), |
| url_generator_(url_generator), |
| resource_id_(resource_id), |
| new_title_(new_title) { |
| DCHECK(!callback.is_null()); |
| } |
| |
| RenameResourceRequest::~RenameResourceRequest() {} |
| |
| URLFetcher::RequestType RenameResourceRequest::GetRequestType() const { |
| return URLFetcher::PUT; |
| } |
| |
| std::vector<std::string> |
| RenameResourceRequest::GetExtraRequestHeaders() const { |
| std::vector<std::string> headers; |
| headers.push_back(util::kIfMatchAllHeader); |
| return headers; |
| } |
| |
| GURL RenameResourceRequest::GetURL() const { |
| return url_generator_.GenerateEditUrl(resource_id_); |
| } |
| |
| bool RenameResourceRequest::GetContentData(std::string* upload_content_type, |
| std::string* upload_content) { |
| upload_content_type->assign("application/atom+xml"); |
| XmlWriter xml_writer; |
| xml_writer.StartWriting(); |
| xml_writer.StartElement("entry"); |
| xml_writer.AddAttribute("xmlns", "http://www.w3.org/2005/Atom"); |
| |
| xml_writer.WriteElement("title", new_title_); |
| |
| xml_writer.EndElement(); // Ends "entry" element. |
| xml_writer.StopWriting(); |
| upload_content->assign(xml_writer.GetWrittenString()); |
| DVLOG(1) << "RenameResourceRequest data: " << *upload_content_type << ", [" |
| << *upload_content << "]"; |
| return true; |
| } |
| |
| //=========================== AuthorizeAppRequest ========================== |
| |
| AuthorizeAppRequest::AuthorizeAppRequest( |
| RequestSender* sender, |
| const GDataWapiUrlGenerator& url_generator, |
| const AuthorizeAppCallback& callback, |
| const std::string& resource_id, |
| const std::string& app_id) |
| : GetDataRequest(sender, |
| base::Bind(&ParseOpenLinkAndRun, app_id, callback)), |
| url_generator_(url_generator), |
| resource_id_(resource_id), |
| app_id_(app_id) { |
| DCHECK(!callback.is_null()); |
| } |
| |
| AuthorizeAppRequest::~AuthorizeAppRequest() {} |
| |
| URLFetcher::RequestType AuthorizeAppRequest::GetRequestType() const { |
| return URLFetcher::PUT; |
| } |
| |
| std::vector<std::string> |
| AuthorizeAppRequest::GetExtraRequestHeaders() const { |
| std::vector<std::string> headers; |
| headers.push_back(util::kIfMatchAllHeader); |
| return headers; |
| } |
| |
| bool AuthorizeAppRequest::GetContentData(std::string* upload_content_type, |
| std::string* upload_content) { |
| upload_content_type->assign("application/atom+xml"); |
| XmlWriter xml_writer; |
| xml_writer.StartWriting(); |
| xml_writer.StartElement("entry"); |
| xml_writer.AddAttribute("xmlns", "http://www.w3.org/2005/Atom"); |
| xml_writer.AddAttribute("xmlns:docs", "http://schemas.google.com/docs/2007"); |
| xml_writer.WriteElement("docs:authorizedApp", app_id_); |
| |
| xml_writer.EndElement(); // Ends "entry" element. |
| xml_writer.StopWriting(); |
| upload_content->assign(xml_writer.GetWrittenString()); |
| DVLOG(1) << "AuthorizeAppRequest data: " << *upload_content_type << ", [" |
| << *upload_content << "]"; |
| return true; |
| } |
| |
| GURL AuthorizeAppRequest::GetURL() const { |
| return url_generator_.GenerateEditUrl(resource_id_); |
| } |
| |
| //======================= AddResourceToDirectoryRequest ====================== |
| |
| AddResourceToDirectoryRequest::AddResourceToDirectoryRequest( |
| RequestSender* sender, |
| const GDataWapiUrlGenerator& url_generator, |
| const EntryActionCallback& callback, |
| const std::string& parent_resource_id, |
| const std::string& resource_id) |
| : EntryActionRequest(sender, callback), |
| url_generator_(url_generator), |
| parent_resource_id_(parent_resource_id), |
| resource_id_(resource_id) { |
| DCHECK(!callback.is_null()); |
| } |
| |
| AddResourceToDirectoryRequest::~AddResourceToDirectoryRequest() {} |
| |
| GURL AddResourceToDirectoryRequest::GetURL() const { |
| return url_generator_.GenerateContentUrl(parent_resource_id_); |
| } |
| |
| URLFetcher::RequestType |
| AddResourceToDirectoryRequest::GetRequestType() const { |
| return URLFetcher::POST; |
| } |
| |
| bool AddResourceToDirectoryRequest::GetContentData( |
| std::string* upload_content_type, std::string* upload_content) { |
| upload_content_type->assign("application/atom+xml"); |
| XmlWriter xml_writer; |
| xml_writer.StartWriting(); |
| xml_writer.StartElement("entry"); |
| xml_writer.AddAttribute("xmlns", "http://www.w3.org/2005/Atom"); |
| |
| xml_writer.WriteElement( |
| "id", url_generator_.GenerateEditUrlWithoutParams(resource_id_).spec()); |
| |
| xml_writer.EndElement(); // Ends "entry" element. |
| xml_writer.StopWriting(); |
| upload_content->assign(xml_writer.GetWrittenString()); |
| DVLOG(1) << "AddResourceToDirectoryRequest data: " << *upload_content_type |
| << ", [" << *upload_content << "]"; |
| return true; |
| } |
| |
| //==================== RemoveResourceFromDirectoryRequest ==================== |
| |
| RemoveResourceFromDirectoryRequest::RemoveResourceFromDirectoryRequest( |
| RequestSender* sender, |
| const GDataWapiUrlGenerator& url_generator, |
| const EntryActionCallback& callback, |
| const std::string& parent_resource_id, |
| const std::string& document_resource_id) |
| : EntryActionRequest(sender, callback), |
| url_generator_(url_generator), |
| resource_id_(document_resource_id), |
| parent_resource_id_(parent_resource_id) { |
| DCHECK(!callback.is_null()); |
| } |
| |
| RemoveResourceFromDirectoryRequest::~RemoveResourceFromDirectoryRequest() { |
| } |
| |
| GURL RemoveResourceFromDirectoryRequest::GetURL() const { |
| return url_generator_.GenerateResourceUrlForRemoval( |
| parent_resource_id_, resource_id_); |
| } |
| |
| URLFetcher::RequestType |
| RemoveResourceFromDirectoryRequest::GetRequestType() const { |
| return URLFetcher::DELETE_REQUEST; |
| } |
| |
| std::vector<std::string> |
| RemoveResourceFromDirectoryRequest::GetExtraRequestHeaders() const { |
| std::vector<std::string> headers; |
| headers.push_back(util::kIfMatchAllHeader); |
| return headers; |
| } |
| |
| //======================= InitiateUploadNewFileRequest ======================= |
| |
| InitiateUploadNewFileRequest::InitiateUploadNewFileRequest( |
| RequestSender* sender, |
| const GDataWapiUrlGenerator& url_generator, |
| const InitiateUploadCallback& callback, |
| const std::string& content_type, |
| int64 content_length, |
| const std::string& parent_resource_id, |
| const std::string& title) |
| : InitiateUploadRequestBase(sender, callback, content_type, content_length), |
| url_generator_(url_generator), |
| parent_resource_id_(parent_resource_id), |
| title_(title) { |
| } |
| |
| InitiateUploadNewFileRequest::~InitiateUploadNewFileRequest() {} |
| |
| GURL InitiateUploadNewFileRequest::GetURL() const { |
| return url_generator_.GenerateInitiateUploadNewFileUrl(parent_resource_id_); |
| } |
| |
| net::URLFetcher::RequestType |
| InitiateUploadNewFileRequest::GetRequestType() const { |
| return net::URLFetcher::POST; |
| } |
| |
| bool InitiateUploadNewFileRequest::GetContentData( |
| std::string* upload_content_type, |
| std::string* upload_content) { |
| upload_content_type->assign("application/atom+xml"); |
| XmlWriter xml_writer; |
| xml_writer.StartWriting(); |
| xml_writer.StartElement("entry"); |
| xml_writer.AddAttribute("xmlns", "http://www.w3.org/2005/Atom"); |
| xml_writer.AddAttribute("xmlns:docs", |
| "http://schemas.google.com/docs/2007"); |
| xml_writer.WriteElement("title", title_); |
| xml_writer.EndElement(); // Ends "entry" element. |
| xml_writer.StopWriting(); |
| upload_content->assign(xml_writer.GetWrittenString()); |
| DVLOG(1) << "InitiateUploadNewFile: " << *upload_content_type << ", [" |
| << *upload_content << "]"; |
| return true; |
| } |
| |
| //===================== InitiateUploadExistingFileRequest ==================== |
| |
| InitiateUploadExistingFileRequest::InitiateUploadExistingFileRequest( |
| RequestSender* sender, |
| const GDataWapiUrlGenerator& url_generator, |
| const InitiateUploadCallback& callback, |
| const std::string& content_type, |
| int64 content_length, |
| const std::string& resource_id, |
| const std::string& etag) |
| : InitiateUploadRequestBase(sender, callback, content_type, content_length), |
| url_generator_(url_generator), |
| resource_id_(resource_id), |
| etag_(etag) { |
| } |
| |
| InitiateUploadExistingFileRequest::~InitiateUploadExistingFileRequest() {} |
| |
| GURL InitiateUploadExistingFileRequest::GetURL() const { |
| return url_generator_.GenerateInitiateUploadExistingFileUrl(resource_id_); |
| } |
| |
| net::URLFetcher::RequestType |
| InitiateUploadExistingFileRequest::GetRequestType() const { |
| return net::URLFetcher::PUT; |
| } |
| |
| bool InitiateUploadExistingFileRequest::GetContentData( |
| std::string* upload_content_type, |
| std::string* upload_content) { |
| // According to the document there is no need to send the content-type. |
| // However, the server would return 500 server error without the |
| // content-type. |
| // As its workaround, send "text/plain" content-type here. |
| *upload_content_type = "text/plain"; |
| *upload_content = ""; |
| return true; |
| } |
| |
| std::vector<std::string> |
| InitiateUploadExistingFileRequest::GetExtraRequestHeaders() const { |
| std::vector<std::string> headers( |
| InitiateUploadRequestBase::GetExtraRequestHeaders()); |
| headers.push_back(util::GenerateIfMatchHeader(etag_)); |
| return headers; |
| } |
| |
| //============================ ResumeUploadRequest =========================== |
| |
| ResumeUploadRequest::ResumeUploadRequest( |
| RequestSender* sender, |
| const UploadRangeCallback& callback, |
| const ProgressCallback& progress_callback, |
| const GURL& upload_location, |
| int64 start_position, |
| int64 end_position, |
| int64 content_length, |
| const std::string& content_type, |
| const base::FilePath& local_file_path) |
| : ResumeUploadRequestBase(sender, |
| upload_location, |
| start_position, |
| end_position, |
| content_length, |
| content_type, |
| local_file_path), |
| callback_(callback), |
| progress_callback_(progress_callback) { |
| DCHECK(!callback_.is_null()); |
| } |
| |
| ResumeUploadRequest::~ResumeUploadRequest() {} |
| |
| void ResumeUploadRequest::OnRangeRequestComplete( |
| const UploadRangeResponse& response, scoped_ptr<base::Value> value) { |
| callback_.Run(response, ParseResourceEntry(value.Pass())); |
| } |
| |
| void ResumeUploadRequest::OnURLFetchUploadProgress( |
| const URLFetcher* source, int64 current, int64 total) { |
| if (!progress_callback_.is_null()) |
| progress_callback_.Run(current, total); |
| } |
| |
| //========================== GetUploadStatusRequest ========================== |
| |
| GetUploadStatusRequest::GetUploadStatusRequest( |
| RequestSender* sender, |
| const UploadRangeCallback& callback, |
| const GURL& upload_url, |
| int64 content_length) |
| : GetUploadStatusRequestBase(sender, upload_url, content_length), |
| callback_(callback) { |
| DCHECK(!callback.is_null()); |
| } |
| |
| GetUploadStatusRequest::~GetUploadStatusRequest() {} |
| |
| void GetUploadStatusRequest::OnRangeRequestComplete( |
| const UploadRangeResponse& response, scoped_ptr<base::Value> value) { |
| callback_.Run(response, ParseResourceEntry(value.Pass())); |
| } |
| |
| //========================== DownloadFileRequest ========================== |
| |
| DownloadFileRequest::DownloadFileRequest( |
| RequestSender* sender, |
| const GDataWapiUrlGenerator& url_generator, |
| const DownloadActionCallback& download_action_callback, |
| const GetContentCallback& get_content_callback, |
| const ProgressCallback& progress_callback, |
| const std::string& resource_id, |
| const base::FilePath& output_file_path) |
| : DownloadFileRequestBase( |
| sender, |
| download_action_callback, |
| get_content_callback, |
| progress_callback, |
| url_generator.GenerateDownloadFileUrl(resource_id), |
| output_file_path) { |
| } |
| |
| DownloadFileRequest::~DownloadFileRequest() { |
| } |
| |
| } // namespace google_apis |