Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1113)

Side by Side Diff: chrome/browser/chromeos/gdata/gdata_file_system.cc

Issue 10874028: Rename GDataFileSystem* to DriveFileSystem* (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Remaining manual changes Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698