OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/drive/fake_drive_service.h" | 5 #include "chrome/browser/drive/fake_drive_service.h" |
6 | 6 |
7 #include <string> | 7 #include <string> |
8 | 8 |
| 9 #include "base/file_util.h" |
9 #include "base/logging.h" | 10 #include "base/logging.h" |
| 11 #include "base/md5.h" |
10 #include "base/message_loop.h" | 12 #include "base/message_loop.h" |
11 #include "base/strings/string_number_conversions.h" | 13 #include "base/strings/string_number_conversions.h" |
12 #include "base/strings/string_split.h" | 14 #include "base/strings/string_split.h" |
13 #include "base/strings/string_tokenizer.h" | 15 #include "base/strings/string_tokenizer.h" |
14 #include "base/strings/string_util.h" | 16 #include "base/strings/string_util.h" |
15 #include "base/strings/stringprintf.h" | 17 #include "base/strings/stringprintf.h" |
16 #include "base/strings/utf_string_conversions.h" | 18 #include "base/strings/utf_string_conversions.h" |
17 #include "chrome/browser/google_apis/drive_api_parser.h" | 19 #include "chrome/browser/google_apis/drive_api_parser.h" |
18 #include "chrome/browser/google_apis/gdata_wapi_parser.h" | 20 #include "chrome/browser/google_apis/gdata_wapi_parser.h" |
19 #include "chrome/browser/google_apis/test_util.h" | 21 #include "chrome/browser/google_apis/test_util.h" |
(...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
89 // TODO(peria): Deal with other attributes than title. | 91 // TODO(peria): Deal with other attributes than title. |
90 if (!key.empty() && key != "title") | 92 if (!key.empty() && key != "title") |
91 return false; | 93 return false; |
92 // Search query in the title. | 94 // Search query in the title. |
93 if (entry.title().find(value) == std::string::npos) | 95 if (entry.title().find(value) == std::string::npos) |
94 return false; | 96 return false; |
95 } | 97 } |
96 return true; | 98 return true; |
97 } | 99 } |
98 | 100 |
99 // Gets the upload URL from the given entry. Returns an empty URL if not | |
100 // found. | |
101 GURL GetUploadUrl(const base::DictionaryValue& entry) { | |
102 std::string upload_url; | |
103 const base::ListValue* links = NULL; | |
104 if (entry.GetList("link", &links) && links) { | |
105 for (size_t link_index = 0; | |
106 link_index < links->GetSize(); | |
107 ++link_index) { | |
108 const base::DictionaryValue* link = NULL; | |
109 std::string rel; | |
110 if (links->GetDictionary(link_index, &link) && | |
111 link && link->GetString("rel", &rel) && | |
112 rel == kUploadUrlRel && | |
113 link->GetString("href", &upload_url)) { | |
114 break; | |
115 } | |
116 } | |
117 } | |
118 return GURL(upload_url); | |
119 } | |
120 | |
121 // Returns |url| without query parameter. | 101 // Returns |url| without query parameter. |
122 GURL RemoveQueryParameter(const GURL& url) { | 102 GURL RemoveQueryParameter(const GURL& url) { |
123 GURL::Replacements replacements; | 103 GURL::Replacements replacements; |
124 replacements.ClearQuery(); | 104 replacements.ClearQuery(); |
125 return url.ReplaceComponents(replacements); | 105 return url.ReplaceComponents(replacements); |
126 } | 106 } |
127 | 107 |
| 108 void ScheduleUploadRangeCallback(const UploadRangeCallback& callback, |
| 109 int64 start_position, |
| 110 int64 end_position, |
| 111 GDataErrorCode error, |
| 112 scoped_ptr<ResourceEntry> entry) { |
| 113 base::MessageLoop::current()->PostTask( |
| 114 FROM_HERE, |
| 115 base::Bind(callback, |
| 116 UploadRangeResponse(error, |
| 117 start_position, |
| 118 end_position), |
| 119 base::Passed(&entry))); |
| 120 } |
| 121 |
128 } // namespace | 122 } // namespace |
129 | 123 |
| 124 struct FakeDriveService::UploadSession { |
| 125 std::string content_type; |
| 126 int64 content_length; |
| 127 std::string parent_resource_id; |
| 128 std::string resource_id; |
| 129 std::string etag; |
| 130 std::string title; |
| 131 |
| 132 int64 uploaded_size; |
| 133 |
| 134 UploadSession() |
| 135 : content_length(0), |
| 136 uploaded_size(0) {} |
| 137 |
| 138 UploadSession( |
| 139 std::string content_type, |
| 140 int64 content_length, |
| 141 std::string parent_resource_id, |
| 142 std::string resource_id, |
| 143 std::string etag, |
| 144 std::string title) |
| 145 : content_type(content_type), |
| 146 content_length(content_length), |
| 147 parent_resource_id(parent_resource_id), |
| 148 resource_id(resource_id), |
| 149 etag(etag), |
| 150 title(title), |
| 151 uploaded_size(0) { |
| 152 } |
| 153 }; |
| 154 |
130 FakeDriveService::FakeDriveService() | 155 FakeDriveService::FakeDriveService() |
131 : largest_changestamp_(0), | 156 : largest_changestamp_(0), |
132 published_date_seq_(0), | 157 published_date_seq_(0), |
| 158 next_upload_sequence_number_(0), |
133 default_max_results_(0), | 159 default_max_results_(0), |
134 resource_id_count_(0), | 160 resource_id_count_(0), |
135 resource_list_load_count_(0), | 161 resource_list_load_count_(0), |
136 change_list_load_count_(0), | 162 change_list_load_count_(0), |
137 directory_load_count_(0), | 163 directory_load_count_(0), |
138 about_resource_load_count_(0), | 164 about_resource_load_count_(0), |
139 offline_(false) { | 165 offline_(false) { |
140 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 166 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
141 } | 167 } |
142 | 168 |
(...skipping 397 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
540 if (!entry) { | 566 if (!entry) { |
541 base::MessageLoopProxy::current()->PostTask( | 567 base::MessageLoopProxy::current()->PostTask( |
542 FROM_HERE, | 568 FROM_HERE, |
543 base::Bind(download_action_callback, HTTP_NOT_FOUND, base::FilePath())); | 569 base::Bind(download_action_callback, HTTP_NOT_FOUND, base::FilePath())); |
544 return CancelCallback(); | 570 return CancelCallback(); |
545 } | 571 } |
546 | 572 |
547 // Write "x"s of the file size specified in the entry. | 573 // Write "x"s of the file size specified in the entry. |
548 std::string file_size_string; | 574 std::string file_size_string; |
549 entry->GetString("docs$size.$t", &file_size_string); | 575 entry->GetString("docs$size.$t", &file_size_string); |
550 // TODO(satorux): To be correct, we should update docs$md5Checksum.$t here. | |
551 int64 file_size = 0; | 576 int64 file_size = 0; |
552 if (base::StringToInt64(file_size_string, &file_size)) { | 577 if (base::StringToInt64(file_size_string, &file_size)) { |
553 base::BinaryValue* content_binary_data; | 578 base::BinaryValue* content_binary_data; |
554 std::string content_data; | 579 std::string content_data; |
555 if (entry->GetBinary("test$data", &content_binary_data)) { | 580 if (entry->GetBinary("test$data", &content_binary_data)) { |
556 content_data = std::string(content_binary_data->GetBuffer(), | 581 content_data = std::string(content_binary_data->GetBuffer(), |
557 content_binary_data->GetSize()); | 582 content_binary_data->GetSize()); |
558 } | 583 } |
559 DCHECK_EQ(static_cast<size_t>(file_size), content_data.size()); | 584 DCHECK_EQ(static_cast<size_t>(file_size), content_data.size()); |
560 | 585 |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
642 entry->Set("link", links); | 667 entry->Set("link", links); |
643 } | 668 } |
644 links->Clear(); | 669 links->Clear(); |
645 | 670 |
646 base::DictionaryValue* link = new base::DictionaryValue; | 671 base::DictionaryValue* link = new base::DictionaryValue; |
647 link->SetString( | 672 link->SetString( |
648 "rel", "http://schemas.google.com/docs/2007#parent"); | 673 "rel", "http://schemas.google.com/docs/2007#parent"); |
649 link->SetString("href", GetFakeLinkUrl(parent_resource_id).spec()); | 674 link->SetString("href", GetFakeLinkUrl(parent_resource_id).spec()); |
650 links->Append(link); | 675 links->Append(link); |
651 | 676 |
652 AddNewChangestamp(copied_entry.get()); | 677 AddNewChangestampAndETag(copied_entry.get()); |
653 | 678 |
654 // Parse the new entry. | 679 // Parse the new entry. |
655 scoped_ptr<ResourceEntry> resource_entry = | 680 scoped_ptr<ResourceEntry> resource_entry = |
656 ResourceEntry::CreateFrom(*copied_entry); | 681 ResourceEntry::CreateFrom(*copied_entry); |
657 // Add it to the resource list. | 682 // Add it to the resource list. |
658 entries->Append(copied_entry.release()); | 683 entries->Append(copied_entry.release()); |
659 | 684 |
660 base::MessageLoop::current()->PostTask( | 685 base::MessageLoop::current()->PostTask( |
661 FROM_HERE, | 686 FROM_HERE, |
662 base::Bind(callback, | 687 base::Bind(callback, |
(...skipping 30 matching lines...) Expand all Loading... |
693 | 718 |
694 if (offline_) { | 719 if (offline_) { |
695 base::MessageLoop::current()->PostTask( | 720 base::MessageLoop::current()->PostTask( |
696 FROM_HERE, base::Bind(callback, GDATA_NO_CONNECTION)); | 721 FROM_HERE, base::Bind(callback, GDATA_NO_CONNECTION)); |
697 return CancelCallback(); | 722 return CancelCallback(); |
698 } | 723 } |
699 | 724 |
700 base::DictionaryValue* entry = FindEntryByResourceId(resource_id); | 725 base::DictionaryValue* entry = FindEntryByResourceId(resource_id); |
701 if (entry) { | 726 if (entry) { |
702 entry->SetString("title.$t", new_name); | 727 entry->SetString("title.$t", new_name); |
703 AddNewChangestamp(entry); | 728 AddNewChangestampAndETag(entry); |
704 base::MessageLoop::current()->PostTask( | 729 base::MessageLoop::current()->PostTask( |
705 FROM_HERE, base::Bind(callback, HTTP_SUCCESS)); | 730 FROM_HERE, base::Bind(callback, HTTP_SUCCESS)); |
706 return CancelCallback(); | 731 return CancelCallback(); |
707 } | 732 } |
708 | 733 |
709 base::MessageLoop::current()->PostTask( | 734 base::MessageLoop::current()->PostTask( |
710 FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND)); | 735 FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND)); |
711 return CancelCallback(); | 736 return CancelCallback(); |
712 } | 737 } |
713 | 738 |
(...skipping 21 matching lines...) Expand all Loading... |
735 FROM_HERE, | 760 FROM_HERE, |
736 base::Bind(callback, HTTP_NOT_FOUND, | 761 base::Bind(callback, HTTP_NOT_FOUND, |
737 base::Passed(scoped_ptr<ResourceEntry>()))); | 762 base::Passed(scoped_ptr<ResourceEntry>()))); |
738 return CancelCallback(); | 763 return CancelCallback(); |
739 } | 764 } |
740 | 765 |
741 entry->SetString("updated.$t", | 766 entry->SetString("updated.$t", |
742 util::FormatTimeAsString(modified_date)); | 767 util::FormatTimeAsString(modified_date)); |
743 entry->SetString("gd$lastViewed.$t", | 768 entry->SetString("gd$lastViewed.$t", |
744 util::FormatTimeAsString(last_viewed_by_me_date)); | 769 util::FormatTimeAsString(last_viewed_by_me_date)); |
745 AddNewChangestamp(entry); | 770 AddNewChangestampAndETag(entry); |
746 | 771 |
747 scoped_ptr<ResourceEntry> parsed_entry(ResourceEntry::CreateFrom(*entry)); | 772 scoped_ptr<ResourceEntry> parsed_entry(ResourceEntry::CreateFrom(*entry)); |
748 base::MessageLoop::current()->PostTask( | 773 base::MessageLoop::current()->PostTask( |
749 FROM_HERE, | 774 FROM_HERE, |
750 base::Bind(callback, HTTP_SUCCESS, base::Passed(&parsed_entry))); | 775 base::Bind(callback, HTTP_SUCCESS, base::Passed(&parsed_entry))); |
751 return CancelCallback(); | 776 return CancelCallback(); |
752 } | 777 } |
753 | 778 |
754 CancelCallback FakeDriveService::AddResourceToDirectory( | 779 CancelCallback FakeDriveService::AddResourceToDirectory( |
755 const std::string& parent_resource_id, | 780 const std::string& parent_resource_id, |
(...skipping 19 matching lines...) Expand all Loading... |
775 // On the real Drive server, resources do not necessary shape a tree | 800 // On the real Drive server, resources do not necessary shape a tree |
776 // structure. That is, each resource can have multiple parent. | 801 // structure. That is, each resource can have multiple parent. |
777 // We mimic the behavior here; AddResourceToDirectoy just adds | 802 // We mimic the behavior here; AddResourceToDirectoy just adds |
778 // one more parent link, not overwriting old links. | 803 // one more parent link, not overwriting old links. |
779 base::DictionaryValue* link = new base::DictionaryValue; | 804 base::DictionaryValue* link = new base::DictionaryValue; |
780 link->SetString("rel", "http://schemas.google.com/docs/2007#parent"); | 805 link->SetString("rel", "http://schemas.google.com/docs/2007#parent"); |
781 link->SetString( | 806 link->SetString( |
782 "href", GetFakeLinkUrl(parent_resource_id).spec()); | 807 "href", GetFakeLinkUrl(parent_resource_id).spec()); |
783 links->Append(link); | 808 links->Append(link); |
784 | 809 |
785 AddNewChangestamp(entry); | 810 AddNewChangestampAndETag(entry); |
786 base::MessageLoop::current()->PostTask( | 811 base::MessageLoop::current()->PostTask( |
787 FROM_HERE, base::Bind(callback, HTTP_SUCCESS)); | 812 FROM_HERE, base::Bind(callback, HTTP_SUCCESS)); |
788 return CancelCallback(); | 813 return CancelCallback(); |
789 } | 814 } |
790 | 815 |
791 base::MessageLoop::current()->PostTask( | 816 base::MessageLoop::current()->PostTask( |
792 FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND)); | 817 FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND)); |
793 return CancelCallback(); | 818 return CancelCallback(); |
794 } | 819 } |
795 | 820 |
(...skipping 18 matching lines...) Expand all Loading... |
814 for (size_t i = 0; i < links->GetSize(); ++i) { | 839 for (size_t i = 0; i < links->GetSize(); ++i) { |
815 base::DictionaryValue* link = NULL; | 840 base::DictionaryValue* link = NULL; |
816 std::string rel; | 841 std::string rel; |
817 std::string href; | 842 std::string href; |
818 if (links->GetDictionary(i, &link) && | 843 if (links->GetDictionary(i, &link) && |
819 link->GetString("rel", &rel) && | 844 link->GetString("rel", &rel) && |
820 link->GetString("href", &href) && | 845 link->GetString("href", &href) && |
821 rel == "http://schemas.google.com/docs/2007#parent" && | 846 rel == "http://schemas.google.com/docs/2007#parent" && |
822 GURL(href) == parent_content_url) { | 847 GURL(href) == parent_content_url) { |
823 links->Remove(i, NULL); | 848 links->Remove(i, NULL); |
824 AddNewChangestamp(entry); | 849 AddNewChangestampAndETag(entry); |
825 base::MessageLoop::current()->PostTask( | 850 base::MessageLoop::current()->PostTask( |
826 FROM_HERE, base::Bind(callback, HTTP_SUCCESS)); | 851 FROM_HERE, base::Bind(callback, HTTP_SUCCESS)); |
827 return CancelCallback(); | 852 return CancelCallback(); |
828 } | 853 } |
829 } | 854 } |
830 } | 855 } |
831 } | 856 } |
832 | 857 |
833 base::MessageLoop::current()->PostTask( | 858 base::MessageLoop::current()->PostTask( |
834 FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND)); | 859 FROM_HERE, base::Bind(callback, HTTP_NOT_FOUND)); |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
883 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 908 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
884 DCHECK(!callback.is_null()); | 909 DCHECK(!callback.is_null()); |
885 | 910 |
886 if (offline_) { | 911 if (offline_) { |
887 base::MessageLoop::current()->PostTask( | 912 base::MessageLoop::current()->PostTask( |
888 FROM_HERE, | 913 FROM_HERE, |
889 base::Bind(callback, GDATA_NO_CONNECTION, GURL())); | 914 base::Bind(callback, GDATA_NO_CONNECTION, GURL())); |
890 return CancelCallback(); | 915 return CancelCallback(); |
891 } | 916 } |
892 | 917 |
893 // Content length should be zero, as we'll create an empty file first. The | 918 if (parent_resource_id != GetRootResourceId() && |
894 // content will be added in ResumeUpload(). | 919 !FindEntryByResourceId(parent_resource_id)) { |
895 const base::DictionaryValue* new_entry = AddNewEntry(content_type, | |
896 "", // content_data | |
897 parent_resource_id, | |
898 title, | |
899 false, // shared_with_me | |
900 "file"); | |
901 if (!new_entry) { | |
902 base::MessageLoop::current()->PostTask( | 920 base::MessageLoop::current()->PostTask( |
903 FROM_HERE, | 921 FROM_HERE, |
904 base::Bind(callback, HTTP_NOT_FOUND, GURL())); | 922 base::Bind(callback, HTTP_NOT_FOUND, GURL())); |
905 return CancelCallback(); | 923 return CancelCallback(); |
906 } | 924 } |
907 const GURL upload_url = GetUploadUrl(*new_entry); | 925 |
908 DCHECK(upload_url.is_valid()); | 926 GURL session_url = GetNewUploadSessionUrl(); |
| 927 upload_sessions_[session_url] = |
| 928 UploadSession(content_type, content_length, |
| 929 parent_resource_id, |
| 930 "", // resource_id |
| 931 "", // etag |
| 932 title); |
909 | 933 |
910 base::MessageLoop::current()->PostTask( | 934 base::MessageLoop::current()->PostTask( |
911 FROM_HERE, | 935 FROM_HERE, |
912 base::Bind(callback, HTTP_SUCCESS, | 936 base::Bind(callback, HTTP_SUCCESS, session_url)); |
913 net::AppendQueryParameter(upload_url, "mode", "newfile"))); | |
914 return CancelCallback(); | 937 return CancelCallback(); |
915 } | 938 } |
916 | 939 |
917 CancelCallback FakeDriveService::InitiateUploadExistingFile( | 940 CancelCallback FakeDriveService::InitiateUploadExistingFile( |
918 const std::string& content_type, | 941 const std::string& content_type, |
919 int64 content_length, | 942 int64 content_length, |
920 const std::string& resource_id, | 943 const std::string& resource_id, |
921 const std::string& etag, | 944 const std::string& etag, |
922 const InitiateUploadCallback& callback) { | 945 const InitiateUploadCallback& callback) { |
923 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 946 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
(...skipping 15 matching lines...) Expand all Loading... |
939 } | 962 } |
940 | 963 |
941 std::string entry_etag; | 964 std::string entry_etag; |
942 entry->GetString("gd$etag", &entry_etag); | 965 entry->GetString("gd$etag", &entry_etag); |
943 if (!etag.empty() && etag != entry_etag) { | 966 if (!etag.empty() && etag != entry_etag) { |
944 base::MessageLoop::current()->PostTask( | 967 base::MessageLoop::current()->PostTask( |
945 FROM_HERE, | 968 FROM_HERE, |
946 base::Bind(callback, HTTP_PRECONDITION, GURL())); | 969 base::Bind(callback, HTTP_PRECONDITION, GURL())); |
947 return CancelCallback(); | 970 return CancelCallback(); |
948 } | 971 } |
949 entry->SetString("docs$size.$t", "0"); | |
950 | 972 |
951 const GURL upload_url = GetUploadUrl(*entry); | 973 GURL session_url = GetNewUploadSessionUrl(); |
952 DCHECK(upload_url.is_valid()); | 974 upload_sessions_[session_url] = |
| 975 UploadSession(content_type, content_length, |
| 976 "", // parent_resource_id |
| 977 resource_id, |
| 978 entry_etag, |
| 979 "" /* title */); |
953 | 980 |
954 base::MessageLoop::current()->PostTask( | 981 base::MessageLoop::current()->PostTask( |
955 FROM_HERE, | 982 FROM_HERE, |
956 base::Bind(callback, HTTP_SUCCESS, | 983 base::Bind(callback, HTTP_SUCCESS, session_url)); |
957 net::AppendQueryParameter(upload_url, "mode", "existing"))); | |
958 return CancelCallback(); | 984 return CancelCallback(); |
959 } | 985 } |
960 | 986 |
961 CancelCallback FakeDriveService::GetUploadStatus( | 987 CancelCallback FakeDriveService::GetUploadStatus( |
962 const GURL& upload_url, | 988 const GURL& upload_url, |
963 int64 content_length, | 989 int64 content_length, |
964 const UploadRangeCallback& callback) { | 990 const UploadRangeCallback& callback) { |
965 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 991 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
966 DCHECK(!callback.is_null()); | 992 DCHECK(!callback.is_null()); |
967 return CancelCallback(); | 993 return CancelCallback(); |
968 } | 994 } |
969 | 995 |
970 CancelCallback FakeDriveService::ResumeUpload( | 996 CancelCallback FakeDriveService::ResumeUpload( |
971 const GURL& upload_url, | 997 const GURL& upload_url, |
972 int64 start_position, | 998 int64 start_position, |
973 int64 end_position, | 999 int64 end_position, |
974 int64 content_length, | 1000 int64 content_length, |
975 const std::string& content_type, | 1001 const std::string& content_type, |
976 const base::FilePath& local_file_path, | 1002 const base::FilePath& local_file_path, |
977 const UploadRangeCallback& callback, | 1003 const UploadRangeCallback& callback, |
978 const ProgressCallback& progress_callback) { | 1004 const ProgressCallback& progress_callback) { |
979 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 1005 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
980 DCHECK(!callback.is_null()); | 1006 DCHECK(!callback.is_null()); |
981 | 1007 |
982 scoped_ptr<ResourceEntry> result_entry; | 1008 GetResourceEntryCallback completion_callback |
| 1009 = base::Bind(&ScheduleUploadRangeCallback, |
| 1010 callback, start_position, end_position); |
983 | 1011 |
984 if (offline_) { | 1012 if (offline_) { |
985 base::MessageLoop::current()->PostTask( | 1013 completion_callback.Run(GDATA_NO_CONNECTION, scoped_ptr<ResourceEntry>()); |
986 FROM_HERE, | |
987 base::Bind(callback, | |
988 UploadRangeResponse(GDATA_NO_CONNECTION, | |
989 start_position, | |
990 end_position), | |
991 base::Passed(&result_entry))); | |
992 return CancelCallback(); | 1014 return CancelCallback(); |
993 } | 1015 } |
994 | 1016 |
995 DictionaryValue* entry = NULL; | 1017 if (!upload_sessions_.count(upload_url)) { |
996 entry = FindEntryByUploadUrl(RemoveQueryParameter(upload_url)); | 1018 completion_callback.Run(HTTP_NOT_FOUND, scoped_ptr<ResourceEntry>()); |
997 if (!entry) { | |
998 base::MessageLoop::current()->PostTask( | |
999 FROM_HERE, | |
1000 base::Bind(callback, | |
1001 UploadRangeResponse(HTTP_NOT_FOUND, | |
1002 start_position, | |
1003 end_position), | |
1004 base::Passed(&result_entry))); | |
1005 return CancelCallback(); | 1019 return CancelCallback(); |
1006 } | 1020 } |
1007 | 1021 |
| 1022 UploadSession* session = &upload_sessions_[upload_url]; |
| 1023 |
1008 // Chunks are required to be sent in such a ways that they fill from the start | 1024 // Chunks are required to be sent in such a ways that they fill from the start |
1009 // of the not-yet-uploaded part with no gaps nor overlaps. | 1025 // of the not-yet-uploaded part with no gaps nor overlaps. |
1010 std::string current_size_string; | 1026 if (session->uploaded_size != start_position) { |
1011 int64 current_size; | 1027 completion_callback.Run(HTTP_BAD_REQUEST, scoped_ptr<ResourceEntry>()); |
1012 if (!entry->GetString("docs$size.$t", ¤t_size_string) || | |
1013 !base::StringToInt64(current_size_string, ¤t_size) || | |
1014 current_size != start_position) { | |
1015 base::MessageLoop::current()->PostTask( | |
1016 FROM_HERE, | |
1017 base::Bind(callback, | |
1018 UploadRangeResponse(HTTP_BAD_REQUEST, | |
1019 start_position, | |
1020 end_position), | |
1021 base::Passed(&result_entry))); | |
1022 return CancelCallback(); | 1028 return CancelCallback(); |
1023 } | 1029 } |
1024 | 1030 |
1025 entry->SetString("docs$size.$t", base::Int64ToString(end_position)); | |
1026 | |
1027 if (!progress_callback.is_null()) { | 1031 if (!progress_callback.is_null()) { |
1028 // In the real GDataWapi/Drive DriveService, progress is reported in | 1032 // In the real GDataWapi/Drive DriveService, progress is reported in |
1029 // nondeterministic timing. In this fake implementation, we choose to call | 1033 // nondeterministic timing. In this fake implementation, we choose to call |
1030 // it twice per one ResumeUpload. This is for making sure that client code | 1034 // it twice per one ResumeUpload. This is for making sure that client code |
1031 // works fine even if the callback is invoked more than once; it is the | 1035 // works fine even if the callback is invoked more than once; it is the |
1032 // crucial difference of the progress callback from others. | 1036 // crucial difference of the progress callback from others. |
1033 // Note that progress is notified in the relative offset in each chunk. | 1037 // Note that progress is notified in the relative offset in each chunk. |
1034 const int64 chunk_size = end_position - start_position; | 1038 const int64 chunk_size = end_position - start_position; |
1035 base::MessageLoop::current()->PostTask( | 1039 base::MessageLoop::current()->PostTask( |
1036 FROM_HERE, base::Bind(progress_callback, chunk_size / 2, chunk_size)); | 1040 FROM_HERE, base::Bind(progress_callback, chunk_size / 2, chunk_size)); |
1037 base::MessageLoop::current()->PostTask( | 1041 base::MessageLoop::current()->PostTask( |
1038 FROM_HERE, base::Bind(progress_callback, chunk_size, chunk_size)); | 1042 FROM_HERE, base::Bind(progress_callback, chunk_size, chunk_size)); |
1039 } | 1043 } |
1040 | 1044 |
1041 if (content_length != end_position) { | 1045 if (content_length != end_position) { |
1042 base::MessageLoop::current()->PostTask( | 1046 session->uploaded_size = end_position; |
1043 FROM_HERE, | 1047 completion_callback.Run(HTTP_RESUME_INCOMPLETE, |
1044 base::Bind(callback, | 1048 scoped_ptr<ResourceEntry>()); |
1045 UploadRangeResponse(HTTP_RESUME_INCOMPLETE, | |
1046 start_position, | |
1047 end_position), | |
1048 base::Passed(&result_entry))); | |
1049 return CancelCallback(); | 1049 return CancelCallback(); |
1050 } | 1050 } |
1051 | 1051 |
1052 AddNewChangestamp(entry); | 1052 std::string content_data; |
1053 result_entry = ResourceEntry::CreateFrom(*entry).Pass(); | 1053 if (!file_util::ReadFileToString(local_file_path, &content_data)) { |
| 1054 session->uploaded_size = end_position; |
| 1055 completion_callback.Run(GDATA_FILE_ERROR, scoped_ptr<ResourceEntry>()); |
| 1056 return CancelCallback(); |
| 1057 } |
| 1058 session->uploaded_size = end_position; |
1054 | 1059 |
1055 std::string upload_mode; | 1060 // |resource_id| is empty if the upload is for new file. |
1056 bool upload_mode_found = | 1061 if (session->resource_id.empty()) { |
1057 net::GetValueForKeyInQuery(upload_url, "mode", &upload_mode); | 1062 DCHECK(!session->parent_resource_id.empty()); |
1058 DCHECK(upload_mode_found && | 1063 DCHECK(!session->title.empty()); |
1059 (upload_mode == "newfile" || upload_mode == "existing")); | 1064 const DictionaryValue* new_entry = AddNewEntry( |
| 1065 session->content_type, |
| 1066 content_data, |
| 1067 session->parent_resource_id, |
| 1068 session->title, |
| 1069 false, // shared_with_me |
| 1070 "file"); |
| 1071 if (!new_entry) { |
| 1072 completion_callback.Run(HTTP_NOT_FOUND, scoped_ptr<ResourceEntry>()); |
| 1073 return CancelCallback(); |
| 1074 } |
1060 | 1075 |
1061 GDataErrorCode return_code = | 1076 completion_callback.Run(HTTP_CREATED, |
1062 upload_mode == "newfile" ? HTTP_CREATED : HTTP_SUCCESS; | 1077 ResourceEntry::CreateFrom(*new_entry)); |
| 1078 return CancelCallback(); |
| 1079 } |
1063 | 1080 |
1064 base::MessageLoop::current()->PostTask( | 1081 DictionaryValue* entry = FindEntryByResourceId(session->resource_id); |
1065 FROM_HERE, | 1082 if (!entry) { |
1066 base::Bind(callback, | 1083 completion_callback.Run(HTTP_NOT_FOUND, scoped_ptr<ResourceEntry>()); |
1067 UploadRangeResponse(return_code, | 1084 return CancelCallback(); |
1068 start_position, | 1085 } |
1069 end_position), | 1086 |
1070 base::Passed(&result_entry))); | 1087 std::string entry_etag; |
| 1088 entry->GetString("gd$etag", &entry_etag); |
| 1089 if (entry_etag.empty() || session->etag != entry_etag) { |
| 1090 completion_callback.Run(HTTP_PRECONDITION, scoped_ptr<ResourceEntry>()); |
| 1091 return CancelCallback(); |
| 1092 } |
| 1093 |
| 1094 entry->SetString("docs$md5Checksum.$t", base::MD5String(content_data)); |
| 1095 entry->Set("test$data", |
| 1096 base::BinaryValue::CreateWithCopiedBuffer( |
| 1097 content_data.data(), content_data.size())); |
| 1098 entry->SetString("docs$size.$t", base::Int64ToString(end_position)); |
| 1099 AddNewChangestampAndETag(entry); |
| 1100 |
| 1101 completion_callback.Run(HTTP_SUCCESS, ResourceEntry::CreateFrom(*entry)); |
1071 return CancelCallback(); | 1102 return CancelCallback(); |
1072 } | 1103 } |
1073 | 1104 |
1074 CancelCallback FakeDriveService::AuthorizeApp( | 1105 CancelCallback FakeDriveService::AuthorizeApp( |
1075 const std::string& resource_id, | 1106 const std::string& resource_id, |
1076 const std::string& app_id, | 1107 const std::string& app_id, |
1077 const AuthorizeAppCallback& callback) { | 1108 const AuthorizeAppCallback& callback) { |
1078 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 1109 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
1079 DCHECK(!callback.is_null()); | 1110 DCHECK(!callback.is_null()); |
1080 return CancelCallback(); | 1111 return CancelCallback(); |
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1202 entry->GetString("content.src", ¤t_content_url) && | 1233 entry->GetString("content.src", ¤t_content_url) && |
1203 content_url == GURL(current_content_url)) { | 1234 content_url == GURL(current_content_url)) { |
1204 return entry; | 1235 return entry; |
1205 } | 1236 } |
1206 } | 1237 } |
1207 } | 1238 } |
1208 | 1239 |
1209 return NULL; | 1240 return NULL; |
1210 } | 1241 } |
1211 | 1242 |
1212 base::DictionaryValue* FakeDriveService::FindEntryByUploadUrl( | |
1213 const GURL& upload_url) { | |
1214 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
1215 | |
1216 base::ListValue* entries = NULL; | |
1217 // Go through entries and return the one that matches |upload_url|. | |
1218 if (resource_list_value_->GetList("entry", &entries)) { | |
1219 for (size_t i = 0; i < entries->GetSize(); ++i) { | |
1220 base::DictionaryValue* entry = NULL; | |
1221 base::ListValue* links = NULL; | |
1222 if (entries->GetDictionary(i, &entry) && | |
1223 entry->GetList("link", &links) && | |
1224 links) { | |
1225 for (size_t link_index = 0; | |
1226 link_index < links->GetSize(); | |
1227 ++link_index) { | |
1228 base::DictionaryValue* link = NULL; | |
1229 std::string rel; | |
1230 std::string found_upload_url; | |
1231 if (links->GetDictionary(link_index, &link) && | |
1232 link && link->GetString("rel", &rel) && | |
1233 rel == kUploadUrlRel && | |
1234 link->GetString("href", &found_upload_url) && | |
1235 GURL(found_upload_url) == upload_url) { | |
1236 return entry; | |
1237 } | |
1238 } | |
1239 } | |
1240 } | |
1241 } | |
1242 | |
1243 return NULL; | |
1244 } | |
1245 | |
1246 std::string FakeDriveService::GetNewResourceId() { | 1243 std::string FakeDriveService::GetNewResourceId() { |
1247 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 1244 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
1248 | 1245 |
1249 ++resource_id_count_; | 1246 ++resource_id_count_; |
1250 return base::StringPrintf("resource_id_%d", resource_id_count_); | 1247 return base::StringPrintf("resource_id_%d", resource_id_count_); |
1251 } | 1248 } |
1252 | 1249 |
1253 void FakeDriveService::AddNewChangestamp(base::DictionaryValue* entry) { | 1250 void FakeDriveService::AddNewChangestampAndETag(base::DictionaryValue* entry) { |
1254 ++largest_changestamp_; | 1251 ++largest_changestamp_; |
1255 entry->SetString("docs$changestamp.value", | 1252 entry->SetString("docs$changestamp.value", |
1256 base::Int64ToString(largest_changestamp_)); | 1253 base::Int64ToString(largest_changestamp_)); |
| 1254 entry->SetString("gd$etag", |
| 1255 "etag_" + base::Int64ToString(largest_changestamp_)); |
1257 } | 1256 } |
1258 | 1257 |
1259 const base::DictionaryValue* FakeDriveService::AddNewEntry( | 1258 const base::DictionaryValue* FakeDriveService::AddNewEntry( |
1260 const std::string& content_type, | 1259 const std::string& content_type, |
1261 const std::string& content_data, | 1260 const std::string& content_data, |
1262 const std::string& parent_resource_id, | 1261 const std::string& parent_resource_id, |
1263 const std::string& title, | 1262 const std::string& title, |
1264 bool shared_with_me, | 1263 bool shared_with_me, |
1265 const std::string& entry_kind) { | 1264 const std::string& entry_kind) { |
1266 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 1265 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
(...skipping 11 matching lines...) Expand all Loading... |
1278 new_entry->SetString("gd$resourceId.$t", resource_id); | 1277 new_entry->SetString("gd$resourceId.$t", resource_id); |
1279 new_entry->SetString("title.$t", title); | 1278 new_entry->SetString("title.$t", title); |
1280 new_entry->SetString("docs$filename", title); | 1279 new_entry->SetString("docs$filename", title); |
1281 // Set the contents, size and MD5 for a file. | 1280 // Set the contents, size and MD5 for a file. |
1282 if (entry_kind == "file") { | 1281 if (entry_kind == "file") { |
1283 new_entry->Set("test$data", | 1282 new_entry->Set("test$data", |
1284 base::BinaryValue::CreateWithCopiedBuffer( | 1283 base::BinaryValue::CreateWithCopiedBuffer( |
1285 content_data.c_str(), content_data.size())); | 1284 content_data.c_str(), content_data.size())); |
1286 new_entry->SetString("docs$size.$t", | 1285 new_entry->SetString("docs$size.$t", |
1287 base::Int64ToString(content_data.size())); | 1286 base::Int64ToString(content_data.size())); |
1288 // TODO(satorux): Set the correct MD5 here. | |
1289 new_entry->SetString("docs$md5Checksum.$t", | 1287 new_entry->SetString("docs$md5Checksum.$t", |
1290 "3b4385ebefec6e743574c76bbd0575de"); | 1288 base::MD5String(content_data)); |
1291 } | 1289 } |
1292 | 1290 |
1293 // Add "category" which sets the resource type to |entry_kind|. | 1291 // Add "category" which sets the resource type to |entry_kind|. |
1294 base::ListValue* categories = new base::ListValue; | 1292 base::ListValue* categories = new base::ListValue; |
1295 base::DictionaryValue* category = new base::DictionaryValue; | 1293 base::DictionaryValue* category = new base::DictionaryValue; |
1296 category->SetString("scheme", "http://schemas.google.com/g/2005#kind"); | 1294 category->SetString("scheme", "http://schemas.google.com/g/2005#kind"); |
1297 category->SetString("term", "http://schemas.google.com/docs/2007#" + | 1295 category->SetString("term", "http://schemas.google.com/docs/2007#" + |
1298 entry_kind); | 1296 entry_kind); |
1299 categories->Append(category); | 1297 categories->Append(category); |
1300 new_entry->Set("category", categories); | 1298 new_entry->Set("category", categories); |
(...skipping 28 matching lines...) Expand all Loading... |
1329 edit_link->SetString("href", "https://xxx/edit/" + escaped_resource_id); | 1327 edit_link->SetString("href", "https://xxx/edit/" + escaped_resource_id); |
1330 edit_link->SetString("rel", "edit"); | 1328 edit_link->SetString("rel", "edit"); |
1331 links->Append(edit_link); | 1329 links->Append(edit_link); |
1332 | 1330 |
1333 base::DictionaryValue* upload_link = new base::DictionaryValue; | 1331 base::DictionaryValue* upload_link = new base::DictionaryValue; |
1334 upload_link->SetString("href", upload_url.spec()); | 1332 upload_link->SetString("href", upload_url.spec()); |
1335 upload_link->SetString("rel", kUploadUrlRel); | 1333 upload_link->SetString("rel", kUploadUrlRel); |
1336 links->Append(upload_link); | 1334 links->Append(upload_link); |
1337 new_entry->Set("link", links); | 1335 new_entry->Set("link", links); |
1338 | 1336 |
1339 AddNewChangestamp(new_entry.get()); | 1337 AddNewChangestampAndETag(new_entry.get()); |
1340 | 1338 |
1341 base::Time published_date = | 1339 base::Time published_date = |
1342 base::Time() + base::TimeDelta::FromMilliseconds(++published_date_seq_); | 1340 base::Time() + base::TimeDelta::FromMilliseconds(++published_date_seq_); |
1343 new_entry->SetString("published.$t", | 1341 new_entry->SetString("published.$t", |
1344 util::FormatTimeAsString(published_date)); | 1342 util::FormatTimeAsString(published_date)); |
1345 | 1343 |
1346 // If there are no entries, prepare an empty entry to add. | 1344 // If there are no entries, prepare an empty entry to add. |
1347 if (!resource_list_value_->HasKey("entry")) | 1345 if (!resource_list_value_->HasKey("entry")) |
1348 resource_list_value_->Set("entry", new ListValue); | 1346 resource_list_value_->Set("entry", new ListValue); |
1349 | 1347 |
(...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1465 | 1463 |
1466 if (load_counter) | 1464 if (load_counter) |
1467 *load_counter += 1; | 1465 *load_counter += 1; |
1468 base::MessageLoop::current()->PostTask( | 1466 base::MessageLoop::current()->PostTask( |
1469 FROM_HERE, | 1467 FROM_HERE, |
1470 base::Bind(callback, | 1468 base::Bind(callback, |
1471 HTTP_SUCCESS, | 1469 HTTP_SUCCESS, |
1472 base::Passed(&resource_list))); | 1470 base::Passed(&resource_list))); |
1473 } | 1471 } |
1474 | 1472 |
| 1473 GURL FakeDriveService::GetNewUploadSessionUrl() { |
| 1474 return GURL("https://upload_session_url/" + |
| 1475 base::Int64ToString(next_upload_sequence_number_++)); |
| 1476 } |
| 1477 |
1475 } // namespace drive | 1478 } // namespace drive |
OLD | NEW |