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/chromeos/gdata/gdata_files.h" | 5 #include "chrome/browser/chromeos/gdata/gdata_files.h" |
6 | 6 |
7 #include <vector> | 7 #include <vector> |
8 | 8 |
9 #include "base/utf_string_conversions.h" | 9 #include "base/utf_string_conversions.h" |
10 #include "base/platform_file.h" | 10 #include "base/platform_file.h" |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
70 // Paste paths parts back together in reverse order from upward tree | 70 // Paste paths parts back together in reverse order from upward tree |
71 // traversal. | 71 // traversal. |
72 for (std::vector<FilePath::StringType>::reverse_iterator iter = | 72 for (std::vector<FilePath::StringType>::reverse_iterator iter = |
73 parts.rbegin(); | 73 parts.rbegin(); |
74 iter != parts.rend(); ++iter) { | 74 iter != parts.rend(); ++iter) { |
75 path = path.Append(*iter); | 75 path = path.Append(*iter); |
76 } | 76 } |
77 return path; | 77 return path; |
78 } | 78 } |
79 | 79 |
| 80 void GDataFileBase::SetFileNameFromTitle() { |
| 81 file_name_ = EscapeUtf8FileName(title_); |
| 82 } |
| 83 |
80 // static. | 84 // static. |
81 GDataFileBase* GDataFileBase::FromDocumentEntry(GDataDirectory* parent, | 85 GDataFileBase* GDataFileBase::FromDocumentEntry(GDataDirectory* parent, |
82 DocumentEntry* doc) { | 86 DocumentEntry* doc) { |
83 DCHECK(parent); | 87 DCHECK(parent); |
84 DCHECK(doc); | 88 DCHECK(doc); |
85 if (doc->is_folder()) | 89 if (doc->is_folder()) |
86 return GDataDirectory::FromDocumentEntry(parent, doc); | 90 return GDataDirectory::FromDocumentEntry(parent, doc); |
87 else if (doc->is_hosted_document() || doc->is_file()) | 91 else if (doc->is_hosted_document() || doc->is_file()) |
88 return GDataFile::FromDocumentEntry(parent, doc); | 92 return GDataFile::FromDocumentEntry(parent, doc); |
89 | 93 |
90 return NULL; | 94 return NULL; |
91 } | 95 } |
92 | 96 |
93 // static. | 97 // static. |
94 // Escapes forward slashes from file names with magic unicode character \u2215 | |
95 // pretty much looks the same in UI. | |
96 std::string GDataFileBase::EscapeUtf8FileName(const std::string& input) { | 98 std::string GDataFileBase::EscapeUtf8FileName(const std::string& input) { |
97 std::string output; | 99 std::string output; |
98 if (ReplaceChars(input, kSlash, std::string(kEscapedSlash), &output)) | 100 if (ReplaceChars(input, kSlash, std::string(kEscapedSlash), &output)) |
99 return output; | 101 return output; |
100 | 102 |
101 return input; | 103 return input; |
102 } | 104 } |
103 | 105 |
104 // static. | 106 // static. |
105 // Unescapes what was escaped in EScapeUtf8FileName. | |
106 std::string GDataFileBase::UnescapeUtf8FileName(const std::string& input) { | 107 std::string GDataFileBase::UnescapeUtf8FileName(const std::string& input) { |
107 std::string output = input; | 108 std::string output = input; |
108 ReplaceSubstringsAfterOffset(&output, 0, std::string(kEscapedSlash), kSlash); | 109 ReplaceSubstringsAfterOffset(&output, 0, std::string(kEscapedSlash), kSlash); |
109 return output; | 110 return output; |
110 } | 111 } |
111 | 112 |
112 // GDataFile class implementation. | 113 // GDataFile class implementation. |
113 | 114 |
114 GDataFile::GDataFile(GDataDirectory* parent) | 115 GDataFile::GDataFile(GDataDirectory* parent) |
115 : GDataFileBase(parent), | 116 : GDataFileBase(parent), |
116 kind_(gdata::DocumentEntry::UNKNOWN), | 117 kind_(gdata::DocumentEntry::UNKNOWN), |
117 is_hosted_document_(false) { | 118 is_hosted_document_(false) { |
118 DCHECK(parent); | 119 DCHECK(parent); |
119 } | 120 } |
120 | 121 |
121 GDataFile::~GDataFile() { | 122 GDataFile::~GDataFile() { |
122 } | 123 } |
123 | 124 |
124 GDataFile* GDataFile::AsGDataFile() { | 125 GDataFile* GDataFile::AsGDataFile() { |
125 return this; | 126 return this; |
126 } | 127 } |
127 | 128 |
| 129 void GDataFile::SetFileNameFromTitle() { |
| 130 if (is_hosted_document_) { |
| 131 file_name_ = EscapeUtf8FileName(title_ + document_extension_); |
| 132 } else { |
| 133 GDataFileBase::SetFileNameFromTitle(); |
| 134 } |
| 135 } |
| 136 |
128 GDataFileBase* GDataFile::FromDocumentEntry(GDataDirectory* parent, | 137 GDataFileBase* GDataFile::FromDocumentEntry(GDataDirectory* parent, |
129 DocumentEntry* doc) { | 138 DocumentEntry* doc) { |
130 DCHECK(doc->is_hosted_document() || doc->is_file()); | 139 DCHECK(doc->is_hosted_document() || doc->is_file()); |
131 GDataFile* file = new GDataFile(parent); | 140 GDataFile* file = new GDataFile(parent); |
| 141 |
| 142 // For regular files, the 'filename' and 'title' attribute in the metadata |
| 143 // may be different (e.g. due to rename). To be consistent with the web |
| 144 // interface and other client to use the 'title' attribute, instead of |
| 145 // 'filename', as the file name in the local snapshot. |
| 146 file->title_ = UTF16ToUTF8(doc->title()); |
| 147 |
132 // Check if this entry is a true file, or... | 148 // Check if this entry is a true file, or... |
133 if (doc->is_file()) { | 149 if (doc->is_file()) { |
134 file->original_file_name_ = UTF16ToUTF8(doc->filename()); | |
135 file->file_name_ = | |
136 GDataFileBase::EscapeUtf8FileName(file->original_file_name_); | |
137 file->file_info_.size = doc->file_size(); | 150 file->file_info_.size = doc->file_size(); |
138 file->file_md5_ = doc->file_md5(); | 151 file->file_md5_ = doc->file_md5(); |
139 } else { | 152 } else { |
140 DCHECK(doc->is_hosted_document()); | |
141 // ... a hosted document. | 153 // ... a hosted document. |
142 file->original_file_name_ = UTF16ToUTF8(doc->title()); | |
143 // Attach .g<something> extension to hosted documents so we can special | 154 // Attach .g<something> extension to hosted documents so we can special |
144 // case their handling in UI. | 155 // case their handling in UI. |
145 // TODO(zelidrag): Figure out better way how to pass entry info like kind | 156 // TODO(zelidrag): Figure out better way how to pass entry info like kind |
146 // to UI through the File API stack. | 157 // to UI through the File API stack. |
147 file->file_name_ = GDataFileBase::EscapeUtf8FileName( | 158 file->document_extension_ = doc->GetHostedDocumentExtension(); |
148 file->original_file_name_ + doc->GetHostedDocumentExtension()); | |
149 // We don't know the size of hosted docs and it does not matter since | 159 // We don't know the size of hosted docs and it does not matter since |
150 // is has no effect on the quota. | 160 // is has no effect on the quota. |
151 file->file_info_.size = 0; | 161 file->file_info_.size = 0; |
152 } | 162 } |
153 file->kind_ = doc->kind(); | 163 file->kind_ = doc->kind(); |
154 const Link* self_link = doc->GetLinkByType(Link::SELF); | 164 const Link* self_link = doc->GetLinkByType(Link::SELF); |
155 if (self_link) | 165 if (self_link) |
156 file->self_url_ = self_link->href(); | 166 file->self_url_ = self_link->href(); |
157 file->content_url_ = doc->content_url(); | 167 file->content_url_ = doc->content_url(); |
158 file->content_mime_type_ = doc->content_mime_type(); | 168 file->content_mime_type_ = doc->content_mime_type(); |
159 file->etag_ = doc->etag(); | 169 file->etag_ = doc->etag(); |
160 file->resource_id_ = doc->resource_id(); | 170 file->resource_id_ = doc->resource_id(); |
161 file->id_ = doc->id(); | 171 file->id_ = doc->id(); |
162 file->is_hosted_document_ = doc->is_hosted_document(); | 172 file->is_hosted_document_ = doc->is_hosted_document(); |
163 file->file_info_.last_modified = doc->updated_time(); | 173 file->file_info_.last_modified = doc->updated_time(); |
164 file->file_info_.last_accessed = doc->updated_time(); | 174 file->file_info_.last_accessed = doc->updated_time(); |
165 file->file_info_.creation_time = doc->published_time(); | 175 file->file_info_.creation_time = doc->published_time(); |
166 | 176 |
| 177 // SetFileNameFromTitle() must be called after |title_|, |
| 178 // |is_hosted_document_| and |document_extension_| are set. |
| 179 file->SetFileNameFromTitle(); |
| 180 |
167 const Link* thumbnail_link = doc->GetLinkByType(Link::THUMBNAIL); | 181 const Link* thumbnail_link = doc->GetLinkByType(Link::THUMBNAIL); |
168 if (thumbnail_link) | 182 if (thumbnail_link) |
169 file->thumbnail_url_ = thumbnail_link->href(); | 183 file->thumbnail_url_ = thumbnail_link->href(); |
170 | 184 |
171 const Link* alternate_link = doc->GetLinkByType(Link::ALTERNATE); | 185 const Link* alternate_link = doc->GetLinkByType(Link::ALTERNATE); |
172 if (alternate_link) | 186 if (alternate_link) |
173 file->edit_url_ = alternate_link->href(); | 187 file->edit_url_ = alternate_link->href(); |
174 | 188 |
175 return file; | 189 return file; |
176 } | 190 } |
177 | 191 |
178 int GDataFile::GetCacheState() { | 192 int GDataFile::GetCacheState() { |
179 return root_->GetCacheState(resource(), file_md5()); | 193 return root_->GetCacheState(resource_id(), file_md5()); |
180 } | 194 } |
181 | 195 |
182 // GDataDirectory class implementation. | 196 // GDataDirectory class implementation. |
183 | 197 |
184 GDataDirectory::GDataDirectory(GDataDirectory* parent) | 198 GDataDirectory::GDataDirectory(GDataDirectory* parent) |
185 : GDataFileBase(parent) { | 199 : GDataFileBase(parent) { |
186 file_info_.is_directory = true; | 200 file_info_.is_directory = true; |
187 } | 201 } |
188 | 202 |
189 GDataDirectory::~GDataDirectory() { | 203 GDataDirectory::~GDataDirectory() { |
190 RemoveChildren(); | 204 RemoveChildren(); |
191 } | 205 } |
192 | 206 |
193 GDataDirectory* GDataDirectory::AsGDataDirectory() { | 207 GDataDirectory* GDataDirectory::AsGDataDirectory() { |
194 return this; | 208 return this; |
195 } | 209 } |
196 | 210 |
197 // static | 211 // static |
198 GDataFileBase* GDataDirectory::FromDocumentEntry(GDataDirectory* parent, | 212 GDataFileBase* GDataDirectory::FromDocumentEntry(GDataDirectory* parent, |
199 DocumentEntry* doc) { | 213 DocumentEntry* doc) { |
200 DCHECK(parent); | 214 DCHECK(parent); |
201 DCHECK(doc->is_folder()); | 215 DCHECK(doc->is_folder()); |
202 GDataDirectory* dir = new GDataDirectory(parent); | 216 GDataDirectory* dir = new GDataDirectory(parent); |
203 dir->original_file_name_ = UTF16ToUTF8(doc->title()); | 217 dir->title_ = UTF16ToUTF8(doc->title()); |
204 dir->file_name_ = GDataFileBase::EscapeUtf8FileName(dir->original_file_name_); | 218 // SetFileNameFromTitle() must be called after |title_| is set. |
| 219 dir->SetFileNameFromTitle(); |
205 dir->file_info_.last_modified = doc->updated_time(); | 220 dir->file_info_.last_modified = doc->updated_time(); |
206 dir->file_info_.last_accessed = doc->updated_time(); | 221 dir->file_info_.last_accessed = doc->updated_time(); |
207 dir->file_info_.creation_time = doc->published_time(); | 222 dir->file_info_.creation_time = doc->published_time(); |
208 // Extract feed link. | 223 // Extract feed link. |
209 dir->start_feed_url_ = doc->content_url(); | 224 dir->start_feed_url_ = doc->content_url(); |
| 225 dir->resource_id_ = doc->resource_id(); |
210 dir->content_url_ = doc->content_url(); | 226 dir->content_url_ = doc->content_url(); |
211 | 227 |
| 228 const Link* self_link = doc->GetLinkByType(Link::SELF); |
| 229 if (self_link) |
| 230 dir->self_url_ = self_link->href(); |
| 231 |
212 const Link* upload_link = doc->GetLinkByType(Link::RESUMABLE_CREATE_MEDIA); | 232 const Link* upload_link = doc->GetLinkByType(Link::RESUMABLE_CREATE_MEDIA); |
213 if (upload_link) | 233 if (upload_link) |
214 dir->upload_url_ = upload_link->href(); | 234 dir->upload_url_ = upload_link->href(); |
215 | 235 |
216 return dir; | 236 return dir; |
217 } | 237 } |
218 | 238 |
219 void GDataDirectory::RemoveChildren() { | 239 void GDataDirectory::RemoveChildren() { |
220 // Remove children from resource map first. | 240 // Remove children from resource map first. |
221 root_->RemoveFilesFromResourceMap(children_); | 241 root_->RemoveFilesFromResourceMap(children_); |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
253 } | 273 } |
254 } | 274 } |
255 if (full_file_name.value() != file->file_name()) | 275 if (full_file_name.value() != file->file_name()) |
256 file->set_file_name(full_file_name.value()); | 276 file->set_file_name(full_file_name.value()); |
257 children_.insert(std::make_pair(file->file_name(), file)); | 277 children_.insert(std::make_pair(file->file_name(), file)); |
258 | 278 |
259 // Add file to resource map. | 279 // Add file to resource map. |
260 root_->AddFileToResourceMap(file); | 280 root_->AddFileToResourceMap(file); |
261 } | 281 } |
262 | 282 |
| 283 bool GDataDirectory::TakeFile(GDataFileBase* file) { |
| 284 DCHECK(file); |
| 285 DCHECK(file->parent()); |
| 286 |
| 287 file->parent()->RemoveFileFromChildrenList(file); |
| 288 |
| 289 // The file name may have been changed due to prior name de-duplication. |
| 290 // We need to first restore the file name based on the title before going |
| 291 // through name de-duplication again when it is added to another directory. |
| 292 file->SetFileNameFromTitle(); |
| 293 AddFile(file); |
| 294 |
| 295 // Use GDataFileBase::set_parent() to change the parent of GDataFileBase |
| 296 // as GDataDirectory:AddFile() does not do that. |
| 297 file->set_parent(this); |
| 298 return true; |
| 299 } |
| 300 |
263 bool GDataDirectory::RemoveFile(GDataFileBase* file) { | 301 bool GDataDirectory::RemoveFile(GDataFileBase* file) { |
| 302 DCHECK(file); |
| 303 |
| 304 if (!RemoveFileFromChildrenList(file)) |
| 305 return false; |
| 306 |
| 307 delete file; |
| 308 return true; |
| 309 } |
| 310 |
| 311 bool GDataDirectory::RemoveFileFromChildrenList(GDataFileBase* file) { |
| 312 DCHECK(file); |
| 313 |
264 GDataFileCollection::iterator iter = children_.find(file->file_name()); | 314 GDataFileCollection::iterator iter = children_.find(file->file_name()); |
265 if (iter == children_.end()) | 315 if (iter == children_.end()) |
266 return false; | 316 return false; |
267 | 317 |
268 DCHECK(iter->second); | 318 DCHECK(iter->second); |
| 319 DCHECK_EQ(file, iter->second); |
269 | 320 |
270 // Remove file from resource map first. | 321 // Remove file from resource map first. |
271 root_->RemoveFileFromResourceMap(file); | 322 root_->RemoveFileFromResourceMap(file); |
272 | 323 |
273 // Then delete it from tree. | 324 // Then delete it from tree. |
274 delete iter->second; | |
275 children_.erase(iter); | 325 children_.erase(iter); |
276 | 326 |
277 return true; | 327 return true; |
278 } | 328 } |
279 | 329 |
280 // GDataRootDirectory class implementation. | 330 // GDataRootDirectory class implementation. |
281 | 331 |
282 GDataRootDirectory::GDataRootDirectory() | 332 GDataRootDirectory::GDataRootDirectory() |
283 : GDataDirectory(NULL) { | 333 : GDataDirectory(NULL) { |
284 root_ = this; | 334 root_ = this; |
285 } | 335 } |
286 | 336 |
287 GDataRootDirectory::~GDataRootDirectory() { | 337 GDataRootDirectory::~GDataRootDirectory() { |
288 STLDeleteValues(&cache_map_); | 338 STLDeleteValues(&cache_map_); |
289 cache_map_.clear(); | 339 cache_map_.clear(); |
290 | 340 |
291 resource_map_.clear(); | 341 resource_map_.clear(); |
292 } | 342 } |
293 | 343 |
294 GDataRootDirectory* GDataRootDirectory::AsGDataRootDirectory() { | 344 GDataRootDirectory* GDataRootDirectory::AsGDataRootDirectory() { |
295 return this; | 345 return this; |
296 } | 346 } |
297 | 347 |
298 void GDataRootDirectory::AddFileToResourceMap(GDataFileBase* file) { | 348 void GDataRootDirectory::AddFileToResourceMap(GDataFileBase* file) { |
299 // GDataFileSystem has already locked. | 349 // GDataFileSystem has already locked. |
300 // Only files have resource. | 350 // Only files have resource. |
301 if (file->AsGDataFile()) { | 351 if (file->AsGDataFile()) { |
302 resource_map_.insert( | 352 resource_map_.insert( |
303 std::make_pair(file->AsGDataFile()->resource(), file)); | 353 std::make_pair(file->AsGDataFile()->resource_id(), file)); |
304 } | 354 } |
305 } | 355 } |
306 | 356 |
307 void GDataRootDirectory::RemoveFileFromResourceMap(GDataFileBase* file) { | 357 void GDataRootDirectory::RemoveFileFromResourceMap(GDataFileBase* file) { |
308 // GDataFileSystem has already locked. | 358 // GDataFileSystem has already locked. |
309 if (file->AsGDataFile()) | 359 if (file->AsGDataFile()) |
310 resource_map_.erase(file->AsGDataFile()->resource()); | 360 resource_map_.erase(file->AsGDataFile()->resource_id()); |
311 } | 361 } |
312 | 362 |
313 void GDataRootDirectory::RemoveFilesFromResourceMap( | 363 void GDataRootDirectory::RemoveFilesFromResourceMap( |
314 const GDataFileCollection& children) { | 364 const GDataFileCollection& children) { |
315 // GDataFileSystem has already locked. | 365 // GDataFileSystem has already locked. |
316 for (GDataFileCollection::const_iterator iter = children.begin(); | 366 for (GDataFileCollection::const_iterator iter = children.begin(); |
317 iter != children.end(); ++iter) { | 367 iter != children.end(); ++iter) { |
318 // Recursively call RemoveFilesFromResourceMap for each directory. | 368 // Recursively call RemoveFilesFromResourceMap for each directory. |
319 if (iter->second->AsGDataDirectory()) { | 369 if (iter->second->AsGDataDirectory()) { |
320 RemoveFilesFromResourceMap(iter->second->AsGDataDirectory()->children()); | 370 RemoveFilesFromResourceMap(iter->second->AsGDataDirectory()->children()); |
321 continue; | 371 continue; |
322 } | 372 } |
323 | 373 |
324 // Only files have resource. | 374 // Only files have resource. |
325 if (iter->second->AsGDataFile()) | 375 if (iter->second->AsGDataFile()) |
326 resource_map_.erase(iter->second->AsGDataFile()->resource()); | 376 resource_map_.erase(iter->second->AsGDataFile()->resource_id()); |
327 } | 377 } |
328 } | 378 } |
329 | 379 |
330 GDataFileBase* GDataRootDirectory::GetFileByResource( | 380 GDataFileBase* GDataRootDirectory::GetFileByResource( |
331 const std::string& resource) { | 381 const std::string& resource) { |
332 // GDataFileSystem has already locked. | 382 // GDataFileSystem has already locked. |
333 ResourceMap::const_iterator iter = resource_map_.find(resource); | 383 ResourceMap::const_iterator iter = resource_map_.find(resource); |
334 if (iter == resource_map_.end()) | 384 if (iter == resource_map_.end()) |
335 return NULL; | 385 return NULL; |
336 return iter->second; | 386 return iter->second; |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
418 cache_state |= GDataFile::CACHE_STATE_PINNED; | 468 cache_state |= GDataFile::CACHE_STATE_PINNED; |
419 | 469 |
420 DVLOG(1) << "Cache state for res_id " << res_id | 470 DVLOG(1) << "Cache state for res_id " << res_id |
421 << ", md5 " << entry->md5 | 471 << ", md5 " << entry->md5 |
422 << ": " << cache_state; | 472 << ": " << cache_state; |
423 | 473 |
424 return cache_state; | 474 return cache_state; |
425 } | 475 } |
426 | 476 |
427 } // namespace gdata | 477 } // namespace gdata |
OLD | NEW |