| OLD | NEW |
| (Empty) |
| 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/fake_drive_service.h" | |
| 6 | |
| 7 #include <string> | |
| 8 | |
| 9 #include "base/files/file_util.h" | |
| 10 #include "base/json/json_string_value_serializer.h" | |
| 11 #include "base/logging.h" | |
| 12 #include "base/md5.h" | |
| 13 #include "base/strings/string_number_conversions.h" | |
| 14 #include "base/strings/string_split.h" | |
| 15 #include "base/strings/string_tokenizer.h" | |
| 16 #include "base/strings/string_util.h" | |
| 17 #include "base/strings/stringprintf.h" | |
| 18 #include "base/strings/utf_string_conversions.h" | |
| 19 #include "base/thread_task_runner_handle.h" | |
| 20 #include "base/values.h" | |
| 21 #include "chrome/browser/drive/drive_api_util.h" | |
| 22 #include "google_apis/drive/drive_api_parser.h" | |
| 23 #include "google_apis/drive/test_util.h" | |
| 24 #include "net/base/escape.h" | |
| 25 #include "net/base/url_util.h" | |
| 26 | |
| 27 using google_apis::AboutResource; | |
| 28 using google_apis::AboutResourceCallback; | |
| 29 using google_apis::AppList; | |
| 30 using google_apis::AppListCallback; | |
| 31 using google_apis::AuthStatusCallback; | |
| 32 using google_apis::AuthorizeAppCallback; | |
| 33 using google_apis::CancelCallback; | |
| 34 using google_apis::ChangeList; | |
| 35 using google_apis::ChangeListCallback; | |
| 36 using google_apis::ChangeResource; | |
| 37 using google_apis::DownloadActionCallback; | |
| 38 using google_apis::EntryActionCallback; | |
| 39 using google_apis::FileList; | |
| 40 using google_apis::FileListCallback; | |
| 41 using google_apis::FileResource; | |
| 42 using google_apis::FileResourceCallback; | |
| 43 using google_apis::DRIVE_FILE_ERROR; | |
| 44 using google_apis::DRIVE_NO_CONNECTION; | |
| 45 using google_apis::DRIVE_OTHER_ERROR; | |
| 46 using google_apis::DriveApiErrorCode; | |
| 47 using google_apis::GetContentCallback; | |
| 48 using google_apis::GetShareUrlCallback; | |
| 49 using google_apis::HTTP_BAD_REQUEST; | |
| 50 using google_apis::HTTP_CREATED; | |
| 51 using google_apis::HTTP_FORBIDDEN; | |
| 52 using google_apis::HTTP_NOT_FOUND; | |
| 53 using google_apis::HTTP_NO_CONTENT; | |
| 54 using google_apis::HTTP_PRECONDITION; | |
| 55 using google_apis::HTTP_RESUME_INCOMPLETE; | |
| 56 using google_apis::HTTP_SUCCESS; | |
| 57 using google_apis::InitiateUploadCallback; | |
| 58 using google_apis::ParentReference; | |
| 59 using google_apis::ProgressCallback; | |
| 60 using google_apis::UploadRangeResponse; | |
| 61 using google_apis::drive::UploadRangeCallback; | |
| 62 namespace test_util = google_apis::test_util; | |
| 63 | |
| 64 namespace drive { | |
| 65 namespace { | |
| 66 | |
| 67 // Returns true if the entry matches with the search query. | |
| 68 // Supports queries consist of following format. | |
| 69 // - Phrases quoted by double/single quotes | |
| 70 // - AND search for multiple words/phrases segmented by space | |
| 71 // - Limited attribute search. Only "title:" is supported. | |
| 72 bool EntryMatchWithQuery(const ChangeResource& entry, | |
| 73 const std::string& query) { | |
| 74 base::StringTokenizer tokenizer(query, " "); | |
| 75 tokenizer.set_quote_chars("\"'"); | |
| 76 while (tokenizer.GetNext()) { | |
| 77 std::string key, value; | |
| 78 const std::string& token = tokenizer.token(); | |
| 79 if (token.find(':') == std::string::npos) { | |
| 80 base::TrimString(token, "\"'", &value); | |
| 81 } else { | |
| 82 base::StringTokenizer key_value(token, ":"); | |
| 83 key_value.set_quote_chars("\"'"); | |
| 84 if (!key_value.GetNext()) | |
| 85 return false; | |
| 86 key = key_value.token(); | |
| 87 if (!key_value.GetNext()) | |
| 88 return false; | |
| 89 base::TrimString(key_value.token(), "\"'", &value); | |
| 90 } | |
| 91 | |
| 92 // TODO(peria): Deal with other attributes than title. | |
| 93 if (!key.empty() && key != "title") | |
| 94 return false; | |
| 95 // Search query in the title. | |
| 96 if (!entry.file() || | |
| 97 entry.file()->title().find(value) == std::string::npos) | |
| 98 return false; | |
| 99 } | |
| 100 return true; | |
| 101 } | |
| 102 | |
| 103 void ScheduleUploadRangeCallback(const UploadRangeCallback& callback, | |
| 104 int64 start_position, | |
| 105 int64 end_position, | |
| 106 DriveApiErrorCode error, | |
| 107 scoped_ptr<FileResource> entry) { | |
| 108 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 109 FROM_HERE, | |
| 110 base::Bind(callback, | |
| 111 UploadRangeResponse(error, | |
| 112 start_position, | |
| 113 end_position), | |
| 114 base::Passed(&entry))); | |
| 115 } | |
| 116 | |
| 117 void FileListCallbackAdapter(const FileListCallback& callback, | |
| 118 DriveApiErrorCode error, | |
| 119 scoped_ptr<ChangeList> change_list) { | |
| 120 scoped_ptr<FileList> file_list; | |
| 121 if (!change_list) { | |
| 122 callback.Run(error, file_list.Pass()); | |
| 123 return; | |
| 124 } | |
| 125 | |
| 126 file_list.reset(new FileList); | |
| 127 file_list->set_next_link(change_list->next_link()); | |
| 128 for (size_t i = 0; i < change_list->items().size(); ++i) { | |
| 129 const ChangeResource& entry = *change_list->items()[i]; | |
| 130 if (entry.file()) | |
| 131 file_list->mutable_items()->push_back(new FileResource(*entry.file())); | |
| 132 } | |
| 133 callback.Run(error, file_list.Pass()); | |
| 134 } | |
| 135 | |
| 136 bool UserHasWriteAccess(google_apis::drive::PermissionRole user_permission) { | |
| 137 switch (user_permission) { | |
| 138 case google_apis::drive::PERMISSION_ROLE_OWNER: | |
| 139 case google_apis::drive::PERMISSION_ROLE_WRITER: | |
| 140 return true; | |
| 141 case google_apis::drive::PERMISSION_ROLE_READER: | |
| 142 case google_apis::drive::PERMISSION_ROLE_COMMENTER: | |
| 143 break; | |
| 144 } | |
| 145 return false; | |
| 146 } | |
| 147 | |
| 148 void CallFileResouceCallback(const FileResourceCallback& callback, | |
| 149 const UploadRangeResponse& response, | |
| 150 scoped_ptr<FileResource> entry) { | |
| 151 callback.Run(response.code, entry.Pass()); | |
| 152 } | |
| 153 | |
| 154 struct CallResumeUpload { | |
| 155 CallResumeUpload() {} | |
| 156 ~CallResumeUpload() {} | |
| 157 | |
| 158 void Run(DriveApiErrorCode code, const GURL& upload_url) { | |
| 159 if (service) { | |
| 160 service->ResumeUpload( | |
| 161 upload_url, | |
| 162 /* start position */ 0, | |
| 163 /* end position */ content_length, | |
| 164 content_length, | |
| 165 content_type, | |
| 166 local_file_path, | |
| 167 base::Bind(&CallFileResouceCallback, callback), | |
| 168 progress_callback); | |
| 169 } | |
| 170 } | |
| 171 | |
| 172 base::WeakPtr<FakeDriveService> service; | |
| 173 int64 content_length; | |
| 174 std::string content_type; | |
| 175 base::FilePath local_file_path; | |
| 176 FileResourceCallback callback; | |
| 177 ProgressCallback progress_callback; | |
| 178 }; | |
| 179 | |
| 180 } // namespace | |
| 181 | |
| 182 struct FakeDriveService::EntryInfo { | |
| 183 EntryInfo() : user_permission(google_apis::drive::PERMISSION_ROLE_OWNER) {} | |
| 184 | |
| 185 google_apis::ChangeResource change_resource; | |
| 186 GURL share_url; | |
| 187 std::string content_data; | |
| 188 | |
| 189 // Behaves in the same way as "userPermission" described in | |
| 190 // https://developers.google.com/drive/v2/reference/files | |
| 191 google_apis::drive::PermissionRole user_permission; | |
| 192 }; | |
| 193 | |
| 194 struct FakeDriveService::UploadSession { | |
| 195 std::string content_type; | |
| 196 int64 content_length; | |
| 197 std::string parent_resource_id; | |
| 198 std::string resource_id; | |
| 199 std::string etag; | |
| 200 std::string title; | |
| 201 | |
| 202 int64 uploaded_size; | |
| 203 | |
| 204 UploadSession() | |
| 205 : content_length(0), | |
| 206 uploaded_size(0) {} | |
| 207 | |
| 208 UploadSession( | |
| 209 std::string content_type, | |
| 210 int64 content_length, | |
| 211 std::string parent_resource_id, | |
| 212 std::string resource_id, | |
| 213 std::string etag, | |
| 214 std::string title) | |
| 215 : content_type(content_type), | |
| 216 content_length(content_length), | |
| 217 parent_resource_id(parent_resource_id), | |
| 218 resource_id(resource_id), | |
| 219 etag(etag), | |
| 220 title(title), | |
| 221 uploaded_size(0) { | |
| 222 } | |
| 223 }; | |
| 224 | |
| 225 FakeDriveService::FakeDriveService() | |
| 226 : about_resource_(new AboutResource), | |
| 227 published_date_seq_(0), | |
| 228 next_upload_sequence_number_(0), | |
| 229 default_max_results_(0), | |
| 230 resource_id_count_(0), | |
| 231 file_list_load_count_(0), | |
| 232 change_list_load_count_(0), | |
| 233 directory_load_count_(0), | |
| 234 about_resource_load_count_(0), | |
| 235 app_list_load_count_(0), | |
| 236 blocked_file_list_load_count_(0), | |
| 237 offline_(false), | |
| 238 never_return_all_file_list_(false), | |
| 239 share_url_base_("https://share_url/"), | |
| 240 weak_ptr_factory_(this) { | |
| 241 about_resource_->set_largest_change_id(654321); | |
| 242 about_resource_->set_quota_bytes_total(9876543210); | |
| 243 about_resource_->set_quota_bytes_used_aggregate(6789012345); | |
| 244 about_resource_->set_root_folder_id(GetRootResourceId()); | |
| 245 } | |
| 246 | |
| 247 FakeDriveService::~FakeDriveService() { | |
| 248 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 249 STLDeleteValues(&entries_); | |
| 250 } | |
| 251 | |
| 252 bool FakeDriveService::LoadAppListForDriveApi( | |
| 253 const std::string& relative_path) { | |
| 254 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 255 | |
| 256 // Load JSON data, which must be a dictionary. | |
| 257 scoped_ptr<base::Value> value = test_util::LoadJSONFile(relative_path); | |
| 258 CHECK_EQ(base::Value::TYPE_DICTIONARY, value->GetType()); | |
| 259 app_info_value_.reset( | |
| 260 static_cast<base::DictionaryValue*>(value.release())); | |
| 261 return app_info_value_; | |
| 262 } | |
| 263 | |
| 264 void FakeDriveService::AddApp(const std::string& app_id, | |
| 265 const std::string& app_name, | |
| 266 const std::string& product_id, | |
| 267 const std::string& create_url, | |
| 268 bool is_removable) { | |
| 269 if (app_json_template_.empty()) { | |
| 270 base::FilePath path = | |
| 271 test_util::GetTestFilePath("drive/applist_app_template.json"); | |
| 272 CHECK(base::ReadFileToString(path, &app_json_template_)); | |
| 273 } | |
| 274 | |
| 275 std::string app_json = app_json_template_; | |
| 276 base::ReplaceSubstringsAfterOffset(&app_json, 0, "$AppId", app_id); | |
| 277 base::ReplaceSubstringsAfterOffset(&app_json, 0, "$AppName", app_name); | |
| 278 base::ReplaceSubstringsAfterOffset(&app_json, 0, "$ProductId", product_id); | |
| 279 base::ReplaceSubstringsAfterOffset(&app_json, 0, "$CreateUrl", create_url); | |
| 280 base::ReplaceSubstringsAfterOffset( | |
| 281 &app_json, 0, "$Removable", is_removable ? "true" : "false"); | |
| 282 | |
| 283 JSONStringValueDeserializer json(app_json); | |
| 284 std::string error_message; | |
| 285 scoped_ptr<base::Value> value(json.Deserialize(NULL, &error_message)); | |
| 286 CHECK_EQ(base::Value::TYPE_DICTIONARY, value->GetType()); | |
| 287 | |
| 288 base::ListValue* item_list; | |
| 289 CHECK(app_info_value_->GetListWithoutPathExpansion("items", &item_list)); | |
| 290 item_list->Append(value.release()); | |
| 291 } | |
| 292 | |
| 293 void FakeDriveService::RemoveAppByProductId(const std::string& product_id) { | |
| 294 base::ListValue* item_list; | |
| 295 CHECK(app_info_value_->GetListWithoutPathExpansion("items", &item_list)); | |
| 296 for (size_t i = 0; i < item_list->GetSize(); ++i) { | |
| 297 base::DictionaryValue* item; | |
| 298 CHECK(item_list->GetDictionary(i, &item)); | |
| 299 const char kKeyProductId[] = "productId"; | |
| 300 std::string item_product_id; | |
| 301 if (item->GetStringWithoutPathExpansion(kKeyProductId, &item_product_id) && | |
| 302 product_id == item_product_id) { | |
| 303 item_list->Remove(i, NULL); | |
| 304 return; | |
| 305 } | |
| 306 } | |
| 307 } | |
| 308 | |
| 309 bool FakeDriveService::HasApp(const std::string& app_id) const { | |
| 310 base::ListValue* item_list; | |
| 311 CHECK(app_info_value_->GetListWithoutPathExpansion("items", &item_list)); | |
| 312 for (size_t i = 0; i < item_list->GetSize(); ++i) { | |
| 313 base::DictionaryValue* item; | |
| 314 CHECK(item_list->GetDictionary(i, &item)); | |
| 315 const char kKeyId[] = "id"; | |
| 316 std::string item_id; | |
| 317 if (item->GetStringWithoutPathExpansion(kKeyId, &item_id) && | |
| 318 item_id == app_id) { | |
| 319 return true; | |
| 320 } | |
| 321 } | |
| 322 | |
| 323 return false; | |
| 324 } | |
| 325 | |
| 326 void FakeDriveService::SetQuotaValue(int64 used, int64 total) { | |
| 327 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 328 | |
| 329 about_resource_->set_quota_bytes_used_aggregate(used); | |
| 330 about_resource_->set_quota_bytes_total(total); | |
| 331 } | |
| 332 | |
| 333 GURL FakeDriveService::GetFakeLinkUrl(const std::string& resource_id) { | |
| 334 return GURL("https://fake_server/" + net::EscapePath(resource_id)); | |
| 335 } | |
| 336 | |
| 337 void FakeDriveService::Initialize(const std::string& account_id) { | |
| 338 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 339 } | |
| 340 | |
| 341 void FakeDriveService::AddObserver(DriveServiceObserver* observer) { | |
| 342 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 343 } | |
| 344 | |
| 345 void FakeDriveService::RemoveObserver(DriveServiceObserver* observer) { | |
| 346 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 347 } | |
| 348 | |
| 349 bool FakeDriveService::CanSendRequest() const { | |
| 350 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 351 return true; | |
| 352 } | |
| 353 | |
| 354 bool FakeDriveService::HasAccessToken() const { | |
| 355 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 356 return true; | |
| 357 } | |
| 358 | |
| 359 void FakeDriveService::RequestAccessToken(const AuthStatusCallback& callback) { | |
| 360 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 361 DCHECK(!callback.is_null()); | |
| 362 callback.Run(google_apis::HTTP_NOT_MODIFIED, "fake_access_token"); | |
| 363 } | |
| 364 | |
| 365 bool FakeDriveService::HasRefreshToken() const { | |
| 366 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 367 return true; | |
| 368 } | |
| 369 | |
| 370 void FakeDriveService::ClearAccessToken() { | |
| 371 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 372 } | |
| 373 | |
| 374 void FakeDriveService::ClearRefreshToken() { | |
| 375 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 376 } | |
| 377 | |
| 378 std::string FakeDriveService::GetRootResourceId() const { | |
| 379 return "fake_root"; | |
| 380 } | |
| 381 | |
| 382 CancelCallback FakeDriveService::GetAllFileList( | |
| 383 const FileListCallback& callback) { | |
| 384 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 385 DCHECK(!callback.is_null()); | |
| 386 | |
| 387 if (never_return_all_file_list_) { | |
| 388 ++blocked_file_list_load_count_; | |
| 389 return CancelCallback(); | |
| 390 } | |
| 391 | |
| 392 GetChangeListInternal(0, // start changestamp | |
| 393 std::string(), // empty search query | |
| 394 std::string(), // no directory resource id, | |
| 395 0, // start offset | |
| 396 default_max_results_, | |
| 397 &file_list_load_count_, | |
| 398 base::Bind(&FileListCallbackAdapter, callback)); | |
| 399 return CancelCallback(); | |
| 400 } | |
| 401 | |
| 402 CancelCallback FakeDriveService::GetFileListInDirectory( | |
| 403 const std::string& directory_resource_id, | |
| 404 const FileListCallback& callback) { | |
| 405 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 406 DCHECK(!directory_resource_id.empty()); | |
| 407 DCHECK(!callback.is_null()); | |
| 408 | |
| 409 GetChangeListInternal(0, // start changestamp | |
| 410 std::string(), // empty search query | |
| 411 directory_resource_id, | |
| 412 0, // start offset | |
| 413 default_max_results_, | |
| 414 &directory_load_count_, | |
| 415 base::Bind(&FileListCallbackAdapter, callback)); | |
| 416 return CancelCallback(); | |
| 417 } | |
| 418 | |
| 419 CancelCallback FakeDriveService::Search( | |
| 420 const std::string& search_query, | |
| 421 const FileListCallback& callback) { | |
| 422 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 423 DCHECK(!search_query.empty()); | |
| 424 DCHECK(!callback.is_null()); | |
| 425 | |
| 426 GetChangeListInternal(0, // start changestamp | |
| 427 search_query, | |
| 428 std::string(), // no directory resource id, | |
| 429 0, // start offset | |
| 430 default_max_results_, | |
| 431 NULL, | |
| 432 base::Bind(&FileListCallbackAdapter, callback)); | |
| 433 return CancelCallback(); | |
| 434 } | |
| 435 | |
| 436 CancelCallback FakeDriveService::SearchByTitle( | |
| 437 const std::string& title, | |
| 438 const std::string& directory_resource_id, | |
| 439 const FileListCallback& callback) { | |
| 440 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 441 DCHECK(!title.empty()); | |
| 442 DCHECK(!callback.is_null()); | |
| 443 | |
| 444 // Note: the search implementation here doesn't support quotation unescape, | |
| 445 // so don't escape here. | |
| 446 GetChangeListInternal(0, // start changestamp | |
| 447 base::StringPrintf("title:'%s'", title.c_str()), | |
| 448 directory_resource_id, | |
| 449 0, // start offset | |
| 450 default_max_results_, | |
| 451 NULL, | |
| 452 base::Bind(&FileListCallbackAdapter, callback)); | |
| 453 return CancelCallback(); | |
| 454 } | |
| 455 | |
| 456 CancelCallback FakeDriveService::GetChangeList( | |
| 457 int64 start_changestamp, | |
| 458 const ChangeListCallback& callback) { | |
| 459 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 460 DCHECK(!callback.is_null()); | |
| 461 | |
| 462 GetChangeListInternal(start_changestamp, | |
| 463 std::string(), // empty search query | |
| 464 std::string(), // no directory resource id, | |
| 465 0, // start offset | |
| 466 default_max_results_, | |
| 467 &change_list_load_count_, | |
| 468 callback); | |
| 469 return CancelCallback(); | |
| 470 } | |
| 471 | |
| 472 CancelCallback FakeDriveService::GetRemainingChangeList( | |
| 473 const GURL& next_link, | |
| 474 const ChangeListCallback& callback) { | |
| 475 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 476 DCHECK(!next_link.is_empty()); | |
| 477 DCHECK(!callback.is_null()); | |
| 478 | |
| 479 // "changestamp", "q", "parent" and "start-offset" are parameters to | |
| 480 // implement "paging" of the result on FakeDriveService. | |
| 481 // The URL should be the one filled in GetChangeListInternal of the | |
| 482 // previous method invocation, so it should start with "http://localhost/?". | |
| 483 // See also GetChangeListInternal. | |
| 484 DCHECK_EQ(next_link.host(), "localhost"); | |
| 485 DCHECK_EQ(next_link.path(), "/"); | |
| 486 | |
| 487 int64 start_changestamp = 0; | |
| 488 std::string search_query; | |
| 489 std::string directory_resource_id; | |
| 490 int start_offset = 0; | |
| 491 int max_results = default_max_results_; | |
| 492 base::StringPairs parameters; | |
| 493 if (base::SplitStringIntoKeyValuePairs( | |
| 494 next_link.query(), '=', '&', ¶meters)) { | |
| 495 for (size_t i = 0; i < parameters.size(); ++i) { | |
| 496 if (parameters[i].first == "changestamp") { | |
| 497 base::StringToInt64(parameters[i].second, &start_changestamp); | |
| 498 } else if (parameters[i].first == "q") { | |
| 499 search_query = | |
| 500 net::UnescapeURLComponent(parameters[i].second, | |
| 501 net::UnescapeRule::URL_SPECIAL_CHARS); | |
| 502 } else if (parameters[i].first == "parent") { | |
| 503 directory_resource_id = | |
| 504 net::UnescapeURLComponent(parameters[i].second, | |
| 505 net::UnescapeRule::URL_SPECIAL_CHARS); | |
| 506 } else if (parameters[i].first == "start-offset") { | |
| 507 base::StringToInt(parameters[i].second, &start_offset); | |
| 508 } else if (parameters[i].first == "max-results") { | |
| 509 base::StringToInt(parameters[i].second, &max_results); | |
| 510 } | |
| 511 } | |
| 512 } | |
| 513 | |
| 514 GetChangeListInternal(start_changestamp, search_query, directory_resource_id, | |
| 515 start_offset, max_results, NULL, callback); | |
| 516 return CancelCallback(); | |
| 517 } | |
| 518 | |
| 519 CancelCallback FakeDriveService::GetRemainingFileList( | |
| 520 const GURL& next_link, | |
| 521 const FileListCallback& callback) { | |
| 522 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 523 DCHECK(!next_link.is_empty()); | |
| 524 DCHECK(!callback.is_null()); | |
| 525 | |
| 526 return GetRemainingChangeList( | |
| 527 next_link, base::Bind(&FileListCallbackAdapter, callback)); | |
| 528 } | |
| 529 | |
| 530 CancelCallback FakeDriveService::GetFileResource( | |
| 531 const std::string& resource_id, | |
| 532 const FileResourceCallback& callback) { | |
| 533 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 534 DCHECK(!callback.is_null()); | |
| 535 | |
| 536 if (offline_) { | |
| 537 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 538 FROM_HERE, | |
| 539 base::Bind(callback, | |
| 540 DRIVE_NO_CONNECTION, | |
| 541 base::Passed(scoped_ptr<FileResource>()))); | |
| 542 return CancelCallback(); | |
| 543 } | |
| 544 | |
| 545 EntryInfo* entry = FindEntryByResourceId(resource_id); | |
| 546 if (entry && entry->change_resource.file()) { | |
| 547 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 548 FROM_HERE, | |
| 549 base::Bind(callback, HTTP_SUCCESS, base::Passed(make_scoped_ptr( | |
| 550 new FileResource(*entry->change_resource.file()))))); | |
| 551 return CancelCallback(); | |
| 552 } | |
| 553 | |
| 554 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 555 FROM_HERE, | |
| 556 base::Bind(callback, HTTP_NOT_FOUND, | |
| 557 base::Passed(scoped_ptr<FileResource>()))); | |
| 558 return CancelCallback(); | |
| 559 } | |
| 560 | |
| 561 CancelCallback FakeDriveService::GetShareUrl( | |
| 562 const std::string& resource_id, | |
| 563 const GURL& /* embed_origin */, | |
| 564 const GetShareUrlCallback& callback) { | |
| 565 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 566 DCHECK(!callback.is_null()); | |
| 567 | |
| 568 if (offline_) { | |
| 569 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 570 FROM_HERE, | |
| 571 base::Bind(callback, | |
| 572 DRIVE_NO_CONNECTION, | |
| 573 GURL())); | |
| 574 return CancelCallback(); | |
| 575 } | |
| 576 | |
| 577 EntryInfo* entry = FindEntryByResourceId(resource_id); | |
| 578 if (entry) { | |
| 579 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 580 FROM_HERE, | |
| 581 base::Bind(callback, HTTP_SUCCESS, entry->share_url)); | |
| 582 return CancelCallback(); | |
| 583 } | |
| 584 | |
| 585 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 586 FROM_HERE, | |
| 587 base::Bind(callback, HTTP_NOT_FOUND, GURL())); | |
| 588 return CancelCallback(); | |
| 589 } | |
| 590 | |
| 591 CancelCallback FakeDriveService::GetAboutResource( | |
| 592 const AboutResourceCallback& callback) { | |
| 593 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 594 DCHECK(!callback.is_null()); | |
| 595 | |
| 596 if (offline_) { | |
| 597 scoped_ptr<AboutResource> null; | |
| 598 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 599 FROM_HERE, | |
| 600 base::Bind(callback, | |
| 601 DRIVE_NO_CONNECTION, base::Passed(&null))); | |
| 602 return CancelCallback(); | |
| 603 } | |
| 604 | |
| 605 ++about_resource_load_count_; | |
| 606 scoped_ptr<AboutResource> about_resource(new AboutResource(*about_resource_)); | |
| 607 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 608 FROM_HERE, | |
| 609 base::Bind(callback, | |
| 610 HTTP_SUCCESS, base::Passed(&about_resource))); | |
| 611 return CancelCallback(); | |
| 612 } | |
| 613 | |
| 614 CancelCallback FakeDriveService::GetAppList(const AppListCallback& callback) { | |
| 615 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 616 DCHECK(!callback.is_null()); | |
| 617 DCHECK(app_info_value_); | |
| 618 | |
| 619 if (offline_) { | |
| 620 scoped_ptr<AppList> null; | |
| 621 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 622 FROM_HERE, | |
| 623 base::Bind(callback, | |
| 624 DRIVE_NO_CONNECTION, | |
| 625 base::Passed(&null))); | |
| 626 return CancelCallback(); | |
| 627 } | |
| 628 | |
| 629 ++app_list_load_count_; | |
| 630 scoped_ptr<AppList> app_list(AppList::CreateFrom(*app_info_value_)); | |
| 631 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 632 FROM_HERE, | |
| 633 base::Bind(callback, HTTP_SUCCESS, base::Passed(&app_list))); | |
| 634 return CancelCallback(); | |
| 635 } | |
| 636 | |
| 637 CancelCallback FakeDriveService::DeleteResource( | |
| 638 const std::string& resource_id, | |
| 639 const std::string& etag, | |
| 640 const EntryActionCallback& callback) { | |
| 641 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 642 DCHECK(!callback.is_null()); | |
| 643 | |
| 644 if (offline_) { | |
| 645 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 646 FROM_HERE, base::Bind(callback, DRIVE_NO_CONNECTION)); | |
| 647 return CancelCallback(); | |
| 648 } | |
| 649 | |
| 650 EntryInfo* entry = FindEntryByResourceId(resource_id); | |
| 651 if (!entry) { | |
| 652 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 653 FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND)); | |
| 654 return CancelCallback(); | |
| 655 } | |
| 656 | |
| 657 ChangeResource* change = &entry->change_resource; | |
| 658 const FileResource* file = change->file(); | |
| 659 if (change->is_deleted()) { | |
| 660 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 661 FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND)); | |
| 662 return CancelCallback(); | |
| 663 } | |
| 664 | |
| 665 if (!etag.empty() && etag != file->etag()) { | |
| 666 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 667 FROM_HERE, base::Bind(callback, HTTP_PRECONDITION)); | |
| 668 return CancelCallback(); | |
| 669 } | |
| 670 | |
| 671 if (entry->user_permission != google_apis::drive::PERMISSION_ROLE_OWNER) { | |
| 672 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 673 FROM_HERE, base::Bind(callback, HTTP_FORBIDDEN)); | |
| 674 return CancelCallback(); | |
| 675 } | |
| 676 | |
| 677 change->set_deleted(true); | |
| 678 AddNewChangestamp(change); | |
| 679 change->set_file(scoped_ptr<FileResource>()); | |
| 680 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 681 FROM_HERE, base::Bind(callback, HTTP_NO_CONTENT)); | |
| 682 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 683 FROM_HERE, | |
| 684 base::Bind(&FakeDriveService::NotifyObservers, | |
| 685 weak_ptr_factory_.GetWeakPtr())); | |
| 686 return CancelCallback(); | |
| 687 } | |
| 688 | |
| 689 CancelCallback FakeDriveService::TrashResource( | |
| 690 const std::string& resource_id, | |
| 691 const EntryActionCallback& callback) { | |
| 692 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 693 DCHECK(!callback.is_null()); | |
| 694 | |
| 695 if (offline_) { | |
| 696 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 697 FROM_HERE, base::Bind(callback, DRIVE_NO_CONNECTION)); | |
| 698 return CancelCallback(); | |
| 699 } | |
| 700 | |
| 701 EntryInfo* entry = FindEntryByResourceId(resource_id); | |
| 702 if (!entry) { | |
| 703 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 704 FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND)); | |
| 705 return CancelCallback(); | |
| 706 } | |
| 707 | |
| 708 ChangeResource* change = &entry->change_resource; | |
| 709 FileResource* file = change->mutable_file(); | |
| 710 if (change->is_deleted() || file->labels().is_trashed()) { | |
| 711 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 712 FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND)); | |
| 713 return CancelCallback(); | |
| 714 } | |
| 715 | |
| 716 if (entry->user_permission != google_apis::drive::PERMISSION_ROLE_OWNER) { | |
| 717 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 718 FROM_HERE, base::Bind(callback, HTTP_FORBIDDEN)); | |
| 719 return CancelCallback(); | |
| 720 } | |
| 721 | |
| 722 file->mutable_labels()->set_trashed(true); | |
| 723 AddNewChangestamp(change); | |
| 724 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 725 FROM_HERE, base::Bind(callback, HTTP_SUCCESS)); | |
| 726 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 727 FROM_HERE, | |
| 728 base::Bind(&FakeDriveService::NotifyObservers, | |
| 729 weak_ptr_factory_.GetWeakPtr())); | |
| 730 return CancelCallback(); | |
| 731 } | |
| 732 | |
| 733 CancelCallback FakeDriveService::DownloadFile( | |
| 734 const base::FilePath& local_cache_path, | |
| 735 const std::string& resource_id, | |
| 736 const DownloadActionCallback& download_action_callback, | |
| 737 const GetContentCallback& get_content_callback, | |
| 738 const ProgressCallback& progress_callback) { | |
| 739 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 740 DCHECK(!download_action_callback.is_null()); | |
| 741 | |
| 742 if (offline_) { | |
| 743 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 744 FROM_HERE, | |
| 745 base::Bind(download_action_callback, | |
| 746 DRIVE_NO_CONNECTION, | |
| 747 base::FilePath())); | |
| 748 return CancelCallback(); | |
| 749 } | |
| 750 | |
| 751 EntryInfo* entry = FindEntryByResourceId(resource_id); | |
| 752 if (!entry || entry->change_resource.file()->IsHostedDocument()) { | |
| 753 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 754 FROM_HERE, | |
| 755 base::Bind(download_action_callback, HTTP_NOT_FOUND, base::FilePath())); | |
| 756 return CancelCallback(); | |
| 757 } | |
| 758 | |
| 759 const FileResource* file = entry->change_resource.file(); | |
| 760 const std::string& content_data = entry->content_data; | |
| 761 int64 file_size = file->file_size(); | |
| 762 DCHECK_EQ(static_cast<size_t>(file_size), content_data.size()); | |
| 763 | |
| 764 if (!get_content_callback.is_null()) { | |
| 765 const int64 kBlockSize = 5; | |
| 766 for (int64 i = 0; i < file_size; i += kBlockSize) { | |
| 767 const int64 size = std::min(kBlockSize, file_size - i); | |
| 768 scoped_ptr<std::string> content_for_callback( | |
| 769 new std::string(content_data.substr(i, size))); | |
| 770 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 771 FROM_HERE, | |
| 772 base::Bind(get_content_callback, HTTP_SUCCESS, | |
| 773 base::Passed(&content_for_callback))); | |
| 774 } | |
| 775 } | |
| 776 | |
| 777 if (!test_util::WriteStringToFile(local_cache_path, content_data)) { | |
| 778 // Failed to write the content. | |
| 779 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 780 FROM_HERE, | |
| 781 base::Bind(download_action_callback, | |
| 782 DRIVE_FILE_ERROR, base::FilePath())); | |
| 783 return CancelCallback(); | |
| 784 } | |
| 785 | |
| 786 if (!progress_callback.is_null()) { | |
| 787 // See also the comment in ResumeUpload(). For testing that clients | |
| 788 // can handle the case progress_callback is called multiple times, | |
| 789 // here we invoke the callback twice. | |
| 790 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 791 FROM_HERE, | |
| 792 base::Bind(progress_callback, file_size / 2, file_size)); | |
| 793 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 794 FROM_HERE, | |
| 795 base::Bind(progress_callback, file_size, file_size)); | |
| 796 } | |
| 797 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 798 FROM_HERE, | |
| 799 base::Bind(download_action_callback, | |
| 800 HTTP_SUCCESS, | |
| 801 local_cache_path)); | |
| 802 return CancelCallback(); | |
| 803 } | |
| 804 | |
| 805 CancelCallback FakeDriveService::CopyResource( | |
| 806 const std::string& resource_id, | |
| 807 const std::string& in_parent_resource_id, | |
| 808 const std::string& new_title, | |
| 809 const base::Time& last_modified, | |
| 810 const FileResourceCallback& callback) { | |
| 811 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 812 DCHECK(!callback.is_null()); | |
| 813 | |
| 814 if (offline_) { | |
| 815 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 816 FROM_HERE, | |
| 817 base::Bind(callback, | |
| 818 DRIVE_NO_CONNECTION, | |
| 819 base::Passed(scoped_ptr<FileResource>()))); | |
| 820 return CancelCallback(); | |
| 821 } | |
| 822 | |
| 823 const std::string& parent_resource_id = in_parent_resource_id.empty() ? | |
| 824 GetRootResourceId() : in_parent_resource_id; | |
| 825 | |
| 826 EntryInfo* entry = FindEntryByResourceId(resource_id); | |
| 827 if (!entry) { | |
| 828 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 829 FROM_HERE, | |
| 830 base::Bind(callback, HTTP_NOT_FOUND, | |
| 831 base::Passed(scoped_ptr<FileResource>()))); | |
| 832 return CancelCallback(); | |
| 833 } | |
| 834 | |
| 835 // Make a copy and set the new resource ID and the new title. | |
| 836 scoped_ptr<EntryInfo> copied_entry(new EntryInfo); | |
| 837 copied_entry->content_data = entry->content_data; | |
| 838 copied_entry->share_url = entry->share_url; | |
| 839 copied_entry->change_resource.set_file( | |
| 840 make_scoped_ptr(new FileResource(*entry->change_resource.file()))); | |
| 841 | |
| 842 ChangeResource* new_change = &copied_entry->change_resource; | |
| 843 FileResource* new_file = new_change->mutable_file(); | |
| 844 const std::string new_resource_id = GetNewResourceId(); | |
| 845 new_change->set_file_id(new_resource_id); | |
| 846 new_file->set_file_id(new_resource_id); | |
| 847 new_file->set_title(new_title); | |
| 848 | |
| 849 ParentReference parent; | |
| 850 parent.set_file_id(parent_resource_id); | |
| 851 parent.set_parent_link(GetFakeLinkUrl(parent_resource_id)); | |
| 852 std::vector<ParentReference> parents; | |
| 853 parents.push_back(parent); | |
| 854 *new_file->mutable_parents() = parents; | |
| 855 | |
| 856 if (!last_modified.is_null()) | |
| 857 new_file->set_modified_date(last_modified); | |
| 858 | |
| 859 AddNewChangestamp(new_change); | |
| 860 UpdateETag(new_file); | |
| 861 | |
| 862 // Add the new entry to the map. | |
| 863 entries_[new_resource_id] = copied_entry.release(); | |
| 864 | |
| 865 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 866 FROM_HERE, | |
| 867 base::Bind(callback, | |
| 868 HTTP_SUCCESS, | |
| 869 base::Passed(make_scoped_ptr(new FileResource(*new_file))))); | |
| 870 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 871 FROM_HERE, | |
| 872 base::Bind(&FakeDriveService::NotifyObservers, | |
| 873 weak_ptr_factory_.GetWeakPtr())); | |
| 874 return CancelCallback(); | |
| 875 } | |
| 876 | |
| 877 CancelCallback FakeDriveService::UpdateResource( | |
| 878 const std::string& resource_id, | |
| 879 const std::string& parent_resource_id, | |
| 880 const std::string& new_title, | |
| 881 const base::Time& last_modified, | |
| 882 const base::Time& last_viewed_by_me, | |
| 883 const google_apis::drive::Properties& properties, | |
| 884 const google_apis::FileResourceCallback& callback) { | |
| 885 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 886 DCHECK(!callback.is_null()); | |
| 887 | |
| 888 if (offline_) { | |
| 889 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 890 FROM_HERE, base::Bind(callback, DRIVE_NO_CONNECTION, | |
| 891 base::Passed(scoped_ptr<FileResource>()))); | |
| 892 return CancelCallback(); | |
| 893 } | |
| 894 | |
| 895 EntryInfo* entry = FindEntryByResourceId(resource_id); | |
| 896 if (!entry) { | |
| 897 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 898 FROM_HERE, | |
| 899 base::Bind(callback, HTTP_NOT_FOUND, | |
| 900 base::Passed(scoped_ptr<FileResource>()))); | |
| 901 return CancelCallback(); | |
| 902 } | |
| 903 | |
| 904 if (!UserHasWriteAccess(entry->user_permission)) { | |
| 905 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 906 FROM_HERE, | |
| 907 base::Bind(callback, HTTP_FORBIDDEN, | |
| 908 base::Passed(scoped_ptr<FileResource>()))); | |
| 909 return CancelCallback(); | |
| 910 } | |
| 911 | |
| 912 ChangeResource* change = &entry->change_resource; | |
| 913 FileResource* file = change->mutable_file(); | |
| 914 | |
| 915 if (!new_title.empty()) | |
| 916 file->set_title(new_title); | |
| 917 | |
| 918 // Set parent if necessary. | |
| 919 if (!parent_resource_id.empty()) { | |
| 920 ParentReference parent; | |
| 921 parent.set_file_id(parent_resource_id); | |
| 922 parent.set_parent_link(GetFakeLinkUrl(parent_resource_id)); | |
| 923 | |
| 924 std::vector<ParentReference> parents; | |
| 925 parents.push_back(parent); | |
| 926 *file->mutable_parents() = parents; | |
| 927 } | |
| 928 | |
| 929 if (!last_modified.is_null()) | |
| 930 file->set_modified_date(last_modified); | |
| 931 | |
| 932 if (!last_viewed_by_me.is_null()) | |
| 933 file->set_last_viewed_by_me_date(last_viewed_by_me); | |
| 934 | |
| 935 AddNewChangestamp(change); | |
| 936 UpdateETag(file); | |
| 937 | |
| 938 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 939 FROM_HERE, | |
| 940 base::Bind(callback, HTTP_SUCCESS, | |
| 941 base::Passed(make_scoped_ptr(new FileResource(*file))))); | |
| 942 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 943 FROM_HERE, | |
| 944 base::Bind(&FakeDriveService::NotifyObservers, | |
| 945 weak_ptr_factory_.GetWeakPtr())); | |
| 946 return CancelCallback(); | |
| 947 } | |
| 948 | |
| 949 CancelCallback FakeDriveService::AddResourceToDirectory( | |
| 950 const std::string& parent_resource_id, | |
| 951 const std::string& resource_id, | |
| 952 const EntryActionCallback& callback) { | |
| 953 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 954 DCHECK(!callback.is_null()); | |
| 955 | |
| 956 if (offline_) { | |
| 957 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 958 FROM_HERE, base::Bind(callback, DRIVE_NO_CONNECTION)); | |
| 959 return CancelCallback(); | |
| 960 } | |
| 961 | |
| 962 EntryInfo* entry = FindEntryByResourceId(resource_id); | |
| 963 if (!entry) { | |
| 964 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 965 FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND)); | |
| 966 return CancelCallback(); | |
| 967 } | |
| 968 | |
| 969 ChangeResource* change = &entry->change_resource; | |
| 970 // On the real Drive server, resources do not necessary shape a tree | |
| 971 // structure. That is, each resource can have multiple parent. | |
| 972 // We mimic the behavior here; AddResourceToDirectoy just adds | |
| 973 // one more parent, not overwriting old ones. | |
| 974 ParentReference parent; | |
| 975 parent.set_file_id(parent_resource_id); | |
| 976 parent.set_parent_link(GetFakeLinkUrl(parent_resource_id)); | |
| 977 change->mutable_file()->mutable_parents()->push_back(parent); | |
| 978 | |
| 979 AddNewChangestamp(change); | |
| 980 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 981 FROM_HERE, base::Bind(callback, HTTP_SUCCESS)); | |
| 982 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 983 FROM_HERE, | |
| 984 base::Bind(&FakeDriveService::NotifyObservers, | |
| 985 weak_ptr_factory_.GetWeakPtr())); | |
| 986 return CancelCallback(); | |
| 987 } | |
| 988 | |
| 989 CancelCallback FakeDriveService::RemoveResourceFromDirectory( | |
| 990 const std::string& parent_resource_id, | |
| 991 const std::string& resource_id, | |
| 992 const EntryActionCallback& callback) { | |
| 993 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 994 DCHECK(!callback.is_null()); | |
| 995 | |
| 996 if (offline_) { | |
| 997 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 998 FROM_HERE, base::Bind(callback, DRIVE_NO_CONNECTION)); | |
| 999 return CancelCallback(); | |
| 1000 } | |
| 1001 | |
| 1002 EntryInfo* entry = FindEntryByResourceId(resource_id); | |
| 1003 if (!entry) { | |
| 1004 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 1005 FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND)); | |
| 1006 return CancelCallback(); | |
| 1007 } | |
| 1008 | |
| 1009 ChangeResource* change = &entry->change_resource; | |
| 1010 FileResource* file = change->mutable_file(); | |
| 1011 std::vector<ParentReference>* parents = file->mutable_parents(); | |
| 1012 for (size_t i = 0; i < parents->size(); ++i) { | |
| 1013 if ((*parents)[i].file_id() == parent_resource_id) { | |
| 1014 parents->erase(parents->begin() + i); | |
| 1015 AddNewChangestamp(change); | |
| 1016 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 1017 FROM_HERE, base::Bind(callback, HTTP_NO_CONTENT)); | |
| 1018 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 1019 FROM_HERE, | |
| 1020 base::Bind(&FakeDriveService::NotifyObservers, | |
| 1021 weak_ptr_factory_.GetWeakPtr())); | |
| 1022 return CancelCallback(); | |
| 1023 } | |
| 1024 } | |
| 1025 | |
| 1026 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 1027 FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND)); | |
| 1028 return CancelCallback(); | |
| 1029 } | |
| 1030 | |
| 1031 CancelCallback FakeDriveService::AddNewDirectory( | |
| 1032 const std::string& parent_resource_id, | |
| 1033 const std::string& directory_title, | |
| 1034 const AddNewDirectoryOptions& options, | |
| 1035 const FileResourceCallback& callback) { | |
| 1036 return AddNewDirectoryWithResourceId( | |
| 1037 "", | |
| 1038 parent_resource_id.empty() ? GetRootResourceId() : parent_resource_id, | |
| 1039 directory_title, | |
| 1040 options, | |
| 1041 callback); | |
| 1042 } | |
| 1043 | |
| 1044 CancelCallback FakeDriveService::InitiateUploadNewFile( | |
| 1045 const std::string& content_type, | |
| 1046 int64 content_length, | |
| 1047 const std::string& parent_resource_id, | |
| 1048 const std::string& title, | |
| 1049 const UploadNewFileOptions& options, | |
| 1050 const InitiateUploadCallback& callback) { | |
| 1051 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 1052 DCHECK(!callback.is_null()); | |
| 1053 | |
| 1054 if (offline_) { | |
| 1055 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 1056 FROM_HERE, | |
| 1057 base::Bind(callback, DRIVE_NO_CONNECTION, GURL())); | |
| 1058 return CancelCallback(); | |
| 1059 } | |
| 1060 | |
| 1061 if (parent_resource_id != GetRootResourceId() && | |
| 1062 !entries_.count(parent_resource_id)) { | |
| 1063 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 1064 FROM_HERE, | |
| 1065 base::Bind(callback, HTTP_NOT_FOUND, GURL())); | |
| 1066 return CancelCallback(); | |
| 1067 } | |
| 1068 | |
| 1069 GURL session_url = GetNewUploadSessionUrl(); | |
| 1070 upload_sessions_[session_url] = | |
| 1071 UploadSession(content_type, content_length, | |
| 1072 parent_resource_id, | |
| 1073 "", // resource_id | |
| 1074 "", // etag | |
| 1075 title); | |
| 1076 | |
| 1077 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 1078 FROM_HERE, | |
| 1079 base::Bind(callback, HTTP_SUCCESS, session_url)); | |
| 1080 return CancelCallback(); | |
| 1081 } | |
| 1082 | |
| 1083 CancelCallback FakeDriveService::InitiateUploadExistingFile( | |
| 1084 const std::string& content_type, | |
| 1085 int64 content_length, | |
| 1086 const std::string& resource_id, | |
| 1087 const UploadExistingFileOptions& options, | |
| 1088 const InitiateUploadCallback& callback) { | |
| 1089 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 1090 DCHECK(!callback.is_null()); | |
| 1091 | |
| 1092 if (offline_) { | |
| 1093 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 1094 FROM_HERE, | |
| 1095 base::Bind(callback, DRIVE_NO_CONNECTION, GURL())); | |
| 1096 return CancelCallback(); | |
| 1097 } | |
| 1098 | |
| 1099 EntryInfo* entry = FindEntryByResourceId(resource_id); | |
| 1100 if (!entry) { | |
| 1101 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 1102 FROM_HERE, | |
| 1103 base::Bind(callback, HTTP_NOT_FOUND, GURL())); | |
| 1104 return CancelCallback(); | |
| 1105 } | |
| 1106 | |
| 1107 if (!UserHasWriteAccess(entry->user_permission)) { | |
| 1108 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 1109 FROM_HERE, | |
| 1110 base::Bind(callback, HTTP_FORBIDDEN, GURL())); | |
| 1111 return CancelCallback(); | |
| 1112 } | |
| 1113 | |
| 1114 FileResource* file = entry->change_resource.mutable_file(); | |
| 1115 if (!options.etag.empty() && options.etag != file->etag()) { | |
| 1116 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 1117 FROM_HERE, | |
| 1118 base::Bind(callback, HTTP_PRECONDITION, GURL())); | |
| 1119 return CancelCallback(); | |
| 1120 } | |
| 1121 // TODO(hashimoto): Update |file|'s metadata with |options|. | |
| 1122 | |
| 1123 GURL session_url = GetNewUploadSessionUrl(); | |
| 1124 upload_sessions_[session_url] = | |
| 1125 UploadSession(content_type, content_length, | |
| 1126 "", // parent_resource_id | |
| 1127 resource_id, | |
| 1128 file->etag(), | |
| 1129 "" /* title */); | |
| 1130 | |
| 1131 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 1132 FROM_HERE, | |
| 1133 base::Bind(callback, HTTP_SUCCESS, session_url)); | |
| 1134 return CancelCallback(); | |
| 1135 } | |
| 1136 | |
| 1137 CancelCallback FakeDriveService::GetUploadStatus( | |
| 1138 const GURL& upload_url, | |
| 1139 int64 content_length, | |
| 1140 const UploadRangeCallback& callback) { | |
| 1141 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 1142 DCHECK(!callback.is_null()); | |
| 1143 return CancelCallback(); | |
| 1144 } | |
| 1145 | |
| 1146 CancelCallback FakeDriveService::ResumeUpload( | |
| 1147 const GURL& upload_url, | |
| 1148 int64 start_position, | |
| 1149 int64 end_position, | |
| 1150 int64 content_length, | |
| 1151 const std::string& content_type, | |
| 1152 const base::FilePath& local_file_path, | |
| 1153 const UploadRangeCallback& callback, | |
| 1154 const ProgressCallback& progress_callback) { | |
| 1155 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 1156 DCHECK(!callback.is_null()); | |
| 1157 | |
| 1158 FileResourceCallback completion_callback | |
| 1159 = base::Bind(&ScheduleUploadRangeCallback, | |
| 1160 callback, start_position, end_position); | |
| 1161 | |
| 1162 if (offline_) { | |
| 1163 completion_callback.Run(DRIVE_NO_CONNECTION, scoped_ptr<FileResource>()); | |
| 1164 return CancelCallback(); | |
| 1165 } | |
| 1166 | |
| 1167 if (!upload_sessions_.count(upload_url)) { | |
| 1168 completion_callback.Run(HTTP_NOT_FOUND, scoped_ptr<FileResource>()); | |
| 1169 return CancelCallback(); | |
| 1170 } | |
| 1171 | |
| 1172 UploadSession* session = &upload_sessions_[upload_url]; | |
| 1173 | |
| 1174 // Chunks are required to be sent in such a ways that they fill from the start | |
| 1175 // of the not-yet-uploaded part with no gaps nor overlaps. | |
| 1176 if (session->uploaded_size != start_position) { | |
| 1177 completion_callback.Run(HTTP_BAD_REQUEST, scoped_ptr<FileResource>()); | |
| 1178 return CancelCallback(); | |
| 1179 } | |
| 1180 | |
| 1181 if (!progress_callback.is_null()) { | |
| 1182 // In the real GDataWapi/Drive DriveService, progress is reported in | |
| 1183 // nondeterministic timing. In this fake implementation, we choose to call | |
| 1184 // it twice per one ResumeUpload. This is for making sure that client code | |
| 1185 // works fine even if the callback is invoked more than once; it is the | |
| 1186 // crucial difference of the progress callback from others. | |
| 1187 // Note that progress is notified in the relative offset in each chunk. | |
| 1188 const int64 chunk_size = end_position - start_position; | |
| 1189 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 1190 FROM_HERE, base::Bind(progress_callback, chunk_size / 2, chunk_size)); | |
| 1191 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 1192 FROM_HERE, base::Bind(progress_callback, chunk_size, chunk_size)); | |
| 1193 } | |
| 1194 | |
| 1195 if (content_length != end_position) { | |
| 1196 session->uploaded_size = end_position; | |
| 1197 completion_callback.Run(HTTP_RESUME_INCOMPLETE, scoped_ptr<FileResource>()); | |
| 1198 return CancelCallback(); | |
| 1199 } | |
| 1200 | |
| 1201 std::string content_data; | |
| 1202 if (!base::ReadFileToString(local_file_path, &content_data)) { | |
| 1203 session->uploaded_size = end_position; | |
| 1204 completion_callback.Run(DRIVE_FILE_ERROR, scoped_ptr<FileResource>()); | |
| 1205 return CancelCallback(); | |
| 1206 } | |
| 1207 session->uploaded_size = end_position; | |
| 1208 | |
| 1209 // |resource_id| is empty if the upload is for new file. | |
| 1210 if (session->resource_id.empty()) { | |
| 1211 DCHECK(!session->parent_resource_id.empty()); | |
| 1212 DCHECK(!session->title.empty()); | |
| 1213 const EntryInfo* new_entry = AddNewEntry( | |
| 1214 "", // auto generate resource id. | |
| 1215 session->content_type, | |
| 1216 content_data, | |
| 1217 session->parent_resource_id, | |
| 1218 session->title, | |
| 1219 false); // shared_with_me | |
| 1220 if (!new_entry) { | |
| 1221 completion_callback.Run(HTTP_NOT_FOUND, scoped_ptr<FileResource>()); | |
| 1222 return CancelCallback(); | |
| 1223 } | |
| 1224 | |
| 1225 completion_callback.Run(HTTP_CREATED, make_scoped_ptr( | |
| 1226 new FileResource(*new_entry->change_resource.file()))); | |
| 1227 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 1228 FROM_HERE, | |
| 1229 base::Bind(&FakeDriveService::NotifyObservers, | |
| 1230 weak_ptr_factory_.GetWeakPtr())); | |
| 1231 return CancelCallback(); | |
| 1232 } | |
| 1233 | |
| 1234 EntryInfo* entry = FindEntryByResourceId(session->resource_id); | |
| 1235 if (!entry) { | |
| 1236 completion_callback.Run(HTTP_NOT_FOUND, scoped_ptr<FileResource>()); | |
| 1237 return CancelCallback(); | |
| 1238 } | |
| 1239 | |
| 1240 ChangeResource* change = &entry->change_resource; | |
| 1241 FileResource* file = change->mutable_file(); | |
| 1242 if (file->etag().empty() || session->etag != file->etag()) { | |
| 1243 completion_callback.Run(HTTP_PRECONDITION, scoped_ptr<FileResource>()); | |
| 1244 return CancelCallback(); | |
| 1245 } | |
| 1246 | |
| 1247 file->set_md5_checksum(base::MD5String(content_data)); | |
| 1248 entry->content_data = content_data; | |
| 1249 file->set_file_size(end_position); | |
| 1250 AddNewChangestamp(change); | |
| 1251 UpdateETag(file); | |
| 1252 | |
| 1253 completion_callback.Run(HTTP_SUCCESS, make_scoped_ptr( | |
| 1254 new FileResource(*file))); | |
| 1255 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 1256 FROM_HERE, | |
| 1257 base::Bind(&FakeDriveService::NotifyObservers, | |
| 1258 weak_ptr_factory_.GetWeakPtr())); | |
| 1259 return CancelCallback(); | |
| 1260 } | |
| 1261 | |
| 1262 CancelCallback FakeDriveService::MultipartUploadNewFile( | |
| 1263 const std::string& content_type, | |
| 1264 int64 content_length, | |
| 1265 const std::string& parent_resource_id, | |
| 1266 const std::string& title, | |
| 1267 const base::FilePath& local_file_path, | |
| 1268 const UploadNewFileOptions& options, | |
| 1269 const FileResourceCallback& callback, | |
| 1270 const ProgressCallback& progress_callback) { | |
| 1271 CallResumeUpload* const call_resume_upload = new CallResumeUpload(); | |
| 1272 call_resume_upload->service = weak_ptr_factory_.GetWeakPtr(); | |
| 1273 call_resume_upload->content_type = content_type; | |
| 1274 call_resume_upload->content_length = content_length; | |
| 1275 call_resume_upload->local_file_path = local_file_path; | |
| 1276 call_resume_upload->callback = callback; | |
| 1277 call_resume_upload->progress_callback = progress_callback; | |
| 1278 InitiateUploadNewFile( | |
| 1279 content_type, | |
| 1280 content_length, | |
| 1281 parent_resource_id, | |
| 1282 title, | |
| 1283 options, | |
| 1284 base::Bind(&CallResumeUpload::Run, base::Owned(call_resume_upload))); | |
| 1285 return CancelCallback(); | |
| 1286 } | |
| 1287 | |
| 1288 CancelCallback FakeDriveService::MultipartUploadExistingFile( | |
| 1289 const std::string& content_type, | |
| 1290 int64 content_length, | |
| 1291 const std::string& resource_id, | |
| 1292 const base::FilePath& local_file_path, | |
| 1293 const UploadExistingFileOptions& options, | |
| 1294 const FileResourceCallback& callback, | |
| 1295 const ProgressCallback& progress_callback) { | |
| 1296 CallResumeUpload* const call_resume_upload = new CallResumeUpload(); | |
| 1297 call_resume_upload->service = weak_ptr_factory_.GetWeakPtr(); | |
| 1298 call_resume_upload->content_type = content_type; | |
| 1299 call_resume_upload->content_length = content_length; | |
| 1300 call_resume_upload->local_file_path = local_file_path; | |
| 1301 call_resume_upload->callback = callback; | |
| 1302 call_resume_upload->progress_callback = progress_callback; | |
| 1303 InitiateUploadExistingFile( | |
| 1304 content_type, | |
| 1305 content_length, | |
| 1306 resource_id, | |
| 1307 options, | |
| 1308 base::Bind(&CallResumeUpload::Run, base::Owned(call_resume_upload))); | |
| 1309 return CancelCallback(); | |
| 1310 } | |
| 1311 | |
| 1312 CancelCallback FakeDriveService::AuthorizeApp( | |
| 1313 const std::string& resource_id, | |
| 1314 const std::string& app_id, | |
| 1315 const AuthorizeAppCallback& callback) { | |
| 1316 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 1317 DCHECK(!callback.is_null()); | |
| 1318 | |
| 1319 if (entries_.count(resource_id) == 0) { | |
| 1320 callback.Run(google_apis::HTTP_NOT_FOUND, GURL()); | |
| 1321 return CancelCallback(); | |
| 1322 } | |
| 1323 | |
| 1324 callback.Run(HTTP_SUCCESS, | |
| 1325 GURL(base::StringPrintf(open_url_format_.c_str(), | |
| 1326 resource_id.c_str(), | |
| 1327 app_id.c_str()))); | |
| 1328 return CancelCallback(); | |
| 1329 } | |
| 1330 | |
| 1331 CancelCallback FakeDriveService::UninstallApp( | |
| 1332 const std::string& app_id, | |
| 1333 const google_apis::EntryActionCallback& callback) { | |
| 1334 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 1335 DCHECK(!callback.is_null()); | |
| 1336 | |
| 1337 if (offline_) { | |
| 1338 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 1339 FROM_HERE, | |
| 1340 base::Bind(callback, google_apis::DRIVE_NO_CONNECTION)); | |
| 1341 return CancelCallback(); | |
| 1342 } | |
| 1343 | |
| 1344 // Find app_id from app_info_value_ and delete. | |
| 1345 base::ListValue* items = NULL; | |
| 1346 if (!app_info_value_->GetList("items", &items)) { | |
| 1347 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 1348 FROM_HERE, | |
| 1349 base::Bind(callback, google_apis::HTTP_NOT_FOUND)); | |
| 1350 return CancelCallback(); | |
| 1351 } | |
| 1352 | |
| 1353 for (size_t i = 0; i < items->GetSize(); ++i) { | |
| 1354 base::DictionaryValue* item = NULL; | |
| 1355 std::string id; | |
| 1356 if (items->GetDictionary(i, &item) && item->GetString("id", &id) && | |
| 1357 id == app_id) { | |
| 1358 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 1359 FROM_HERE, | |
| 1360 base::Bind(callback, | |
| 1361 items->Remove(i, NULL) ? google_apis::HTTP_NO_CONTENT | |
| 1362 : google_apis::HTTP_NOT_FOUND)); | |
| 1363 return CancelCallback(); | |
| 1364 } | |
| 1365 } | |
| 1366 | |
| 1367 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 1368 FROM_HERE, | |
| 1369 base::Bind(callback, google_apis::HTTP_NOT_FOUND)); | |
| 1370 return CancelCallback(); | |
| 1371 } | |
| 1372 | |
| 1373 void FakeDriveService::AddNewFile(const std::string& content_type, | |
| 1374 const std::string& content_data, | |
| 1375 const std::string& parent_resource_id, | |
| 1376 const std::string& title, | |
| 1377 bool shared_with_me, | |
| 1378 const FileResourceCallback& callback) { | |
| 1379 AddNewFileWithResourceId("", content_type, content_data, parent_resource_id, | |
| 1380 title, shared_with_me, callback); | |
| 1381 } | |
| 1382 | |
| 1383 void FakeDriveService::AddNewFileWithResourceId( | |
| 1384 const std::string& resource_id, | |
| 1385 const std::string& content_type, | |
| 1386 const std::string& content_data, | |
| 1387 const std::string& parent_resource_id, | |
| 1388 const std::string& title, | |
| 1389 bool shared_with_me, | |
| 1390 const FileResourceCallback& callback) { | |
| 1391 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 1392 DCHECK(!callback.is_null()); | |
| 1393 | |
| 1394 if (offline_) { | |
| 1395 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 1396 FROM_HERE, | |
| 1397 base::Bind(callback, | |
| 1398 DRIVE_NO_CONNECTION, | |
| 1399 base::Passed(scoped_ptr<FileResource>()))); | |
| 1400 return; | |
| 1401 } | |
| 1402 | |
| 1403 const EntryInfo* new_entry = AddNewEntry(resource_id, | |
| 1404 content_type, | |
| 1405 content_data, | |
| 1406 parent_resource_id, | |
| 1407 title, | |
| 1408 shared_with_me); | |
| 1409 if (!new_entry) { | |
| 1410 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 1411 FROM_HERE, | |
| 1412 base::Bind(callback, HTTP_NOT_FOUND, | |
| 1413 base::Passed(scoped_ptr<FileResource>()))); | |
| 1414 return; | |
| 1415 } | |
| 1416 | |
| 1417 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 1418 FROM_HERE, | |
| 1419 base::Bind(callback, HTTP_CREATED, | |
| 1420 base::Passed(make_scoped_ptr( | |
| 1421 new FileResource(*new_entry->change_resource.file()))))); | |
| 1422 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 1423 FROM_HERE, | |
| 1424 base::Bind(&FakeDriveService::NotifyObservers, | |
| 1425 weak_ptr_factory_.GetWeakPtr())); | |
| 1426 } | |
| 1427 | |
| 1428 CancelCallback FakeDriveService::AddNewDirectoryWithResourceId( | |
| 1429 const std::string& resource_id, | |
| 1430 const std::string& parent_resource_id, | |
| 1431 const std::string& directory_title, | |
| 1432 const AddNewDirectoryOptions& options, | |
| 1433 const FileResourceCallback& callback) { | |
| 1434 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 1435 DCHECK(!callback.is_null()); | |
| 1436 | |
| 1437 if (offline_) { | |
| 1438 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 1439 FROM_HERE, | |
| 1440 base::Bind(callback, | |
| 1441 DRIVE_NO_CONNECTION, | |
| 1442 base::Passed(scoped_ptr<FileResource>()))); | |
| 1443 return CancelCallback(); | |
| 1444 } | |
| 1445 | |
| 1446 const EntryInfo* new_entry = AddNewEntry(resource_id, | |
| 1447 util::kDriveFolderMimeType, | |
| 1448 "", // content_data | |
| 1449 parent_resource_id, | |
| 1450 directory_title, | |
| 1451 false); // shared_with_me | |
| 1452 if (!new_entry) { | |
| 1453 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 1454 FROM_HERE, | |
| 1455 base::Bind(callback, HTTP_NOT_FOUND, | |
| 1456 base::Passed(scoped_ptr<FileResource>()))); | |
| 1457 return CancelCallback(); | |
| 1458 } | |
| 1459 | |
| 1460 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 1461 FROM_HERE, | |
| 1462 base::Bind(callback, HTTP_CREATED, | |
| 1463 base::Passed(make_scoped_ptr( | |
| 1464 new FileResource(*new_entry->change_resource.file()))))); | |
| 1465 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 1466 FROM_HERE, | |
| 1467 base::Bind(&FakeDriveService::NotifyObservers, | |
| 1468 weak_ptr_factory_.GetWeakPtr())); | |
| 1469 return CancelCallback(); | |
| 1470 } | |
| 1471 | |
| 1472 void FakeDriveService::SetLastModifiedTime( | |
| 1473 const std::string& resource_id, | |
| 1474 const base::Time& last_modified_time, | |
| 1475 const FileResourceCallback& callback) { | |
| 1476 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 1477 DCHECK(!callback.is_null()); | |
| 1478 | |
| 1479 if (offline_) { | |
| 1480 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 1481 FROM_HERE, | |
| 1482 base::Bind(callback, | |
| 1483 DRIVE_NO_CONNECTION, | |
| 1484 base::Passed(scoped_ptr<FileResource>()))); | |
| 1485 return; | |
| 1486 } | |
| 1487 | |
| 1488 EntryInfo* entry = FindEntryByResourceId(resource_id); | |
| 1489 if (!entry) { | |
| 1490 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 1491 FROM_HERE, | |
| 1492 base::Bind(callback, HTTP_NOT_FOUND, | |
| 1493 base::Passed(scoped_ptr<FileResource>()))); | |
| 1494 return; | |
| 1495 } | |
| 1496 | |
| 1497 ChangeResource* change = &entry->change_resource; | |
| 1498 FileResource* file = change->mutable_file(); | |
| 1499 file->set_modified_date(last_modified_time); | |
| 1500 | |
| 1501 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 1502 FROM_HERE, | |
| 1503 base::Bind(callback, HTTP_SUCCESS, | |
| 1504 base::Passed(make_scoped_ptr(new FileResource(*file))))); | |
| 1505 } | |
| 1506 | |
| 1507 google_apis::DriveApiErrorCode FakeDriveService::SetUserPermission( | |
| 1508 const std::string& resource_id, | |
| 1509 google_apis::drive::PermissionRole user_permission) { | |
| 1510 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 1511 | |
| 1512 EntryInfo* entry = FindEntryByResourceId(resource_id); | |
| 1513 if (!entry) | |
| 1514 return HTTP_NOT_FOUND; | |
| 1515 | |
| 1516 entry->user_permission = user_permission; | |
| 1517 return HTTP_SUCCESS; | |
| 1518 } | |
| 1519 | |
| 1520 void FakeDriveService::AddChangeObserver(ChangeObserver* change_observer) { | |
| 1521 change_observers_.AddObserver(change_observer); | |
| 1522 } | |
| 1523 | |
| 1524 void FakeDriveService::RemoveChangeObserver(ChangeObserver* change_observer) { | |
| 1525 change_observers_.RemoveObserver(change_observer); | |
| 1526 } | |
| 1527 | |
| 1528 FakeDriveService::EntryInfo* FakeDriveService::FindEntryByResourceId( | |
| 1529 const std::string& resource_id) { | |
| 1530 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 1531 | |
| 1532 EntryInfoMap::iterator it = entries_.find(resource_id); | |
| 1533 // Deleted entries don't have FileResource. | |
| 1534 return it != entries_.end() && it->second->change_resource.file() ? | |
| 1535 it->second : NULL; | |
| 1536 } | |
| 1537 | |
| 1538 std::string FakeDriveService::GetNewResourceId() { | |
| 1539 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 1540 | |
| 1541 ++resource_id_count_; | |
| 1542 return base::StringPrintf("resource_id_%d", resource_id_count_); | |
| 1543 } | |
| 1544 | |
| 1545 void FakeDriveService::UpdateETag(google_apis::FileResource* file) { | |
| 1546 file->set_etag( | |
| 1547 "etag_" + base::Int64ToString(about_resource_->largest_change_id())); | |
| 1548 } | |
| 1549 | |
| 1550 void FakeDriveService::AddNewChangestamp(google_apis::ChangeResource* change) { | |
| 1551 about_resource_->set_largest_change_id( | |
| 1552 about_resource_->largest_change_id() + 1); | |
| 1553 change->set_change_id(about_resource_->largest_change_id()); | |
| 1554 } | |
| 1555 | |
| 1556 const FakeDriveService::EntryInfo* FakeDriveService::AddNewEntry( | |
| 1557 const std::string& given_resource_id, | |
| 1558 const std::string& content_type, | |
| 1559 const std::string& content_data, | |
| 1560 const std::string& parent_resource_id, | |
| 1561 const std::string& title, | |
| 1562 bool shared_with_me) { | |
| 1563 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 1564 | |
| 1565 if (!parent_resource_id.empty() && | |
| 1566 parent_resource_id != GetRootResourceId() && | |
| 1567 !entries_.count(parent_resource_id)) { | |
| 1568 return NULL; | |
| 1569 } | |
| 1570 | |
| 1571 const std::string resource_id = | |
| 1572 given_resource_id.empty() ? GetNewResourceId() : given_resource_id; | |
| 1573 if (entries_.count(resource_id)) | |
| 1574 return NULL; | |
| 1575 GURL upload_url = GURL("https://xxx/upload/" + resource_id); | |
| 1576 | |
| 1577 scoped_ptr<EntryInfo> new_entry(new EntryInfo); | |
| 1578 ChangeResource* new_change = &new_entry->change_resource; | |
| 1579 FileResource* new_file = new FileResource; | |
| 1580 new_change->set_file(make_scoped_ptr(new_file)); | |
| 1581 | |
| 1582 // Set the resource ID and the title | |
| 1583 new_change->set_file_id(resource_id); | |
| 1584 new_file->set_file_id(resource_id); | |
| 1585 new_file->set_title(title); | |
| 1586 // Set the contents, size and MD5 for a file. | |
| 1587 if (content_type != util::kDriveFolderMimeType && | |
| 1588 !util::IsKnownHostedDocumentMimeType(content_type)) { | |
| 1589 new_entry->content_data = content_data; | |
| 1590 new_file->set_file_size(content_data.size()); | |
| 1591 new_file->set_md5_checksum(base::MD5String(content_data)); | |
| 1592 } | |
| 1593 | |
| 1594 if (shared_with_me) { | |
| 1595 // Set current time to mark the file as shared_with_me. | |
| 1596 new_file->set_shared_with_me_date(base::Time::Now()); | |
| 1597 } | |
| 1598 | |
| 1599 std::string escaped_resource_id = net::EscapePath(resource_id); | |
| 1600 | |
| 1601 // Set mime type. | |
| 1602 new_file->set_mime_type(content_type); | |
| 1603 | |
| 1604 // Set alternate link if needed. | |
| 1605 if (content_type == util::kGoogleDocumentMimeType) | |
| 1606 new_file->set_alternate_link(GURL("https://document_alternate_link")); | |
| 1607 | |
| 1608 // Set parents. | |
| 1609 if (!parent_resource_id.empty()) { | |
| 1610 ParentReference parent; | |
| 1611 parent.set_file_id(parent_resource_id); | |
| 1612 parent.set_parent_link(GetFakeLinkUrl(parent.file_id())); | |
| 1613 std::vector<ParentReference> parents; | |
| 1614 parents.push_back(parent); | |
| 1615 *new_file->mutable_parents() = parents; | |
| 1616 } | |
| 1617 | |
| 1618 new_entry->share_url = net::AppendOrReplaceQueryParameter( | |
| 1619 share_url_base_, "name", title); | |
| 1620 | |
| 1621 AddNewChangestamp(new_change); | |
| 1622 UpdateETag(new_file); | |
| 1623 | |
| 1624 base::Time published_date = | |
| 1625 base::Time() + base::TimeDelta::FromMilliseconds(++published_date_seq_); | |
| 1626 new_file->set_created_date(published_date); | |
| 1627 | |
| 1628 EntryInfo* raw_new_entry = new_entry.release(); | |
| 1629 entries_[resource_id] = raw_new_entry; | |
| 1630 return raw_new_entry; | |
| 1631 } | |
| 1632 | |
| 1633 void FakeDriveService::GetChangeListInternal( | |
| 1634 int64 start_changestamp, | |
| 1635 const std::string& search_query, | |
| 1636 const std::string& directory_resource_id, | |
| 1637 int start_offset, | |
| 1638 int max_results, | |
| 1639 int* load_counter, | |
| 1640 const ChangeListCallback& callback) { | |
| 1641 if (offline_) { | |
| 1642 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 1643 FROM_HERE, | |
| 1644 base::Bind(callback, | |
| 1645 DRIVE_NO_CONNECTION, | |
| 1646 base::Passed(scoped_ptr<ChangeList>()))); | |
| 1647 return; | |
| 1648 } | |
| 1649 | |
| 1650 // Filter out entries per parameters like |directory_resource_id| and | |
| 1651 // |search_query|. | |
| 1652 ScopedVector<ChangeResource> entries; | |
| 1653 int num_entries_matched = 0; | |
| 1654 for (EntryInfoMap::iterator it = entries_.begin(); it != entries_.end(); | |
| 1655 ++it) { | |
| 1656 const ChangeResource& entry = it->second->change_resource; | |
| 1657 bool should_exclude = false; | |
| 1658 | |
| 1659 // If |directory_resource_id| is set, exclude the entry if it's not in | |
| 1660 // the target directory. | |
| 1661 if (!directory_resource_id.empty()) { | |
| 1662 // Get the parent resource ID of the entry. | |
| 1663 std::string parent_resource_id; | |
| 1664 if (entry.file() && !entry.file()->parents().empty()) | |
| 1665 parent_resource_id = entry.file()->parents()[0].file_id(); | |
| 1666 | |
| 1667 if (directory_resource_id != parent_resource_id) | |
| 1668 should_exclude = true; | |
| 1669 } | |
| 1670 | |
| 1671 // If |search_query| is set, exclude the entry if it does not contain the | |
| 1672 // search query in the title. | |
| 1673 if (!should_exclude && !search_query.empty() && | |
| 1674 !EntryMatchWithQuery(entry, search_query)) { | |
| 1675 should_exclude = true; | |
| 1676 } | |
| 1677 | |
| 1678 // If |start_changestamp| is set, exclude the entry if the | |
| 1679 // changestamp is older than |largest_changestamp|. | |
| 1680 // See https://developers.google.com/google-apps/documents-list/ | |
| 1681 // #retrieving_all_changes_since_a_given_changestamp | |
| 1682 if (start_changestamp > 0 && entry.change_id() < start_changestamp) | |
| 1683 should_exclude = true; | |
| 1684 | |
| 1685 // If the caller requests other list than change list by specifying | |
| 1686 // zero-|start_changestamp|, exclude deleted entry from the result. | |
| 1687 const bool deleted = entry.is_deleted() || | |
| 1688 (entry.file() && entry.file()->labels().is_trashed()); | |
| 1689 if (!start_changestamp && deleted) | |
| 1690 should_exclude = true; | |
| 1691 | |
| 1692 // The entry matched the criteria for inclusion. | |
| 1693 if (!should_exclude) | |
| 1694 ++num_entries_matched; | |
| 1695 | |
| 1696 // If |start_offset| is set, exclude the entry if the entry is before the | |
| 1697 // start index. <= instead of < as |num_entries_matched| was | |
| 1698 // already incremented. | |
| 1699 if (start_offset > 0 && num_entries_matched <= start_offset) | |
| 1700 should_exclude = true; | |
| 1701 | |
| 1702 if (!should_exclude) { | |
| 1703 scoped_ptr<ChangeResource> entry_copied(new ChangeResource); | |
| 1704 entry_copied->set_change_id(entry.change_id()); | |
| 1705 entry_copied->set_file_id(entry.file_id()); | |
| 1706 entry_copied->set_deleted(entry.is_deleted()); | |
| 1707 if (entry.file()) { | |
| 1708 entry_copied->set_file( | |
| 1709 make_scoped_ptr(new FileResource(*entry.file()))); | |
| 1710 } | |
| 1711 entry_copied->set_modification_date(entry.modification_date()); | |
| 1712 entries.push_back(entry_copied.release()); | |
| 1713 } | |
| 1714 } | |
| 1715 | |
| 1716 scoped_ptr<ChangeList> change_list(new ChangeList); | |
| 1717 if (start_changestamp > 0 && start_offset == 0) { | |
| 1718 change_list->set_largest_change_id(about_resource_->largest_change_id()); | |
| 1719 } | |
| 1720 | |
| 1721 // If |max_results| is set, trim the entries if the number exceeded the max | |
| 1722 // results. | |
| 1723 if (max_results > 0 && entries.size() > static_cast<size_t>(max_results)) { | |
| 1724 entries.erase(entries.begin() + max_results, entries.end()); | |
| 1725 // Adds the next URL. | |
| 1726 // Here, we embed information which is needed for continuing the | |
| 1727 // GetChangeList request in the next invocation into url query | |
| 1728 // parameters. | |
| 1729 GURL next_url(base::StringPrintf( | |
| 1730 "http://localhost/?start-offset=%d&max-results=%d", | |
| 1731 start_offset + max_results, | |
| 1732 max_results)); | |
| 1733 if (start_changestamp > 0) { | |
| 1734 next_url = net::AppendOrReplaceQueryParameter( | |
| 1735 next_url, "changestamp", | |
| 1736 base::Int64ToString(start_changestamp).c_str()); | |
| 1737 } | |
| 1738 if (!search_query.empty()) { | |
| 1739 next_url = net::AppendOrReplaceQueryParameter( | |
| 1740 next_url, "q", search_query); | |
| 1741 } | |
| 1742 if (!directory_resource_id.empty()) { | |
| 1743 next_url = net::AppendOrReplaceQueryParameter( | |
| 1744 next_url, "parent", directory_resource_id); | |
| 1745 } | |
| 1746 | |
| 1747 change_list->set_next_link(next_url); | |
| 1748 } | |
| 1749 *change_list->mutable_items() = entries.Pass(); | |
| 1750 | |
| 1751 if (load_counter) | |
| 1752 *load_counter += 1; | |
| 1753 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
| 1754 FROM_HERE, | |
| 1755 base::Bind(callback, HTTP_SUCCESS, base::Passed(&change_list))); | |
| 1756 } | |
| 1757 | |
| 1758 GURL FakeDriveService::GetNewUploadSessionUrl() { | |
| 1759 return GURL("https://upload_session_url/" + | |
| 1760 base::Int64ToString(next_upload_sequence_number_++)); | |
| 1761 } | |
| 1762 | |
| 1763 google_apis::CancelCallback FakeDriveService::AddPermission( | |
| 1764 const std::string& resource_id, | |
| 1765 const std::string& email, | |
| 1766 google_apis::drive::PermissionRole role, | |
| 1767 const google_apis::EntryActionCallback& callback) { | |
| 1768 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 1769 DCHECK(!callback.is_null()); | |
| 1770 | |
| 1771 NOTREACHED(); | |
| 1772 return CancelCallback(); | |
| 1773 } | |
| 1774 | |
| 1775 scoped_ptr<BatchRequestConfiguratorInterface> | |
| 1776 FakeDriveService::StartBatchRequest() { | |
| 1777 DCHECK(thread_checker_.CalledOnValidThread()); | |
| 1778 | |
| 1779 NOTREACHED(); | |
| 1780 return scoped_ptr<BatchRequestConfiguratorInterface>(); | |
| 1781 } | |
| 1782 | |
| 1783 void FakeDriveService::NotifyObservers() { | |
| 1784 FOR_EACH_OBSERVER(ChangeObserver, change_observers_, OnNewChangeAvailable()); | |
| 1785 } | |
| 1786 | |
| 1787 } // namespace drive | |
| OLD | NEW |