OLD | NEW |
| (Empty) |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "chrome/browser/chromeos/gdata/gdata_file_system.h" | |
6 | |
7 #include <set> | |
8 #include <utility> | |
9 | |
10 #include "base/bind.h" | |
11 #include "base/file_util.h" | |
12 #include "base/json/json_file_value_serializer.h" | |
13 #include "base/message_loop.h" | |
14 #include "base/message_loop_proxy.h" | |
15 #include "base/metrics/histogram.h" | |
16 #include "base/platform_file.h" | |
17 #include "base/threading/sequenced_worker_pool.h" | |
18 #include "base/values.h" | |
19 #include "chrome/browser/chromeos/gdata/drive.pb.h" | |
20 #include "chrome/browser/chromeos/gdata/drive_api_parser.h" | |
21 #include "chrome/browser/chromeos/gdata/drive_files.h" | |
22 #include "chrome/browser/chromeos/gdata/drive_service_interface.h" | |
23 #include "chrome/browser/chromeos/gdata/drive_webapps_registry.h" | |
24 #include "chrome/browser/chromeos/gdata/gdata_download_observer.h" | |
25 #include "chrome/browser/chromeos/gdata/gdata_protocol_handler.h" | |
26 #include "chrome/browser/chromeos/gdata/gdata_system_service.h" | |
27 #include "chrome/browser/chromeos/gdata/gdata_uploader.h" | |
28 #include "chrome/browser/chromeos/gdata/gdata_util.h" | |
29 #include "chrome/browser/chromeos/gdata/task_util.h" | |
30 #include "chrome/browser/prefs/pref_service.h" | |
31 #include "chrome/browser/profiles/profile.h" | |
32 #include "chrome/common/chrome_notification_types.h" | |
33 #include "chrome/common/pref_names.h" | |
34 #include "content/public/browser/browser_thread.h" | |
35 #include "content/public/browser/notification_details.h" | |
36 #include "net/base/mime_util.h" | |
37 | |
38 using content::BrowserThread; | |
39 | |
40 namespace gdata { | |
41 namespace { | |
42 | |
43 const char kMimeTypeJson[] = "application/json"; | |
44 const char kMimeTypeOctetStream[] = "application/octet-stream"; | |
45 | |
46 const char kEmptyFilePath[] = "/dev/null"; | |
47 | |
48 // GData update check interval (in seconds). | |
49 #ifndef NDEBUG | |
50 const int kGDataUpdateCheckIntervalInSec = 5; | |
51 #else | |
52 const int kGDataUpdateCheckIntervalInSec = 60; | |
53 #endif | |
54 | |
55 //================================ Helper functions ============================ | |
56 | |
57 // Runs GetFileCallback with pointers dereferenced. | |
58 // Used for PostTaskAndReply(). | |
59 void RunGetFileCallbackHelper(const GetFileCallback& callback, | |
60 DriveFileError* error, | |
61 FilePath* file_path, | |
62 std::string* mime_type, | |
63 DriveFileType* file_type) { | |
64 DCHECK(error); | |
65 DCHECK(file_path); | |
66 DCHECK(mime_type); | |
67 DCHECK(file_type); | |
68 | |
69 if (!callback.is_null()) | |
70 callback.Run(*error, *file_path, *mime_type, *file_type); | |
71 } | |
72 | |
73 // Ditto for FileOperationCallback | |
74 void RunFileOperationCallbackHelper( | |
75 const FileOperationCallback& callback, | |
76 DriveFileError* error) { | |
77 DCHECK(error); | |
78 | |
79 if (!callback.is_null()) | |
80 callback.Run(*error); | |
81 } | |
82 | |
83 // Callback for cache file operations invoked by AddUploadedFileOnUIThread. | |
84 void OnCacheUpdatedForAddUploadedFile( | |
85 const base::Closure& callback, | |
86 DriveFileError /* error */, | |
87 const std::string& /* resource_id */, | |
88 const std::string& /* md5 */) { | |
89 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
90 if (!callback.is_null()) | |
91 callback.Run(); | |
92 } | |
93 | |
94 // Helper function called upon completion of AddUploadFile invoked by | |
95 // OnTransferCompleted. | |
96 void OnAddUploadFileCompleted( | |
97 const FileOperationCallback& callback, | |
98 DriveFileError error) { | |
99 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
100 if (!callback.is_null()) | |
101 callback.Run(error); | |
102 } | |
103 | |
104 // The class to wait for the initial load of root feed and runs the callback | |
105 // after the initialization. | |
106 class InitialLoadObserver : public GDataFileSystemInterface::Observer { | |
107 public: | |
108 InitialLoadObserver(GDataFileSystemInterface* file_system, | |
109 const base::Closure& callback) | |
110 : file_system_(file_system), callback_(callback) {} | |
111 | |
112 virtual void OnInitialLoadFinished() OVERRIDE { | |
113 if (!callback_.is_null()) | |
114 base::MessageLoopProxy::current()->PostTask(FROM_HERE, callback_); | |
115 file_system_->RemoveObserver(this); | |
116 base::MessageLoopProxy::current()->DeleteSoon(FROM_HERE, this); | |
117 } | |
118 | |
119 private: | |
120 GDataFileSystemInterface* file_system_; | |
121 base::Closure callback_; | |
122 }; | |
123 | |
124 // Gets the file size of |local_file|. | |
125 void GetLocalFileSizeOnBlockingPool(const FilePath& local_file, | |
126 DriveFileError* error, | |
127 int64* file_size) { | |
128 DCHECK(error); | |
129 DCHECK(file_size); | |
130 | |
131 *file_size = 0; | |
132 *error = file_util::GetFileSize(local_file, file_size) ? | |
133 DRIVE_FILE_OK : | |
134 DRIVE_FILE_ERROR_NOT_FOUND; | |
135 } | |
136 | |
137 // Gets the file size and the content type of |local_file|. | |
138 void GetLocalFileInfoOnBlockingPool( | |
139 const FilePath& local_file, | |
140 DriveFileError* error, | |
141 int64* file_size, | |
142 std::string* content_type) { | |
143 DCHECK(error); | |
144 DCHECK(file_size); | |
145 DCHECK(content_type); | |
146 | |
147 if (!net::GetMimeTypeFromExtension(local_file.Extension(), content_type)) | |
148 *content_type = kMimeTypeOctetStream; | |
149 | |
150 *file_size = 0; | |
151 *error = file_util::GetFileSize(local_file, file_size) ? | |
152 DRIVE_FILE_OK : | |
153 DRIVE_FILE_ERROR_NOT_FOUND; | |
154 } | |
155 | |
156 // Checks if a local file at |local_file_path| is a JSON file referencing a | |
157 // hosted document on blocking pool, and if so, gets the resource ID of the | |
158 // document. | |
159 void GetDocumentResourceIdOnBlockingPool( | |
160 const FilePath& local_file_path, | |
161 std::string* resource_id) { | |
162 DCHECK(resource_id); | |
163 | |
164 if (DocumentEntry::HasHostedDocumentExtension(local_file_path)) { | |
165 std::string error; | |
166 DictionaryValue* dict_value = NULL; | |
167 JSONFileValueSerializer serializer(local_file_path); | |
168 scoped_ptr<Value> value(serializer.Deserialize(NULL, &error)); | |
169 if (value.get() && value->GetAsDictionary(&dict_value)) | |
170 dict_value->GetString("resource_id", resource_id); | |
171 } | |
172 } | |
173 | |
174 // Creates a temporary JSON file representing a document with |edit_url| | |
175 // and |resource_id| under |document_dir| on blocking pool. | |
176 void CreateDocumentJsonFileOnBlockingPool( | |
177 const FilePath& document_dir, | |
178 const GURL& edit_url, | |
179 const std::string& resource_id, | |
180 DriveFileError* error, | |
181 FilePath* temp_file_path, | |
182 std::string* mime_type, | |
183 DriveFileType* file_type) { | |
184 DCHECK(error); | |
185 DCHECK(temp_file_path); | |
186 DCHECK(mime_type); | |
187 DCHECK(file_type); | |
188 | |
189 *error = DRIVE_FILE_ERROR_FAILED; | |
190 | |
191 if (file_util::CreateTemporaryFileInDir(document_dir, temp_file_path)) { | |
192 std::string document_content = base::StringPrintf( | |
193 "{\"url\": \"%s\", \"resource_id\": \"%s\"}", | |
194 edit_url.spec().c_str(), resource_id.c_str()); | |
195 int document_size = static_cast<int>(document_content.size()); | |
196 if (file_util::WriteFile(*temp_file_path, document_content.data(), | |
197 document_size) == document_size) { | |
198 *error = DRIVE_FILE_OK; | |
199 } | |
200 } | |
201 | |
202 *mime_type = kMimeTypeJson; | |
203 *file_type = HOSTED_DOCUMENT; | |
204 if (*error != DRIVE_FILE_OK) | |
205 temp_file_path->clear(); | |
206 } | |
207 | |
208 // Gets the information of the file at local path |path|. The information is | |
209 // filled in |file_info|, and if it fails |result| will be assigned false. | |
210 void GetFileInfoOnBlockingPool(const FilePath& path, | |
211 base::PlatformFileInfo* file_info, | |
212 bool* result) { | |
213 *result = file_util::GetFileInfo(path, file_info); | |
214 } | |
215 | |
216 // Copies a file from |src_file_path| to |dest_file_path| on the local | |
217 // file system using file_util::CopyFile. |error| is set to | |
218 // DRIVE_FILE_OK on success or DRIVE_FILE_ERROR_FAILED | |
219 // otherwise. | |
220 void CopyLocalFileOnBlockingPool( | |
221 const FilePath& src_file_path, | |
222 const FilePath& dest_file_path, | |
223 DriveFileError* error) { | |
224 DCHECK(error); | |
225 | |
226 *error = file_util::CopyFile(src_file_path, dest_file_path) ? | |
227 DRIVE_FILE_OK : DRIVE_FILE_ERROR_FAILED; | |
228 } | |
229 | |
230 // Callback for GetEntryByResourceIdAsync. | |
231 // Adds |entry| to |results|. Runs |callback| with |results| when | |
232 // |run_callback| is true. When |entry| is not present in our local file system | |
233 // snapshot, it is not added to |results|. Instead, |entry_skipped_callback| is | |
234 // called. | |
235 void AddEntryToSearchResults( | |
236 std::vector<SearchResultInfo>* results, | |
237 const SearchCallback& callback, | |
238 const base::Closure& entry_skipped_callback, | |
239 DriveFileError error, | |
240 bool run_callback, | |
241 const GURL& next_feed, | |
242 DriveEntry* entry) { | |
243 // If a result is not present in our local file system snapshot, invoke | |
244 // |entry_skipped_callback| and refreshes the snapshot with delta feed. | |
245 // For example, this may happen if the entry has recently been added to the | |
246 // drive (and we still haven't received its delta feed). | |
247 if (entry) { | |
248 const bool is_directory = entry->AsDriveDirectory() != NULL; | |
249 results->push_back(SearchResultInfo(entry->GetFilePath(), is_directory)); | |
250 } else { | |
251 if (!entry_skipped_callback.is_null()) | |
252 entry_skipped_callback.Run(); | |
253 } | |
254 | |
255 if (run_callback) { | |
256 scoped_ptr<std::vector<SearchResultInfo> > result_vec(results); | |
257 if (!callback.is_null()) | |
258 callback.Run(error, next_feed, result_vec.Pass()); | |
259 } | |
260 } | |
261 | |
262 // Helper function for binding |path| to GetEntryInfoWithFilePathCallback and | |
263 // create GetEntryInfoCallback. | |
264 void RunGetEntryInfoWithFilePathCallback( | |
265 const GetEntryInfoWithFilePathCallback& callback, | |
266 const FilePath& path, | |
267 DriveFileError error, | |
268 scoped_ptr<DriveEntryProto> entry_proto) { | |
269 if (!callback.is_null()) | |
270 callback.Run(error, path, entry_proto.Pass()); | |
271 } | |
272 | |
273 } // namespace | |
274 | |
275 // GDataFileSystem::CreateDirectoryParams struct implementation. | |
276 struct GDataFileSystem::CreateDirectoryParams { | |
277 CreateDirectoryParams(const FilePath& created_directory_path, | |
278 const FilePath& target_directory_path, | |
279 bool is_exclusive, | |
280 bool is_recursive, | |
281 const FileOperationCallback& callback); | |
282 ~CreateDirectoryParams(); | |
283 | |
284 const FilePath created_directory_path; | |
285 const FilePath target_directory_path; | |
286 const bool is_exclusive; | |
287 const bool is_recursive; | |
288 FileOperationCallback callback; | |
289 }; | |
290 | |
291 GDataFileSystem::CreateDirectoryParams::CreateDirectoryParams( | |
292 const FilePath& created_directory_path, | |
293 const FilePath& target_directory_path, | |
294 bool is_exclusive, | |
295 bool is_recursive, | |
296 const FileOperationCallback& callback) | |
297 : created_directory_path(created_directory_path), | |
298 target_directory_path(target_directory_path), | |
299 is_exclusive(is_exclusive), | |
300 is_recursive(is_recursive), | |
301 callback(callback) { | |
302 } | |
303 | |
304 GDataFileSystem::CreateDirectoryParams::~CreateDirectoryParams() { | |
305 } | |
306 | |
307 // GDataFileSystem::GetFileCompleteForOpenParams struct implementation. | |
308 struct GDataFileSystem::GetFileCompleteForOpenParams { | |
309 GetFileCompleteForOpenParams(const std::string& resource_id, | |
310 const std::string& md5); | |
311 ~GetFileCompleteForOpenParams(); | |
312 std::string resource_id; | |
313 std::string md5; | |
314 }; | |
315 | |
316 GDataFileSystem::GetFileCompleteForOpenParams::GetFileCompleteForOpenParams( | |
317 const std::string& resource_id, | |
318 const std::string& md5) | |
319 : resource_id(resource_id), | |
320 md5(md5) { | |
321 } | |
322 | |
323 GDataFileSystem::GetFileCompleteForOpenParams::~GetFileCompleteForOpenParams() { | |
324 } | |
325 | |
326 // GDataFileSystem::GetFileFromCacheParams struct implementation. | |
327 struct GDataFileSystem::GetFileFromCacheParams { | |
328 GetFileFromCacheParams( | |
329 const FilePath& virtual_file_path, | |
330 const FilePath& local_tmp_path, | |
331 const GURL& content_url, | |
332 const std::string& resource_id, | |
333 const std::string& md5, | |
334 const std::string& mime_type, | |
335 const GetFileCallback& get_file_callback, | |
336 const GetContentCallback& get_content_callback); | |
337 ~GetFileFromCacheParams(); | |
338 | |
339 FilePath virtual_file_path; | |
340 FilePath local_tmp_path; | |
341 GURL content_url; | |
342 std::string resource_id; | |
343 std::string md5; | |
344 std::string mime_type; | |
345 const GetFileCallback get_file_callback; | |
346 const GetContentCallback get_content_callback; | |
347 }; | |
348 | |
349 GDataFileSystem::GetFileFromCacheParams::GetFileFromCacheParams( | |
350 const FilePath& virtual_file_path, | |
351 const FilePath& local_tmp_path, | |
352 const GURL& content_url, | |
353 const std::string& resource_id, | |
354 const std::string& md5, | |
355 const std::string& mime_type, | |
356 const GetFileCallback& get_file_callback, | |
357 const GetContentCallback& get_content_callback) | |
358 : virtual_file_path(virtual_file_path), | |
359 local_tmp_path(local_tmp_path), | |
360 content_url(content_url), | |
361 resource_id(resource_id), | |
362 md5(md5), | |
363 mime_type(mime_type), | |
364 get_file_callback(get_file_callback), | |
365 get_content_callback(get_content_callback) { | |
366 } | |
367 | |
368 GDataFileSystem::GetFileFromCacheParams::~GetFileFromCacheParams() { | |
369 } | |
370 | |
371 // GDataFileSystem::StartFileUploadParams implementation. | |
372 struct GDataFileSystem::StartFileUploadParams { | |
373 StartFileUploadParams(const FilePath& in_local_file_path, | |
374 const FilePath& in_remote_file_path, | |
375 const FileOperationCallback& in_callback) | |
376 : local_file_path(in_local_file_path), | |
377 remote_file_path(in_remote_file_path), | |
378 callback(in_callback) {} | |
379 | |
380 const FilePath local_file_path; | |
381 const FilePath remote_file_path; | |
382 const FileOperationCallback callback; | |
383 }; | |
384 | |
385 // GDataFileSystem::AddUploadedFileParams implementation. | |
386 struct GDataFileSystem::AddUploadedFileParams { | |
387 AddUploadedFileParams(UploadMode upload_mode, | |
388 DriveDirectory* parent_dir, | |
389 scoped_ptr<DriveEntry> new_entry, | |
390 const FilePath& file_content_path, | |
391 DriveCache::FileOperationType cache_operation, | |
392 const base::Closure& callback) | |
393 : upload_mode(upload_mode), | |
394 parent_dir(parent_dir), | |
395 new_entry(new_entry.Pass()), | |
396 file_content_path(file_content_path), | |
397 cache_operation(cache_operation), | |
398 callback(callback) { | |
399 } | |
400 | |
401 UploadMode upload_mode; | |
402 DriveDirectory* parent_dir; | |
403 scoped_ptr<DriveEntry> new_entry; | |
404 FilePath file_content_path; | |
405 DriveCache::FileOperationType cache_operation; | |
406 base::Closure callback; | |
407 std::string resource_id; | |
408 std::string md5; | |
409 }; | |
410 | |
411 | |
412 // GDataFileSystem class implementation. | |
413 | |
414 GDataFileSystem::GDataFileSystem( | |
415 Profile* profile, | |
416 DriveCache* cache, | |
417 DriveServiceInterface* drive_service, | |
418 GDataUploaderInterface* uploader, | |
419 DriveWebAppsRegistryInterface* webapps_registry, | |
420 base::SequencedTaskRunner* blocking_task_runner) | |
421 : profile_(profile), | |
422 cache_(cache), | |
423 uploader_(uploader), | |
424 drive_service_(drive_service), | |
425 webapps_registry_(webapps_registry), | |
426 update_timer_(true /* retain_user_task */, true /* is_repeating */), | |
427 hide_hosted_docs_(false), | |
428 blocking_task_runner_(blocking_task_runner), | |
429 ui_weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), | |
430 ui_weak_ptr_(ui_weak_ptr_factory_.GetWeakPtr()) { | |
431 // Should be created from the file browser extension API on UI thread. | |
432 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
433 } | |
434 | |
435 void GDataFileSystem::Initialize() { | |
436 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
437 | |
438 drive_service_->Initialize(profile_); | |
439 | |
440 resource_metadata_.reset(new DriveResourceMetadata); | |
441 feed_loader_.reset(new GDataWapiFeedLoader(resource_metadata_.get(), | |
442 drive_service_, | |
443 webapps_registry_, | |
444 cache_, | |
445 blocking_task_runner_)); | |
446 feed_loader_->AddObserver(this); | |
447 | |
448 PrefService* pref_service = profile_->GetPrefs(); | |
449 hide_hosted_docs_ = pref_service->GetBoolean(prefs::kDisableGDataHostedFiles); | |
450 | |
451 InitializePreferenceObserver(); | |
452 } | |
453 | |
454 void GDataFileSystem::CheckForUpdates() { | |
455 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
456 ContentOrigin initial_origin = resource_metadata_->origin(); | |
457 if (initial_origin == FROM_SERVER) { | |
458 resource_metadata_->set_origin(REFRESHING); | |
459 feed_loader_->ReloadFromServerIfNeeded( | |
460 initial_origin, | |
461 resource_metadata_->largest_changestamp(), | |
462 base::Bind(&GDataFileSystem::OnUpdateChecked, | |
463 ui_weak_ptr_, | |
464 initial_origin)); | |
465 } | |
466 } | |
467 | |
468 void GDataFileSystem::OnUpdateChecked(ContentOrigin initial_origin, | |
469 DriveFileError error) { | |
470 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
471 | |
472 if (error != DRIVE_FILE_OK) | |
473 resource_metadata_->set_origin(initial_origin); | |
474 } | |
475 | |
476 GDataFileSystem::~GDataFileSystem() { | |
477 // This should be called from UI thread, from GDataSystemService shutdown. | |
478 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
479 | |
480 feed_loader_->RemoveObserver(this); | |
481 | |
482 // Cancel all the in-flight operations. | |
483 // This asynchronously cancels the URL fetch operations. | |
484 drive_service_->CancelAll(); | |
485 } | |
486 | |
487 void GDataFileSystem::AddObserver( | |
488 GDataFileSystemInterface::Observer* observer) { | |
489 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
490 observers_.AddObserver(observer); | |
491 } | |
492 | |
493 void GDataFileSystem::RemoveObserver( | |
494 GDataFileSystemInterface::Observer* observer) { | |
495 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
496 observers_.RemoveObserver(observer); | |
497 } | |
498 | |
499 void GDataFileSystem::StartUpdates() { | |
500 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
501 | |
502 DCHECK(!update_timer_.IsRunning()); | |
503 update_timer_.Start(FROM_HERE, | |
504 base::TimeDelta::FromSeconds( | |
505 kGDataUpdateCheckIntervalInSec), | |
506 base::Bind(&GDataFileSystem::CheckForUpdates, | |
507 ui_weak_ptr_)); | |
508 } | |
509 | |
510 void GDataFileSystem::StopUpdates() { | |
511 // If unmount request comes from filesystem side, this method may be called | |
512 // twice. First is just after unmounting on filesystem, second is after | |
513 // unmounting on filemanager on JS. In other words, if this is called from | |
514 // GDataSystemService::RemoveDriveMountPoint(), this will be called again from | |
515 // FileBrowserEventRouter::HandleRemoteUpdateRequestOnUIThread(). | |
516 // We choose to stopping updates asynchronous without waiting for filemanager, | |
517 // rather than waiting for completion of unmounting on filemanager. | |
518 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
519 if (update_timer_.IsRunning()) | |
520 update_timer_.Stop(); | |
521 } | |
522 | |
523 void GDataFileSystem::GetEntryInfoByResourceId( | |
524 const std::string& resource_id, | |
525 const GetEntryInfoWithFilePathCallback& callback) { | |
526 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || | |
527 BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
528 DCHECK(!callback.is_null()); | |
529 | |
530 RunTaskOnUIThread( | |
531 base::Bind(&GDataFileSystem::GetEntryInfoByResourceIdOnUIThread, | |
532 ui_weak_ptr_, | |
533 resource_id, | |
534 CreateRelayCallback(callback))); | |
535 } | |
536 | |
537 void GDataFileSystem::GetEntryInfoByResourceIdOnUIThread( | |
538 const std::string& resource_id, | |
539 const GetEntryInfoWithFilePathCallback& callback) { | |
540 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
541 DCHECK(!callback.is_null()); | |
542 | |
543 resource_metadata_->GetEntryByResourceIdAsync(resource_id, | |
544 base::Bind(&GDataFileSystem::GetEntryInfoByEntryOnUIThread, | |
545 ui_weak_ptr_, | |
546 callback)); | |
547 } | |
548 | |
549 void GDataFileSystem::GetEntryInfoByEntryOnUIThread( | |
550 const GetEntryInfoWithFilePathCallback& callback, | |
551 DriveEntry* entry) { | |
552 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
553 DCHECK(!callback.is_null()); | |
554 | |
555 if (entry) { | |
556 scoped_ptr<DriveEntryProto> entry_proto(new DriveEntryProto); | |
557 entry->ToProtoFull(entry_proto.get()); | |
558 CheckLocalModificationAndRun( | |
559 entry_proto.Pass(), | |
560 base::Bind(&RunGetEntryInfoWithFilePathCallback, | |
561 callback, entry->GetFilePath())); | |
562 } else { | |
563 callback.Run(DRIVE_FILE_ERROR_NOT_FOUND, | |
564 FilePath(), | |
565 scoped_ptr<DriveEntryProto>()); | |
566 } | |
567 } | |
568 | |
569 void GDataFileSystem::LoadFeedIfNeeded(const FileOperationCallback& callback) { | |
570 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
571 DCHECK(!callback.is_null()); | |
572 | |
573 if (resource_metadata_->origin() == INITIALIZING) { | |
574 // If root feed is not initialized but the initialization process has | |
575 // already started, add an observer to execute the remaining task after | |
576 // the end of the initialization. | |
577 AddObserver(new InitialLoadObserver(this, | |
578 base::Bind(callback, DRIVE_FILE_OK))); | |
579 return; | |
580 } else if (resource_metadata_->origin() == UNINITIALIZED) { | |
581 // Load root feed from this disk cache. Upon completion, kick off server | |
582 // fetching. | |
583 resource_metadata_->set_origin(INITIALIZING); | |
584 feed_loader_->LoadFromCache( | |
585 true, // should_load_from_server | |
586 base::Bind(&GDataFileSystem::NotifyInitialLoadFinishedAndRun, | |
587 ui_weak_ptr_, | |
588 callback)); | |
589 return; | |
590 } | |
591 | |
592 // The feed has already been loaded, so we have nothing to do, but post a | |
593 // task to the same thread, rather than calling it here, as | |
594 // LoadFeedIfNeeded() is asynchronous. | |
595 base::MessageLoopProxy::current()->PostTask( | |
596 FROM_HERE, | |
597 base::Bind(callback, DRIVE_FILE_OK)); | |
598 } | |
599 | |
600 void GDataFileSystem::TransferFileFromRemoteToLocal( | |
601 const FilePath& remote_src_file_path, | |
602 const FilePath& local_dest_file_path, | |
603 const FileOperationCallback& callback) { | |
604 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
605 DCHECK(!callback.is_null()); | |
606 | |
607 GetFileByPath(remote_src_file_path, | |
608 base::Bind(&GDataFileSystem::OnGetFileCompleteForTransferFile, | |
609 ui_weak_ptr_, | |
610 local_dest_file_path, | |
611 callback), | |
612 GetContentCallback()); | |
613 } | |
614 | |
615 void GDataFileSystem::TransferFileFromLocalToRemote( | |
616 const FilePath& local_src_file_path, | |
617 const FilePath& remote_dest_file_path, | |
618 const FileOperationCallback& callback) { | |
619 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
620 DCHECK(!callback.is_null()); | |
621 | |
622 // Make sure the destination directory exists. | |
623 resource_metadata_->GetEntryInfoByPath( | |
624 remote_dest_file_path.DirName(), | |
625 base::Bind( | |
626 &GDataFileSystem::TransferFileFromLocalToRemoteAfterGetEntryInfo, | |
627 ui_weak_ptr_, | |
628 local_src_file_path, | |
629 remote_dest_file_path, | |
630 callback)); | |
631 } | |
632 | |
633 void GDataFileSystem::TransferFileFromLocalToRemoteAfterGetEntryInfo( | |
634 const FilePath& local_src_file_path, | |
635 const FilePath& remote_dest_file_path, | |
636 const FileOperationCallback& callback, | |
637 DriveFileError error, | |
638 scoped_ptr<DriveEntryProto> entry_proto) { | |
639 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
640 DCHECK(!callback.is_null()); | |
641 | |
642 if (error != DRIVE_FILE_OK) { | |
643 callback.Run(error); | |
644 return; | |
645 } | |
646 | |
647 DCHECK(entry_proto.get()); | |
648 if (!entry_proto->file_info().is_directory()) { | |
649 // The parent of |remote_dest_file_path| is not a directory. | |
650 callback.Run(DRIVE_FILE_ERROR_NOT_A_DIRECTORY); | |
651 return; | |
652 } | |
653 | |
654 std::string* resource_id = new std::string; | |
655 util::PostBlockingPoolSequencedTaskAndReply( | |
656 FROM_HERE, | |
657 blocking_task_runner_, | |
658 base::Bind(&GetDocumentResourceIdOnBlockingPool, | |
659 local_src_file_path, | |
660 resource_id), | |
661 base::Bind(&GDataFileSystem::TransferFileForResourceId, | |
662 ui_weak_ptr_, | |
663 local_src_file_path, | |
664 remote_dest_file_path, | |
665 callback, | |
666 base::Owned(resource_id))); | |
667 } | |
668 | |
669 void GDataFileSystem::TransferFileForResourceId( | |
670 const FilePath& local_file_path, | |
671 const FilePath& remote_dest_file_path, | |
672 const FileOperationCallback& callback, | |
673 std::string* resource_id) { | |
674 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
675 DCHECK(resource_id); | |
676 DCHECK(!callback.is_null()); | |
677 | |
678 if (resource_id->empty()) { | |
679 // If |resource_id| is empty, upload the local file as a regular file. | |
680 TransferRegularFile(local_file_path, remote_dest_file_path, callback); | |
681 return; | |
682 } | |
683 | |
684 // Otherwise, copy the document on the server side and add the new copy | |
685 // to the destination directory (collection). | |
686 CopyDocumentToDirectory( | |
687 remote_dest_file_path.DirName(), | |
688 *resource_id, | |
689 // Drop the document extension, which should not be | |
690 // in the document title. | |
691 remote_dest_file_path.BaseName().RemoveExtension().value(), | |
692 callback); | |
693 } | |
694 | |
695 void GDataFileSystem::TransferRegularFile( | |
696 const FilePath& local_file_path, | |
697 const FilePath& remote_dest_file_path, | |
698 const FileOperationCallback& callback) { | |
699 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
700 | |
701 DriveFileError* error = | |
702 new DriveFileError(DRIVE_FILE_OK); | |
703 int64* file_size = new int64; | |
704 std::string* content_type = new std::string; | |
705 util::PostBlockingPoolSequencedTaskAndReply( | |
706 FROM_HERE, | |
707 blocking_task_runner_, | |
708 base::Bind(&GetLocalFileInfoOnBlockingPool, | |
709 local_file_path, | |
710 error, | |
711 file_size, | |
712 content_type), | |
713 base::Bind(&GDataFileSystem::StartFileUploadOnUIThread, | |
714 ui_weak_ptr_, | |
715 StartFileUploadParams(local_file_path, | |
716 remote_dest_file_path, | |
717 callback), | |
718 base::Owned(error), | |
719 base::Owned(file_size), | |
720 base::Owned(content_type))); | |
721 } | |
722 | |
723 void GDataFileSystem::StartFileUploadOnUIThread( | |
724 const StartFileUploadParams& params, | |
725 DriveFileError* error, | |
726 int64* file_size, | |
727 std::string* content_type) { | |
728 // This method needs to run on the UI thread as required by | |
729 // GDataUploader::UploadNewFile(). | |
730 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
731 DCHECK(error); | |
732 DCHECK(file_size); | |
733 DCHECK(content_type); | |
734 | |
735 if (*error != DRIVE_FILE_OK) { | |
736 if (!params.callback.is_null()) | |
737 params.callback.Run(*error); | |
738 | |
739 return; | |
740 } | |
741 | |
742 // Make sure the destination directory exists. | |
743 resource_metadata_->GetEntryInfoByPath( | |
744 params.remote_file_path.DirName(), | |
745 base::Bind( | |
746 &GDataFileSystem::StartFileUploadOnUIThreadAfterGetEntryInfo, | |
747 ui_weak_ptr_, | |
748 params, | |
749 *file_size, | |
750 *content_type)); | |
751 } | |
752 | |
753 void GDataFileSystem::StartFileUploadOnUIThreadAfterGetEntryInfo( | |
754 const StartFileUploadParams& params, | |
755 int64 file_size, | |
756 std::string content_type, | |
757 DriveFileError error, | |
758 scoped_ptr<DriveEntryProto> entry_proto) { | |
759 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
760 | |
761 if (entry_proto.get() && !entry_proto->file_info().is_directory()) | |
762 error = DRIVE_FILE_ERROR_NOT_A_DIRECTORY; | |
763 | |
764 if (error != DRIVE_FILE_OK) { | |
765 if (!params.callback.is_null()) | |
766 params.callback.Run(error); | |
767 return; | |
768 } | |
769 DCHECK(entry_proto.get()); | |
770 | |
771 // Fill in values of UploadFileInfo. | |
772 scoped_ptr<UploadFileInfo> upload_file_info(new UploadFileInfo); | |
773 upload_file_info->file_path = params.local_file_path; | |
774 upload_file_info->file_size = file_size; | |
775 upload_file_info->gdata_path = params.remote_file_path; | |
776 // Use the file name as the title. | |
777 upload_file_info->title = params.remote_file_path.BaseName().value(); | |
778 upload_file_info->content_length = file_size; | |
779 upload_file_info->all_bytes_present = true; | |
780 upload_file_info->content_type = content_type; | |
781 upload_file_info->initial_upload_location = GURL(entry_proto->upload_url()); | |
782 upload_file_info->upload_mode = UPLOAD_NEW_FILE; | |
783 | |
784 upload_file_info->completion_callback = | |
785 base::Bind(&GDataFileSystem::OnTransferCompleted, | |
786 ui_weak_ptr_, | |
787 params.callback); | |
788 | |
789 uploader_->UploadNewFile(upload_file_info.Pass()); | |
790 } | |
791 | |
792 void GDataFileSystem::OnTransferCompleted( | |
793 const FileOperationCallback& callback, | |
794 DriveFileError error, | |
795 scoped_ptr<UploadFileInfo> upload_file_info) { | |
796 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
797 DCHECK(upload_file_info.get()); | |
798 | |
799 if (error == DRIVE_FILE_OK && upload_file_info->entry.get()) { | |
800 AddUploadedFile(UPLOAD_NEW_FILE, | |
801 upload_file_info->gdata_path.DirName(), | |
802 upload_file_info->entry.Pass(), | |
803 upload_file_info->file_path, | |
804 DriveCache::FILE_OPERATION_COPY, | |
805 base::Bind(&OnAddUploadFileCompleted, callback, error)); | |
806 } else if (!callback.is_null()) { | |
807 callback.Run(error); | |
808 } | |
809 } | |
810 | |
811 void GDataFileSystem::Copy(const FilePath& src_file_path, | |
812 const FilePath& dest_file_path, | |
813 const FileOperationCallback& callback) { | |
814 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || | |
815 BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
816 DCHECK(!callback.is_null()); | |
817 | |
818 RunTaskOnUIThread(base::Bind(&GDataFileSystem::CopyOnUIThread, | |
819 ui_weak_ptr_, | |
820 src_file_path, | |
821 dest_file_path, | |
822 CreateRelayCallback(callback))); | |
823 } | |
824 | |
825 void GDataFileSystem::CopyOnUIThread(const FilePath& src_file_path, | |
826 const FilePath& dest_file_path, | |
827 const FileOperationCallback& callback) { | |
828 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
829 DCHECK(!callback.is_null()); | |
830 | |
831 resource_metadata_->GetEntryInfoPairByPaths( | |
832 src_file_path, | |
833 dest_file_path.DirName(), | |
834 base::Bind(&GDataFileSystem::CopyOnUIThreadAfterGetEntryInfoPair, | |
835 ui_weak_ptr_, | |
836 dest_file_path, | |
837 callback)); | |
838 } | |
839 | |
840 void GDataFileSystem::CopyOnUIThreadAfterGetEntryInfoPair( | |
841 const FilePath& dest_file_path, | |
842 const FileOperationCallback& callback, | |
843 scoped_ptr<EntryInfoPairResult> result) { | |
844 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
845 DCHECK(!callback.is_null()); | |
846 DCHECK(result.get()); | |
847 | |
848 if (result->first.error != DRIVE_FILE_OK) { | |
849 callback.Run(result->first.error); | |
850 return; | |
851 } else if (result->second.error != DRIVE_FILE_OK) { | |
852 callback.Run(result->second.error); | |
853 return; | |
854 } | |
855 | |
856 scoped_ptr<DriveEntryProto> src_file_proto = result->first.proto.Pass(); | |
857 scoped_ptr<DriveEntryProto> dest_parent_proto = result->second.proto.Pass(); | |
858 | |
859 if (!dest_parent_proto->file_info().is_directory()) { | |
860 callback.Run(DRIVE_FILE_ERROR_NOT_A_DIRECTORY); | |
861 return; | |
862 } else if (src_file_proto->file_info().is_directory()) { | |
863 // TODO(kochi): Implement copy for directories. In the interim, | |
864 // we handle recursive directory copy in the file manager. | |
865 // crbug.com/141596 | |
866 callback.Run(DRIVE_FILE_ERROR_INVALID_OPERATION); | |
867 return; | |
868 } | |
869 | |
870 if (src_file_proto->file_specific_info().is_hosted_document()) { | |
871 CopyDocumentToDirectory(dest_file_path.DirName(), | |
872 src_file_proto->resource_id(), | |
873 // Drop the document extension, which should not be | |
874 // in the document title. | |
875 dest_file_path.BaseName().RemoveExtension().value(), | |
876 callback); | |
877 return; | |
878 } | |
879 | |
880 // TODO(kochi): Reimplement this once the server API supports | |
881 // copying of regular files directly on the server side. crbug.com/138273 | |
882 const FilePath& src_file_path = result->first.path; | |
883 GetFileByPath(src_file_path, | |
884 base::Bind(&GDataFileSystem::OnGetFileCompleteForCopy, | |
885 ui_weak_ptr_, | |
886 dest_file_path, | |
887 callback), | |
888 GetContentCallback()); | |
889 } | |
890 | |
891 void GDataFileSystem::OnGetFileCompleteForCopy( | |
892 const FilePath& remote_dest_file_path, | |
893 const FileOperationCallback& callback, | |
894 DriveFileError error, | |
895 const FilePath& local_file_path, | |
896 const std::string& unused_mime_type, | |
897 DriveFileType file_type) { | |
898 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
899 DCHECK(!callback.is_null()); | |
900 | |
901 if (error != DRIVE_FILE_OK) { | |
902 callback.Run(error); | |
903 return; | |
904 } | |
905 | |
906 // This callback is only triggered for a regular file via Copy(). | |
907 DCHECK_EQ(REGULAR_FILE, file_type); | |
908 TransferRegularFile(local_file_path, remote_dest_file_path, callback); | |
909 } | |
910 | |
911 void GDataFileSystem::OnGetFileCompleteForTransferFile( | |
912 const FilePath& local_dest_file_path, | |
913 const FileOperationCallback& callback, | |
914 DriveFileError error, | |
915 const FilePath& local_file_path, | |
916 const std::string& unused_mime_type, | |
917 DriveFileType file_type) { | |
918 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
919 DCHECK(!callback.is_null()); | |
920 | |
921 if (error != DRIVE_FILE_OK) { | |
922 callback.Run(error); | |
923 return; | |
924 } | |
925 | |
926 // GetFileByPath downloads the file from gdata to a local cache, which is then | |
927 // copied to the actual destination path on the local file system using | |
928 // CopyLocalFileOnBlockingPool. | |
929 DriveFileError* copy_file_error = | |
930 new DriveFileError(DRIVE_FILE_OK); | |
931 util::PostBlockingPoolSequencedTaskAndReply( | |
932 FROM_HERE, | |
933 blocking_task_runner_, | |
934 base::Bind(&CopyLocalFileOnBlockingPool, | |
935 local_file_path, | |
936 local_dest_file_path, | |
937 copy_file_error), | |
938 base::Bind(&RunFileOperationCallbackHelper, | |
939 callback, | |
940 base::Owned(copy_file_error))); | |
941 } | |
942 | |
943 void GDataFileSystem::CopyDocumentToDirectory( | |
944 const FilePath& dir_path, | |
945 const std::string& resource_id, | |
946 const FilePath::StringType& new_name, | |
947 const FileOperationCallback& callback) { | |
948 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
949 DCHECK(!callback.is_null()); | |
950 | |
951 drive_service_->CopyDocument(resource_id, new_name, | |
952 base::Bind(&GDataFileSystem::OnCopyDocumentCompleted, | |
953 ui_weak_ptr_, | |
954 dir_path, | |
955 callback)); | |
956 } | |
957 | |
958 void GDataFileSystem::Rename(const FilePath& file_path, | |
959 const FilePath::StringType& new_name, | |
960 const FileMoveCallback& callback) { | |
961 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
962 DCHECK(!callback.is_null()); | |
963 | |
964 // It is a no-op if the file is renamed to the same name. | |
965 if (file_path.BaseName().value() == new_name) { | |
966 callback.Run(DRIVE_FILE_OK, file_path); | |
967 return; | |
968 } | |
969 | |
970 // Get the edit URL of an entry at |file_path|. | |
971 resource_metadata_->GetEntryInfoByPath( | |
972 file_path, | |
973 base::Bind( | |
974 &GDataFileSystem::RenameAfterGetEntryInfo, | |
975 ui_weak_ptr_, | |
976 file_path, | |
977 new_name, | |
978 callback)); | |
979 } | |
980 | |
981 void GDataFileSystem::RenameAfterGetEntryInfo( | |
982 const FilePath& file_path, | |
983 const FilePath::StringType& new_name, | |
984 const FileMoveCallback& callback, | |
985 DriveFileError error, | |
986 scoped_ptr<DriveEntryProto> entry_proto) { | |
987 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
988 | |
989 if (error != DRIVE_FILE_OK) { | |
990 if (!callback.is_null()) | |
991 callback.Run(error, file_path); | |
992 return; | |
993 } | |
994 DCHECK(entry_proto.get()); | |
995 | |
996 // Drop the .g<something> extension from |new_name| if the file being | |
997 // renamed is a hosted document and |new_name| has the same .g<something> | |
998 // extension as the file. | |
999 FilePath::StringType file_name = new_name; | |
1000 if (entry_proto->has_file_specific_info() && | |
1001 entry_proto->file_specific_info().is_hosted_document()) { | |
1002 FilePath new_file(file_name); | |
1003 if (new_file.Extension() == | |
1004 entry_proto->file_specific_info().document_extension()) { | |
1005 file_name = new_file.RemoveExtension().value(); | |
1006 } | |
1007 } | |
1008 | |
1009 drive_service_->RenameResource( | |
1010 GURL(entry_proto->edit_url()), | |
1011 file_name, | |
1012 base::Bind(&GDataFileSystem::RenameEntryLocally, | |
1013 ui_weak_ptr_, | |
1014 file_path, | |
1015 file_name, | |
1016 callback)); | |
1017 } | |
1018 | |
1019 void GDataFileSystem::Move(const FilePath& src_file_path, | |
1020 const FilePath& dest_file_path, | |
1021 const FileOperationCallback& callback) { | |
1022 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || | |
1023 BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
1024 DCHECK(!callback.is_null()); | |
1025 | |
1026 RunTaskOnUIThread(base::Bind(&GDataFileSystem::MoveOnUIThread, | |
1027 ui_weak_ptr_, | |
1028 src_file_path, | |
1029 dest_file_path, | |
1030 CreateRelayCallback(callback))); | |
1031 } | |
1032 | |
1033 void GDataFileSystem::MoveOnUIThread(const FilePath& src_file_path, | |
1034 const FilePath& dest_file_path, | |
1035 const FileOperationCallback& callback) { | |
1036 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
1037 DCHECK(!callback.is_null()); | |
1038 | |
1039 resource_metadata_->GetEntryInfoPairByPaths( | |
1040 src_file_path, | |
1041 dest_file_path.DirName(), | |
1042 base::Bind(&GDataFileSystem::MoveOnUIThreadAfterGetEntryInfoPair, | |
1043 ui_weak_ptr_, | |
1044 dest_file_path, | |
1045 callback)); | |
1046 } | |
1047 | |
1048 void GDataFileSystem::MoveOnUIThreadAfterGetEntryInfoPair( | |
1049 const FilePath& dest_file_path, | |
1050 const FileOperationCallback& callback, | |
1051 scoped_ptr<EntryInfoPairResult> result) { | |
1052 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
1053 DCHECK(!callback.is_null()); | |
1054 DCHECK(result.get()); | |
1055 | |
1056 if (result->first.error != DRIVE_FILE_OK) { | |
1057 callback.Run(result->first.error); | |
1058 return; | |
1059 } else if (result->second.error != DRIVE_FILE_OK) { | |
1060 callback.Run(result->second.error); | |
1061 return; | |
1062 } | |
1063 | |
1064 scoped_ptr<DriveEntryProto> dest_parent_proto = result->second.proto.Pass(); | |
1065 if (!dest_parent_proto->file_info().is_directory()) { | |
1066 callback.Run(DRIVE_FILE_ERROR_NOT_A_DIRECTORY); | |
1067 return; | |
1068 } | |
1069 | |
1070 // If the file/directory is moved to the same directory, just rename it. | |
1071 const FilePath& src_file_path = result->first.path; | |
1072 const FilePath& dest_parent_path = result->second.path; | |
1073 if (src_file_path.DirName() == dest_parent_path) { | |
1074 FileMoveCallback final_file_path_update_callback = | |
1075 base::Bind(&GDataFileSystem::OnFilePathUpdated, | |
1076 ui_weak_ptr_, | |
1077 callback); | |
1078 | |
1079 Rename(src_file_path, dest_file_path.BaseName().value(), | |
1080 final_file_path_update_callback); | |
1081 return; | |
1082 } | |
1083 | |
1084 // Otherwise, the move operation involves three steps: | |
1085 // 1. Renames the file at |src_file_path| to basename(|dest_file_path|) | |
1086 // within the same directory. The rename operation is a no-op if | |
1087 // basename(|src_file_path|) equals to basename(|dest_file_path|). | |
1088 // 2. Removes the file from its parent directory (the file is not deleted), | |
1089 // which effectively moves the file to the root directory. | |
1090 // 3. Adds the file to the parent directory of |dest_file_path|, which | |
1091 // effectively moves the file from the root directory to the parent | |
1092 // directory of |dest_file_path|. | |
1093 const FileMoveCallback add_file_to_directory_callback = | |
1094 base::Bind(&GDataFileSystem::MoveEntryFromRootDirectory, | |
1095 ui_weak_ptr_, | |
1096 dest_file_path.DirName(), | |
1097 callback); | |
1098 | |
1099 const FileMoveCallback remove_file_from_directory_callback = | |
1100 base::Bind(&GDataFileSystem::RemoveEntryFromNonRootDirectory, | |
1101 ui_weak_ptr_, | |
1102 add_file_to_directory_callback); | |
1103 | |
1104 Rename(src_file_path, dest_file_path.BaseName().value(), | |
1105 remove_file_from_directory_callback); | |
1106 } | |
1107 | |
1108 void GDataFileSystem::MoveEntryFromRootDirectory( | |
1109 const FilePath& dir_path, | |
1110 const FileOperationCallback& callback, | |
1111 DriveFileError error, | |
1112 const FilePath& file_path) { | |
1113 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
1114 DCHECK(!callback.is_null()); | |
1115 DCHECK_EQ(kDriveRootDirectory, file_path.DirName().value()); | |
1116 | |
1117 // Return if there is an error or |dir_path| is the root directory. | |
1118 if (error != DRIVE_FILE_OK || dir_path == FilePath(kDriveRootDirectory)) { | |
1119 callback.Run(error); | |
1120 return; | |
1121 } | |
1122 | |
1123 resource_metadata_->GetEntryInfoPairByPaths( | |
1124 file_path, | |
1125 dir_path, | |
1126 base::Bind( | |
1127 &GDataFileSystem::MoveEntryFromRootDirectoryAfterGetEntryInfoPair, | |
1128 ui_weak_ptr_, | |
1129 callback)); | |
1130 } | |
1131 | |
1132 void GDataFileSystem::MoveEntryFromRootDirectoryAfterGetEntryInfoPair( | |
1133 const FileOperationCallback& callback, | |
1134 scoped_ptr<EntryInfoPairResult> result) { | |
1135 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
1136 DCHECK(!callback.is_null()); | |
1137 DCHECK(result.get()); | |
1138 | |
1139 if (result->first.error != DRIVE_FILE_OK) { | |
1140 callback.Run(result->first.error); | |
1141 return; | |
1142 } else if (result->second.error != DRIVE_FILE_OK) { | |
1143 callback.Run(result->second.error); | |
1144 return; | |
1145 } | |
1146 | |
1147 scoped_ptr<DriveEntryProto> src_proto = result->first.proto.Pass(); | |
1148 scoped_ptr<DriveEntryProto> dir_proto = result->second.proto.Pass(); | |
1149 | |
1150 if (!dir_proto->file_info().is_directory()) { | |
1151 callback.Run(DRIVE_FILE_ERROR_NOT_A_DIRECTORY); | |
1152 return; | |
1153 } | |
1154 | |
1155 const FilePath& file_path = result->first.path; | |
1156 const FilePath& dir_path = result->second.path; | |
1157 drive_service_->AddResourceToDirectory( | |
1158 GURL(dir_proto->content_url()), | |
1159 GURL(src_proto->edit_url()), | |
1160 base::Bind(&GDataFileSystem::OnMoveEntryFromRootDirectoryCompleted, | |
1161 ui_weak_ptr_, | |
1162 callback, | |
1163 file_path, | |
1164 dir_path)); | |
1165 } | |
1166 | |
1167 void GDataFileSystem::RemoveEntryFromNonRootDirectory( | |
1168 const FileMoveCallback& callback, | |
1169 DriveFileError error, | |
1170 const FilePath& file_path) { | |
1171 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
1172 DCHECK(!callback.is_null()); | |
1173 | |
1174 const FilePath dir_path = file_path.DirName(); | |
1175 // Return if there is an error or |dir_path| is the root directory. | |
1176 if (error != DRIVE_FILE_OK || dir_path == FilePath(kDriveRootDirectory)) { | |
1177 callback.Run(error, file_path); | |
1178 return; | |
1179 } | |
1180 | |
1181 resource_metadata_->GetEntryInfoPairByPaths( | |
1182 file_path, | |
1183 dir_path, | |
1184 base::Bind( | |
1185 &GDataFileSystem::RemoveEntryFromNonRootDirectoryAfterEntryInfoPair, | |
1186 ui_weak_ptr_, | |
1187 callback)); | |
1188 } | |
1189 | |
1190 void GDataFileSystem::RemoveEntryFromNonRootDirectoryAfterEntryInfoPair( | |
1191 const FileMoveCallback& callback, | |
1192 scoped_ptr<EntryInfoPairResult> result) { | |
1193 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
1194 DCHECK(!callback.is_null()); | |
1195 DCHECK(result.get()); | |
1196 | |
1197 const FilePath& file_path = result->first.path; | |
1198 const FilePath& dir_path = result->second.path; | |
1199 if (result->first.error != DRIVE_FILE_OK) { | |
1200 callback.Run(result->first.error, file_path); | |
1201 return; | |
1202 } else if (result->second.error != DRIVE_FILE_OK) { | |
1203 callback.Run(result->second.error, file_path); | |
1204 return; | |
1205 } | |
1206 | |
1207 scoped_ptr<DriveEntryProto> entry_proto = result->first.proto.Pass(); | |
1208 scoped_ptr<DriveEntryProto> dir_proto = result->second.proto.Pass(); | |
1209 | |
1210 if (!dir_proto->file_info().is_directory()) { | |
1211 callback.Run(DRIVE_FILE_ERROR_NOT_A_DIRECTORY, file_path); | |
1212 return; | |
1213 } | |
1214 | |
1215 drive_service_->RemoveResourceFromDirectory( | |
1216 GURL(dir_proto->content_url()), | |
1217 GURL(entry_proto->edit_url()), | |
1218 entry_proto->resource_id(), | |
1219 base::Bind(&GDataFileSystem::MoveEntryToRootDirectoryLocally, | |
1220 ui_weak_ptr_, | |
1221 callback, | |
1222 file_path, | |
1223 dir_path)); | |
1224 } | |
1225 | |
1226 void GDataFileSystem::Remove(const FilePath& file_path, | |
1227 bool is_recursive, | |
1228 const FileOperationCallback& callback) { | |
1229 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || | |
1230 BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
1231 RunTaskOnUIThread(base::Bind(&GDataFileSystem::RemoveOnUIThread, | |
1232 ui_weak_ptr_, | |
1233 file_path, | |
1234 is_recursive, | |
1235 CreateRelayCallback(callback))); | |
1236 } | |
1237 | |
1238 void GDataFileSystem::RemoveOnUIThread( | |
1239 const FilePath& file_path, | |
1240 bool is_recursive, | |
1241 const FileOperationCallback& callback) { | |
1242 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
1243 | |
1244 // Get the edit URL of an entry at |file_path|. | |
1245 resource_metadata_->GetEntryInfoByPath( | |
1246 file_path, | |
1247 base::Bind( | |
1248 &GDataFileSystem::RemoveOnUIThreadAfterGetEntryInfo, | |
1249 ui_weak_ptr_, | |
1250 file_path, | |
1251 is_recursive, | |
1252 callback)); | |
1253 } | |
1254 | |
1255 void GDataFileSystem::RemoveOnUIThreadAfterGetEntryInfo( | |
1256 const FilePath& file_path, | |
1257 bool /* is_recursive */, | |
1258 const FileOperationCallback& callback, | |
1259 DriveFileError error, | |
1260 scoped_ptr<DriveEntryProto> entry_proto) { | |
1261 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
1262 | |
1263 if (error != DRIVE_FILE_OK) { | |
1264 if (!callback.is_null()) { | |
1265 base::MessageLoopProxy::current()->PostTask( | |
1266 FROM_HERE, base::Bind(callback, error)); | |
1267 } | |
1268 return; | |
1269 } | |
1270 | |
1271 DCHECK(entry_proto.get()); | |
1272 drive_service_->DeleteDocument( | |
1273 GURL(entry_proto->edit_url()), | |
1274 base::Bind(&GDataFileSystem::OnRemovedDocument, | |
1275 ui_weak_ptr_, | |
1276 callback, | |
1277 file_path)); | |
1278 } | |
1279 | |
1280 void GDataFileSystem::CreateDirectory( | |
1281 const FilePath& directory_path, | |
1282 bool is_exclusive, | |
1283 bool is_recursive, | |
1284 const FileOperationCallback& callback) { | |
1285 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || | |
1286 BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
1287 RunTaskOnUIThread(base::Bind(&GDataFileSystem::CreateDirectoryOnUIThread, | |
1288 ui_weak_ptr_, | |
1289 directory_path, | |
1290 is_exclusive, | |
1291 is_recursive, | |
1292 CreateRelayCallback(callback))); | |
1293 } | |
1294 | |
1295 void GDataFileSystem::CreateDirectoryOnUIThread( | |
1296 const FilePath& directory_path, | |
1297 bool is_exclusive, | |
1298 bool is_recursive, | |
1299 const FileOperationCallback& callback) { | |
1300 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
1301 | |
1302 FilePath last_parent_dir_path; | |
1303 FilePath first_missing_path; | |
1304 GURL last_parent_dir_url; | |
1305 FindMissingDirectoryResult result = | |
1306 FindFirstMissingParentDirectory(directory_path, | |
1307 &last_parent_dir_url, | |
1308 &first_missing_path); | |
1309 switch (result) { | |
1310 case FOUND_INVALID: { | |
1311 if (!callback.is_null()) { | |
1312 MessageLoop::current()->PostTask(FROM_HERE, | |
1313 base::Bind(callback, DRIVE_FILE_ERROR_NOT_FOUND)); | |
1314 } | |
1315 | |
1316 return; | |
1317 } | |
1318 case DIRECTORY_ALREADY_PRESENT: { | |
1319 if (!callback.is_null()) { | |
1320 MessageLoop::current()->PostTask(FROM_HERE, | |
1321 base::Bind(callback, | |
1322 is_exclusive ? DRIVE_FILE_ERROR_EXISTS : | |
1323 DRIVE_FILE_OK)); | |
1324 } | |
1325 | |
1326 return; | |
1327 } | |
1328 case FOUND_MISSING: { | |
1329 // There is a missing folder to be created here, move on with the rest of | |
1330 // this function. | |
1331 break; | |
1332 } | |
1333 default: { | |
1334 NOTREACHED(); | |
1335 break; | |
1336 } | |
1337 } | |
1338 | |
1339 // Do we have a parent directory here as well? We can't then create target | |
1340 // directory if this is not a recursive operation. | |
1341 if (directory_path != first_missing_path && !is_recursive) { | |
1342 if (!callback.is_null()) { | |
1343 MessageLoop::current()->PostTask(FROM_HERE, | |
1344 base::Bind(callback, DRIVE_FILE_ERROR_NOT_FOUND)); | |
1345 } | |
1346 return; | |
1347 } | |
1348 | |
1349 drive_service_->CreateDirectory( | |
1350 last_parent_dir_url, | |
1351 first_missing_path.BaseName().value(), | |
1352 base::Bind(&GDataFileSystem::OnCreateDirectoryCompleted, | |
1353 ui_weak_ptr_, | |
1354 CreateDirectoryParams( | |
1355 first_missing_path, | |
1356 directory_path, | |
1357 is_exclusive, | |
1358 is_recursive, | |
1359 callback))); | |
1360 } | |
1361 | |
1362 void GDataFileSystem::CreateFile(const FilePath& file_path, | |
1363 bool is_exclusive, | |
1364 const FileOperationCallback& callback) { | |
1365 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || | |
1366 BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
1367 DCHECK(!callback.is_null()); | |
1368 | |
1369 RunTaskOnUIThread(base::Bind(&GDataFileSystem::CreateFileOnUIThread, | |
1370 ui_weak_ptr_, | |
1371 file_path, | |
1372 is_exclusive, | |
1373 CreateRelayCallback(callback))); | |
1374 } | |
1375 | |
1376 void GDataFileSystem::CreateFileOnUIThread( | |
1377 const FilePath& file_path, | |
1378 bool is_exclusive, | |
1379 const FileOperationCallback& callback) { | |
1380 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
1381 DCHECK(!callback.is_null()); | |
1382 | |
1383 // First, checks the existence of a file at |file_path|. | |
1384 resource_metadata_->GetEntryInfoByPath( | |
1385 file_path, | |
1386 base::Bind(&GDataFileSystem::OnGetEntryInfoForCreateFile, | |
1387 ui_weak_ptr_, | |
1388 file_path, | |
1389 is_exclusive, | |
1390 callback)); | |
1391 } | |
1392 | |
1393 void GDataFileSystem::OnGetEntryInfoForCreateFile( | |
1394 const FilePath& file_path, | |
1395 bool is_exclusive, | |
1396 const FileOperationCallback& callback, | |
1397 DriveFileError result, | |
1398 scoped_ptr<DriveEntryProto> entry_proto) { | |
1399 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
1400 DCHECK(!callback.is_null()); | |
1401 | |
1402 // The |file_path| is invalid. It is an error. | |
1403 if (result != DRIVE_FILE_ERROR_NOT_FOUND && | |
1404 result != DRIVE_FILE_OK) { | |
1405 callback.Run(result); | |
1406 return; | |
1407 } | |
1408 | |
1409 // An entry already exists at |file_path|. | |
1410 if (result == DRIVE_FILE_OK) { | |
1411 DCHECK(entry_proto.get()); | |
1412 // If an exclusive mode is requested, or the entry is not a regular file, | |
1413 // it is an error. | |
1414 if (is_exclusive || | |
1415 entry_proto->file_info().is_directory() || | |
1416 entry_proto->file_specific_info().is_hosted_document()) { | |
1417 callback.Run(DRIVE_FILE_ERROR_EXISTS); | |
1418 return; | |
1419 } | |
1420 | |
1421 // Otherwise nothing more to do. Succeeded. | |
1422 callback.Run(DRIVE_FILE_OK); | |
1423 return; | |
1424 } | |
1425 | |
1426 // No entry found at |file_path|. Let's create a brand new file. | |
1427 // For now, it is implemented by uploading an empty file (/dev/null). | |
1428 // TODO(kinaba): http://crbug.com/135143. Implement in a nicer way. | |
1429 TransferRegularFile(FilePath(kEmptyFilePath), file_path, callback); | |
1430 } | |
1431 | |
1432 void GDataFileSystem::GetFileByPath( | |
1433 const FilePath& file_path, | |
1434 const GetFileCallback& get_file_callback, | |
1435 const GetContentCallback& get_content_callback) { | |
1436 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || | |
1437 BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
1438 RunTaskOnUIThread( | |
1439 base::Bind(&GDataFileSystem::GetFileByPathOnUIThread, | |
1440 ui_weak_ptr_, | |
1441 file_path, | |
1442 CreateRelayCallback(get_file_callback), | |
1443 CreateRelayCallback(get_content_callback))); | |
1444 } | |
1445 | |
1446 void GDataFileSystem::GetFileByPathOnUIThread( | |
1447 const FilePath& file_path, | |
1448 const GetFileCallback& get_file_callback, | |
1449 const GetContentCallback& get_content_callback) { | |
1450 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
1451 | |
1452 resource_metadata_->GetEntryInfoByPath( | |
1453 file_path, | |
1454 base::Bind(&GDataFileSystem::OnGetEntryInfoCompleteForGetFileByPath, | |
1455 ui_weak_ptr_, | |
1456 file_path, | |
1457 CreateRelayCallback(get_file_callback), | |
1458 CreateRelayCallback(get_content_callback))); | |
1459 } | |
1460 | |
1461 void GDataFileSystem::OnGetEntryInfoCompleteForGetFileByPath( | |
1462 const FilePath& file_path, | |
1463 const GetFileCallback& get_file_callback, | |
1464 const GetContentCallback& get_content_callback, | |
1465 DriveFileError error, | |
1466 scoped_ptr<DriveEntryProto> entry_proto) { | |
1467 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
1468 | |
1469 // If |error| == PLATFORM_FILE_OK then |entry_proto| must be valid. | |
1470 DCHECK(error != DRIVE_FILE_OK || | |
1471 (entry_proto.get() && !entry_proto->resource_id().empty())); | |
1472 GetResolvedFileByPath(file_path, | |
1473 get_file_callback, | |
1474 get_content_callback, | |
1475 error, | |
1476 entry_proto.get()); | |
1477 } | |
1478 | |
1479 void GDataFileSystem::GetResolvedFileByPath( | |
1480 const FilePath& file_path, | |
1481 const GetFileCallback& get_file_callback, | |
1482 const GetContentCallback& get_content_callback, | |
1483 DriveFileError error, | |
1484 const DriveEntryProto* entry_proto) { | |
1485 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
1486 | |
1487 if (entry_proto && !entry_proto->has_file_specific_info()) | |
1488 error = DRIVE_FILE_ERROR_NOT_FOUND; | |
1489 | |
1490 if (error != DRIVE_FILE_OK) { | |
1491 if (!get_file_callback.is_null()) { | |
1492 MessageLoop::current()->PostTask( | |
1493 FROM_HERE, | |
1494 base::Bind(get_file_callback, | |
1495 DRIVE_FILE_ERROR_NOT_FOUND, | |
1496 FilePath(), | |
1497 std::string(), | |
1498 REGULAR_FILE)); | |
1499 } | |
1500 return; | |
1501 } | |
1502 | |
1503 // For a hosted document, we create a special JSON file to represent the | |
1504 // document instead of fetching the document content in one of the exported | |
1505 // formats. The JSON file contains the edit URL and resource ID of the | |
1506 // document. | |
1507 if (entry_proto->file_specific_info().is_hosted_document()) { | |
1508 DriveFileError* error = | |
1509 new DriveFileError(DRIVE_FILE_OK); | |
1510 FilePath* temp_file_path = new FilePath; | |
1511 std::string* mime_type = new std::string; | |
1512 DriveFileType* file_type = new DriveFileType(REGULAR_FILE); | |
1513 util::PostBlockingPoolSequencedTaskAndReply( | |
1514 FROM_HERE, | |
1515 blocking_task_runner_, | |
1516 base::Bind(&CreateDocumentJsonFileOnBlockingPool, | |
1517 cache_->GetCacheDirectoryPath( | |
1518 DriveCache::CACHE_TYPE_TMP_DOCUMENTS), | |
1519 GURL(entry_proto->file_specific_info().alternate_url()), | |
1520 entry_proto->resource_id(), | |
1521 error, | |
1522 temp_file_path, | |
1523 mime_type, | |
1524 file_type), | |
1525 base::Bind(&RunGetFileCallbackHelper, | |
1526 get_file_callback, | |
1527 base::Owned(error), | |
1528 base::Owned(temp_file_path), | |
1529 base::Owned(mime_type), | |
1530 base::Owned(file_type))); | |
1531 return; | |
1532 } | |
1533 | |
1534 // Returns absolute path of the file if it were cached or to be cached. | |
1535 FilePath local_tmp_path = cache_->GetCacheFilePath( | |
1536 entry_proto->resource_id(), | |
1537 entry_proto->file_specific_info().file_md5(), | |
1538 DriveCache::CACHE_TYPE_TMP, | |
1539 DriveCache::CACHED_FILE_FROM_SERVER); | |
1540 cache_->GetFileOnUIThread( | |
1541 entry_proto->resource_id(), | |
1542 entry_proto->file_specific_info().file_md5(), | |
1543 base::Bind( | |
1544 &GDataFileSystem::OnGetFileFromCache, | |
1545 ui_weak_ptr_, | |
1546 GetFileFromCacheParams( | |
1547 file_path, | |
1548 local_tmp_path, | |
1549 GURL(entry_proto->content_url()), | |
1550 entry_proto->resource_id(), | |
1551 entry_proto->file_specific_info().file_md5(), | |
1552 entry_proto->file_specific_info().content_mime_type(), | |
1553 get_file_callback, | |
1554 get_content_callback))); | |
1555 } | |
1556 | |
1557 void GDataFileSystem::GetFileByResourceId( | |
1558 const std::string& resource_id, | |
1559 const GetFileCallback& get_file_callback, | |
1560 const GetContentCallback& get_content_callback) { | |
1561 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || | |
1562 BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
1563 RunTaskOnUIThread( | |
1564 base::Bind(&GDataFileSystem::GetFileByResourceIdOnUIThread, | |
1565 ui_weak_ptr_, | |
1566 resource_id, | |
1567 CreateRelayCallback(get_file_callback), | |
1568 CreateRelayCallback(get_content_callback))); | |
1569 } | |
1570 | |
1571 void GDataFileSystem::GetFileByResourceIdOnUIThread( | |
1572 const std::string& resource_id, | |
1573 const GetFileCallback& get_file_callback, | |
1574 const GetContentCallback& get_content_callback) { | |
1575 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
1576 | |
1577 resource_metadata_->GetEntryByResourceIdAsync(resource_id, | |
1578 base::Bind(&GDataFileSystem::GetFileByEntryOnUIThread, | |
1579 ui_weak_ptr_, | |
1580 get_file_callback, | |
1581 get_content_callback)); | |
1582 } | |
1583 | |
1584 void GDataFileSystem::GetFileByEntryOnUIThread( | |
1585 const GetFileCallback& get_file_callback, | |
1586 const GetContentCallback& get_content_callback, | |
1587 DriveEntry* entry) { | |
1588 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
1589 | |
1590 FilePath file_path; | |
1591 if (entry) { | |
1592 DriveFile* file = entry->AsDriveFile(); | |
1593 if (file) | |
1594 file_path = file->GetFilePath(); | |
1595 } | |
1596 | |
1597 // Report an error immediately if the file for the resource ID is not | |
1598 // found. | |
1599 if (file_path.empty()) { | |
1600 if (!get_file_callback.is_null()) { | |
1601 base::MessageLoopProxy::current()->PostTask( | |
1602 FROM_HERE, | |
1603 base::Bind(get_file_callback, | |
1604 DRIVE_FILE_ERROR_NOT_FOUND, | |
1605 FilePath(), | |
1606 std::string(), | |
1607 REGULAR_FILE)); | |
1608 } | |
1609 return; | |
1610 } | |
1611 | |
1612 GetFileByPath(file_path, get_file_callback, get_content_callback); | |
1613 } | |
1614 | |
1615 void GDataFileSystem::OnGetFileFromCache(const GetFileFromCacheParams& params, | |
1616 DriveFileError error, | |
1617 const std::string& resource_id, | |
1618 const std::string& md5, | |
1619 const FilePath& cache_file_path) { | |
1620 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
1621 | |
1622 // Have we found the file in cache? If so, return it back to the caller. | |
1623 if (error == DRIVE_FILE_OK) { | |
1624 if (!params.get_file_callback.is_null()) { | |
1625 params.get_file_callback.Run(error, | |
1626 cache_file_path, | |
1627 params.mime_type, | |
1628 REGULAR_FILE); | |
1629 } | |
1630 return; | |
1631 } | |
1632 | |
1633 // If cache file is not found, try to download the file from the server | |
1634 // instead. This logic is rather complicated but here's how this works: | |
1635 // | |
1636 // Retrieve fresh file metadata from server. We will extract file size and | |
1637 // content url from there (we want to make sure used content url is not | |
1638 // stale). | |
1639 // | |
1640 // Check if we have enough space, based on the expected file size. | |
1641 // - if we don't have enough space, try to free up the disk space | |
1642 // - if we still don't have enough space, return "no space" error | |
1643 // - if we have enough space, start downloading the file from the server | |
1644 drive_service_->GetDocumentEntry( | |
1645 resource_id, | |
1646 base::Bind(&GDataFileSystem::OnGetDocumentEntry, | |
1647 ui_weak_ptr_, | |
1648 cache_file_path, | |
1649 GetFileFromCacheParams(params.virtual_file_path, | |
1650 params.local_tmp_path, | |
1651 params.content_url, | |
1652 params.resource_id, | |
1653 params.md5, | |
1654 params.mime_type, | |
1655 params.get_file_callback, | |
1656 params.get_content_callback))); | |
1657 } | |
1658 | |
1659 void GDataFileSystem::OnGetDocumentEntry(const FilePath& cache_file_path, | |
1660 const GetFileFromCacheParams& params, | |
1661 GDataErrorCode status, | |
1662 scoped_ptr<base::Value> data) { | |
1663 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
1664 | |
1665 DriveFileError error = util::GDataToDriveFileError(status); | |
1666 | |
1667 scoped_ptr<DriveEntry> fresh_entry; | |
1668 if (error == DRIVE_FILE_OK) { | |
1669 scoped_ptr<DocumentEntry> doc_entry(DocumentEntry::ExtractAndParse(*data)); | |
1670 if (doc_entry.get()) | |
1671 fresh_entry.reset(resource_metadata_->FromDocumentEntry(*doc_entry)); | |
1672 if (!fresh_entry.get() || !fresh_entry->AsDriveFile()) { | |
1673 LOG(ERROR) << "Got invalid entry from server for " << params.resource_id; | |
1674 error = DRIVE_FILE_ERROR_FAILED; | |
1675 } | |
1676 } | |
1677 | |
1678 if (error != DRIVE_FILE_OK) { | |
1679 if (!params.get_file_callback.is_null()) { | |
1680 params.get_file_callback.Run(error, | |
1681 cache_file_path, | |
1682 params.mime_type, | |
1683 REGULAR_FILE); | |
1684 } | |
1685 return; | |
1686 } | |
1687 | |
1688 GURL content_url = fresh_entry->content_url(); | |
1689 int64 file_size = fresh_entry->file_info().size; | |
1690 | |
1691 DCHECK_EQ(params.resource_id, fresh_entry->resource_id()); | |
1692 scoped_ptr<DriveFile> fresh_entry_as_file( | |
1693 fresh_entry.release()->AsDriveFile()); | |
1694 resource_metadata_->RefreshFile(fresh_entry_as_file.Pass()); | |
1695 | |
1696 bool* has_enough_space = new bool(false); | |
1697 util::PostBlockingPoolSequencedTaskAndReply( | |
1698 FROM_HERE, | |
1699 blocking_task_runner_, | |
1700 base::Bind(&DriveCache::FreeDiskSpaceIfNeededFor, | |
1701 base::Unretained(cache_), | |
1702 file_size, | |
1703 has_enough_space), | |
1704 base::Bind(&GDataFileSystem::StartDownloadFileIfEnoughSpace, | |
1705 ui_weak_ptr_, | |
1706 params, | |
1707 content_url, | |
1708 cache_file_path, | |
1709 base::Owned(has_enough_space))); | |
1710 } | |
1711 | |
1712 void GDataFileSystem::StartDownloadFileIfEnoughSpace( | |
1713 const GetFileFromCacheParams& params, | |
1714 const GURL& content_url, | |
1715 const FilePath& cache_file_path, | |
1716 bool* has_enough_space) { | |
1717 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
1718 | |
1719 if (!*has_enough_space) { | |
1720 // If no enough space, return PLATFORM_FILE_ERROR_NO_SPACE. | |
1721 if (!params.get_file_callback.is_null()) { | |
1722 params.get_file_callback.Run(DRIVE_FILE_ERROR_NO_SPACE, | |
1723 cache_file_path, | |
1724 params.mime_type, | |
1725 REGULAR_FILE); | |
1726 } | |
1727 return; | |
1728 } | |
1729 | |
1730 // We have enough disk space. Start downloading the file. | |
1731 drive_service_->DownloadFile( | |
1732 params.virtual_file_path, | |
1733 params.local_tmp_path, | |
1734 content_url, | |
1735 base::Bind(&GDataFileSystem::OnFileDownloaded, | |
1736 ui_weak_ptr_, | |
1737 params), | |
1738 params.get_content_callback); | |
1739 } | |
1740 | |
1741 void GDataFileSystem::GetEntryInfoByPath(const FilePath& file_path, | |
1742 const GetEntryInfoCallback& callback) { | |
1743 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || | |
1744 BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
1745 DCHECK(!callback.is_null()); | |
1746 | |
1747 RunTaskOnUIThread( | |
1748 base::Bind(&GDataFileSystem::GetEntryInfoByPathOnUIThread, | |
1749 ui_weak_ptr_, | |
1750 file_path, | |
1751 CreateRelayCallback(callback))); | |
1752 } | |
1753 | |
1754 void GDataFileSystem::GetEntryInfoByPathOnUIThread( | |
1755 const FilePath& file_path, | |
1756 const GetEntryInfoCallback& callback) { | |
1757 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
1758 DCHECK(!callback.is_null()); | |
1759 | |
1760 LoadFeedIfNeeded( | |
1761 base::Bind(&GDataFileSystem::GetEntryInfoByPathOnUIThreadAfterLoad, | |
1762 ui_weak_ptr_, | |
1763 file_path, | |
1764 callback)); | |
1765 } | |
1766 | |
1767 void GDataFileSystem::GetEntryInfoByPathOnUIThreadAfterLoad( | |
1768 const FilePath& file_path, | |
1769 const GetEntryInfoCallback& callback, | |
1770 DriveFileError error) { | |
1771 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
1772 DCHECK(!callback.is_null()); | |
1773 | |
1774 if (error != DRIVE_FILE_OK) { | |
1775 callback.Run(error, scoped_ptr<DriveEntryProto>()); | |
1776 return; | |
1777 } | |
1778 | |
1779 resource_metadata_->GetEntryInfoByPath( | |
1780 file_path, | |
1781 base::Bind(&GDataFileSystem::GetEntryInfoByPathOnUIThreadAfterGetEntry, | |
1782 ui_weak_ptr_, | |
1783 callback)); | |
1784 } | |
1785 | |
1786 void GDataFileSystem::GetEntryInfoByPathOnUIThreadAfterGetEntry( | |
1787 const GetEntryInfoCallback& callback, | |
1788 DriveFileError error, | |
1789 scoped_ptr<DriveEntryProto> entry_proto) { | |
1790 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
1791 DCHECK(!callback.is_null()); | |
1792 | |
1793 if (error != DRIVE_FILE_OK) { | |
1794 callback.Run(error, scoped_ptr<DriveEntryProto>()); | |
1795 return; | |
1796 } | |
1797 DCHECK(entry_proto.get()); | |
1798 | |
1799 CheckLocalModificationAndRun(entry_proto.Pass(), callback); | |
1800 } | |
1801 | |
1802 void GDataFileSystem::ReadDirectoryByPath( | |
1803 const FilePath& file_path, | |
1804 const ReadDirectoryWithSettingCallback& callback) { | |
1805 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || | |
1806 BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
1807 DCHECK(!callback.is_null()); | |
1808 | |
1809 RunTaskOnUIThread( | |
1810 base::Bind(&GDataFileSystem::ReadDirectoryByPathOnUIThread, | |
1811 ui_weak_ptr_, | |
1812 file_path, | |
1813 CreateRelayCallback(callback))); | |
1814 } | |
1815 | |
1816 void GDataFileSystem::ReadDirectoryByPathOnUIThread( | |
1817 const FilePath& file_path, | |
1818 const ReadDirectoryWithSettingCallback& callback) { | |
1819 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
1820 DCHECK(!callback.is_null()); | |
1821 | |
1822 LoadFeedIfNeeded( | |
1823 base::Bind(&GDataFileSystem::ReadDirectoryByPathOnUIThreadAfterLoad, | |
1824 ui_weak_ptr_, | |
1825 file_path, | |
1826 callback)); | |
1827 } | |
1828 | |
1829 void GDataFileSystem::ReadDirectoryByPathOnUIThreadAfterLoad( | |
1830 const FilePath& file_path, | |
1831 const ReadDirectoryWithSettingCallback& callback, | |
1832 DriveFileError error) { | |
1833 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
1834 DCHECK(!callback.is_null()); | |
1835 | |
1836 if (error != DRIVE_FILE_OK) { | |
1837 callback.Run(error, | |
1838 hide_hosted_docs_, | |
1839 scoped_ptr<DriveEntryProtoVector>()); | |
1840 return; | |
1841 } | |
1842 | |
1843 resource_metadata_->ReadDirectoryByPath( | |
1844 file_path, | |
1845 base::Bind(&GDataFileSystem::ReadDirectoryByPathOnUIThreadAfterRead, | |
1846 ui_weak_ptr_, | |
1847 callback)); | |
1848 } | |
1849 | |
1850 void GDataFileSystem::ReadDirectoryByPathOnUIThreadAfterRead( | |
1851 const ReadDirectoryWithSettingCallback& callback, | |
1852 DriveFileError error, | |
1853 scoped_ptr<DriveEntryProtoVector> entries) { | |
1854 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
1855 DCHECK(!callback.is_null()); | |
1856 | |
1857 if (error != DRIVE_FILE_OK) { | |
1858 callback.Run(error, | |
1859 hide_hosted_docs_, | |
1860 scoped_ptr<DriveEntryProtoVector>()); | |
1861 return; | |
1862 } | |
1863 DCHECK(entries.get()); // This is valid for emptry directories too. | |
1864 | |
1865 callback.Run(DRIVE_FILE_OK, hide_hosted_docs_, entries.Pass()); | |
1866 } | |
1867 | |
1868 void GDataFileSystem::RequestDirectoryRefresh(const FilePath& file_path) { | |
1869 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || | |
1870 BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
1871 RunTaskOnUIThread( | |
1872 base::Bind(&GDataFileSystem::RequestDirectoryRefreshOnUIThread, | |
1873 ui_weak_ptr_, | |
1874 file_path)); | |
1875 } | |
1876 | |
1877 void GDataFileSystem::RequestDirectoryRefreshOnUIThread( | |
1878 const FilePath& file_path) { | |
1879 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
1880 | |
1881 // Make sure the destination directory exists. | |
1882 resource_metadata_->GetEntryInfoByPath( | |
1883 file_path, | |
1884 base::Bind( | |
1885 &GDataFileSystem::RequestDirectoryRefreshOnUIThreadAfterGetEntryInfo, | |
1886 ui_weak_ptr_, | |
1887 file_path)); | |
1888 } | |
1889 | |
1890 void GDataFileSystem::RequestDirectoryRefreshOnUIThreadAfterGetEntryInfo( | |
1891 const FilePath& file_path, | |
1892 DriveFileError error, | |
1893 scoped_ptr<DriveEntryProto> entry_proto) { | |
1894 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
1895 | |
1896 if (error != DRIVE_FILE_OK || | |
1897 !entry_proto->file_info().is_directory()) { | |
1898 LOG(ERROR) << "Directory entry not found: " << file_path.value(); | |
1899 return; | |
1900 } | |
1901 | |
1902 feed_loader_->LoadDirectoryFromServer( | |
1903 resource_metadata_->origin(), | |
1904 entry_proto->resource_id(), | |
1905 base::Bind(&GDataFileSystem::OnRequestDirectoryRefresh, | |
1906 ui_weak_ptr_, | |
1907 file_path)); | |
1908 } | |
1909 | |
1910 void GDataFileSystem::OnRequestDirectoryRefresh( | |
1911 const FilePath& directory_path, | |
1912 GetDocumentsParams* params, | |
1913 DriveFileError error) { | |
1914 DCHECK(params); | |
1915 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
1916 | |
1917 if (error != DRIVE_FILE_OK) { | |
1918 LOG(ERROR) << "Failed to refresh directory: " << directory_path.value() | |
1919 << ": " << error; | |
1920 return; | |
1921 } | |
1922 | |
1923 int64 unused_delta_feed_changestamp = 0; | |
1924 FeedToFileResourceMapUmaStats unused_uma_stats; | |
1925 FileResourceIdMap file_map; | |
1926 GDataWapiFeedProcessor feed_processor(resource_metadata_.get()); | |
1927 error = feed_processor.FeedToFileResourceMap( | |
1928 *params->feed_list, | |
1929 &file_map, | |
1930 &unused_delta_feed_changestamp, | |
1931 &unused_uma_stats); | |
1932 if (error != DRIVE_FILE_OK) { | |
1933 LOG(ERROR) << "Failed to convert feed: " << directory_path.value() | |
1934 << ": " << error; | |
1935 return; | |
1936 } | |
1937 | |
1938 resource_metadata_->RefreshDirectory( | |
1939 params->directory_resource_id, | |
1940 file_map, | |
1941 base::Bind(&GDataFileSystem::OnDirectoryChangeFileMoveCallback, | |
1942 ui_weak_ptr_)); | |
1943 } | |
1944 | |
1945 void GDataFileSystem::UpdateFileByResourceId( | |
1946 const std::string& resource_id, | |
1947 const FileOperationCallback& callback) { | |
1948 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || | |
1949 BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
1950 DCHECK(!callback.is_null()); | |
1951 | |
1952 RunTaskOnUIThread( | |
1953 base::Bind(&GDataFileSystem::UpdateFileByResourceIdOnUIThread, | |
1954 ui_weak_ptr_, | |
1955 resource_id, | |
1956 CreateRelayCallback(callback))); | |
1957 } | |
1958 | |
1959 void GDataFileSystem::UpdateFileByResourceIdOnUIThread( | |
1960 const std::string& resource_id, | |
1961 const FileOperationCallback& callback) { | |
1962 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
1963 DCHECK(!callback.is_null()); | |
1964 | |
1965 // TODO(satorux): GetEntryInfoByResourceId() is called twice for | |
1966 // UpdateFileByResourceIdOnUIThread(). crbug.com/143873 | |
1967 resource_metadata_->GetEntryInfoByResourceId( | |
1968 resource_id, | |
1969 base::Bind(&GDataFileSystem::UpdateFileByEntryInfo, | |
1970 ui_weak_ptr_, | |
1971 callback)); | |
1972 } | |
1973 | |
1974 void GDataFileSystem::UpdateFileByEntryInfo( | |
1975 const FileOperationCallback& callback, | |
1976 DriveFileError error, | |
1977 const FilePath& /* dive_file_path */, | |
1978 scoped_ptr<DriveEntryProto> entry_proto) { | |
1979 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
1980 DCHECK(!callback.is_null()); | |
1981 | |
1982 if (error != DRIVE_FILE_OK) { | |
1983 callback.Run(error); | |
1984 return; | |
1985 } | |
1986 | |
1987 DCHECK(entry_proto.get()); | |
1988 if (entry_proto->file_info().is_directory()) { | |
1989 callback.Run(DRIVE_FILE_ERROR_NOT_FOUND); | |
1990 return; | |
1991 } | |
1992 | |
1993 cache_->GetFileOnUIThread( | |
1994 entry_proto->resource_id(), | |
1995 entry_proto->file_specific_info().file_md5(), | |
1996 base::Bind(&GDataFileSystem::OnGetFileCompleteForUpdateFile, | |
1997 ui_weak_ptr_, | |
1998 callback)); | |
1999 } | |
2000 | |
2001 void GDataFileSystem::OnGetFileCompleteForUpdateFile( | |
2002 const FileOperationCallback& callback, | |
2003 DriveFileError error, | |
2004 const std::string& resource_id, | |
2005 const std::string& /* md5 */, | |
2006 const FilePath& cache_file_path) { | |
2007 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
2008 DCHECK(!callback.is_null()); | |
2009 | |
2010 if (error != DRIVE_FILE_OK) { | |
2011 callback.Run(error); | |
2012 return; | |
2013 } | |
2014 | |
2015 // Gets the size of the cache file. Since the file is locally modified, the | |
2016 // file size information stored in DriveEntry is not correct. | |
2017 DriveFileError* get_size_error = new DriveFileError(DRIVE_FILE_ERROR_FAILED); | |
2018 int64* file_size = new int64(-1); | |
2019 util::PostBlockingPoolSequencedTaskAndReply( | |
2020 FROM_HERE, | |
2021 blocking_task_runner_, | |
2022 base::Bind(&GetLocalFileSizeOnBlockingPool, | |
2023 cache_file_path, | |
2024 get_size_error, | |
2025 file_size), | |
2026 base::Bind(&GDataFileSystem::OnGetFileSizeCompleteForUpdateFile, | |
2027 ui_weak_ptr_, | |
2028 callback, | |
2029 resource_id, | |
2030 cache_file_path, | |
2031 base::Owned(get_size_error), | |
2032 base::Owned(file_size))); | |
2033 } | |
2034 | |
2035 void GDataFileSystem::OnGetFileSizeCompleteForUpdateFile( | |
2036 const FileOperationCallback& callback, | |
2037 const std::string& resource_id, | |
2038 const FilePath& cache_file_path, | |
2039 DriveFileError* error, | |
2040 int64* file_size) { | |
2041 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
2042 DCHECK(!callback.is_null()); | |
2043 | |
2044 if (*error != DRIVE_FILE_OK) { | |
2045 callback.Run(*error); | |
2046 return; | |
2047 } | |
2048 | |
2049 // TODO(satorux): GetEntryInfoByResourceId() is called twice for | |
2050 // UpdateFileByResourceIdOnUIThread(). crbug.com/143873 | |
2051 resource_metadata_->GetEntryInfoByResourceId( | |
2052 resource_id, | |
2053 base::Bind(&GDataFileSystem::OnGetFileCompleteForUpdateFileByEntry, | |
2054 ui_weak_ptr_, | |
2055 callback, | |
2056 *file_size, | |
2057 cache_file_path)); | |
2058 } | |
2059 | |
2060 void GDataFileSystem::OnGetFileCompleteForUpdateFileByEntry( | |
2061 const FileOperationCallback& callback, | |
2062 int64 file_size, | |
2063 const FilePath& cache_file_path, | |
2064 DriveFileError error, | |
2065 const FilePath& drive_file_path, | |
2066 scoped_ptr<DriveEntryProto> entry_proto) { | |
2067 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
2068 DCHECK(!callback.is_null()); | |
2069 | |
2070 if (error != DRIVE_FILE_OK) { | |
2071 callback.Run(error); | |
2072 return; | |
2073 } | |
2074 | |
2075 DCHECK(entry_proto.get()); | |
2076 if (entry_proto->file_info().is_directory()) { | |
2077 callback.Run(DRIVE_FILE_ERROR_NOT_FOUND); | |
2078 return; | |
2079 } | |
2080 | |
2081 uploader_->UploadExistingFile( | |
2082 GURL(entry_proto->upload_url()), | |
2083 drive_file_path, | |
2084 cache_file_path, | |
2085 file_size, | |
2086 entry_proto->file_specific_info().content_mime_type(), | |
2087 base::Bind(&GDataFileSystem::OnUpdatedFileUploaded, | |
2088 ui_weak_ptr_, | |
2089 callback)); | |
2090 } | |
2091 | |
2092 void GDataFileSystem::OnUpdatedFileUploaded( | |
2093 const FileOperationCallback& callback, | |
2094 DriveFileError error, | |
2095 scoped_ptr<UploadFileInfo> upload_file_info) { | |
2096 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
2097 DCHECK(upload_file_info.get()); | |
2098 | |
2099 if (error != DRIVE_FILE_OK) { | |
2100 if (!callback.is_null()) | |
2101 callback.Run(error); | |
2102 return; | |
2103 } | |
2104 | |
2105 AddUploadedFile(UPLOAD_EXISTING_FILE, | |
2106 upload_file_info->gdata_path.DirName(), | |
2107 upload_file_info->entry.Pass(), | |
2108 upload_file_info->file_path, | |
2109 DriveCache::FILE_OPERATION_MOVE, | |
2110 base::Bind(&OnAddUploadFileCompleted, callback, error)); | |
2111 } | |
2112 | |
2113 void GDataFileSystem::GetAvailableSpace( | |
2114 const GetAvailableSpaceCallback& callback) { | |
2115 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || | |
2116 BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
2117 RunTaskOnUIThread(base::Bind(&GDataFileSystem::GetAvailableSpaceOnUIThread, | |
2118 ui_weak_ptr_, | |
2119 CreateRelayCallback(callback))); | |
2120 } | |
2121 | |
2122 void GDataFileSystem::GetAvailableSpaceOnUIThread( | |
2123 const GetAvailableSpaceCallback& callback) { | |
2124 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
2125 DCHECK(!callback.is_null()); | |
2126 | |
2127 drive_service_->GetAccountMetadata( | |
2128 gdata::util::IsDriveV2ApiEnabled() ? | |
2129 base::Bind(&GDataFileSystem::OnGetAboutResource, | |
2130 ui_weak_ptr_, | |
2131 callback) : | |
2132 base::Bind(&GDataFileSystem::OnGetAvailableSpace, | |
2133 ui_weak_ptr_, | |
2134 callback)); | |
2135 } | |
2136 | |
2137 void GDataFileSystem::OnGetAvailableSpace( | |
2138 const GetAvailableSpaceCallback& callback, | |
2139 GDataErrorCode status, | |
2140 scoped_ptr<base::Value> data) { | |
2141 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
2142 DCHECK(!callback.is_null()); | |
2143 | |
2144 DriveFileError error = util::GDataToDriveFileError(status); | |
2145 if (error != DRIVE_FILE_OK) { | |
2146 callback.Run(error, -1, -1); | |
2147 return; | |
2148 } | |
2149 | |
2150 scoped_ptr<AccountMetadataFeed> feed; | |
2151 if (data.get()) | |
2152 feed = AccountMetadataFeed::CreateFrom(*data); | |
2153 if (!feed.get()) { | |
2154 callback.Run(DRIVE_FILE_ERROR_FAILED, -1, -1); | |
2155 return; | |
2156 } | |
2157 | |
2158 callback.Run(DRIVE_FILE_OK, | |
2159 feed->quota_bytes_total(), | |
2160 feed->quota_bytes_used()); | |
2161 } | |
2162 | |
2163 void GDataFileSystem::OnGetAboutResource( | |
2164 const GetAvailableSpaceCallback& callback, | |
2165 GDataErrorCode status, | |
2166 scoped_ptr<base::Value> resource_json) { | |
2167 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
2168 DCHECK(!callback.is_null()); | |
2169 | |
2170 DriveFileError error = util::GDataToDriveFileError(status); | |
2171 if (error != DRIVE_FILE_OK) { | |
2172 callback.Run(error, -1, -1); | |
2173 return; | |
2174 } | |
2175 | |
2176 scoped_ptr<AboutResource> about; | |
2177 if (resource_json.get()) | |
2178 about = AboutResource::CreateFrom(*resource_json); | |
2179 | |
2180 if (!about.get()) { | |
2181 callback.Run(DRIVE_FILE_ERROR_FAILED, -1, -1); | |
2182 return; | |
2183 } | |
2184 | |
2185 callback.Run(DRIVE_FILE_OK, | |
2186 about->quota_bytes_total(), | |
2187 about->quota_bytes_used()); | |
2188 } | |
2189 | |
2190 void GDataFileSystem::OnCreateDirectoryCompleted( | |
2191 const CreateDirectoryParams& params, | |
2192 GDataErrorCode status, | |
2193 scoped_ptr<base::Value> data) { | |
2194 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
2195 | |
2196 DriveFileError error = util::GDataToDriveFileError(status); | |
2197 if (error != DRIVE_FILE_OK) { | |
2198 if (!params.callback.is_null()) | |
2199 params.callback.Run(error); | |
2200 | |
2201 return; | |
2202 } | |
2203 | |
2204 base::DictionaryValue* dict_value = NULL; | |
2205 base::Value* created_entry = NULL; | |
2206 if (data.get() && data->GetAsDictionary(&dict_value) && dict_value) | |
2207 dict_value->Get("entry", &created_entry); | |
2208 error = AddNewDirectory(params.created_directory_path.DirName(), | |
2209 created_entry); | |
2210 | |
2211 if (error != DRIVE_FILE_OK) { | |
2212 if (!params.callback.is_null()) | |
2213 params.callback.Run(error); | |
2214 | |
2215 return; | |
2216 } | |
2217 | |
2218 // Not done yet with recursive directory creation? | |
2219 if (params.target_directory_path != params.created_directory_path && | |
2220 params.is_recursive) { | |
2221 CreateDirectory(params.target_directory_path, | |
2222 params.is_exclusive, | |
2223 params.is_recursive, | |
2224 params.callback); | |
2225 return; | |
2226 } | |
2227 | |
2228 if (!params.callback.is_null()) { | |
2229 // Finally done with the create request. | |
2230 params.callback.Run(DRIVE_FILE_OK); | |
2231 } | |
2232 } | |
2233 | |
2234 void GDataFileSystem::OnSearch(const SearchCallback& callback, | |
2235 GetDocumentsParams* params, | |
2236 DriveFileError error) { | |
2237 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
2238 | |
2239 if (error != DRIVE_FILE_OK) { | |
2240 if (!callback.is_null()) | |
2241 callback.Run(error, GURL(), scoped_ptr<std::vector<SearchResultInfo> >()); | |
2242 return; | |
2243 } | |
2244 | |
2245 // The search results will be returned using virtual directory. | |
2246 // The directory is not really part of the file system, so it has no parent or | |
2247 // root. | |
2248 std::vector<SearchResultInfo>* results(new std::vector<SearchResultInfo>()); | |
2249 | |
2250 DCHECK_EQ(1u, params->feed_list->size()); | |
2251 DocumentFeed* feed = params->feed_list->at(0); | |
2252 | |
2253 // TODO(tbarzic): Limit total number of returned results for the query. | |
2254 GURL next_feed; | |
2255 feed->GetNextFeedURL(&next_feed); | |
2256 | |
2257 if (feed->entries().empty()) { | |
2258 scoped_ptr<std::vector<SearchResultInfo> > result_vec(results); | |
2259 if (!callback.is_null()) | |
2260 callback.Run(error, next_feed, result_vec.Pass()); | |
2261 return; | |
2262 } | |
2263 | |
2264 // Go through all entires generated by the feed and add them to the search | |
2265 // result directory. | |
2266 for (size_t i = 0; i < feed->entries().size(); ++i) { | |
2267 DocumentEntry* doc = const_cast<DocumentEntry*>(feed->entries()[i]); | |
2268 scoped_ptr<DriveEntry> entry(resource_metadata_->FromDocumentEntry(*doc)); | |
2269 | |
2270 if (!entry.get()) | |
2271 continue; | |
2272 | |
2273 DCHECK_EQ(doc->resource_id(), entry->resource_id()); | |
2274 DCHECK(!entry->is_deleted()); | |
2275 | |
2276 std::string entry_resource_id = entry->resource_id(); | |
2277 | |
2278 // This will do nothing if the entry is not already present in file system. | |
2279 if (entry->AsDriveFile()) { | |
2280 scoped_ptr<DriveFile> entry_as_file(entry.release()->AsDriveFile()); | |
2281 resource_metadata_->RefreshFile(entry_as_file.Pass()); | |
2282 // We shouldn't use entry object after this point. | |
2283 DCHECK(!entry.get()); | |
2284 } | |
2285 | |
2286 // We will need information about result entry to create info for callback. | |
2287 // We can't use |entry| anymore, so we have to refetch entry from file | |
2288 // system. Also, |entry| doesn't have file path set before |RefreshFile| | |
2289 // call, so we can't get file path from there. | |
2290 resource_metadata_->GetEntryByResourceIdAsync(entry_resource_id, | |
2291 base::Bind(&AddEntryToSearchResults, | |
2292 results, | |
2293 callback, | |
2294 base::Bind(&GDataFileSystem::CheckForUpdates, ui_weak_ptr_), | |
2295 error, | |
2296 i+1 == feed->entries().size(), | |
2297 next_feed)); | |
2298 } | |
2299 } | |
2300 | |
2301 void GDataFileSystem::Search(const std::string& search_query, | |
2302 const GURL& next_feed, | |
2303 const SearchCallback& callback) { | |
2304 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || | |
2305 BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
2306 RunTaskOnUIThread(base::Bind(&GDataFileSystem::SearchAsyncOnUIThread, | |
2307 ui_weak_ptr_, | |
2308 search_query, | |
2309 next_feed, | |
2310 CreateRelayCallback(callback))); | |
2311 } | |
2312 | |
2313 void GDataFileSystem::SearchAsyncOnUIThread( | |
2314 const std::string& search_query, | |
2315 const GURL& next_feed, | |
2316 const SearchCallback& callback) { | |
2317 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
2318 | |
2319 feed_loader_->SearchFromServer( | |
2320 resource_metadata_->origin(), | |
2321 search_query, | |
2322 next_feed, | |
2323 base::Bind(&GDataFileSystem::OnSearch, ui_weak_ptr_, callback)); | |
2324 } | |
2325 | |
2326 void GDataFileSystem::OnDirectoryChanged(const FilePath& directory_path) { | |
2327 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
2328 | |
2329 FOR_EACH_OBSERVER(GDataFileSystemInterface::Observer, observers_, | |
2330 OnDirectoryChanged(directory_path)); | |
2331 } | |
2332 | |
2333 void GDataFileSystem::OnDocumentFeedFetched(int num_accumulated_entries) { | |
2334 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
2335 | |
2336 FOR_EACH_OBSERVER(GDataFileSystemInterface::Observer, observers_, | |
2337 OnDocumentFeedFetched(num_accumulated_entries)); | |
2338 } | |
2339 | |
2340 void GDataFileSystem::OnFeedFromServerLoaded() { | |
2341 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
2342 | |
2343 FOR_EACH_OBSERVER(GDataFileSystemInterface::Observer, observers_, | |
2344 OnFeedFromServerLoaded()); | |
2345 } | |
2346 | |
2347 void GDataFileSystem::LoadRootFeedFromCacheForTesting() { | |
2348 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
2349 | |
2350 feed_loader_->LoadFromCache( | |
2351 false, // should_load_from_server. | |
2352 FileOperationCallback()); | |
2353 } | |
2354 | |
2355 DriveFileError GDataFileSystem::UpdateFromFeedForTesting( | |
2356 const std::vector<DocumentFeed*>& feed_list, | |
2357 int64 start_changestamp, | |
2358 int64 root_feed_changestamp) { | |
2359 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
2360 | |
2361 return feed_loader_->UpdateFromFeed(feed_list, | |
2362 start_changestamp, | |
2363 root_feed_changestamp); | |
2364 } | |
2365 | |
2366 void GDataFileSystem::OnFilePathUpdated(const FileOperationCallback& callback, | |
2367 DriveFileError error, | |
2368 const FilePath& /* file_path */) { | |
2369 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
2370 if (!callback.is_null()) | |
2371 callback.Run(error); | |
2372 } | |
2373 | |
2374 void GDataFileSystem::OnCopyDocumentCompleted( | |
2375 const FilePath& dir_path, | |
2376 const FileOperationCallback& callback, | |
2377 GDataErrorCode status, | |
2378 scoped_ptr<base::Value> data) { | |
2379 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
2380 DCHECK(!callback.is_null()); | |
2381 | |
2382 DriveFileError error = util::GDataToDriveFileError(status); | |
2383 if (error != DRIVE_FILE_OK) { | |
2384 callback.Run(error); | |
2385 return; | |
2386 } | |
2387 | |
2388 scoped_ptr<DocumentEntry> doc_entry(DocumentEntry::ExtractAndParse(*data)); | |
2389 if (!doc_entry.get()) { | |
2390 callback.Run(DRIVE_FILE_ERROR_FAILED); | |
2391 return; | |
2392 } | |
2393 | |
2394 DriveEntry* entry = resource_metadata_->FromDocumentEntry(*doc_entry); | |
2395 if (!entry) { | |
2396 callback.Run(DRIVE_FILE_ERROR_FAILED); | |
2397 return; | |
2398 } | |
2399 | |
2400 // |entry| was added in the root directory on the server, so we should | |
2401 // first add it to |root_| to mirror the state and then move it to the | |
2402 // destination directory by MoveEntryFromRootDirectory(). | |
2403 resource_metadata_->AddEntryToDirectory( | |
2404 resource_metadata_->root(), | |
2405 entry, | |
2406 base::Bind(&GDataFileSystem::MoveEntryFromRootDirectory, | |
2407 ui_weak_ptr_, | |
2408 dir_path, | |
2409 callback)); | |
2410 } | |
2411 | |
2412 void GDataFileSystem::OnMoveEntryFromRootDirectoryCompleted( | |
2413 const FileOperationCallback& callback, | |
2414 const FilePath& file_path, | |
2415 const FilePath& dir_path, | |
2416 GDataErrorCode status, | |
2417 const GURL& document_url) { | |
2418 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
2419 DCHECK(!callback.is_null()); | |
2420 | |
2421 DriveFileError error = util::GDataToDriveFileError(status); | |
2422 if (error == DRIVE_FILE_OK) { | |
2423 DriveEntry* entry = resource_metadata_->FindEntryByPathSync(file_path); | |
2424 if (entry) { | |
2425 DCHECK_EQ(resource_metadata_->root(), entry->parent()); | |
2426 resource_metadata_->MoveEntryToDirectory( | |
2427 dir_path, | |
2428 entry, | |
2429 base::Bind( | |
2430 &GDataFileSystem::NotifyAndRunFileOperationCallback, | |
2431 ui_weak_ptr_, | |
2432 callback)); | |
2433 return; | |
2434 } else { | |
2435 error = DRIVE_FILE_ERROR_NOT_FOUND; | |
2436 } | |
2437 } | |
2438 | |
2439 callback.Run(error); | |
2440 } | |
2441 | |
2442 void GDataFileSystem::OnRemovedDocument( | |
2443 const FileOperationCallback& callback, | |
2444 const FilePath& file_path, | |
2445 GDataErrorCode status, | |
2446 const GURL& document_url) { | |
2447 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
2448 | |
2449 DriveFileError error = util::GDataToDriveFileError(status); | |
2450 | |
2451 if (error == DRIVE_FILE_OK) | |
2452 error = RemoveEntryAndCacheLocally(file_path); | |
2453 | |
2454 if (!callback.is_null()) { | |
2455 callback.Run(error); | |
2456 } | |
2457 } | |
2458 | |
2459 void GDataFileSystem::OnFileDownloaded( | |
2460 const GetFileFromCacheParams& params, | |
2461 GDataErrorCode status, | |
2462 const GURL& content_url, | |
2463 const FilePath& downloaded_file_path) { | |
2464 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
2465 | |
2466 // If user cancels download of a pinned-but-not-fetched file, mark file as | |
2467 // unpinned so that we do not sync the file again. | |
2468 if (status == GDATA_CANCELLED) { | |
2469 cache_->GetCacheEntryOnUIThread( | |
2470 params.resource_id, | |
2471 params.md5, | |
2472 base::Bind(&GDataFileSystem::UnpinIfPinned, | |
2473 ui_weak_ptr_, | |
2474 params.resource_id, | |
2475 params.md5)); | |
2476 } | |
2477 | |
2478 // At this point, the disk can be full or nearly full for several reasons: | |
2479 // - The expected file size was incorrect and the file was larger | |
2480 // - There was an in-flight download operation and it used up space | |
2481 // - The disk became full for some user actions we cannot control | |
2482 // (ex. the user might have downloaded a large file from a regular web site) | |
2483 // | |
2484 // If we don't have enough space, we return PLATFORM_FILE_ERROR_NO_SPACE, | |
2485 // and try to free up space, even if the file was downloaded successfully. | |
2486 bool* has_enough_space = new bool(false); | |
2487 util::PostBlockingPoolSequencedTaskAndReply( | |
2488 FROM_HERE, | |
2489 blocking_task_runner_, | |
2490 base::Bind(&DriveCache::FreeDiskSpaceIfNeededFor, | |
2491 base::Unretained(cache_), | |
2492 0, | |
2493 has_enough_space), | |
2494 base::Bind(&GDataFileSystem::OnFileDownloadedAndSpaceChecked, | |
2495 ui_weak_ptr_, | |
2496 params, | |
2497 status, | |
2498 content_url, | |
2499 downloaded_file_path, | |
2500 base::Owned(has_enough_space))); | |
2501 } | |
2502 | |
2503 void GDataFileSystem::UnpinIfPinned( | |
2504 const std::string& resource_id, | |
2505 const std::string& md5, | |
2506 bool success, | |
2507 const DriveCacheEntry& cache_entry) { | |
2508 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
2509 // TODO(hshi): http://crbug.com/127138 notify when file properties change. | |
2510 // This allows file manager to clear the "Available offline" checkbox. | |
2511 if (success && cache_entry.is_pinned()) | |
2512 cache_->UnpinOnUIThread(resource_id, md5, CacheOperationCallback()); | |
2513 } | |
2514 | |
2515 void GDataFileSystem::OnFileDownloadedAndSpaceChecked( | |
2516 const GetFileFromCacheParams& params, | |
2517 GDataErrorCode status, | |
2518 const GURL& content_url, | |
2519 const FilePath& downloaded_file_path, | |
2520 bool* has_enough_space) { | |
2521 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
2522 | |
2523 DriveFileError error = util::GDataToDriveFileError(status); | |
2524 | |
2525 // Make sure that downloaded file is properly stored in cache. We don't have | |
2526 // to wait for this operation to finish since the user can already use the | |
2527 // downloaded file. | |
2528 if (error == DRIVE_FILE_OK) { | |
2529 if (*has_enough_space) { | |
2530 cache_->StoreOnUIThread( | |
2531 params.resource_id, | |
2532 params.md5, | |
2533 downloaded_file_path, | |
2534 DriveCache::FILE_OPERATION_MOVE, | |
2535 base::Bind(&GDataFileSystem::OnDownloadStoredToCache, | |
2536 ui_weak_ptr_)); | |
2537 } else { | |
2538 // If we don't have enough space, remove the downloaded file, and | |
2539 // report "no space" error. | |
2540 util::PostBlockingPoolSequencedTask( | |
2541 FROM_HERE, | |
2542 blocking_task_runner_, | |
2543 base::Bind(base::IgnoreResult(&file_util::Delete), | |
2544 downloaded_file_path, | |
2545 false /* recursive*/)); | |
2546 error = DRIVE_FILE_ERROR_NO_SPACE; | |
2547 } | |
2548 } | |
2549 | |
2550 if (!params.get_file_callback.is_null()) { | |
2551 params.get_file_callback.Run(error, | |
2552 downloaded_file_path, | |
2553 params.mime_type, | |
2554 REGULAR_FILE); | |
2555 } | |
2556 } | |
2557 | |
2558 void GDataFileSystem::OnDownloadStoredToCache(DriveFileError error, | |
2559 const std::string& resource_id, | |
2560 const std::string& md5) { | |
2561 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
2562 // Nothing much to do here for now. | |
2563 } | |
2564 | |
2565 void GDataFileSystem::RenameEntryLocally( | |
2566 const FilePath& file_path, | |
2567 const FilePath::StringType& new_name, | |
2568 const FileMoveCallback& callback, | |
2569 GDataErrorCode status, | |
2570 const GURL& document_url) { | |
2571 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
2572 | |
2573 const DriveFileError error = util::GDataToDriveFileError(status); | |
2574 if (error != DRIVE_FILE_OK) { | |
2575 if (!callback.is_null()) | |
2576 callback.Run(error, FilePath()); | |
2577 return; | |
2578 } | |
2579 | |
2580 DriveEntry* entry = resource_metadata_->FindEntryByPathSync(file_path); | |
2581 if (!entry) { | |
2582 if (!callback.is_null()) | |
2583 callback.Run(DRIVE_FILE_ERROR_NOT_FOUND, FilePath()); | |
2584 return; | |
2585 } | |
2586 | |
2587 DCHECK(entry->parent()); | |
2588 entry->set_title(new_name); | |
2589 // After changing the title of the entry, call MoveEntryToDirectory() to | |
2590 // remove the entry from its parent directory and then add it back in order to | |
2591 // go through the file name de-duplication. | |
2592 // TODO(achuith/satorux/zel): This code is fragile. The title has been | |
2593 // changed, but not the file_name. MoveEntryToDirectory calls RemoveChild to | |
2594 // remove the child based on the old file_name, and then re-adds the child by | |
2595 // first assigning the new title to file_name. http://crbug.com/30157 | |
2596 resource_metadata_->MoveEntryToDirectory( | |
2597 entry->parent()->GetFilePath(), | |
2598 entry, | |
2599 base::Bind(&GDataFileSystem::NotifyAndRunFileMoveCallback, | |
2600 ui_weak_ptr_, | |
2601 callback)); | |
2602 } | |
2603 | |
2604 void GDataFileSystem::MoveEntryToRootDirectoryLocally( | |
2605 const FileMoveCallback& callback, | |
2606 const FilePath& file_path, | |
2607 const FilePath& dir_path, | |
2608 GDataErrorCode status, | |
2609 const GURL& document_url) { | |
2610 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
2611 DCHECK(!callback.is_null()); | |
2612 | |
2613 const DriveFileError error = util::GDataToDriveFileError(status); | |
2614 if (error != DRIVE_FILE_OK) { | |
2615 callback.Run(error, FilePath()); | |
2616 return; | |
2617 } | |
2618 | |
2619 DriveEntry* entry = resource_metadata_->FindEntryByPathSync(file_path); | |
2620 if (!entry) { | |
2621 callback.Run(DRIVE_FILE_ERROR_NOT_FOUND, FilePath()); | |
2622 return; | |
2623 } | |
2624 | |
2625 resource_metadata_->MoveEntryToDirectory( | |
2626 resource_metadata_->root()->GetFilePath(), | |
2627 entry, | |
2628 base::Bind(&GDataFileSystem::NotifyAndRunFileMoveCallback, | |
2629 ui_weak_ptr_, | |
2630 callback)); | |
2631 } | |
2632 | |
2633 void GDataFileSystem::NotifyAndRunFileMoveCallback( | |
2634 const FileMoveCallback& callback, | |
2635 DriveFileError error, | |
2636 const FilePath& moved_file_path) { | |
2637 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
2638 | |
2639 if (error == DRIVE_FILE_OK) | |
2640 OnDirectoryChanged(moved_file_path.DirName()); | |
2641 | |
2642 if (!callback.is_null()) | |
2643 callback.Run(error, moved_file_path); | |
2644 } | |
2645 | |
2646 void GDataFileSystem::NotifyAndRunFileOperationCallback( | |
2647 const FileOperationCallback& callback, | |
2648 DriveFileError error, | |
2649 const FilePath& moved_file_path) { | |
2650 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
2651 DCHECK(!callback.is_null()); | |
2652 | |
2653 if (error == DRIVE_FILE_OK) | |
2654 OnDirectoryChanged(moved_file_path.DirName()); | |
2655 | |
2656 callback.Run(error); | |
2657 } | |
2658 | |
2659 void GDataFileSystem::OnDirectoryChangeFileMoveCallback( | |
2660 DriveFileError error, | |
2661 const FilePath& directory_path) { | |
2662 if (error == DRIVE_FILE_OK) | |
2663 OnDirectoryChanged(directory_path); | |
2664 } | |
2665 | |
2666 DriveFileError GDataFileSystem::RemoveEntryAndCacheLocally( | |
2667 const FilePath& file_path) { | |
2668 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
2669 | |
2670 std::string resource_id; | |
2671 DriveFileError error = RemoveEntryLocally(file_path, &resource_id); | |
2672 if (error != DRIVE_FILE_OK) | |
2673 return error; | |
2674 | |
2675 // If resource_id is not empty, remove its corresponding file from cache. | |
2676 if (!resource_id.empty()) | |
2677 cache_->RemoveOnUIThread(resource_id, CacheOperationCallback()); | |
2678 | |
2679 return DRIVE_FILE_OK; | |
2680 } | |
2681 | |
2682 void GDataFileSystem::RemoveStaleEntryOnUpload( | |
2683 const std::string& resource_id, | |
2684 DriveDirectory* parent_dir, | |
2685 const FileMoveCallback& callback, | |
2686 DriveEntry* existing_entry) { | |
2687 if (existing_entry && | |
2688 // This should always match, but just in case. | |
2689 existing_entry->parent() == parent_dir) { | |
2690 resource_metadata_->RemoveEntryFromParent(existing_entry, callback); | |
2691 } else { | |
2692 callback.Run(DRIVE_FILE_ERROR_NOT_FOUND, FilePath()); | |
2693 LOG(ERROR) << "Entry for the existing file not found: " << resource_id; | |
2694 } | |
2695 } | |
2696 | |
2697 void GDataFileSystem::NotifyFileSystemMounted() { | |
2698 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
2699 | |
2700 DVLOG(1) << "File System is mounted"; | |
2701 // Notify the observers that the file system is mounted. | |
2702 FOR_EACH_OBSERVER(GDataFileSystemInterface::Observer, observers_, | |
2703 OnFileSystemMounted()); | |
2704 } | |
2705 | |
2706 void GDataFileSystem::NotifyFileSystemToBeUnmounted() { | |
2707 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
2708 | |
2709 DVLOG(1) << "File System is to be unmounted"; | |
2710 // Notify the observers that the file system is being unmounted. | |
2711 FOR_EACH_OBSERVER(GDataFileSystemInterface::Observer, observers_, | |
2712 OnFileSystemBeingUnmounted()); | |
2713 } | |
2714 | |
2715 void GDataFileSystem::NotifyInitialLoadFinishedAndRun( | |
2716 const FileOperationCallback& callback, | |
2717 DriveFileError error) { | |
2718 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
2719 DCHECK(!callback.is_null()); | |
2720 | |
2721 // Notify the observers that root directory has been initialized. | |
2722 FOR_EACH_OBSERVER(GDataFileSystemInterface::Observer, observers_, | |
2723 OnInitialLoadFinished()); | |
2724 | |
2725 callback.Run(error); | |
2726 } | |
2727 | |
2728 DriveFileError GDataFileSystem::AddNewDirectory( | |
2729 const FilePath& directory_path, base::Value* entry_value) { | |
2730 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
2731 | |
2732 if (!entry_value) | |
2733 return DRIVE_FILE_ERROR_FAILED; | |
2734 | |
2735 scoped_ptr<DocumentEntry> doc_entry(DocumentEntry::CreateFrom(*entry_value)); | |
2736 | |
2737 if (!doc_entry.get()) | |
2738 return DRIVE_FILE_ERROR_FAILED; | |
2739 | |
2740 // Find parent directory element within the cached file system snapshot. | |
2741 DriveEntry* entry = resource_metadata_->FindEntryByPathSync(directory_path); | |
2742 if (!entry) | |
2743 return DRIVE_FILE_ERROR_FAILED; | |
2744 | |
2745 // Check if parent is a directory since in theory since this is a callback | |
2746 // something could in the meantime have nuked the parent dir and created a | |
2747 // file with the exact same name. | |
2748 DriveDirectory* parent_dir = entry->AsDriveDirectory(); | |
2749 if (!parent_dir) | |
2750 return DRIVE_FILE_ERROR_FAILED; | |
2751 | |
2752 DriveEntry* new_entry = | |
2753 resource_metadata_->FromDocumentEntry(*doc_entry); | |
2754 if (!new_entry) | |
2755 return DRIVE_FILE_ERROR_FAILED; | |
2756 | |
2757 resource_metadata_->AddEntryToDirectory( | |
2758 parent_dir, | |
2759 new_entry, | |
2760 base::Bind(&GDataFileSystem::NotifyAndRunFileMoveCallback, | |
2761 ui_weak_ptr_, | |
2762 FileMoveCallback())); | |
2763 return DRIVE_FILE_OK; | |
2764 } | |
2765 | |
2766 GDataFileSystem::FindMissingDirectoryResult | |
2767 GDataFileSystem::FindFirstMissingParentDirectory( | |
2768 const FilePath& directory_path, | |
2769 GURL* last_dir_content_url, | |
2770 FilePath* first_missing_parent_path) { | |
2771 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
2772 | |
2773 // Let's find which how deep is the existing directory structure and | |
2774 // get the first element that's missing. | |
2775 std::vector<FilePath::StringType> path_parts; | |
2776 directory_path.GetComponents(&path_parts); | |
2777 FilePath current_path; | |
2778 | |
2779 for (std::vector<FilePath::StringType>::const_iterator iter = | |
2780 path_parts.begin(); | |
2781 iter != path_parts.end(); ++iter) { | |
2782 current_path = current_path.Append(*iter); | |
2783 DriveEntry* entry = resource_metadata_->FindEntryByPathSync(current_path); | |
2784 if (entry) { | |
2785 if (entry->file_info().is_directory) { | |
2786 *last_dir_content_url = entry->content_url(); | |
2787 } else { | |
2788 // Huh, the segment found is a file not a directory? | |
2789 return FOUND_INVALID; | |
2790 } | |
2791 } else { | |
2792 *first_missing_parent_path = current_path; | |
2793 return FOUND_MISSING; | |
2794 } | |
2795 } | |
2796 return DIRECTORY_ALREADY_PRESENT; | |
2797 } | |
2798 | |
2799 DriveFileError GDataFileSystem::RemoveEntryLocally( | |
2800 const FilePath& file_path, std::string* resource_id) { | |
2801 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
2802 | |
2803 resource_id->clear(); | |
2804 | |
2805 // Find directory element within the cached file system snapshot. | |
2806 DriveEntry* entry = resource_metadata_->FindEntryByPathSync(file_path); | |
2807 | |
2808 if (!entry) | |
2809 return DRIVE_FILE_ERROR_NOT_FOUND; | |
2810 | |
2811 // You can't remove root element. | |
2812 if (!entry->parent()) | |
2813 return DRIVE_FILE_ERROR_ACCESS_DENIED; | |
2814 | |
2815 // If it's a file (only files have resource id), get its resource id so that | |
2816 // we can remove it after releasing the auto lock. | |
2817 if (entry->AsDriveFile()) | |
2818 *resource_id = entry->AsDriveFile()->resource_id(); | |
2819 | |
2820 resource_metadata_->RemoveEntryFromParent( | |
2821 entry, | |
2822 base::Bind(&GDataFileSystem::OnDirectoryChangeFileMoveCallback, | |
2823 ui_weak_ptr_)); | |
2824 return DRIVE_FILE_OK; | |
2825 } | |
2826 | |
2827 void GDataFileSystem::AddUploadedFile( | |
2828 UploadMode upload_mode, | |
2829 const FilePath& virtual_dir_path, | |
2830 scoped_ptr<DocumentEntry> entry, | |
2831 const FilePath& file_content_path, | |
2832 DriveCache::FileOperationType cache_operation, | |
2833 const base::Closure& callback) { | |
2834 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
2835 | |
2836 // Post a task to the same thread, rather than calling it here, as | |
2837 // AddUploadedFile() is asynchronous. | |
2838 base::MessageLoopProxy::current()->PostTask( | |
2839 FROM_HERE, | |
2840 base::Bind(&GDataFileSystem::AddUploadedFileOnUIThread, | |
2841 ui_weak_ptr_, | |
2842 upload_mode, | |
2843 virtual_dir_path, | |
2844 base::Passed(&entry), | |
2845 file_content_path, | |
2846 cache_operation, | |
2847 callback)); | |
2848 } | |
2849 | |
2850 void GDataFileSystem::AddUploadedFileOnUIThread( | |
2851 UploadMode upload_mode, | |
2852 const FilePath& virtual_dir_path, | |
2853 scoped_ptr<DocumentEntry> entry, | |
2854 const FilePath& file_content_path, | |
2855 DriveCache::FileOperationType cache_operation, | |
2856 const base::Closure& callback) { | |
2857 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
2858 | |
2859 // ScopedClosureRunner ensures that the specified callback is always invoked | |
2860 // upon return or passed on. | |
2861 base::ScopedClosureRunner callback_runner(callback); | |
2862 | |
2863 if (!entry.get()) { | |
2864 NOTREACHED(); | |
2865 return; | |
2866 } | |
2867 | |
2868 DriveEntry* dir_entry = resource_metadata_->FindEntryByPathSync( | |
2869 virtual_dir_path); | |
2870 if (!dir_entry) | |
2871 return; | |
2872 | |
2873 DriveDirectory* parent_dir = dir_entry->AsDriveDirectory(); | |
2874 if (!parent_dir) | |
2875 return; | |
2876 | |
2877 scoped_ptr<DriveEntry> new_entry( | |
2878 resource_metadata_->FromDocumentEntry(*entry)); | |
2879 if (!new_entry.get()) | |
2880 return; | |
2881 | |
2882 const std::string& resource_id = new_entry->resource_id(); | |
2883 AddUploadedFileParams* params = | |
2884 new AddUploadedFileParams(upload_mode, | |
2885 parent_dir, | |
2886 new_entry.Pass(), | |
2887 file_content_path, | |
2888 cache_operation, | |
2889 callback_runner.Release()); | |
2890 | |
2891 const FileMoveCallback file_move_callback = | |
2892 base::Bind(&GDataFileSystem::ContinueAddUploadedFile, | |
2893 ui_weak_ptr_, params); | |
2894 | |
2895 if (upload_mode == UPLOAD_EXISTING_FILE) { | |
2896 // Remove an existing entry, which should be present. | |
2897 resource_metadata_->GetEntryByResourceIdAsync( | |
2898 resource_id, | |
2899 base::Bind(&GDataFileSystem::RemoveStaleEntryOnUpload, | |
2900 ui_weak_ptr_, | |
2901 resource_id, | |
2902 parent_dir, | |
2903 file_move_callback)); | |
2904 } else { | |
2905 file_move_callback.Run(DRIVE_FILE_OK, FilePath()); | |
2906 } | |
2907 } | |
2908 | |
2909 void GDataFileSystem::ContinueAddUploadedFile( | |
2910 AddUploadedFileParams* params, | |
2911 DriveFileError error, | |
2912 const FilePath& file_path) { | |
2913 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
2914 DCHECK_EQ(DRIVE_FILE_OK, error); | |
2915 DCHECK(params->new_entry.get()); | |
2916 DriveFile* file = params->new_entry->AsDriveFile(); | |
2917 DCHECK(file); | |
2918 | |
2919 params->resource_id = file->resource_id(); | |
2920 params->md5 = file->file_md5(); | |
2921 resource_metadata_->AddEntryToDirectory( | |
2922 params->parent_dir, | |
2923 params->new_entry.release(), | |
2924 base::Bind(&GDataFileSystem::NotifyAndRunFileMoveCallback, | |
2925 ui_weak_ptr_, | |
2926 base::Bind(&GDataFileSystem::AddUploadedFileToCache, | |
2927 ui_weak_ptr_, | |
2928 base::Owned(params)))); | |
2929 } | |
2930 | |
2931 void GDataFileSystem::AddUploadedFileToCache( | |
2932 AddUploadedFileParams* params, | |
2933 DriveFileError error, | |
2934 const FilePath& file_path) { | |
2935 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
2936 | |
2937 if (params->upload_mode == UPLOAD_NEW_FILE) { | |
2938 // Add the file to the cache if we have uploaded a new file. | |
2939 cache_->StoreOnUIThread(params->resource_id, | |
2940 params->md5, | |
2941 params->file_content_path, | |
2942 params->cache_operation, | |
2943 base::Bind(&OnCacheUpdatedForAddUploadedFile, | |
2944 params->callback)); | |
2945 } else if (params->upload_mode == UPLOAD_EXISTING_FILE) { | |
2946 // Clear the dirty bit if we have updated an existing file. | |
2947 cache_->ClearDirtyOnUIThread(params->resource_id, | |
2948 params->md5, | |
2949 base::Bind(&OnCacheUpdatedForAddUploadedFile, | |
2950 params->callback)); | |
2951 } else { | |
2952 NOTREACHED() << "Unexpected upload mode: " << params->upload_mode; | |
2953 // Shouldn't reach here, so the line below should not make much sense, but | |
2954 // since calling |callback| exactly once is our obligation, we'd better call | |
2955 // it for not to clutter further more. | |
2956 params->callback.Run(); | |
2957 } | |
2958 } | |
2959 | |
2960 void GDataFileSystem::UpdateEntryData(const std::string& resource_id, | |
2961 const std::string& md5, | |
2962 scoped_ptr<DocumentEntry> entry, | |
2963 const FilePath& file_content_path, | |
2964 const base::Closure& callback) { | |
2965 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
2966 | |
2967 // Post a task to the same thread, rather than calling it here, as | |
2968 // UpdateEntryData() is asynchronous. | |
2969 base::MessageLoopProxy::current()->PostTask( | |
2970 FROM_HERE, | |
2971 base::Bind(&GDataFileSystem::UpdateEntryDataOnUIThread, | |
2972 ui_weak_ptr_, | |
2973 resource_id, | |
2974 md5, | |
2975 base::Passed(&entry), | |
2976 file_content_path, | |
2977 callback)); | |
2978 } | |
2979 | |
2980 void GDataFileSystem::UpdateEntryDataOnUIThread( | |
2981 const std::string& resource_id, | |
2982 const std::string& md5, | |
2983 scoped_ptr<DocumentEntry> entry, | |
2984 const FilePath& file_content_path, | |
2985 const base::Closure& callback) { | |
2986 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
2987 | |
2988 scoped_ptr<DriveFile> new_entry( | |
2989 resource_metadata_->FromDocumentEntry(*entry)->AsDriveFile()); | |
2990 if (!new_entry.get()) { | |
2991 return; | |
2992 } | |
2993 | |
2994 resource_metadata_->RefreshFile(new_entry.Pass()); | |
2995 | |
2996 // Add the file to the cache if we have uploaded a new file. | |
2997 cache_->StoreOnUIThread(resource_id, | |
2998 md5, | |
2999 file_content_path, | |
3000 DriveCache::FILE_OPERATION_MOVE, | |
3001 base::Bind(&OnCacheUpdatedForAddUploadedFile, | |
3002 callback)); | |
3003 } | |
3004 | |
3005 | |
3006 void GDataFileSystem::Observe(int type, | |
3007 const content::NotificationSource& source, | |
3008 const content::NotificationDetails& details) { | |
3009 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
3010 | |
3011 if (type == chrome::NOTIFICATION_PREF_CHANGED) { | |
3012 PrefService* pref_service = profile_->GetPrefs(); | |
3013 std::string* pref_name = content::Details<std::string>(details).ptr(); | |
3014 if (*pref_name == prefs::kDisableGDataHostedFiles) { | |
3015 SetHideHostedDocuments( | |
3016 pref_service->GetBoolean(prefs::kDisableGDataHostedFiles)); | |
3017 } | |
3018 } else { | |
3019 NOTREACHED(); | |
3020 } | |
3021 } | |
3022 | |
3023 void GDataFileSystem::SetHideHostedDocuments(bool hide) { | |
3024 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
3025 | |
3026 if (hide == hide_hosted_docs_) | |
3027 return; | |
3028 | |
3029 hide_hosted_docs_ = hide; | |
3030 const FilePath root_path = resource_metadata_->root()->GetFilePath(); | |
3031 | |
3032 // Kick off directory refresh when this setting changes. | |
3033 FOR_EACH_OBSERVER(GDataFileSystemInterface::Observer, observers_, | |
3034 OnDirectoryChanged(root_path)); | |
3035 } | |
3036 | |
3037 //============= GDataFileSystem: internal helper functions ===================== | |
3038 | |
3039 void GDataFileSystem::InitializePreferenceObserver() { | |
3040 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
3041 | |
3042 pref_registrar_.reset(new PrefChangeRegistrar()); | |
3043 pref_registrar_->Init(profile_->GetPrefs()); | |
3044 pref_registrar_->Add(prefs::kDisableGDataHostedFiles, this); | |
3045 } | |
3046 | |
3047 void GDataFileSystem::OpenFile(const FilePath& file_path, | |
3048 const OpenFileCallback& callback) { | |
3049 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || | |
3050 BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
3051 RunTaskOnUIThread(base::Bind(&GDataFileSystem::OpenFileOnUIThread, | |
3052 ui_weak_ptr_, | |
3053 file_path, | |
3054 CreateRelayCallback(callback))); | |
3055 } | |
3056 | |
3057 void GDataFileSystem::OpenFileOnUIThread(const FilePath& file_path, | |
3058 const OpenFileCallback& callback) { | |
3059 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
3060 | |
3061 // If the file is already opened, it cannot be opened again before closed. | |
3062 // This is for avoiding simultaneous modification to the file, and moreover | |
3063 // to avoid an inconsistent cache state (suppose an operation sequence like | |
3064 // Open->Open->modify->Close->modify->Close; the second modify may not be | |
3065 // synchronized to the server since it is already Closed on the cache). | |
3066 if (open_files_.find(file_path) != open_files_.end()) { | |
3067 MessageLoop::current()->PostTask( | |
3068 FROM_HERE, | |
3069 base::Bind(callback, DRIVE_FILE_ERROR_IN_USE, FilePath())); | |
3070 return; | |
3071 } | |
3072 open_files_.insert(file_path); | |
3073 | |
3074 resource_metadata_->GetEntryInfoByPath( | |
3075 file_path, | |
3076 base::Bind(&GDataFileSystem::OnGetEntryInfoCompleteForOpenFile, | |
3077 ui_weak_ptr_, | |
3078 file_path, | |
3079 base::Bind(&GDataFileSystem::OnOpenFileFinished, | |
3080 ui_weak_ptr_, | |
3081 file_path, | |
3082 callback))); | |
3083 } | |
3084 | |
3085 void GDataFileSystem::OnGetEntryInfoCompleteForOpenFile( | |
3086 const FilePath& file_path, | |
3087 const OpenFileCallback& callback, | |
3088 DriveFileError error, | |
3089 scoped_ptr<DriveEntryProto> entry_proto) { | |
3090 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
3091 | |
3092 if (entry_proto.get() && !entry_proto->has_file_specific_info()) | |
3093 error = DRIVE_FILE_ERROR_NOT_FOUND; | |
3094 | |
3095 if (error == DRIVE_FILE_OK) { | |
3096 if (entry_proto->file_specific_info().file_md5().empty() || | |
3097 entry_proto->file_specific_info().is_hosted_document()) { | |
3098 // No support for opening a directory or hosted document. | |
3099 error = DRIVE_FILE_ERROR_INVALID_OPERATION; | |
3100 } | |
3101 } | |
3102 | |
3103 if (error != DRIVE_FILE_OK) { | |
3104 if (!callback.is_null()) | |
3105 callback.Run(error, FilePath()); | |
3106 return; | |
3107 } | |
3108 | |
3109 DCHECK(!entry_proto->resource_id().empty()); | |
3110 GetResolvedFileByPath( | |
3111 file_path, | |
3112 base::Bind(&GDataFileSystem::OnGetFileCompleteForOpenFile, | |
3113 ui_weak_ptr_, | |
3114 callback, | |
3115 GetFileCompleteForOpenParams( | |
3116 entry_proto->resource_id(), | |
3117 entry_proto->file_specific_info().file_md5())), | |
3118 GetContentCallback(), | |
3119 error, | |
3120 entry_proto.get()); | |
3121 } | |
3122 | |
3123 void GDataFileSystem::OnGetFileCompleteForOpenFile( | |
3124 const OpenFileCallback& callback, | |
3125 const GetFileCompleteForOpenParams& entry_proto, | |
3126 DriveFileError error, | |
3127 const FilePath& file_path, | |
3128 const std::string& mime_type, | |
3129 DriveFileType file_type) { | |
3130 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
3131 | |
3132 if (error != DRIVE_FILE_OK) { | |
3133 if (!callback.is_null()) | |
3134 callback.Run(error, FilePath()); | |
3135 return; | |
3136 } | |
3137 | |
3138 // OpenFileOnUIThread ensures that the file is a regular file. | |
3139 DCHECK_EQ(REGULAR_FILE, file_type); | |
3140 | |
3141 cache_->MarkDirtyOnUIThread( | |
3142 entry_proto.resource_id, | |
3143 entry_proto.md5, | |
3144 base::Bind(&GDataFileSystem::OnMarkDirtyInCacheCompleteForOpenFile, | |
3145 ui_weak_ptr_, | |
3146 callback)); | |
3147 } | |
3148 | |
3149 void GDataFileSystem::OnMarkDirtyInCacheCompleteForOpenFile( | |
3150 const OpenFileCallback& callback, | |
3151 DriveFileError error, | |
3152 const std::string& resource_id, | |
3153 const std::string& md5, | |
3154 const FilePath& cache_file_path) { | |
3155 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
3156 | |
3157 if (!callback.is_null()) | |
3158 callback.Run(error, cache_file_path); | |
3159 } | |
3160 | |
3161 void GDataFileSystem::OnOpenFileFinished(const FilePath& file_path, | |
3162 const OpenFileCallback& callback, | |
3163 DriveFileError result, | |
3164 const FilePath& cache_file_path) { | |
3165 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
3166 | |
3167 // All the invocation of |callback| from operations initiated from OpenFile | |
3168 // must go through here. Removes the |file_path| from the remembered set when | |
3169 // the file was not successfully opened. | |
3170 if (result != DRIVE_FILE_OK) | |
3171 open_files_.erase(file_path); | |
3172 | |
3173 if (!callback.is_null()) | |
3174 callback.Run(result, cache_file_path); | |
3175 } | |
3176 | |
3177 void GDataFileSystem::CloseFile(const FilePath& file_path, | |
3178 const FileOperationCallback& callback) { | |
3179 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) || | |
3180 BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
3181 DCHECK(!callback.is_null()); | |
3182 | |
3183 RunTaskOnUIThread(base::Bind(&GDataFileSystem::CloseFileOnUIThread, | |
3184 ui_weak_ptr_, | |
3185 file_path, | |
3186 CreateRelayCallback(callback))); | |
3187 } | |
3188 | |
3189 void GDataFileSystem::CloseFileOnUIThread( | |
3190 const FilePath& file_path, | |
3191 const FileOperationCallback& callback) { | |
3192 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
3193 DCHECK(!callback.is_null()); | |
3194 | |
3195 if (open_files_.find(file_path) == open_files_.end()) { | |
3196 // The file is not being opened. | |
3197 MessageLoop::current()->PostTask( | |
3198 FROM_HERE, | |
3199 base::Bind(callback, DRIVE_FILE_ERROR_NOT_FOUND)); | |
3200 return; | |
3201 } | |
3202 | |
3203 // Step 1 of CloseFile: Get resource_id and md5 for |file_path|. | |
3204 resource_metadata_->GetEntryInfoByPath( | |
3205 file_path, | |
3206 base::Bind(&GDataFileSystem::CloseFileOnUIThreadAfterGetEntryInfo, | |
3207 ui_weak_ptr_, | |
3208 file_path, | |
3209 base::Bind(&GDataFileSystem::CloseFileOnUIThreadFinalize, | |
3210 ui_weak_ptr_, | |
3211 file_path, | |
3212 callback))); | |
3213 } | |
3214 | |
3215 void GDataFileSystem::CloseFileOnUIThreadAfterGetEntryInfo( | |
3216 const FilePath& file_path, | |
3217 const FileOperationCallback& callback, | |
3218 DriveFileError error, | |
3219 scoped_ptr<DriveEntryProto> entry_proto) { | |
3220 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
3221 DCHECK(!callback.is_null()); | |
3222 | |
3223 if (entry_proto.get() && !entry_proto->has_file_specific_info()) | |
3224 error = DRIVE_FILE_ERROR_NOT_FOUND; | |
3225 | |
3226 if (error != DRIVE_FILE_OK) { | |
3227 callback.Run(error); | |
3228 return; | |
3229 } | |
3230 | |
3231 // Step 2 of CloseFile: Commit the modification in cache. This will trigger | |
3232 // background upload. | |
3233 // TODO(benchan,kinaba): Call ClearDirtyInCache instead of CommitDirtyInCache | |
3234 // if the file has not been modified. Come up with a way to detect the | |
3235 // intactness effectively, or provide a method for user to declare it when | |
3236 // calling CloseFile(). | |
3237 cache_->CommitDirtyOnUIThread( | |
3238 entry_proto->resource_id(), | |
3239 entry_proto->file_specific_info().file_md5(), | |
3240 base::Bind(&GDataFileSystem::CloseFileOnUIThreadAfterCommitDirtyInCache, | |
3241 ui_weak_ptr_, | |
3242 callback)); | |
3243 } | |
3244 | |
3245 void GDataFileSystem::CloseFileOnUIThreadAfterCommitDirtyInCache( | |
3246 const FileOperationCallback& callback, | |
3247 DriveFileError error, | |
3248 const std::string& /* resource_id */, | |
3249 const std::string& /* md5 */) { | |
3250 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
3251 DCHECK(!callback.is_null()); | |
3252 | |
3253 callback.Run(error); | |
3254 } | |
3255 | |
3256 void GDataFileSystem::CloseFileOnUIThreadFinalize( | |
3257 const FilePath& file_path, | |
3258 const FileOperationCallback& callback, | |
3259 DriveFileError result) { | |
3260 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
3261 DCHECK(!callback.is_null()); | |
3262 | |
3263 // Step 3 of CloseFile. | |
3264 // All the invocation of |callback| from operations initiated from CloseFile | |
3265 // must go through here. Removes the |file_path| from the remembered set so | |
3266 // that subsequent operations can open the file again. | |
3267 open_files_.erase(file_path); | |
3268 | |
3269 // Then invokes the user-supplied callback function. | |
3270 callback.Run(result); | |
3271 } | |
3272 | |
3273 void GDataFileSystem::CheckLocalModificationAndRun( | |
3274 scoped_ptr<DriveEntryProto> entry_proto, | |
3275 const GetEntryInfoCallback& callback) { | |
3276 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
3277 DCHECK(entry_proto.get()); | |
3278 DCHECK(!callback.is_null()); | |
3279 | |
3280 // For entries that will never be cached, use the original entry info as is. | |
3281 if (!entry_proto->has_file_specific_info() || | |
3282 entry_proto->file_specific_info().is_hosted_document()) { | |
3283 callback.Run(DRIVE_FILE_OK, entry_proto.Pass()); | |
3284 return; | |
3285 } | |
3286 | |
3287 // Checks if the file is cached and modified locally. | |
3288 const std::string resource_id = entry_proto->resource_id(); | |
3289 const std::string md5 = entry_proto->file_specific_info().file_md5(); | |
3290 cache_->GetCacheEntryOnUIThread( | |
3291 resource_id, | |
3292 md5, | |
3293 base::Bind( | |
3294 &GDataFileSystem::CheckLocalModificationAndRunAfterGetCacheEntry, | |
3295 ui_weak_ptr_, base::Passed(&entry_proto), callback)); | |
3296 } | |
3297 | |
3298 void GDataFileSystem::CheckLocalModificationAndRunAfterGetCacheEntry( | |
3299 scoped_ptr<DriveEntryProto> entry_proto, | |
3300 const GetEntryInfoCallback& callback, | |
3301 bool success, | |
3302 const DriveCacheEntry& cache_entry) { | |
3303 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
3304 DCHECK(!callback.is_null()); | |
3305 | |
3306 // When no dirty cache is found, use the original entry info as is. | |
3307 if (!success || !cache_entry.is_dirty()) { | |
3308 callback.Run(DRIVE_FILE_OK, entry_proto.Pass()); | |
3309 return; | |
3310 } | |
3311 | |
3312 // Gets the cache file path. | |
3313 const std::string& resource_id = entry_proto->resource_id(); | |
3314 const std::string& md5 = entry_proto->file_specific_info().file_md5(); | |
3315 cache_->GetFileOnUIThread( | |
3316 resource_id, | |
3317 md5, | |
3318 base::Bind( | |
3319 &GDataFileSystem::CheckLocalModificationAndRunAfterGetCacheFile, | |
3320 ui_weak_ptr_, base::Passed(&entry_proto), callback)); | |
3321 } | |
3322 | |
3323 void GDataFileSystem::CheckLocalModificationAndRunAfterGetCacheFile( | |
3324 scoped_ptr<DriveEntryProto> entry_proto, | |
3325 const GetEntryInfoCallback& callback, | |
3326 DriveFileError error, | |
3327 const std::string& resource_id, | |
3328 const std::string& md5, | |
3329 const FilePath& local_cache_path) { | |
3330 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
3331 DCHECK(!callback.is_null()); | |
3332 | |
3333 // When no dirty cache is found, use the original entry info as is. | |
3334 if (error != DRIVE_FILE_OK) { | |
3335 callback.Run(DRIVE_FILE_OK, entry_proto.Pass()); | |
3336 return; | |
3337 } | |
3338 | |
3339 // If the cache is dirty, obtain the file info from the cache file itself. | |
3340 base::PlatformFileInfo* file_info = new base::PlatformFileInfo; | |
3341 bool* get_file_info_result = new bool(false); | |
3342 util::PostBlockingPoolSequencedTaskAndReply( | |
3343 FROM_HERE, | |
3344 blocking_task_runner_, | |
3345 base::Bind(&GetFileInfoOnBlockingPool, | |
3346 local_cache_path, | |
3347 base::Unretained(file_info), | |
3348 base::Unretained(get_file_info_result)), | |
3349 base::Bind(&GDataFileSystem::CheckLocalModificationAndRunAfterGetFileInfo, | |
3350 ui_weak_ptr_, | |
3351 base::Passed(&entry_proto), | |
3352 callback, | |
3353 base::Owned(file_info), | |
3354 base::Owned(get_file_info_result))); | |
3355 } | |
3356 | |
3357 void GDataFileSystem::CheckLocalModificationAndRunAfterGetFileInfo( | |
3358 scoped_ptr<DriveEntryProto> entry_proto, | |
3359 const GetEntryInfoCallback& callback, | |
3360 base::PlatformFileInfo* file_info, | |
3361 bool* get_file_info_result) { | |
3362 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
3363 DCHECK(!callback.is_null()); | |
3364 | |
3365 if (!*get_file_info_result) { | |
3366 callback.Run(DRIVE_FILE_ERROR_NOT_FOUND, scoped_ptr<DriveEntryProto>()); | |
3367 return; | |
3368 } | |
3369 | |
3370 PlatformFileInfoProto entry_file_info; | |
3371 DriveEntry::ConvertPlatformFileInfoToProto(*file_info, &entry_file_info); | |
3372 *entry_proto->mutable_file_info() = entry_file_info; | |
3373 callback.Run(DRIVE_FILE_OK, entry_proto.Pass()); | |
3374 } | |
3375 | |
3376 } // namespace gdata | |
OLD | NEW |