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 "google_apis/drive/drive_api_requests.h" 6 7 #include "base/bind.h" 8 #include "base/callback.h" 9 #include "base/json/json_writer.h" 10 #include "base/location.h" 11 #include "base/sequenced_task_runner.h" 12 #include "base/task_runner_util.h" 13 #include "base/values.h" 14 #include "google_apis/drive/drive_api_parser.h" 15 #include "google_apis/drive/request_sender.h" 16 #include "google_apis/drive/request_util.h" 17 #include "google_apis/drive/time_util.h" 18 #include "net/base/url_util.h" 19 20 namespace google_apis { 21 namespace { 22 23 const char kContentTypeApplicationJson[] = "application/json"; 24 const char kParentLinkKind[] = "drive#fileLink"; 25 26 // Parses the JSON value to a resource typed |T| and runs |callback| on the UI 27 // thread once parsing is done. 28 template<typename T> 29 void ParseJsonAndRun( 30 const base::Callback<void(GDataErrorCode, scoped_ptr<T>)>& callback, 31 GDataErrorCode error, 32 scoped_ptr<base::Value> value) { 33 DCHECK(!callback.is_null()); 34 35 scoped_ptr<T> resource; 36 if (value) { 37 resource = T::CreateFrom(*value); 38 if (!resource) { 39 // Failed to parse the JSON value, although the JSON value is available, 40 // so let the callback know the parsing error. 41 error = GDATA_PARSE_ERROR; 42 } 43 } 44 45 callback.Run(error, resource.Pass()); 46 } 47 48 // Thin adapter of T::CreateFrom. 49 template<typename T> 50 scoped_ptr<T> ParseJsonOnBlockingPool(scoped_ptr<base::Value> value) { 51 return T::CreateFrom(*value); 52 } 53 54 // Runs |callback| with given |error| and |value|. If |value| is null, 55 // overwrites |error| to GDATA_PARSE_ERROR. 56 template<typename T> 57 void ParseJsonOnBlockingPoolAndRunAfterBlockingPoolTask( 58 const base::Callback<void(GDataErrorCode, scoped_ptr<T>)>& callback, 59 GDataErrorCode error, scoped_ptr<T> value) { 60 if (!value) 61 error = GDATA_PARSE_ERROR; 62 callback.Run(error, value.Pass()); 63 } 64 65 // Parses the JSON value to a resource typed |T| and runs |callback| on 66 // blocking pool, and then run on the current thread. 67 // TODO(hidehiko): Move this and ParseJsonAndRun defined above into base with 68 // merging the tasks running on blocking pool into one. 69 template<typename T> 70 void ParseJsonOnBlockingPoolAndRun( 71 scoped_refptr<base::TaskRunner> blocking_task_runner, 72 const base::Callback<void(GDataErrorCode, scoped_ptr<T>)>& callback, 73 GDataErrorCode error, 74 scoped_ptr<base::Value> value) { 75 DCHECK(!callback.is_null()); 76 77 if (!value) { 78 callback.Run(error, scoped_ptr<T>()); 79 return; 80 } 81 82 base::PostTaskAndReplyWithResult( 83 blocking_task_runner, 84 FROM_HERE, 85 base::Bind(&ParseJsonOnBlockingPool<T>, base::Passed(&value)), 86 base::Bind(&ParseJsonOnBlockingPoolAndRunAfterBlockingPoolTask<T>, 87 callback, error)); 88 } 89 90 // Parses the JSON value to FileResource instance and runs |callback| on the 91 // UI thread once parsing is done. 92 // This is customized version of ParseJsonAndRun defined above to adapt the 93 // remaining response type. 94 void ParseFileResourceWithUploadRangeAndRun( 95 const drive::UploadRangeCallback& callback, 96 const UploadRangeResponse& response, 97 scoped_ptr<base::Value> value) { 98 DCHECK(!callback.is_null()); 99 100 scoped_ptr<FileResource> file_resource; 101 if (value) { 102 file_resource = FileResource::CreateFrom(*value); 103 if (!file_resource) { 104 callback.Run( 105 UploadRangeResponse(GDATA_PARSE_ERROR, 106 response.start_position_received, 107 response.end_position_received), 108 scoped_ptr<FileResource>()); 109 return; 110 } 111 } 112 113 callback.Run(response, file_resource.Pass()); 114 } 115 116 } // namespace 117 118 namespace drive { 119 120 //============================ DriveApiDataRequest =========================== 121 122 DriveApiDataRequest::DriveApiDataRequest(RequestSender* sender, 123 const GetDataCallback& callback) 124 : GetDataRequest(sender, callback) { 125 } 126 127 DriveApiDataRequest::~DriveApiDataRequest() { 128 } 129 130 GURL DriveApiDataRequest::GetURL() const { 131 GURL url = GetURLInternal(); 132 if (!fields_.empty()) 133 url = net::AppendOrReplaceQueryParameter(url, "fields", fields_); 134 return url; 135 } 136 137 //=============================== FilesGetRequest ============================= 138 139 FilesGetRequest::FilesGetRequest( 140 RequestSender* sender, 141 const DriveApiUrlGenerator& url_generator, 142 const FileResourceCallback& callback) 143 : DriveApiDataRequest( 144 sender, 145 base::Bind(&ParseJsonAndRun<FileResource>, callback)), 146 url_generator_(url_generator) { 147 DCHECK(!callback.is_null()); 148 } 149 150 FilesGetRequest::~FilesGetRequest() {} 151 152 GURL FilesGetRequest::GetURLInternal() const { 153 return url_generator_.GetFilesGetUrl(file_id_); 154 } 155 156 //============================ FilesInsertRequest ============================ 157 158 FilesInsertRequest::FilesInsertRequest( 159 RequestSender* sender, 160 const DriveApiUrlGenerator& url_generator, 161 const FileResourceCallback& callback) 162 : DriveApiDataRequest( 163 sender, 164 base::Bind(&ParseJsonAndRun<FileResource>, callback)), 165 url_generator_(url_generator) { 166 DCHECK(!callback.is_null()); 167 } 168 169 FilesInsertRequest::~FilesInsertRequest() {} 170 171 net::URLFetcher::RequestType FilesInsertRequest::GetRequestType() const { 172 return net::URLFetcher::POST; 173 } 174 175 bool FilesInsertRequest::GetContentData(std::string* upload_content_type, 176 std::string* upload_content) { 177 *upload_content_type = kContentTypeApplicationJson; 178 179 base::DictionaryValue root; 180 181 if (!mime_type_.empty()) 182 root.SetString("mimeType", mime_type_); 183 184 if (!parents_.empty()) { 185 base::ListValue* parents_value = new base::ListValue; 186 for (size_t i = 0; i < parents_.size(); ++i) { 187 base::DictionaryValue* parent = new base::DictionaryValue; 188 parent->SetString("id", parents_[i]); 189 parents_value->Append(parent); 190 } 191 root.Set("parents", parents_value); 192 } 193 194 if (!title_.empty()) 195 root.SetString("title", title_); 196 197 base::JSONWriter::Write(&root, upload_content); 198 DVLOG(1) << "FilesInsert data: " << *upload_content_type << ", [" 199 << *upload_content << "]"; 200 return true; 201 } 202 203 GURL FilesInsertRequest::GetURLInternal() const { 204 return url_generator_.GetFilesInsertUrl(); 205 } 206 207 //============================== FilesPatchRequest ============================ 208 209 FilesPatchRequest::FilesPatchRequest( 210 RequestSender* sender, 211 const DriveApiUrlGenerator& url_generator, 212 const FileResourceCallback& callback) 213 : DriveApiDataRequest( 214 sender, 215 base::Bind(&ParseJsonAndRun<FileResource>, callback)), 216 url_generator_(url_generator), 217 set_modified_date_(false), 218 update_viewed_date_(true) { 219 DCHECK(!callback.is_null()); 220 } 221 222 FilesPatchRequest::~FilesPatchRequest() {} 223 224 net::URLFetcher::RequestType FilesPatchRequest::GetRequestType() const { 225 return net::URLFetcher::PATCH; 226 } 227 228 std::vector<std::string> FilesPatchRequest::GetExtraRequestHeaders() const { 229 std::vector<std::string> headers; 230 headers.push_back(util::kIfMatchAllHeader); 231 return headers; 232 } 233 234 GURL FilesPatchRequest::GetURLInternal() const { 235 return url_generator_.GetFilesPatchUrl( 236 file_id_, set_modified_date_, update_viewed_date_); 237 } 238 239 bool FilesPatchRequest::GetContentData(std::string* upload_content_type, 240 std::string* upload_content) { 241 if (title_.empty() && 242 modified_date_.is_null() && 243 last_viewed_by_me_date_.is_null() && 244 parents_.empty()) 245 return false; 246 247 *upload_content_type = kContentTypeApplicationJson; 248 249 base::DictionaryValue root; 250 if (!title_.empty()) 251 root.SetString("title", title_); 252 253 if (!modified_date_.is_null()) 254 root.SetString("modifiedDate", util::FormatTimeAsString(modified_date_)); 255 256 if (!last_viewed_by_me_date_.is_null()) { 257 root.SetString("lastViewedByMeDate", 258 util::FormatTimeAsString(last_viewed_by_me_date_)); 259 } 260 261 if (!parents_.empty()) { 262 base::ListValue* parents_value = new base::ListValue; 263 for (size_t i = 0; i < parents_.size(); ++i) { 264 base::DictionaryValue* parent = new base::DictionaryValue; 265 parent->SetString("id", parents_[i]); 266 parents_value->Append(parent); 267 } 268 root.Set("parents", parents_value); 269 } 270 271 base::JSONWriter::Write(&root, upload_content); 272 DVLOG(1) << "FilesPatch data: " << *upload_content_type << ", [" 273 << *upload_content << "]"; 274 return true; 275 } 276 277 //============================= FilesCopyRequest ============================== 278 279 FilesCopyRequest::FilesCopyRequest( 280 RequestSender* sender, 281 const DriveApiUrlGenerator& url_generator, 282 const FileResourceCallback& callback) 283 : DriveApiDataRequest( 284 sender, 285 base::Bind(&ParseJsonAndRun<FileResource>, callback)), 286 url_generator_(url_generator) { 287 DCHECK(!callback.is_null()); 288 } 289 290 FilesCopyRequest::~FilesCopyRequest() { 291 } 292 293 net::URLFetcher::RequestType FilesCopyRequest::GetRequestType() const { 294 return net::URLFetcher::POST; 295 } 296 297 GURL FilesCopyRequest::GetURLInternal() const { 298 return url_generator_.GetFilesCopyUrl(file_id_); 299 } 300 301 bool FilesCopyRequest::GetContentData(std::string* upload_content_type, 302 std::string* upload_content) { 303 if (parents_.empty() && title_.empty()) 304 return false; 305 306 *upload_content_type = kContentTypeApplicationJson; 307 308 base::DictionaryValue root; 309 310 if (!modified_date_.is_null()) 311 root.SetString("modifiedDate", util::FormatTimeAsString(modified_date_)); 312 313 if (!parents_.empty()) { 314 base::ListValue* parents_value = new base::ListValue; 315 for (size_t i = 0; i < parents_.size(); ++i) { 316 base::DictionaryValue* parent = new base::DictionaryValue; 317 parent->SetString("id", parents_[i]); 318 parents_value->Append(parent); 319 } 320 root.Set("parents", parents_value); 321 } 322 323 if (!title_.empty()) 324 root.SetString("title", title_); 325 326 base::JSONWriter::Write(&root, upload_content); 327 DVLOG(1) << "FilesCopy data: " << *upload_content_type << ", [" 328 << *upload_content << "]"; 329 return true; 330 } 331 332 //============================= FilesListRequest ============================= 333 334 FilesListRequest::FilesListRequest( 335 RequestSender* sender, 336 const DriveApiUrlGenerator& url_generator, 337 const FileListCallback& callback) 338 : DriveApiDataRequest( 339 sender, 340 base::Bind(&ParseJsonOnBlockingPoolAndRun<FileList>, 341 make_scoped_refptr(sender->blocking_task_runner()), 342 callback)), 343 url_generator_(url_generator), 344 max_results_(100) { 345 DCHECK(!callback.is_null()); 346 } 347 348 FilesListRequest::~FilesListRequest() {} 349 350 GURL FilesListRequest::GetURLInternal() const { 351 return url_generator_.GetFilesListUrl(max_results_, page_token_, q_); 352 } 353 354 //======================== FilesListNextPageRequest ========================= 355 356 FilesListNextPageRequest::FilesListNextPageRequest( 357 RequestSender* sender, 358 const FileListCallback& callback) 359 : DriveApiDataRequest( 360 sender, 361 base::Bind(&ParseJsonOnBlockingPoolAndRun<FileList>, 362 make_scoped_refptr(sender->blocking_task_runner()), 363 callback)) { 364 DCHECK(!callback.is_null()); 365 } 366 367 FilesListNextPageRequest::~FilesListNextPageRequest() { 368 } 369 370 GURL FilesListNextPageRequest::GetURLInternal() const { 371 return next_link_; 372 } 373 374 //============================ FilesDeleteRequest ============================= 375 376 FilesDeleteRequest::FilesDeleteRequest( 377 RequestSender* sender, 378 const DriveApiUrlGenerator& url_generator, 379 const EntryActionCallback& callback) 380 : EntryActionRequest(sender, callback), 381 url_generator_(url_generator) { 382 DCHECK(!callback.is_null()); 383 } 384 385 FilesDeleteRequest::~FilesDeleteRequest() {} 386 387 net::URLFetcher::RequestType FilesDeleteRequest::GetRequestType() const { 388 return net::URLFetcher::DELETE_REQUEST; 389 } 390 391 GURL FilesDeleteRequest::GetURL() const { 392 return url_generator_.GetFilesDeleteUrl(file_id_); 393 } 394 395 std::vector<std::string> FilesDeleteRequest::GetExtraRequestHeaders() const { 396 std::vector<std::string> headers( 397 EntryActionRequest::GetExtraRequestHeaders()); 398 headers.push_back(util::GenerateIfMatchHeader(etag_)); 399 return headers; 400 } 401 402 //============================ FilesTrashRequest ============================= 403 404 FilesTrashRequest::FilesTrashRequest( 405 RequestSender* sender, 406 const DriveApiUrlGenerator& url_generator, 407 const FileResourceCallback& callback) 408 : DriveApiDataRequest( 409 sender, 410 base::Bind(&ParseJsonAndRun<FileResource>, callback)), 411 url_generator_(url_generator) { 412 DCHECK(!callback.is_null()); 413 } 414 415 FilesTrashRequest::~FilesTrashRequest() {} 416 417 net::URLFetcher::RequestType FilesTrashRequest::GetRequestType() const { 418 return net::URLFetcher::POST; 419 } 420 421 GURL FilesTrashRequest::GetURLInternal() const { 422 return url_generator_.GetFilesTrashUrl(file_id_); 423 } 424 425 //============================== AboutGetRequest ============================= 426 427 AboutGetRequest::AboutGetRequest( 428 RequestSender* sender, 429 const DriveApiUrlGenerator& url_generator, 430 const AboutResourceCallback& callback) 431 : DriveApiDataRequest( 432 sender, 433 base::Bind(&ParseJsonAndRun<AboutResource>, callback)), 434 url_generator_(url_generator) { 435 DCHECK(!callback.is_null()); 436 } 437 438 AboutGetRequest::~AboutGetRequest() {} 439 440 GURL AboutGetRequest::GetURLInternal() const { 441 return url_generator_.GetAboutGetUrl(); 442 } 443 444 //============================ ChangesListRequest =========================== 445 446 ChangesListRequest::ChangesListRequest( 447 RequestSender* sender, 448 const DriveApiUrlGenerator& url_generator, 449 const ChangeListCallback& callback) 450 : DriveApiDataRequest( 451 sender, 452 base::Bind(&ParseJsonOnBlockingPoolAndRun<ChangeList>, 453 make_scoped_refptr(sender->blocking_task_runner()), 454 callback)), 455 url_generator_(url_generator), 456 include_deleted_(true), 457 max_results_(100), 458 start_change_id_(0) { 459 DCHECK(!callback.is_null()); 460 } 461 462 ChangesListRequest::~ChangesListRequest() {} 463 464 GURL ChangesListRequest::GetURLInternal() const { 465 return url_generator_.GetChangesListUrl( 466 include_deleted_, max_results_, page_token_, start_change_id_); 467 } 468 469 //======================== ChangesListNextPageRequest ========================= 470 471 ChangesListNextPageRequest::ChangesListNextPageRequest( 472 RequestSender* sender, 473 const ChangeListCallback& callback) 474 : DriveApiDataRequest( 475 sender, 476 base::Bind(&ParseJsonOnBlockingPoolAndRun<ChangeList>, 477 make_scoped_refptr(sender->blocking_task_runner()), 478 callback)) { 479 DCHECK(!callback.is_null()); 480 } 481 482 ChangesListNextPageRequest::~ChangesListNextPageRequest() { 483 } 484 485 GURL ChangesListNextPageRequest::GetURLInternal() const { 486 return next_link_; 487 } 488 489 //============================== AppsListRequest =========================== 490 491 AppsListRequest::AppsListRequest( 492 RequestSender* sender, 493 const DriveApiUrlGenerator& url_generator, 494 const AppListCallback& callback) 495 : DriveApiDataRequest( 496 sender, 497 base::Bind(&ParseJsonAndRun<AppList>, callback)), 498 url_generator_(url_generator) { 499 DCHECK(!callback.is_null()); 500 } 501 502 AppsListRequest::~AppsListRequest() {} 503 504 GURL AppsListRequest::GetURLInternal() const { 505 return url_generator_.GetAppsListUrl(); 506 } 507 508 //========================== ChildrenInsertRequest ============================ 509 510 ChildrenInsertRequest::ChildrenInsertRequest( 511 RequestSender* sender, 512 const DriveApiUrlGenerator& url_generator, 513 const EntryActionCallback& callback) 514 : EntryActionRequest(sender, callback), 515 url_generator_(url_generator) { 516 DCHECK(!callback.is_null()); 517 } 518 519 ChildrenInsertRequest::~ChildrenInsertRequest() {} 520 521 net::URLFetcher::RequestType ChildrenInsertRequest::GetRequestType() const { 522 return net::URLFetcher::POST; 523 } 524 525 GURL ChildrenInsertRequest::GetURL() const { 526 return url_generator_.GetChildrenInsertUrl(folder_id_); 527 } 528 529 bool ChildrenInsertRequest::GetContentData(std::string* upload_content_type, 530 std::string* upload_content) { 531 *upload_content_type = kContentTypeApplicationJson; 532 533 base::DictionaryValue root; 534 root.SetString("id", id_); 535 536 base::JSONWriter::Write(&root, upload_content); 537 DVLOG(1) << "InsertResource data: " << *upload_content_type << ", [" 538 << *upload_content << "]"; 539 return true; 540 } 541 542 //========================== ChildrenDeleteRequest ============================ 543 544 ChildrenDeleteRequest::ChildrenDeleteRequest( 545 RequestSender* sender, 546 const DriveApiUrlGenerator& url_generator, 547 const EntryActionCallback& callback) 548 : EntryActionRequest(sender, callback), 549 url_generator_(url_generator) { 550 DCHECK(!callback.is_null()); 551 } 552 553 ChildrenDeleteRequest::~ChildrenDeleteRequest() {} 554 555 net::URLFetcher::RequestType ChildrenDeleteRequest::GetRequestType() const { 556 return net::URLFetcher::DELETE_REQUEST; 557 } 558 559 GURL ChildrenDeleteRequest::GetURL() const { 560 return url_generator_.GetChildrenDeleteUrl(child_id_, folder_id_); 561 } 562 563 //======================= InitiateUploadNewFileRequest ======================= 564 565 InitiateUploadNewFileRequest::InitiateUploadNewFileRequest( 566 RequestSender* sender, 567 const DriveApiUrlGenerator& url_generator, 568 const std::string& content_type, 569 int64 content_length, 570 const std::string& parent_resource_id, 571 const std::string& title, 572 const InitiateUploadCallback& callback) 573 : InitiateUploadRequestBase(sender, 574 callback, 575 content_type, 576 content_length), 577 url_generator_(url_generator), 578 parent_resource_id_(parent_resource_id), 579 title_(title) { 580 } 581 582 InitiateUploadNewFileRequest::~InitiateUploadNewFileRequest() {} 583 584 GURL InitiateUploadNewFileRequest::GetURL() const { 585 return url_generator_.GetInitiateUploadNewFileUrl(); 586 } 587 588 net::URLFetcher::RequestType 589 InitiateUploadNewFileRequest::GetRequestType() const { 590 return net::URLFetcher::POST; 591 } 592 593 bool InitiateUploadNewFileRequest::GetContentData( 594 std::string* upload_content_type, 595 std::string* upload_content) { 596 *upload_content_type = kContentTypeApplicationJson; 597 598 base::DictionaryValue root; 599 root.SetString("title", title_); 600 601 // Fill parent link. 602 { 603 scoped_ptr<base::DictionaryValue> parent(new base::DictionaryValue); 604 parent->SetString("kind", kParentLinkKind); 605 parent->SetString("id", parent_resource_id_); 606 607 scoped_ptr<base::ListValue> parents(new base::ListValue); 608 parents->Append(parent.release()); 609 610 root.Set("parents", parents.release()); 611 } 612 613 base::JSONWriter::Write(&root, upload_content); 614 615 DVLOG(1) << "InitiateUploadNewFile data: " << *upload_content_type << ", [" 616 << *upload_content << "]"; 617 return true; 618 } 619 620 //===================== InitiateUploadExistingFileRequest ==================== 621 622 InitiateUploadExistingFileRequest::InitiateUploadExistingFileRequest( 623 RequestSender* sender, 624 const DriveApiUrlGenerator& url_generator, 625 const std::string& content_type, 626 int64 content_length, 627 const std::string& resource_id, 628 const std::string& etag, 629 const InitiateUploadCallback& callback) 630 : InitiateUploadRequestBase(sender, 631 callback, 632 content_type, 633 content_length), 634 url_generator_(url_generator), 635 resource_id_(resource_id), 636 etag_(etag) { 637 } 638 639 InitiateUploadExistingFileRequest::~InitiateUploadExistingFileRequest() {} 640 641 GURL InitiateUploadExistingFileRequest::GetURL() const { 642 return url_generator_.GetInitiateUploadExistingFileUrl(resource_id_); 643 } 644 645 net::URLFetcher::RequestType 646 InitiateUploadExistingFileRequest::GetRequestType() const { 647 return net::URLFetcher::PUT; 648 } 649 650 std::vector<std::string> 651 InitiateUploadExistingFileRequest::GetExtraRequestHeaders() const { 652 std::vector<std::string> headers( 653 InitiateUploadRequestBase::GetExtraRequestHeaders()); 654 headers.push_back(util::GenerateIfMatchHeader(etag_)); 655 return headers; 656 } 657 658 //============================ ResumeUploadRequest =========================== 659 660 ResumeUploadRequest::ResumeUploadRequest( 661 RequestSender* sender, 662 const GURL& upload_location, 663 int64 start_position, 664 int64 end_position, 665 int64 content_length, 666 const std::string& content_type, 667 const base::FilePath& local_file_path, 668 const UploadRangeCallback& callback, 669 const ProgressCallback& progress_callback) 670 : ResumeUploadRequestBase(sender, 671 upload_location, 672 start_position, 673 end_position, 674 content_length, 675 content_type, 676 local_file_path), 677 callback_(callback), 678 progress_callback_(progress_callback) { 679 DCHECK(!callback_.is_null()); 680 } 681 682 ResumeUploadRequest::~ResumeUploadRequest() {} 683 684 void ResumeUploadRequest::OnRangeRequestComplete( 685 const UploadRangeResponse& response, 686 scoped_ptr<base::Value> value) { 687 DCHECK(CalledOnValidThread()); 688 ParseFileResourceWithUploadRangeAndRun(callback_, response, value.Pass()); 689 } 690 691 void ResumeUploadRequest::OnURLFetchUploadProgress( 692 const net::URLFetcher* source, int64 current, int64 total) { 693 if (!progress_callback_.is_null()) 694 progress_callback_.Run(current, total); 695 } 696 697 //========================== GetUploadStatusRequest ========================== 698 699 GetUploadStatusRequest::GetUploadStatusRequest( 700 RequestSender* sender, 701 const GURL& upload_url, 702 int64 content_length, 703 const UploadRangeCallback& callback) 704 : GetUploadStatusRequestBase(sender, 705 upload_url, 706 content_length), 707 callback_(callback) { 708 DCHECK(!callback.is_null()); 709 } 710 711 GetUploadStatusRequest::~GetUploadStatusRequest() {} 712 713 void GetUploadStatusRequest::OnRangeRequestComplete( 714 const UploadRangeResponse& response, 715 scoped_ptr<base::Value> value) { 716 DCHECK(CalledOnValidThread()); 717 ParseFileResourceWithUploadRangeAndRun(callback_, response, value.Pass()); 718 } 719 720 //========================== DownloadFileRequest ========================== 721 722 DownloadFileRequest::DownloadFileRequest( 723 RequestSender* sender, 724 const DriveApiUrlGenerator& url_generator, 725 const std::string& resource_id, 726 const base::FilePath& output_file_path, 727 const DownloadActionCallback& download_action_callback, 728 const GetContentCallback& get_content_callback, 729 const ProgressCallback& progress_callback) 730 : DownloadFileRequestBase( 731 sender, 732 download_action_callback, 733 get_content_callback, 734 progress_callback, 735 url_generator.GenerateDownloadFileUrl(resource_id), 736 output_file_path) { 737 } 738 739 DownloadFileRequest::~DownloadFileRequest() { 740 } 741 742 } // namespace drive 743 } // namespace google_apis 744