Adding instrumentation to locate the source of jankiness
[chromium-blink-merge.git] / chrome / browser / drive / drive_api_util.cc
blob0fe163f36309792476378b3a1b54b54646ff0f84
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/drive_api_util.h"
7 #include <string>
9 #include "base/files/file.h"
10 #include "base/logging.h"
11 #include "base/md5.h"
12 #include "base/strings/string16.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/stringprintf.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/values.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "google_apis/drive/drive_api_parser.h"
19 #include "google_apis/drive/gdata_wapi_parser.h"
20 #include "net/base/escape.h"
21 #include "third_party/re2/re2/re2.h"
22 #include "url/gurl.h"
24 namespace drive {
25 namespace util {
26 namespace {
28 struct HostedDocumentKind {
29 const char* mime_type;
30 const char* extension;
33 const HostedDocumentKind kHostedDocumentKinds[] = {
34 {kGoogleDocumentMimeType, ".gdoc"},
35 {kGoogleSpreadsheetMimeType, ".gsheet"},
36 {kGooglePresentationMimeType, ".gslides"},
37 {kGoogleDrawingMimeType, ".gdraw"},
38 {kGoogleTableMimeType, ".gtable"},
39 {kGoogleFormMimeType, ".gform"}
42 const char kUnknownHostedDocumentExtension[] = ".glink";
44 } // namespace
46 std::string EscapeQueryStringValue(const std::string& str) {
47 std::string result;
48 result.reserve(str.size());
49 for (size_t i = 0; i < str.size(); ++i) {
50 if (str[i] == '\\' || str[i] == '\'') {
51 result.push_back('\\');
53 result.push_back(str[i]);
55 return result;
58 std::string TranslateQuery(const std::string& original_query) {
59 // In order to handle non-ascii white spaces correctly, convert to UTF16.
60 base::string16 query = base::UTF8ToUTF16(original_query);
61 const base::string16 kDelimiter(
62 base::kWhitespaceUTF16 + base::ASCIIToUTF16("\""));
64 std::string result;
65 for (size_t index = query.find_first_not_of(base::kWhitespaceUTF16);
66 index != base::string16::npos;
67 index = query.find_first_not_of(base::kWhitespaceUTF16, index)) {
68 bool is_exclusion = (query[index] == '-');
69 if (is_exclusion)
70 ++index;
71 if (index == query.length()) {
72 // Here, the token is '-' and it should be ignored.
73 continue;
76 size_t begin_token = index;
77 base::string16 token;
78 if (query[begin_token] == '"') {
79 // Quoted query.
80 ++begin_token;
81 size_t end_token = query.find('"', begin_token);
82 if (end_token == base::string16::npos) {
83 // This is kind of syntax error, since quoted string isn't finished.
84 // However, the query is built by user manually, so here we treat
85 // whole remaining string as a token as a fallback, by appending
86 // a missing double-quote character.
87 end_token = query.length();
88 query.push_back('"');
91 token = query.substr(begin_token, end_token - begin_token);
92 index = end_token + 1; // Consume last '"', too.
93 } else {
94 size_t end_token = query.find_first_of(kDelimiter, begin_token);
95 if (end_token == base::string16::npos) {
96 end_token = query.length();
99 token = query.substr(begin_token, end_token - begin_token);
100 index = end_token;
103 if (token.empty()) {
104 // Just ignore an empty token.
105 continue;
108 if (!result.empty()) {
109 // If there are two or more tokens, need to connect with "and".
110 result.append(" and ");
113 // The meaning of "fullText" should include title, description and content.
114 base::StringAppendF(
115 &result,
116 "%sfullText contains \'%s\'",
117 is_exclusion ? "not " : "",
118 EscapeQueryStringValue(base::UTF16ToUTF8(token)).c_str());
121 return result;
124 std::string ExtractResourceIdFromUrl(const GURL& url) {
125 return net::UnescapeURLComponent(url.ExtractFileName(),
126 net::UnescapeRule::URL_SPECIAL_CHARS);
129 std::string CanonicalizeResourceId(const std::string& resource_id) {
130 // If resource ID is in the old WAPI format starting with a prefix like
131 // "document:", strip it and return the remaining part.
132 std::string stripped_resource_id;
133 if (RE2::FullMatch(resource_id, "^[a-z-]+(?::|%3A)([\\w-]+)$",
134 &stripped_resource_id))
135 return stripped_resource_id;
136 return resource_id;
139 scoped_ptr<google_apis::ResourceEntry>
140 ConvertFileResourceToResourceEntry(
141 const google_apis::FileResource& file_resource) {
142 scoped_ptr<google_apis::ResourceEntry> entry(new google_apis::ResourceEntry);
144 // ResourceEntry
145 entry->set_resource_id(file_resource.file_id());
146 entry->set_id(file_resource.file_id());
147 if (file_resource.IsDirectory())
148 entry->set_kind(google_apis::ResourceEntry::ENTRY_KIND_FOLDER);
149 else if (file_resource.IsHostedDocument())
150 entry->set_kind(google_apis::ResourceEntry::ENTRY_KIND_UNKNOWN);
151 else
152 entry->set_kind(google_apis::ResourceEntry::ENTRY_KIND_FILE);
153 entry->set_title(file_resource.title());
154 entry->set_published_time(file_resource.created_date());
156 std::vector<std::string> labels;
157 if (!file_resource.shared_with_me_date().is_null())
158 labels.push_back("shared-with-me");
159 if (file_resource.shared())
160 labels.push_back("shared");
161 entry->set_labels(labels);
163 // This should be the url to download the file_resource.
165 google_apis::Content content;
166 content.set_mime_type(file_resource.mime_type());
167 entry->set_content(content);
169 // TODO(kochi): entry->resource_links_
171 // For file entries
172 entry->set_filename(file_resource.title());
173 entry->set_suggested_filename(file_resource.title());
174 entry->set_file_md5(file_resource.md5_checksum());
175 entry->set_file_size(file_resource.file_size());
177 // If file is removed completely, that information is only available in
178 // ChangeResource, and is reflected in |removed_|. If file is trashed, the
179 // file entry still exists but with its "trashed" label true.
180 entry->set_deleted(file_resource.labels().is_trashed());
182 // ImageMediaMetadata
183 entry->set_image_width(file_resource.image_media_metadata().width());
184 entry->set_image_height(file_resource.image_media_metadata().height());
185 entry->set_image_rotation(file_resource.image_media_metadata().rotation());
187 // CommonMetadata
188 entry->set_etag(file_resource.etag());
189 // entry->authors_
190 // entry->links_.
191 ScopedVector<google_apis::Link> links;
192 for (size_t i = 0; i < file_resource.parents().size(); ++i) {
193 google_apis::Link* link = new google_apis::Link;
194 link->set_type(google_apis::Link::LINK_PARENT);
195 link->set_href(file_resource.parents()[i].parent_link());
196 links.push_back(link);
198 if (!file_resource.alternate_link().is_empty()) {
199 google_apis::Link* link = new google_apis::Link;
200 link->set_type(google_apis::Link::LINK_ALTERNATE);
201 link->set_href(file_resource.alternate_link());
202 links.push_back(link);
204 entry->set_links(links.Pass());
206 // entry->categories_
207 entry->set_updated_time(file_resource.modified_date());
208 entry->set_last_viewed_time(file_resource.last_viewed_by_me_date());
210 entry->FillRemainingFields();
211 return entry.Pass();
214 scoped_ptr<google_apis::ResourceEntry>
215 ConvertChangeResourceToResourceEntry(
216 const google_apis::ChangeResource& change_resource) {
217 scoped_ptr<google_apis::ResourceEntry> entry;
218 if (change_resource.file())
219 entry = ConvertFileResourceToResourceEntry(*change_resource.file()).Pass();
220 else
221 entry.reset(new google_apis::ResourceEntry);
223 entry->set_resource_id(change_resource.file_id());
224 // If |is_deleted()| returns true, the file is removed from Drive.
225 entry->set_removed(change_resource.is_deleted());
226 entry->set_changestamp(change_resource.change_id());
227 entry->set_modification_date(change_resource.modification_date());
229 return entry.Pass();
232 scoped_ptr<google_apis::ResourceList>
233 ConvertFileListToResourceList(const google_apis::FileList& file_list) {
234 scoped_ptr<google_apis::ResourceList> feed(new google_apis::ResourceList);
236 const ScopedVector<google_apis::FileResource>& items = file_list.items();
237 ScopedVector<google_apis::ResourceEntry> entries;
238 for (size_t i = 0; i < items.size(); ++i)
239 entries.push_back(ConvertFileResourceToResourceEntry(*items[i]).release());
240 feed->set_entries(entries.Pass());
242 ScopedVector<google_apis::Link> links;
243 if (!file_list.next_link().is_empty()) {
244 google_apis::Link* link = new google_apis::Link;
245 link->set_type(google_apis::Link::LINK_NEXT);
246 link->set_href(file_list.next_link());
247 links.push_back(link);
249 feed->set_links(links.Pass());
251 return feed.Pass();
254 scoped_ptr<google_apis::ResourceList>
255 ConvertChangeListToResourceList(const google_apis::ChangeList& change_list) {
256 scoped_ptr<google_apis::ResourceList> feed(new google_apis::ResourceList);
258 const ScopedVector<google_apis::ChangeResource>& items = change_list.items();
259 ScopedVector<google_apis::ResourceEntry> entries;
260 for (size_t i = 0; i < items.size(); ++i) {
261 entries.push_back(
262 ConvertChangeResourceToResourceEntry(*items[i]).release());
264 feed->set_entries(entries.Pass());
266 feed->set_largest_changestamp(change_list.largest_change_id());
268 ScopedVector<google_apis::Link> links;
269 if (!change_list.next_link().is_empty()) {
270 google_apis::Link* link = new google_apis::Link;
271 link->set_type(google_apis::Link::LINK_NEXT);
272 link->set_href(change_list.next_link());
273 links.push_back(link);
275 feed->set_links(links.Pass());
277 return feed.Pass();
280 std::string GetMd5Digest(const base::FilePath& file_path) {
281 const int kBufferSize = 512 * 1024; // 512kB.
283 base::File file(file_path, base::File::FLAG_OPEN | base::File::FLAG_READ);
284 if (!file.IsValid())
285 return std::string();
287 base::MD5Context context;
288 base::MD5Init(&context);
290 int64 offset = 0;
291 scoped_ptr<char[]> buffer(new char[kBufferSize]);
292 while (true) {
293 int result = file.Read(offset, buffer.get(), kBufferSize);
294 if (result < 0) {
295 // Found an error.
296 return std::string();
299 if (result == 0) {
300 // End of file.
301 break;
304 offset += result;
305 base::MD5Update(&context, base::StringPiece(buffer.get(), result));
308 base::MD5Digest digest;
309 base::MD5Final(&digest, &context);
310 return MD5DigestToBase16(digest);
313 std::string GetHostedDocumentExtension(const std::string& mime_type) {
314 for (size_t i = 0; i < arraysize(kHostedDocumentKinds); ++i) {
315 if (mime_type == kHostedDocumentKinds[i].mime_type)
316 return kHostedDocumentKinds[i].extension;
318 return kUnknownHostedDocumentExtension;
321 bool IsKnownHostedDocumentMimeType(const std::string& mime_type) {
322 for (size_t i = 0; i < arraysize(kHostedDocumentKinds); ++i) {
323 if (mime_type == kHostedDocumentKinds[i].mime_type)
324 return true;
326 return false;
329 bool HasHostedDocumentExtension(const base::FilePath& path) {
330 const std::string extension = base::FilePath(path.Extension()).AsUTF8Unsafe();
331 for (size_t i = 0; i < arraysize(kHostedDocumentKinds); ++i) {
332 if (extension == kHostedDocumentKinds[i].extension)
333 return true;
335 return extension == kUnknownHostedDocumentExtension;
338 } // namespace util
339 } // namespace drive