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.
5 #include "chrome/browser/drive/fake_drive_service.h"
9 #include "base/files/file_util.h"
10 #include "base/json/json_string_value_serializer.h"
11 #include "base/logging.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/string_split.h"
16 #include "base/strings/string_tokenizer.h"
17 #include "base/strings/string_util.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/strings/utf_string_conversions.h"
20 #include "base/values.h"
21 #include "chrome/browser/drive/drive_api_util.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "google_apis/drive/drive_api_parser.h"
24 #include "google_apis/drive/test_util.h"
25 #include "net/base/escape.h"
26 #include "net/base/url_util.h"
28 using content::BrowserThread;
29 using google_apis::AboutResource;
30 using google_apis::AboutResourceCallback;
31 using google_apis::AppList;
32 using google_apis::AppListCallback;
33 using google_apis::AuthStatusCallback;
34 using google_apis::AuthorizeAppCallback;
35 using google_apis::CancelCallback;
36 using google_apis::ChangeList;
37 using google_apis::ChangeListCallback;
38 using google_apis::ChangeResource;
39 using google_apis::DownloadActionCallback;
40 using google_apis::EntryActionCallback;
41 using google_apis::FileList;
42 using google_apis::FileListCallback;
43 using google_apis::FileResource;
44 using google_apis::FileResourceCallback;
45 using google_apis::GDATA_FILE_ERROR;
46 using google_apis::GDATA_NO_CONNECTION;
47 using google_apis::GDATA_OTHER_ERROR;
48 using google_apis::GDataErrorCode;
49 using google_apis::GetContentCallback;
50 using google_apis::GetShareUrlCallback;
51 using google_apis::HTTP_BAD_REQUEST;
52 using google_apis::HTTP_CREATED;
53 using google_apis::HTTP_FORBIDDEN;
54 using google_apis::HTTP_NOT_FOUND;
55 using google_apis::HTTP_NO_CONTENT;
56 using google_apis::HTTP_PRECONDITION;
57 using google_apis::HTTP_RESUME_INCOMPLETE;
58 using google_apis::HTTP_SUCCESS;
59 using google_apis::InitiateUploadCallback;
60 using google_apis::ParentReference;
61 using google_apis::ProgressCallback;
62 using google_apis::UploadRangeResponse;
63 using google_apis::drive::UploadRangeCallback;
64 namespace test_util = google_apis::test_util;
69 // Returns true if the entry matches with the search query.
70 // Supports queries consist of following format.
71 // - Phrases quoted by double/single quotes
72 // - AND search for multiple words/phrases segmented by space
73 // - Limited attribute search. Only "title:" is supported.
74 bool EntryMatchWithQuery(const ChangeResource& entry,
75 const std::string& query) {
76 base::StringTokenizer tokenizer(query, " ");
77 tokenizer.set_quote_chars("\"'");
78 while (tokenizer.GetNext()) {
79 std::string key, value;
80 const std::string& token = tokenizer.token();
81 if (token.find(':') == std::string::npos) {
82 base::TrimString(token, "\"'", &value);
84 base::StringTokenizer key_value(token, ":");
85 key_value.set_quote_chars("\"'");
86 if (!key_value.GetNext())
88 key = key_value.token();
89 if (!key_value.GetNext())
91 base::TrimString(key_value.token(), "\"'", &value);
94 // TODO(peria): Deal with other attributes than title.
95 if (!key.empty() && key != "title")
97 // Search query in the title.
99 entry.file()->title().find(value) == std::string::npos)
105 void ScheduleUploadRangeCallback(const UploadRangeCallback& callback,
106 int64 start_position,
108 GDataErrorCode error,
109 scoped_ptr<FileResource> entry) {
110 base::MessageLoop::current()->PostTask(
113 UploadRangeResponse(error,
116 base::Passed(&entry)));
119 void FileListCallbackAdapter(const FileListCallback& callback,
120 GDataErrorCode error,
121 scoped_ptr<ChangeList> change_list) {
122 scoped_ptr<FileList> file_list;
124 callback.Run(error, file_list.Pass());
128 file_list.reset(new FileList);
129 file_list->set_next_link(change_list->next_link());
130 for (size_t i = 0; i < change_list->items().size(); ++i) {
131 const ChangeResource& entry = *change_list->items()[i];
133 file_list->mutable_items()->push_back(new FileResource(*entry.file()));
135 callback.Run(error, file_list.Pass());
138 bool UserHasWriteAccess(google_apis::drive::PermissionRole user_permission) {
139 switch (user_permission) {
140 case google_apis::drive::PERMISSION_ROLE_OWNER:
141 case google_apis::drive::PERMISSION_ROLE_WRITER:
143 case google_apis::drive::PERMISSION_ROLE_READER:
144 case google_apis::drive::PERMISSION_ROLE_COMMENTER:
152 struct FakeDriveService::EntryInfo {
153 EntryInfo() : user_permission(google_apis::drive::PERMISSION_ROLE_OWNER) {}
155 google_apis::ChangeResource change_resource;
157 std::string content_data;
159 // Behaves in the same way as "userPermission" described in
160 // https://developers.google.com/drive/v2/reference/files
161 google_apis::drive::PermissionRole user_permission;
164 struct FakeDriveService::UploadSession {
165 std::string content_type;
166 int64 content_length;
167 std::string parent_resource_id;
168 std::string resource_id;
179 std::string content_type,
180 int64 content_length,
181 std::string parent_resource_id,
182 std::string resource_id,
185 : content_type(content_type),
186 content_length(content_length),
187 parent_resource_id(parent_resource_id),
188 resource_id(resource_id),
195 FakeDriveService::FakeDriveService()
196 : about_resource_(new AboutResource),
197 published_date_seq_(0),
198 next_upload_sequence_number_(0),
199 default_max_results_(0),
200 resource_id_count_(0),
201 file_list_load_count_(0),
202 change_list_load_count_(0),
203 directory_load_count_(0),
204 about_resource_load_count_(0),
205 app_list_load_count_(0),
206 blocked_file_list_load_count_(0),
208 never_return_all_file_list_(false),
209 share_url_base_("https://share_url/"),
210 weak_ptr_factory_(this) {
211 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
213 about_resource_->set_largest_change_id(654321);
214 about_resource_->set_quota_bytes_total(9876543210);
215 about_resource_->set_quota_bytes_used(6789012345);
216 about_resource_->set_root_folder_id(GetRootResourceId());
219 FakeDriveService::~FakeDriveService() {
220 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
221 STLDeleteValues(&entries_);
224 bool FakeDriveService::LoadAppListForDriveApi(
225 const std::string& relative_path) {
226 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
228 // Load JSON data, which must be a dictionary.
229 scoped_ptr<base::Value> value = test_util::LoadJSONFile(relative_path);
230 CHECK_EQ(base::Value::TYPE_DICTIONARY, value->GetType());
231 app_info_value_.reset(
232 static_cast<base::DictionaryValue*>(value.release()));
233 return app_info_value_;
236 void FakeDriveService::AddApp(const std::string& app_id,
237 const std::string& app_name,
238 const std::string& product_id,
239 const std::string& create_url) {
240 if (app_json_template_.empty()) {
241 base::FilePath path =
242 test_util::GetTestFilePath("drive/applist_app_template.json");
243 CHECK(base::ReadFileToString(path, &app_json_template_));
246 std::string app_json = app_json_template_;
247 ReplaceSubstringsAfterOffset(&app_json, 0, "$AppId", app_id);
248 ReplaceSubstringsAfterOffset(&app_json, 0, "$AppName", app_name);
249 ReplaceSubstringsAfterOffset(&app_json, 0, "$ProductId", product_id);
250 ReplaceSubstringsAfterOffset(&app_json, 0, "$CreateUrl", create_url);
252 JSONStringValueSerializer json(app_json);
253 std::string error_message;
254 scoped_ptr<base::Value> value(json.Deserialize(NULL, &error_message));
255 CHECK_EQ(base::Value::TYPE_DICTIONARY, value->GetType());
257 base::ListValue* item_list;
258 CHECK(app_info_value_->GetListWithoutPathExpansion("items", &item_list));
259 item_list->Append(value.release());
262 void FakeDriveService::RemoveAppByProductId(const std::string& product_id) {
263 base::ListValue* item_list;
264 CHECK(app_info_value_->GetListWithoutPathExpansion("items", &item_list));
265 for (size_t i = 0; i < item_list->GetSize(); ++i) {
266 base::DictionaryValue* item;
267 CHECK(item_list->GetDictionary(i, &item));
268 const char kKeyProductId[] = "productId";
269 std::string item_product_id;
270 if (item->GetStringWithoutPathExpansion(kKeyProductId, &item_product_id) &&
271 product_id == item_product_id) {
272 item_list->Remove(i, NULL);
278 bool FakeDriveService::HasApp(const std::string& app_id) const {
279 base::ListValue* item_list;
280 CHECK(app_info_value_->GetListWithoutPathExpansion("items", &item_list));
281 for (size_t i = 0; i < item_list->GetSize(); ++i) {
282 base::DictionaryValue* item;
283 CHECK(item_list->GetDictionary(i, &item));
284 const char kKeyId[] = "id";
286 if (item->GetStringWithoutPathExpansion(kKeyId, &item_id) &&
295 void FakeDriveService::SetQuotaValue(int64 used, int64 total) {
296 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
298 about_resource_->set_quota_bytes_used(used);
299 about_resource_->set_quota_bytes_total(total);
302 GURL FakeDriveService::GetFakeLinkUrl(const std::string& resource_id) {
303 return GURL("https://fake_server/" + net::EscapePath(resource_id));
306 void FakeDriveService::Initialize(const std::string& account_id) {
307 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
310 void FakeDriveService::AddObserver(DriveServiceObserver* observer) {
311 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
314 void FakeDriveService::RemoveObserver(DriveServiceObserver* observer) {
315 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
318 bool FakeDriveService::CanSendRequest() const {
319 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
323 bool FakeDriveService::HasAccessToken() const {
324 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
328 void FakeDriveService::RequestAccessToken(const AuthStatusCallback& callback) {
329 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
330 DCHECK(!callback.is_null());
331 callback.Run(google_apis::HTTP_NOT_MODIFIED, "fake_access_token");
334 bool FakeDriveService::HasRefreshToken() const {
335 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
339 void FakeDriveService::ClearAccessToken() {
340 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
343 void FakeDriveService::ClearRefreshToken() {
344 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
347 std::string FakeDriveService::GetRootResourceId() const {
351 CancelCallback FakeDriveService::GetAllFileList(
352 const FileListCallback& callback) {
353 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
354 DCHECK(!callback.is_null());
356 if (never_return_all_file_list_) {
357 ++blocked_file_list_load_count_;
358 return CancelCallback();
361 GetChangeListInternal(0, // start changestamp
362 std::string(), // empty search query
363 std::string(), // no directory resource id,
365 default_max_results_,
366 &file_list_load_count_,
367 base::Bind(&FileListCallbackAdapter, callback));
368 return CancelCallback();
371 CancelCallback FakeDriveService::GetFileListInDirectory(
372 const std::string& directory_resource_id,
373 const FileListCallback& callback) {
374 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
375 DCHECK(!directory_resource_id.empty());
376 DCHECK(!callback.is_null());
378 GetChangeListInternal(0, // start changestamp
379 std::string(), // empty search query
380 directory_resource_id,
382 default_max_results_,
383 &directory_load_count_,
384 base::Bind(&FileListCallbackAdapter, callback));
385 return CancelCallback();
388 CancelCallback FakeDriveService::Search(
389 const std::string& search_query,
390 const FileListCallback& callback) {
391 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
392 DCHECK(!search_query.empty());
393 DCHECK(!callback.is_null());
395 GetChangeListInternal(0, // start changestamp
397 std::string(), // no directory resource id,
399 default_max_results_,
401 base::Bind(&FileListCallbackAdapter, callback));
402 return CancelCallback();
405 CancelCallback FakeDriveService::SearchByTitle(
406 const std::string& title,
407 const std::string& directory_resource_id,
408 const FileListCallback& callback) {
409 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
410 DCHECK(!title.empty());
411 DCHECK(!callback.is_null());
413 // Note: the search implementation here doesn't support quotation unescape,
414 // so don't escape here.
415 GetChangeListInternal(0, // start changestamp
416 base::StringPrintf("title:'%s'", title.c_str()),
417 directory_resource_id,
419 default_max_results_,
421 base::Bind(&FileListCallbackAdapter, callback));
422 return CancelCallback();
425 CancelCallback FakeDriveService::GetChangeList(
426 int64 start_changestamp,
427 const ChangeListCallback& callback) {
428 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
429 DCHECK(!callback.is_null());
431 GetChangeListInternal(start_changestamp,
432 std::string(), // empty search query
433 std::string(), // no directory resource id,
435 default_max_results_,
436 &change_list_load_count_,
438 return CancelCallback();
441 CancelCallback FakeDriveService::GetRemainingChangeList(
442 const GURL& next_link,
443 const ChangeListCallback& callback) {
444 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
445 DCHECK(!next_link.is_empty());
446 DCHECK(!callback.is_null());
448 // "changestamp", "q", "parent" and "start-offset" are parameters to
449 // implement "paging" of the result on FakeDriveService.
450 // The URL should be the one filled in GetChangeListInternal of the
451 // previous method invocation, so it should start with "http://localhost/?".
452 // See also GetChangeListInternal.
453 DCHECK_EQ(next_link.host(), "localhost");
454 DCHECK_EQ(next_link.path(), "/");
456 int64 start_changestamp = 0;
457 std::string search_query;
458 std::string directory_resource_id;
459 int start_offset = 0;
460 int max_results = default_max_results_;
461 base::StringPairs parameters;
462 if (base::SplitStringIntoKeyValuePairs(
463 next_link.query(), '=', '&', ¶meters)) {
464 for (size_t i = 0; i < parameters.size(); ++i) {
465 if (parameters[i].first == "changestamp") {
466 base::StringToInt64(parameters[i].second, &start_changestamp);
467 } else if (parameters[i].first == "q") {
469 net::UnescapeURLComponent(parameters[i].second,
470 net::UnescapeRule::URL_SPECIAL_CHARS);
471 } else if (parameters[i].first == "parent") {
472 directory_resource_id =
473 net::UnescapeURLComponent(parameters[i].second,
474 net::UnescapeRule::URL_SPECIAL_CHARS);
475 } else if (parameters[i].first == "start-offset") {
476 base::StringToInt(parameters[i].second, &start_offset);
477 } else if (parameters[i].first == "max-results") {
478 base::StringToInt(parameters[i].second, &max_results);
483 GetChangeListInternal(start_changestamp, search_query, directory_resource_id,
484 start_offset, max_results, NULL, callback);
485 return CancelCallback();
488 CancelCallback FakeDriveService::GetRemainingFileList(
489 const GURL& next_link,
490 const FileListCallback& callback) {
491 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
492 DCHECK(!next_link.is_empty());
493 DCHECK(!callback.is_null());
495 return GetRemainingChangeList(
496 next_link, base::Bind(&FileListCallbackAdapter, callback));
499 CancelCallback FakeDriveService::GetFileResource(
500 const std::string& resource_id,
501 const FileResourceCallback& callback) {
502 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
503 DCHECK(!callback.is_null());
506 base::MessageLoop::current()->PostTask(
510 base::Passed(scoped_ptr<FileResource>())));
511 return CancelCallback();
514 EntryInfo* entry = FindEntryByResourceId(resource_id);
515 if (entry && entry->change_resource.file()) {
516 base::MessageLoop::current()->PostTask(
518 base::Bind(callback, HTTP_SUCCESS, base::Passed(make_scoped_ptr(
519 new FileResource(*entry->change_resource.file())))));
520 return CancelCallback();
523 base::MessageLoop::current()->PostTask(
525 base::Bind(callback, HTTP_NOT_FOUND,
526 base::Passed(scoped_ptr<FileResource>())));
527 return CancelCallback();
530 CancelCallback FakeDriveService::GetShareUrl(
531 const std::string& resource_id,
532 const GURL& /* embed_origin */,
533 const GetShareUrlCallback& callback) {
534 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
535 DCHECK(!callback.is_null());
538 base::MessageLoop::current()->PostTask(
543 return CancelCallback();
546 EntryInfo* entry = FindEntryByResourceId(resource_id);
548 base::MessageLoop::current()->PostTask(
550 base::Bind(callback, HTTP_SUCCESS, entry->share_url));
551 return CancelCallback();
554 base::MessageLoop::current()->PostTask(
556 base::Bind(callback, HTTP_NOT_FOUND, GURL()));
557 return CancelCallback();
560 CancelCallback FakeDriveService::GetAboutResource(
561 const AboutResourceCallback& callback) {
562 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
563 DCHECK(!callback.is_null());
566 scoped_ptr<AboutResource> null;
567 base::MessageLoop::current()->PostTask(
570 GDATA_NO_CONNECTION, base::Passed(&null)));
571 return CancelCallback();
574 ++about_resource_load_count_;
575 scoped_ptr<AboutResource> about_resource(new AboutResource(*about_resource_));
576 base::MessageLoop::current()->PostTask(
579 HTTP_SUCCESS, base::Passed(&about_resource)));
580 return CancelCallback();
583 CancelCallback FakeDriveService::GetAppList(const AppListCallback& callback) {
584 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
585 DCHECK(!callback.is_null());
586 DCHECK(app_info_value_);
589 scoped_ptr<AppList> null;
590 base::MessageLoop::current()->PostTask(
594 base::Passed(&null)));
595 return CancelCallback();
598 ++app_list_load_count_;
599 scoped_ptr<AppList> app_list(AppList::CreateFrom(*app_info_value_));
600 base::MessageLoop::current()->PostTask(
602 base::Bind(callback, HTTP_SUCCESS, base::Passed(&app_list)));
603 return CancelCallback();
606 CancelCallback FakeDriveService::DeleteResource(
607 const std::string& resource_id,
608 const std::string& etag,
609 const EntryActionCallback& callback) {
610 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
611 DCHECK(!callback.is_null());
614 base::MessageLoop::current()->PostTask(
615 FROM_HERE, base::Bind(callback, GDATA_NO_CONNECTION));
616 return CancelCallback();
619 EntryInfo* entry = FindEntryByResourceId(resource_id);
621 base::MessageLoop::current()->PostTask(
622 FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND));
623 return CancelCallback();
626 ChangeResource* change = &entry->change_resource;
627 const FileResource* file = change->file();
628 if (change->is_deleted()) {
629 base::MessageLoop::current()->PostTask(
630 FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND));
631 return CancelCallback();
634 if (!etag.empty() && etag != file->etag()) {
635 base::MessageLoop::current()->PostTask(
636 FROM_HERE, base::Bind(callback, HTTP_PRECONDITION));
637 return CancelCallback();
640 if (entry->user_permission != google_apis::drive::PERMISSION_ROLE_OWNER) {
641 base::MessageLoop::current()->PostTask(
642 FROM_HERE, base::Bind(callback, HTTP_FORBIDDEN));
643 return CancelCallback();
646 change->set_deleted(true);
647 AddNewChangestamp(change);
648 change->set_file(scoped_ptr<FileResource>());
649 base::MessageLoop::current()->PostTask(
650 FROM_HERE, base::Bind(callback, HTTP_NO_CONTENT));
651 base::MessageLoop::current()->PostTask(
653 base::Bind(&FakeDriveService::NotifyObservers,
654 weak_ptr_factory_.GetWeakPtr()));
655 return CancelCallback();
658 CancelCallback FakeDriveService::TrashResource(
659 const std::string& resource_id,
660 const EntryActionCallback& callback) {
661 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
662 DCHECK(!callback.is_null());
665 base::MessageLoop::current()->PostTask(
666 FROM_HERE, base::Bind(callback, GDATA_NO_CONNECTION));
667 return CancelCallback();
670 EntryInfo* entry = FindEntryByResourceId(resource_id);
672 base::MessageLoop::current()->PostTask(
673 FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND));
674 return CancelCallback();
677 ChangeResource* change = &entry->change_resource;
678 FileResource* file = change->mutable_file();
679 if (change->is_deleted() || file->labels().is_trashed()) {
680 base::MessageLoop::current()->PostTask(
681 FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND));
682 return CancelCallback();
685 if (entry->user_permission != google_apis::drive::PERMISSION_ROLE_OWNER) {
686 base::MessageLoop::current()->PostTask(
687 FROM_HERE, base::Bind(callback, HTTP_FORBIDDEN));
688 return CancelCallback();
691 file->mutable_labels()->set_trashed(true);
692 AddNewChangestamp(change);
693 base::MessageLoop::current()->PostTask(
694 FROM_HERE, base::Bind(callback, HTTP_SUCCESS));
695 base::MessageLoop::current()->PostTask(
697 base::Bind(&FakeDriveService::NotifyObservers,
698 weak_ptr_factory_.GetWeakPtr()));
699 return CancelCallback();
702 CancelCallback FakeDriveService::DownloadFile(
703 const base::FilePath& local_cache_path,
704 const std::string& resource_id,
705 const DownloadActionCallback& download_action_callback,
706 const GetContentCallback& get_content_callback,
707 const ProgressCallback& progress_callback) {
708 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
709 DCHECK(!download_action_callback.is_null());
712 base::MessageLoop::current()->PostTask(
714 base::Bind(download_action_callback,
717 return CancelCallback();
720 EntryInfo* entry = FindEntryByResourceId(resource_id);
721 if (!entry || entry->change_resource.file()->IsHostedDocument()) {
722 base::MessageLoopProxy::current()->PostTask(
724 base::Bind(download_action_callback, HTTP_NOT_FOUND, base::FilePath()));
725 return CancelCallback();
728 const FileResource* file = entry->change_resource.file();
729 const std::string& content_data = entry->content_data;
730 int64 file_size = file->file_size();
731 DCHECK_EQ(static_cast<size_t>(file_size), content_data.size());
733 if (!get_content_callback.is_null()) {
734 const int64 kBlockSize = 5;
735 for (int64 i = 0; i < file_size; i += kBlockSize) {
736 const int64 size = std::min(kBlockSize, file_size - i);
737 scoped_ptr<std::string> content_for_callback(
738 new std::string(content_data.substr(i, size)));
739 base::MessageLoopProxy::current()->PostTask(
741 base::Bind(get_content_callback, HTTP_SUCCESS,
742 base::Passed(&content_for_callback)));
746 if (!test_util::WriteStringToFile(local_cache_path, content_data)) {
747 // Failed to write the content.
748 base::MessageLoopProxy::current()->PostTask(
750 base::Bind(download_action_callback,
751 GDATA_FILE_ERROR, base::FilePath()));
752 return CancelCallback();
755 if (!progress_callback.is_null()) {
756 // See also the comment in ResumeUpload(). For testing that clients
757 // can handle the case progress_callback is called multiple times,
758 // here we invoke the callback twice.
759 base::MessageLoopProxy::current()->PostTask(
761 base::Bind(progress_callback, file_size / 2, file_size));
762 base::MessageLoopProxy::current()->PostTask(
764 base::Bind(progress_callback, file_size, file_size));
766 base::MessageLoopProxy::current()->PostTask(
768 base::Bind(download_action_callback,
771 return CancelCallback();
774 CancelCallback FakeDriveService::CopyResource(
775 const std::string& resource_id,
776 const std::string& in_parent_resource_id,
777 const std::string& new_title,
778 const base::Time& last_modified,
779 const FileResourceCallback& callback) {
780 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
781 DCHECK(!callback.is_null());
784 base::MessageLoop::current()->PostTask(
788 base::Passed(scoped_ptr<FileResource>())));
789 return CancelCallback();
792 const std::string& parent_resource_id = in_parent_resource_id.empty() ?
793 GetRootResourceId() : in_parent_resource_id;
795 EntryInfo* entry = FindEntryByResourceId(resource_id);
797 base::MessageLoop::current()->PostTask(
799 base::Bind(callback, HTTP_NOT_FOUND,
800 base::Passed(scoped_ptr<FileResource>())));
801 return CancelCallback();
804 // Make a copy and set the new resource ID and the new title.
805 scoped_ptr<EntryInfo> copied_entry(new EntryInfo);
806 copied_entry->content_data = entry->content_data;
807 copied_entry->share_url = entry->share_url;
808 copied_entry->change_resource.set_file(
809 make_scoped_ptr(new FileResource(*entry->change_resource.file())));
811 ChangeResource* new_change = &copied_entry->change_resource;
812 FileResource* new_file = new_change->mutable_file();
813 const std::string new_resource_id = GetNewResourceId();
814 new_change->set_file_id(new_resource_id);
815 new_file->set_file_id(new_resource_id);
816 new_file->set_title(new_title);
818 ParentReference parent;
819 parent.set_file_id(parent_resource_id);
820 parent.set_parent_link(GetFakeLinkUrl(parent_resource_id));
821 std::vector<ParentReference> parents;
822 parents.push_back(parent);
823 *new_file->mutable_parents() = parents;
825 if (!last_modified.is_null())
826 new_file->set_modified_date(last_modified);
828 AddNewChangestamp(new_change);
829 UpdateETag(new_file);
831 // Add the new entry to the map.
832 entries_[new_resource_id] = copied_entry.release();
834 base::MessageLoop::current()->PostTask(
838 base::Passed(make_scoped_ptr(new FileResource(*new_file)))));
839 base::MessageLoop::current()->PostTask(
841 base::Bind(&FakeDriveService::NotifyObservers,
842 weak_ptr_factory_.GetWeakPtr()));
843 return CancelCallback();
846 CancelCallback FakeDriveService::UpdateResource(
847 const std::string& resource_id,
848 const std::string& parent_resource_id,
849 const std::string& new_title,
850 const base::Time& last_modified,
851 const base::Time& last_viewed_by_me,
852 const google_apis::FileResourceCallback& callback) {
853 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
854 DCHECK(!callback.is_null());
857 base::MessageLoop::current()->PostTask(
858 FROM_HERE, base::Bind(callback, GDATA_NO_CONNECTION,
859 base::Passed(scoped_ptr<FileResource>())));
860 return CancelCallback();
863 EntryInfo* entry = FindEntryByResourceId(resource_id);
865 base::MessageLoop::current()->PostTask(
867 base::Bind(callback, HTTP_NOT_FOUND,
868 base::Passed(scoped_ptr<FileResource>())));
869 return CancelCallback();
872 if (!UserHasWriteAccess(entry->user_permission)) {
873 base::MessageLoop::current()->PostTask(
875 base::Bind(callback, HTTP_FORBIDDEN,
876 base::Passed(scoped_ptr<FileResource>())));
877 return CancelCallback();
880 ChangeResource* change = &entry->change_resource;
881 FileResource* file = change->mutable_file();
883 if (!new_title.empty())
884 file->set_title(new_title);
886 // Set parent if necessary.
887 if (!parent_resource_id.empty()) {
888 ParentReference parent;
889 parent.set_file_id(parent_resource_id);
890 parent.set_parent_link(GetFakeLinkUrl(parent_resource_id));
892 std::vector<ParentReference> parents;
893 parents.push_back(parent);
894 *file->mutable_parents() = parents;
897 if (!last_modified.is_null())
898 file->set_modified_date(last_modified);
900 if (!last_viewed_by_me.is_null())
901 file->set_last_viewed_by_me_date(last_viewed_by_me);
903 AddNewChangestamp(change);
906 base::MessageLoop::current()->PostTask(
908 base::Bind(callback, HTTP_SUCCESS,
909 base::Passed(make_scoped_ptr(new FileResource(*file)))));
910 base::MessageLoop::current()->PostTask(
912 base::Bind(&FakeDriveService::NotifyObservers,
913 weak_ptr_factory_.GetWeakPtr()));
914 return CancelCallback();
917 CancelCallback FakeDriveService::AddResourceToDirectory(
918 const std::string& parent_resource_id,
919 const std::string& resource_id,
920 const EntryActionCallback& callback) {
921 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
922 DCHECK(!callback.is_null());
925 base::MessageLoop::current()->PostTask(
926 FROM_HERE, base::Bind(callback, GDATA_NO_CONNECTION));
927 return CancelCallback();
930 EntryInfo* entry = FindEntryByResourceId(resource_id);
932 base::MessageLoop::current()->PostTask(
933 FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND));
934 return CancelCallback();
937 ChangeResource* change = &entry->change_resource;
938 // On the real Drive server, resources do not necessary shape a tree
939 // structure. That is, each resource can have multiple parent.
940 // We mimic the behavior here; AddResourceToDirectoy just adds
941 // one more parent, not overwriting old ones.
942 ParentReference parent;
943 parent.set_file_id(parent_resource_id);
944 parent.set_parent_link(GetFakeLinkUrl(parent_resource_id));
945 change->mutable_file()->mutable_parents()->push_back(parent);
947 AddNewChangestamp(change);
948 base::MessageLoop::current()->PostTask(
949 FROM_HERE, base::Bind(callback, HTTP_SUCCESS));
950 base::MessageLoop::current()->PostTask(
952 base::Bind(&FakeDriveService::NotifyObservers,
953 weak_ptr_factory_.GetWeakPtr()));
954 return CancelCallback();
957 CancelCallback FakeDriveService::RemoveResourceFromDirectory(
958 const std::string& parent_resource_id,
959 const std::string& resource_id,
960 const EntryActionCallback& callback) {
961 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
962 DCHECK(!callback.is_null());
965 base::MessageLoop::current()->PostTask(
966 FROM_HERE, base::Bind(callback, GDATA_NO_CONNECTION));
967 return CancelCallback();
970 EntryInfo* entry = FindEntryByResourceId(resource_id);
972 base::MessageLoop::current()->PostTask(
973 FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND));
974 return CancelCallback();
977 ChangeResource* change = &entry->change_resource;
978 FileResource* file = change->mutable_file();
979 std::vector<ParentReference>* parents = file->mutable_parents();
980 for (size_t i = 0; i < parents->size(); ++i) {
981 if ((*parents)[i].file_id() == parent_resource_id) {
982 parents->erase(parents->begin() + i);
983 AddNewChangestamp(change);
984 base::MessageLoop::current()->PostTask(
985 FROM_HERE, base::Bind(callback, HTTP_NO_CONTENT));
986 base::MessageLoop::current()->PostTask(
988 base::Bind(&FakeDriveService::NotifyObservers,
989 weak_ptr_factory_.GetWeakPtr()));
990 return CancelCallback();
994 base::MessageLoop::current()->PostTask(
995 FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND));
996 return CancelCallback();
999 CancelCallback FakeDriveService::AddNewDirectory(
1000 const std::string& parent_resource_id,
1001 const std::string& directory_title,
1002 const AddNewDirectoryOptions& options,
1003 const FileResourceCallback& callback) {
1004 return AddNewDirectoryWithResourceId(
1006 parent_resource_id.empty() ? GetRootResourceId() : parent_resource_id,
1012 CancelCallback FakeDriveService::InitiateUploadNewFile(
1013 const std::string& content_type,
1014 int64 content_length,
1015 const std::string& parent_resource_id,
1016 const std::string& title,
1017 const InitiateUploadNewFileOptions& options,
1018 const InitiateUploadCallback& callback) {
1019 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1020 DCHECK(!callback.is_null());
1023 base::MessageLoop::current()->PostTask(
1025 base::Bind(callback, GDATA_NO_CONNECTION, GURL()));
1026 return CancelCallback();
1029 if (parent_resource_id != GetRootResourceId() &&
1030 !entries_.count(parent_resource_id)) {
1031 base::MessageLoop::current()->PostTask(
1033 base::Bind(callback, HTTP_NOT_FOUND, GURL()));
1034 return CancelCallback();
1037 GURL session_url = GetNewUploadSessionUrl();
1038 upload_sessions_[session_url] =
1039 UploadSession(content_type, content_length,
1045 base::MessageLoop::current()->PostTask(
1047 base::Bind(callback, HTTP_SUCCESS, session_url));
1048 return CancelCallback();
1051 CancelCallback FakeDriveService::InitiateUploadExistingFile(
1052 const std::string& content_type,
1053 int64 content_length,
1054 const std::string& resource_id,
1055 const InitiateUploadExistingFileOptions& options,
1056 const InitiateUploadCallback& callback) {
1057 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1058 DCHECK(!callback.is_null());
1061 base::MessageLoop::current()->PostTask(
1063 base::Bind(callback, GDATA_NO_CONNECTION, GURL()));
1064 return CancelCallback();
1067 EntryInfo* entry = FindEntryByResourceId(resource_id);
1069 base::MessageLoop::current()->PostTask(
1071 base::Bind(callback, HTTP_NOT_FOUND, GURL()));
1072 return CancelCallback();
1075 if (!UserHasWriteAccess(entry->user_permission)) {
1076 base::MessageLoop::current()->PostTask(
1078 base::Bind(callback, HTTP_FORBIDDEN, GURL()));
1079 return CancelCallback();
1082 FileResource* file = entry->change_resource.mutable_file();
1083 if (!options.etag.empty() && options.etag != file->etag()) {
1084 base::MessageLoop::current()->PostTask(
1086 base::Bind(callback, HTTP_PRECONDITION, GURL()));
1087 return CancelCallback();
1089 // TODO(hashimoto): Update |file|'s metadata with |options|.
1091 GURL session_url = GetNewUploadSessionUrl();
1092 upload_sessions_[session_url] =
1093 UploadSession(content_type, content_length,
1094 "", // parent_resource_id
1099 base::MessageLoop::current()->PostTask(
1101 base::Bind(callback, HTTP_SUCCESS, session_url));
1102 return CancelCallback();
1105 CancelCallback FakeDriveService::GetUploadStatus(
1106 const GURL& upload_url,
1107 int64 content_length,
1108 const UploadRangeCallback& callback) {
1109 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1110 DCHECK(!callback.is_null());
1111 return CancelCallback();
1114 CancelCallback FakeDriveService::ResumeUpload(
1115 const GURL& upload_url,
1116 int64 start_position,
1118 int64 content_length,
1119 const std::string& content_type,
1120 const base::FilePath& local_file_path,
1121 const UploadRangeCallback& callback,
1122 const ProgressCallback& progress_callback) {
1123 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1124 DCHECK(!callback.is_null());
1126 FileResourceCallback completion_callback
1127 = base::Bind(&ScheduleUploadRangeCallback,
1128 callback, start_position, end_position);
1131 completion_callback.Run(GDATA_NO_CONNECTION, scoped_ptr<FileResource>());
1132 return CancelCallback();
1135 if (!upload_sessions_.count(upload_url)) {
1136 completion_callback.Run(HTTP_NOT_FOUND, scoped_ptr<FileResource>());
1137 return CancelCallback();
1140 UploadSession* session = &upload_sessions_[upload_url];
1142 // Chunks are required to be sent in such a ways that they fill from the start
1143 // of the not-yet-uploaded part with no gaps nor overlaps.
1144 if (session->uploaded_size != start_position) {
1145 completion_callback.Run(HTTP_BAD_REQUEST, scoped_ptr<FileResource>());
1146 return CancelCallback();
1149 if (!progress_callback.is_null()) {
1150 // In the real GDataWapi/Drive DriveService, progress is reported in
1151 // nondeterministic timing. In this fake implementation, we choose to call
1152 // it twice per one ResumeUpload. This is for making sure that client code
1153 // works fine even if the callback is invoked more than once; it is the
1154 // crucial difference of the progress callback from others.
1155 // Note that progress is notified in the relative offset in each chunk.
1156 const int64 chunk_size = end_position - start_position;
1157 base::MessageLoop::current()->PostTask(
1158 FROM_HERE, base::Bind(progress_callback, chunk_size / 2, chunk_size));
1159 base::MessageLoop::current()->PostTask(
1160 FROM_HERE, base::Bind(progress_callback, chunk_size, chunk_size));
1163 if (content_length != end_position) {
1164 session->uploaded_size = end_position;
1165 completion_callback.Run(HTTP_RESUME_INCOMPLETE, scoped_ptr<FileResource>());
1166 return CancelCallback();
1169 std::string content_data;
1170 if (!base::ReadFileToString(local_file_path, &content_data)) {
1171 session->uploaded_size = end_position;
1172 completion_callback.Run(GDATA_FILE_ERROR, scoped_ptr<FileResource>());
1173 return CancelCallback();
1175 session->uploaded_size = end_position;
1177 // |resource_id| is empty if the upload is for new file.
1178 if (session->resource_id.empty()) {
1179 DCHECK(!session->parent_resource_id.empty());
1180 DCHECK(!session->title.empty());
1181 const EntryInfo* new_entry = AddNewEntry(
1182 "", // auto generate resource id.
1183 session->content_type,
1185 session->parent_resource_id,
1187 false); // shared_with_me
1189 completion_callback.Run(HTTP_NOT_FOUND, scoped_ptr<FileResource>());
1190 return CancelCallback();
1193 completion_callback.Run(HTTP_CREATED, make_scoped_ptr(
1194 new FileResource(*new_entry->change_resource.file())));
1195 base::MessageLoop::current()->PostTask(
1197 base::Bind(&FakeDriveService::NotifyObservers,
1198 weak_ptr_factory_.GetWeakPtr()));
1199 return CancelCallback();
1202 EntryInfo* entry = FindEntryByResourceId(session->resource_id);
1204 completion_callback.Run(HTTP_NOT_FOUND, scoped_ptr<FileResource>());
1205 return CancelCallback();
1208 ChangeResource* change = &entry->change_resource;
1209 FileResource* file = change->mutable_file();
1210 if (file->etag().empty() || session->etag != file->etag()) {
1211 completion_callback.Run(HTTP_PRECONDITION, scoped_ptr<FileResource>());
1212 return CancelCallback();
1215 file->set_md5_checksum(base::MD5String(content_data));
1216 entry->content_data = content_data;
1217 file->set_file_size(end_position);
1218 AddNewChangestamp(change);
1221 completion_callback.Run(HTTP_SUCCESS, make_scoped_ptr(
1222 new FileResource(*file)));
1223 base::MessageLoop::current()->PostTask(
1225 base::Bind(&FakeDriveService::NotifyObservers,
1226 weak_ptr_factory_.GetWeakPtr()));
1227 return CancelCallback();
1230 CancelCallback FakeDriveService::AuthorizeApp(
1231 const std::string& resource_id,
1232 const std::string& app_id,
1233 const AuthorizeAppCallback& callback) {
1234 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1235 DCHECK(!callback.is_null());
1237 if (entries_.count(resource_id) == 0) {
1238 callback.Run(google_apis::HTTP_NOT_FOUND, GURL());
1239 return CancelCallback();
1242 callback.Run(HTTP_SUCCESS,
1243 GURL(base::StringPrintf(open_url_format_.c_str(),
1244 resource_id.c_str(),
1246 return CancelCallback();
1249 CancelCallback FakeDriveService::UninstallApp(
1250 const std::string& app_id,
1251 const google_apis::EntryActionCallback& callback) {
1252 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1253 DCHECK(!callback.is_null());
1256 base::MessageLoop::current()->PostTask(
1258 base::Bind(callback, google_apis::GDATA_NO_CONNECTION));
1259 return CancelCallback();
1262 // Find app_id from app_info_value_ and delete.
1263 base::ListValue* items = NULL;
1264 if (!app_info_value_->GetList("items", &items)) {
1265 base::MessageLoop::current()->PostTask(
1267 base::Bind(callback, google_apis::HTTP_NOT_FOUND));
1268 return CancelCallback();
1271 for (size_t i = 0; i < items->GetSize(); ++i) {
1272 base::DictionaryValue* item = NULL;
1274 if (items->GetDictionary(i, &item) && item->GetString("id", &id) &&
1276 base::MessageLoop::current()->PostTask(
1278 base::Bind(callback,
1279 items->Remove(i, NULL) ? google_apis::HTTP_NO_CONTENT
1280 : google_apis::HTTP_NOT_FOUND));
1281 return CancelCallback();
1285 base::MessageLoop::current()->PostTask(
1287 base::Bind(callback, google_apis::HTTP_NOT_FOUND));
1288 return CancelCallback();
1291 void FakeDriveService::AddNewFile(const std::string& content_type,
1292 const std::string& content_data,
1293 const std::string& parent_resource_id,
1294 const std::string& title,
1295 bool shared_with_me,
1296 const FileResourceCallback& callback) {
1297 AddNewFileWithResourceId("", content_type, content_data, parent_resource_id,
1298 title, shared_with_me, callback);
1301 void FakeDriveService::AddNewFileWithResourceId(
1302 const std::string& resource_id,
1303 const std::string& content_type,
1304 const std::string& content_data,
1305 const std::string& parent_resource_id,
1306 const std::string& title,
1307 bool shared_with_me,
1308 const FileResourceCallback& callback) {
1309 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1310 DCHECK(!callback.is_null());
1313 base::MessageLoop::current()->PostTask(
1315 base::Bind(callback,
1316 GDATA_NO_CONNECTION,
1317 base::Passed(scoped_ptr<FileResource>())));
1321 const EntryInfo* new_entry = AddNewEntry(resource_id,
1328 base::MessageLoop::current()->PostTask(
1330 base::Bind(callback, HTTP_NOT_FOUND,
1331 base::Passed(scoped_ptr<FileResource>())));
1335 base::MessageLoop::current()->PostTask(
1337 base::Bind(callback, HTTP_CREATED,
1338 base::Passed(make_scoped_ptr(
1339 new FileResource(*new_entry->change_resource.file())))));
1340 base::MessageLoop::current()->PostTask(
1342 base::Bind(&FakeDriveService::NotifyObservers,
1343 weak_ptr_factory_.GetWeakPtr()));
1346 CancelCallback FakeDriveService::AddNewDirectoryWithResourceId(
1347 const std::string& resource_id,
1348 const std::string& parent_resource_id,
1349 const std::string& directory_title,
1350 const AddNewDirectoryOptions& options,
1351 const FileResourceCallback& callback) {
1352 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1353 DCHECK(!callback.is_null());
1356 base::MessageLoop::current()->PostTask(
1358 base::Bind(callback,
1359 GDATA_NO_CONNECTION,
1360 base::Passed(scoped_ptr<FileResource>())));
1361 return CancelCallback();
1364 const EntryInfo* new_entry = AddNewEntry(resource_id,
1365 util::kDriveFolderMimeType,
1369 false); // shared_with_me
1371 base::MessageLoop::current()->PostTask(
1373 base::Bind(callback, HTTP_NOT_FOUND,
1374 base::Passed(scoped_ptr<FileResource>())));
1375 return CancelCallback();
1378 base::MessageLoop::current()->PostTask(
1380 base::Bind(callback, HTTP_CREATED,
1381 base::Passed(make_scoped_ptr(
1382 new FileResource(*new_entry->change_resource.file())))));
1383 base::MessageLoop::current()->PostTask(
1385 base::Bind(&FakeDriveService::NotifyObservers,
1386 weak_ptr_factory_.GetWeakPtr()));
1387 return CancelCallback();
1390 void FakeDriveService::SetLastModifiedTime(
1391 const std::string& resource_id,
1392 const base::Time& last_modified_time,
1393 const FileResourceCallback& callback) {
1394 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1395 DCHECK(!callback.is_null());
1398 base::MessageLoop::current()->PostTask(
1400 base::Bind(callback,
1401 GDATA_NO_CONNECTION,
1402 base::Passed(scoped_ptr<FileResource>())));
1406 EntryInfo* entry = FindEntryByResourceId(resource_id);
1408 base::MessageLoop::current()->PostTask(
1410 base::Bind(callback, HTTP_NOT_FOUND,
1411 base::Passed(scoped_ptr<FileResource>())));
1415 ChangeResource* change = &entry->change_resource;
1416 FileResource* file = change->mutable_file();
1417 file->set_modified_date(last_modified_time);
1419 base::MessageLoop::current()->PostTask(
1421 base::Bind(callback, HTTP_SUCCESS,
1422 base::Passed(make_scoped_ptr(new FileResource(*file)))));
1425 google_apis::GDataErrorCode FakeDriveService::SetUserPermission(
1426 const std::string& resource_id,
1427 google_apis::drive::PermissionRole user_permission) {
1428 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1430 EntryInfo* entry = FindEntryByResourceId(resource_id);
1432 return HTTP_NOT_FOUND;
1434 entry->user_permission = user_permission;
1435 return HTTP_SUCCESS;
1438 void FakeDriveService::AddChangeObserver(ChangeObserver* change_observer) {
1439 change_observers_.AddObserver(change_observer);
1442 void FakeDriveService::RemoveChangeObserver(ChangeObserver* change_observer) {
1443 change_observers_.RemoveObserver(change_observer);
1446 FakeDriveService::EntryInfo* FakeDriveService::FindEntryByResourceId(
1447 const std::string& resource_id) {
1448 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1450 EntryInfoMap::iterator it = entries_.find(resource_id);
1451 // Deleted entries don't have FileResource.
1452 return it != entries_.end() && it->second->change_resource.file() ?
1456 std::string FakeDriveService::GetNewResourceId() {
1457 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1459 ++resource_id_count_;
1460 return base::StringPrintf("resource_id_%d", resource_id_count_);
1463 void FakeDriveService::UpdateETag(google_apis::FileResource* file) {
1465 "etag_" + base::Int64ToString(about_resource_->largest_change_id()));
1468 void FakeDriveService::AddNewChangestamp(google_apis::ChangeResource* change) {
1469 about_resource_->set_largest_change_id(
1470 about_resource_->largest_change_id() + 1);
1471 change->set_change_id(about_resource_->largest_change_id());
1474 const FakeDriveService::EntryInfo* FakeDriveService::AddNewEntry(
1475 const std::string& given_resource_id,
1476 const std::string& content_type,
1477 const std::string& content_data,
1478 const std::string& parent_resource_id,
1479 const std::string& title,
1480 bool shared_with_me) {
1481 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1483 if (!parent_resource_id.empty() &&
1484 parent_resource_id != GetRootResourceId() &&
1485 !entries_.count(parent_resource_id)) {
1489 const std::string resource_id =
1490 given_resource_id.empty() ? GetNewResourceId() : given_resource_id;
1491 if (entries_.count(resource_id))
1493 GURL upload_url = GURL("https://xxx/upload/" + resource_id);
1495 scoped_ptr<EntryInfo> new_entry(new EntryInfo);
1496 ChangeResource* new_change = &new_entry->change_resource;
1497 FileResource* new_file = new FileResource;
1498 new_change->set_file(make_scoped_ptr(new_file));
1500 // Set the resource ID and the title
1501 new_change->set_file_id(resource_id);
1502 new_file->set_file_id(resource_id);
1503 new_file->set_title(title);
1504 // Set the contents, size and MD5 for a file.
1505 if (content_type != util::kDriveFolderMimeType &&
1506 !util::IsKnownHostedDocumentMimeType(content_type)) {
1507 new_entry->content_data = content_data;
1508 new_file->set_file_size(content_data.size());
1509 new_file->set_md5_checksum(base::MD5String(content_data));
1512 if (shared_with_me) {
1513 // Set current time to mark the file as shared_with_me.
1514 new_file->set_shared_with_me_date(base::Time::Now());
1517 std::string escaped_resource_id = net::EscapePath(resource_id);
1520 new_file->set_mime_type(content_type);
1522 // Set alternate link if needed.
1523 if (content_type == util::kGoogleDocumentMimeType)
1524 new_file->set_alternate_link(GURL("https://document_alternate_link"));
1527 if (!parent_resource_id.empty()) {
1528 ParentReference parent;
1529 parent.set_file_id(parent_resource_id);
1530 parent.set_parent_link(GetFakeLinkUrl(parent.file_id()));
1531 std::vector<ParentReference> parents;
1532 parents.push_back(parent);
1533 *new_file->mutable_parents() = parents;
1536 new_entry->share_url = net::AppendOrReplaceQueryParameter(
1537 share_url_base_, "name", title);
1539 AddNewChangestamp(new_change);
1540 UpdateETag(new_file);
1542 base::Time published_date =
1543 base::Time() + base::TimeDelta::FromMilliseconds(++published_date_seq_);
1544 new_file->set_created_date(published_date);
1546 EntryInfo* raw_new_entry = new_entry.release();
1547 entries_[resource_id] = raw_new_entry;
1548 return raw_new_entry;
1551 void FakeDriveService::GetChangeListInternal(
1552 int64 start_changestamp,
1553 const std::string& search_query,
1554 const std::string& directory_resource_id,
1558 const ChangeListCallback& callback) {
1560 base::MessageLoop::current()->PostTask(
1562 base::Bind(callback,
1563 GDATA_NO_CONNECTION,
1564 base::Passed(scoped_ptr<ChangeList>())));
1568 // Filter out entries per parameters like |directory_resource_id| and
1570 ScopedVector<ChangeResource> entries;
1571 int num_entries_matched = 0;
1572 for (EntryInfoMap::iterator it = entries_.begin(); it != entries_.end();
1574 const ChangeResource& entry = it->second->change_resource;
1575 bool should_exclude = false;
1577 // If |directory_resource_id| is set, exclude the entry if it's not in
1578 // the target directory.
1579 if (!directory_resource_id.empty()) {
1580 // Get the parent resource ID of the entry.
1581 std::string parent_resource_id;
1582 if (entry.file() && !entry.file()->parents().empty())
1583 parent_resource_id = entry.file()->parents()[0].file_id();
1585 if (directory_resource_id != parent_resource_id)
1586 should_exclude = true;
1589 // If |search_query| is set, exclude the entry if it does not contain the
1590 // search query in the title.
1591 if (!should_exclude && !search_query.empty() &&
1592 !EntryMatchWithQuery(entry, search_query)) {
1593 should_exclude = true;
1596 // If |start_changestamp| is set, exclude the entry if the
1597 // changestamp is older than |largest_changestamp|.
1598 // See https://developers.google.com/google-apps/documents-list/
1599 // #retrieving_all_changes_since_a_given_changestamp
1600 if (start_changestamp > 0 && entry.change_id() < start_changestamp)
1601 should_exclude = true;
1603 // If the caller requests other list than change list by specifying
1604 // zero-|start_changestamp|, exclude deleted entry from the result.
1605 const bool deleted = entry.is_deleted() ||
1606 (entry.file() && entry.file()->labels().is_trashed());
1607 if (!start_changestamp && deleted)
1608 should_exclude = true;
1610 // The entry matched the criteria for inclusion.
1611 if (!should_exclude)
1612 ++num_entries_matched;
1614 // If |start_offset| is set, exclude the entry if the entry is before the
1615 // start index. <= instead of < as |num_entries_matched| was
1616 // already incremented.
1617 if (start_offset > 0 && num_entries_matched <= start_offset)
1618 should_exclude = true;
1620 if (!should_exclude) {
1621 scoped_ptr<ChangeResource> entry_copied(new ChangeResource);
1622 entry_copied->set_change_id(entry.change_id());
1623 entry_copied->set_file_id(entry.file_id());
1624 entry_copied->set_deleted(entry.is_deleted());
1626 entry_copied->set_file(
1627 make_scoped_ptr(new FileResource(*entry.file())));
1629 entry_copied->set_modification_date(entry.modification_date());
1630 entries.push_back(entry_copied.release());
1634 scoped_ptr<ChangeList> change_list(new ChangeList);
1635 if (start_changestamp > 0 && start_offset == 0) {
1636 change_list->set_largest_change_id(about_resource_->largest_change_id());
1639 // If |max_results| is set, trim the entries if the number exceeded the max
1641 if (max_results > 0 && entries.size() > static_cast<size_t>(max_results)) {
1642 entries.erase(entries.begin() + max_results, entries.end());
1643 // Adds the next URL.
1644 // Here, we embed information which is needed for continuing the
1645 // GetChangeList request in the next invocation into url query
1647 GURL next_url(base::StringPrintf(
1648 "http://localhost/?start-offset=%d&max-results=%d",
1649 start_offset + max_results,
1651 if (start_changestamp > 0) {
1652 next_url = net::AppendOrReplaceQueryParameter(
1653 next_url, "changestamp",
1654 base::Int64ToString(start_changestamp).c_str());
1656 if (!search_query.empty()) {
1657 next_url = net::AppendOrReplaceQueryParameter(
1658 next_url, "q", search_query);
1660 if (!directory_resource_id.empty()) {
1661 next_url = net::AppendOrReplaceQueryParameter(
1662 next_url, "parent", directory_resource_id);
1665 change_list->set_next_link(next_url);
1667 *change_list->mutable_items() = entries.Pass();
1671 base::MessageLoop::current()->PostTask(
1673 base::Bind(callback, HTTP_SUCCESS, base::Passed(&change_list)));
1676 GURL FakeDriveService::GetNewUploadSessionUrl() {
1677 return GURL("https://upload_session_url/" +
1678 base::Int64ToString(next_upload_sequence_number_++));
1681 google_apis::CancelCallback FakeDriveService::AddPermission(
1682 const std::string& resource_id,
1683 const std::string& email,
1684 google_apis::drive::PermissionRole role,
1685 const google_apis::EntryActionCallback& callback) {
1686 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1687 DCHECK(!callback.is_null());
1690 return CancelCallback();
1693 void FakeDriveService::NotifyObservers() {
1694 FOR_EACH_OBSERVER(ChangeObserver, change_observers_, OnNewChangeAvailable());
1697 } // namespace drive