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

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: rebase. Created 8 years, 3 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
783 upload_file_info->completion_callback =
784 base::Bind(&GDataFileSystem::OnTransferCompleted,
785 ui_weak_ptr_,
786 params.callback);
787
788 uploader_->UploadNewFile(upload_file_info.Pass());
789 }
790
791 void GDataFileSystem::OnTransferCompleted(
792 const FileOperationCallback& callback,
793 DriveFileError error,
794 scoped_ptr<UploadFileInfo> upload_file_info) {
795 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
796 DCHECK(upload_file_info.get());
797
798 if (error == DRIVE_FILE_OK && upload_file_info->entry.get()) {
799 AddUploadedFile(UPLOAD_NEW_FILE,
800 upload_file_info->gdata_path.DirName(),
801 upload_file_info->entry.Pass(),
802 upload_file_info->file_path,
803 DriveCache::FILE_OPERATION_COPY,
804 base::Bind(&OnAddUploadFileCompleted, callback, error));
805 } else if (!callback.is_null()) {
806 callback.Run(error);
807 }
808 }
809
810 void GDataFileSystem::Copy(const FilePath& src_file_path,
811 const FilePath& dest_file_path,
812 const FileOperationCallback& callback) {
813 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
814 BrowserThread::CurrentlyOn(BrowserThread::IO));
815 DCHECK(!callback.is_null());
816
817 RunTaskOnUIThread(base::Bind(&GDataFileSystem::CopyOnUIThread,
818 ui_weak_ptr_,
819 src_file_path,
820 dest_file_path,
821 CreateRelayCallback(callback)));
822 }
823
824 void GDataFileSystem::CopyOnUIThread(const FilePath& src_file_path,
825 const FilePath& dest_file_path,
826 const FileOperationCallback& callback) {
827 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
828 DCHECK(!callback.is_null());
829
830 resource_metadata_->GetEntryInfoPairByPaths(
831 src_file_path,
832 dest_file_path.DirName(),
833 base::Bind(&GDataFileSystem::CopyOnUIThreadAfterGetEntryInfoPair,
834 ui_weak_ptr_,
835 dest_file_path,
836 callback));
837 }
838
839 void GDataFileSystem::CopyOnUIThreadAfterGetEntryInfoPair(
840 const FilePath& dest_file_path,
841 const FileOperationCallback& callback,
842 scoped_ptr<EntryInfoPairResult> result) {
843 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
844 DCHECK(!callback.is_null());
845 DCHECK(result.get());
846
847 if (result->first.error != DRIVE_FILE_OK) {
848 callback.Run(result->first.error);
849 return;
850 } else if (result->second.error != DRIVE_FILE_OK) {
851 callback.Run(result->second.error);
852 return;
853 }
854
855 scoped_ptr<DriveEntryProto> src_file_proto = result->first.proto.Pass();
856 scoped_ptr<DriveEntryProto> dest_parent_proto = result->second.proto.Pass();
857
858 if (!dest_parent_proto->file_info().is_directory()) {
859 callback.Run(DRIVE_FILE_ERROR_NOT_A_DIRECTORY);
860 return;
861 } else if (src_file_proto->file_info().is_directory()) {
862 // TODO(kochi): Implement copy for directories. In the interim,
863 // we handle recursive directory copy in the file manager.
864 // crbug.com/141596
865 callback.Run(DRIVE_FILE_ERROR_INVALID_OPERATION);
866 return;
867 }
868
869 if (src_file_proto->file_specific_info().is_hosted_document()) {
870 CopyDocumentToDirectory(dest_file_path.DirName(),
871 src_file_proto->resource_id(),
872 // Drop the document extension, which should not be
873 // in the document title.
874 dest_file_path.BaseName().RemoveExtension().value(),
875 callback);
876 return;
877 }
878
879 // TODO(kochi): Reimplement this once the server API supports
880 // copying of regular files directly on the server side. crbug.com/138273
881 const FilePath& src_file_path = result->first.path;
882 GetFileByPath(src_file_path,
883 base::Bind(&GDataFileSystem::OnGetFileCompleteForCopy,
884 ui_weak_ptr_,
885 dest_file_path,
886 callback),
887 GetContentCallback());
888 }
889
890 void GDataFileSystem::OnGetFileCompleteForCopy(
891 const FilePath& remote_dest_file_path,
892 const FileOperationCallback& callback,
893 DriveFileError error,
894 const FilePath& local_file_path,
895 const std::string& unused_mime_type,
896 DriveFileType file_type) {
897 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
898 DCHECK(!callback.is_null());
899
900 if (error != DRIVE_FILE_OK) {
901 callback.Run(error);
902 return;
903 }
904
905 // This callback is only triggered for a regular file via Copy().
906 DCHECK_EQ(REGULAR_FILE, file_type);
907 TransferRegularFile(local_file_path, remote_dest_file_path, callback);
908 }
909
910 void GDataFileSystem::OnGetFileCompleteForTransferFile(
911 const FilePath& local_dest_file_path,
912 const FileOperationCallback& callback,
913 DriveFileError error,
914 const FilePath& local_file_path,
915 const std::string& unused_mime_type,
916 DriveFileType file_type) {
917 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
918 DCHECK(!callback.is_null());
919
920 if (error != DRIVE_FILE_OK) {
921 callback.Run(error);
922 return;
923 }
924
925 // GetFileByPath downloads the file from gdata to a local cache, which is then
926 // copied to the actual destination path on the local file system using
927 // CopyLocalFileOnBlockingPool.
928 DriveFileError* copy_file_error =
929 new DriveFileError(DRIVE_FILE_OK);
930 util::PostBlockingPoolSequencedTaskAndReply(
931 FROM_HERE,
932 blocking_task_runner_,
933 base::Bind(&CopyLocalFileOnBlockingPool,
934 local_file_path,
935 local_dest_file_path,
936 copy_file_error),
937 base::Bind(&RunFileOperationCallbackHelper,
938 callback,
939 base::Owned(copy_file_error)));
940 }
941
942 void GDataFileSystem::CopyDocumentToDirectory(
943 const FilePath& dir_path,
944 const std::string& resource_id,
945 const FilePath::StringType& new_name,
946 const FileOperationCallback& callback) {
947 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
948 DCHECK(!callback.is_null());
949
950 drive_service_->CopyDocument(resource_id, new_name,
951 base::Bind(&GDataFileSystem::OnCopyDocumentCompleted,
952 ui_weak_ptr_,
953 dir_path,
954 callback));
955 }
956
957 void GDataFileSystem::Rename(const FilePath& file_path,
958 const FilePath::StringType& new_name,
959 const FileMoveCallback& callback) {
960 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
961 DCHECK(!callback.is_null());
962
963 // It is a no-op if the file is renamed to the same name.
964 if (file_path.BaseName().value() == new_name) {
965 callback.Run(DRIVE_FILE_OK, file_path);
966 return;
967 }
968
969 // Get the edit URL of an entry at |file_path|.
970 resource_metadata_->GetEntryInfoByPath(
971 file_path,
972 base::Bind(
973 &GDataFileSystem::RenameAfterGetEntryInfo,
974 ui_weak_ptr_,
975 file_path,
976 new_name,
977 callback));
978 }
979
980 void GDataFileSystem::RenameAfterGetEntryInfo(
981 const FilePath& file_path,
982 const FilePath::StringType& new_name,
983 const FileMoveCallback& callback,
984 DriveFileError error,
985 scoped_ptr<DriveEntryProto> entry_proto) {
986 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
987
988 if (error != DRIVE_FILE_OK) {
989 if (!callback.is_null())
990 callback.Run(error, file_path);
991 return;
992 }
993 DCHECK(entry_proto.get());
994
995 // Drop the .g<something> extension from |new_name| if the file being
996 // renamed is a hosted document and |new_name| has the same .g<something>
997 // extension as the file.
998 FilePath::StringType file_name = new_name;
999 if (entry_proto->has_file_specific_info() &&
1000 entry_proto->file_specific_info().is_hosted_document()) {
1001 FilePath new_file(file_name);
1002 if (new_file.Extension() ==
1003 entry_proto->file_specific_info().document_extension()) {
1004 file_name = new_file.RemoveExtension().value();
1005 }
1006 }
1007
1008 drive_service_->RenameResource(
1009 GURL(entry_proto->edit_url()),
1010 file_name,
1011 base::Bind(&GDataFileSystem::RenameEntryLocally,
1012 ui_weak_ptr_,
1013 file_path,
1014 file_name,
1015 callback));
1016 }
1017
1018 void GDataFileSystem::Move(const FilePath& src_file_path,
1019 const FilePath& dest_file_path,
1020 const FileOperationCallback& callback) {
1021 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
1022 BrowserThread::CurrentlyOn(BrowserThread::IO));
1023 DCHECK(!callback.is_null());
1024
1025 RunTaskOnUIThread(base::Bind(&GDataFileSystem::MoveOnUIThread,
1026 ui_weak_ptr_,
1027 src_file_path,
1028 dest_file_path,
1029 CreateRelayCallback(callback)));
1030 }
1031
1032 void GDataFileSystem::MoveOnUIThread(const FilePath& src_file_path,
1033 const FilePath& dest_file_path,
1034 const FileOperationCallback& callback) {
1035 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1036 DCHECK(!callback.is_null());
1037
1038 resource_metadata_->GetEntryInfoPairByPaths(
1039 src_file_path,
1040 dest_file_path.DirName(),
1041 base::Bind(&GDataFileSystem::MoveOnUIThreadAfterGetEntryInfoPair,
1042 ui_weak_ptr_,
1043 dest_file_path,
1044 callback));
1045 }
1046
1047 void GDataFileSystem::MoveOnUIThreadAfterGetEntryInfoPair(
1048 const FilePath& dest_file_path,
1049 const FileOperationCallback& callback,
1050 scoped_ptr<EntryInfoPairResult> result) {
1051 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1052 DCHECK(!callback.is_null());
1053 DCHECK(result.get());
1054
1055 if (result->first.error != DRIVE_FILE_OK) {
1056 callback.Run(result->first.error);
1057 return;
1058 } else if (result->second.error != DRIVE_FILE_OK) {
1059 callback.Run(result->second.error);
1060 return;
1061 }
1062
1063 scoped_ptr<DriveEntryProto> dest_parent_proto = result->second.proto.Pass();
1064 if (!dest_parent_proto->file_info().is_directory()) {
1065 callback.Run(DRIVE_FILE_ERROR_NOT_A_DIRECTORY);
1066 return;
1067 }
1068
1069 // If the file/directory is moved to the same directory, just rename it.
1070 const FilePath& src_file_path = result->first.path;
1071 const FilePath& dest_parent_path = result->second.path;
1072 if (src_file_path.DirName() == dest_parent_path) {
1073 FileMoveCallback final_file_path_update_callback =
1074 base::Bind(&GDataFileSystem::OnFilePathUpdated,
1075 ui_weak_ptr_,
1076 callback);
1077
1078 Rename(src_file_path, dest_file_path.BaseName().value(),
1079 final_file_path_update_callback);
1080 return;
1081 }
1082
1083 // Otherwise, the move operation involves three steps:
1084 // 1. Renames the file at |src_file_path| to basename(|dest_file_path|)
1085 // within the same directory. The rename operation is a no-op if
1086 // basename(|src_file_path|) equals to basename(|dest_file_path|).
1087 // 2. Removes the file from its parent directory (the file is not deleted),
1088 // which effectively moves the file to the root directory.
1089 // 3. Adds the file to the parent directory of |dest_file_path|, which
1090 // effectively moves the file from the root directory to the parent
1091 // directory of |dest_file_path|.
1092 const FileMoveCallback add_file_to_directory_callback =
1093 base::Bind(&GDataFileSystem::MoveEntryFromRootDirectory,
1094 ui_weak_ptr_,
1095 dest_file_path.DirName(),
1096 callback);
1097
1098 const FileMoveCallback remove_file_from_directory_callback =
1099 base::Bind(&GDataFileSystem::RemoveEntryFromNonRootDirectory,
1100 ui_weak_ptr_,
1101 add_file_to_directory_callback);
1102
1103 Rename(src_file_path, dest_file_path.BaseName().value(),
1104 remove_file_from_directory_callback);
1105 }
1106
1107 void GDataFileSystem::MoveEntryFromRootDirectory(
1108 const FilePath& dir_path,
1109 const FileOperationCallback& callback,
1110 DriveFileError error,
1111 const FilePath& file_path) {
1112 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1113 DCHECK(!callback.is_null());
1114 DCHECK_EQ(kDriveRootDirectory, file_path.DirName().value());
1115
1116 // Return if there is an error or |dir_path| is the root directory.
1117 if (error != DRIVE_FILE_OK || dir_path == FilePath(kDriveRootDirectory)) {
1118 callback.Run(error);
1119 return;
1120 }
1121
1122 resource_metadata_->GetEntryInfoPairByPaths(
1123 file_path,
1124 dir_path,
1125 base::Bind(
1126 &GDataFileSystem::MoveEntryFromRootDirectoryAfterGetEntryInfoPair,
1127 ui_weak_ptr_,
1128 callback));
1129 }
1130
1131 void GDataFileSystem::MoveEntryFromRootDirectoryAfterGetEntryInfoPair(
1132 const FileOperationCallback& callback,
1133 scoped_ptr<EntryInfoPairResult> result) {
1134 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1135 DCHECK(!callback.is_null());
1136 DCHECK(result.get());
1137
1138 if (result->first.error != DRIVE_FILE_OK) {
1139 callback.Run(result->first.error);
1140 return;
1141 } else if (result->second.error != DRIVE_FILE_OK) {
1142 callback.Run(result->second.error);
1143 return;
1144 }
1145
1146 scoped_ptr<DriveEntryProto> src_proto = result->first.proto.Pass();
1147 scoped_ptr<DriveEntryProto> dir_proto = result->second.proto.Pass();
1148
1149 if (!dir_proto->file_info().is_directory()) {
1150 callback.Run(DRIVE_FILE_ERROR_NOT_A_DIRECTORY);
1151 return;
1152 }
1153
1154 const FilePath& file_path = result->first.path;
1155 const FilePath& dir_path = result->second.path;
1156 drive_service_->AddResourceToDirectory(
1157 GURL(dir_proto->content_url()),
1158 GURL(src_proto->edit_url()),
1159 base::Bind(&GDataFileSystem::OnMoveEntryFromRootDirectoryCompleted,
1160 ui_weak_ptr_,
1161 callback,
1162 file_path,
1163 dir_path));
1164 }
1165
1166 void GDataFileSystem::RemoveEntryFromNonRootDirectory(
1167 const FileMoveCallback& callback,
1168 DriveFileError error,
1169 const FilePath& file_path) {
1170 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1171 DCHECK(!callback.is_null());
1172
1173 const FilePath dir_path = file_path.DirName();
1174 // Return if there is an error or |dir_path| is the root directory.
1175 if (error != DRIVE_FILE_OK || dir_path == FilePath(kDriveRootDirectory)) {
1176 callback.Run(error, file_path);
1177 return;
1178 }
1179
1180 resource_metadata_->GetEntryInfoPairByPaths(
1181 file_path,
1182 dir_path,
1183 base::Bind(
1184 &GDataFileSystem::RemoveEntryFromNonRootDirectoryAfterEntryInfoPair,
1185 ui_weak_ptr_,
1186 callback));
1187 }
1188
1189 void GDataFileSystem::RemoveEntryFromNonRootDirectoryAfterEntryInfoPair(
1190 const FileMoveCallback& callback,
1191 scoped_ptr<EntryInfoPairResult> result) {
1192 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1193 DCHECK(!callback.is_null());
1194 DCHECK(result.get());
1195
1196 const FilePath& file_path = result->first.path;
1197 const FilePath& dir_path = result->second.path;
1198 if (result->first.error != DRIVE_FILE_OK) {
1199 callback.Run(result->first.error, file_path);
1200 return;
1201 } else if (result->second.error != DRIVE_FILE_OK) {
1202 callback.Run(result->second.error, file_path);
1203 return;
1204 }
1205
1206 scoped_ptr<DriveEntryProto> entry_proto = result->first.proto.Pass();
1207 scoped_ptr<DriveEntryProto> dir_proto = result->second.proto.Pass();
1208
1209 if (!dir_proto->file_info().is_directory()) {
1210 callback.Run(DRIVE_FILE_ERROR_NOT_A_DIRECTORY, file_path);
1211 return;
1212 }
1213
1214 drive_service_->RemoveResourceFromDirectory(
1215 GURL(dir_proto->content_url()),
1216 GURL(entry_proto->edit_url()),
1217 entry_proto->resource_id(),
1218 base::Bind(&GDataFileSystem::MoveEntryToRootDirectoryLocally,
1219 ui_weak_ptr_,
1220 callback,
1221 file_path,
1222 dir_path));
1223 }
1224
1225 void GDataFileSystem::Remove(const FilePath& file_path,
1226 bool is_recursive,
1227 const FileOperationCallback& callback) {
1228 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
1229 BrowserThread::CurrentlyOn(BrowserThread::IO));
1230 RunTaskOnUIThread(base::Bind(&GDataFileSystem::RemoveOnUIThread,
1231 ui_weak_ptr_,
1232 file_path,
1233 is_recursive,
1234 CreateRelayCallback(callback)));
1235 }
1236
1237 void GDataFileSystem::RemoveOnUIThread(
1238 const FilePath& file_path,
1239 bool is_recursive,
1240 const FileOperationCallback& callback) {
1241 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1242
1243 // Get the edit URL of an entry at |file_path|.
1244 resource_metadata_->GetEntryInfoByPath(
1245 file_path,
1246 base::Bind(
1247 &GDataFileSystem::RemoveOnUIThreadAfterGetEntryInfo,
1248 ui_weak_ptr_,
1249 file_path,
1250 is_recursive,
1251 callback));
1252 }
1253
1254 void GDataFileSystem::RemoveOnUIThreadAfterGetEntryInfo(
1255 const FilePath& file_path,
1256 bool /* is_recursive */,
1257 const FileOperationCallback& callback,
1258 DriveFileError error,
1259 scoped_ptr<DriveEntryProto> entry_proto) {
1260 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1261
1262 if (error != DRIVE_FILE_OK) {
1263 if (!callback.is_null()) {
1264 base::MessageLoopProxy::current()->PostTask(
1265 FROM_HERE, base::Bind(callback, error));
1266 }
1267 return;
1268 }
1269
1270 DCHECK(entry_proto.get());
1271 drive_service_->DeleteDocument(
1272 GURL(entry_proto->edit_url()),
1273 base::Bind(&GDataFileSystem::OnRemovedDocument,
1274 ui_weak_ptr_,
1275 callback,
1276 file_path));
1277 }
1278
1279 void GDataFileSystem::CreateDirectory(
1280 const FilePath& directory_path,
1281 bool is_exclusive,
1282 bool is_recursive,
1283 const FileOperationCallback& callback) {
1284 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
1285 BrowserThread::CurrentlyOn(BrowserThread::IO));
1286 RunTaskOnUIThread(base::Bind(&GDataFileSystem::CreateDirectoryOnUIThread,
1287 ui_weak_ptr_,
1288 directory_path,
1289 is_exclusive,
1290 is_recursive,
1291 CreateRelayCallback(callback)));
1292 }
1293
1294 void GDataFileSystem::CreateDirectoryOnUIThread(
1295 const FilePath& directory_path,
1296 bool is_exclusive,
1297 bool is_recursive,
1298 const FileOperationCallback& callback) {
1299 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1300
1301 FilePath last_parent_dir_path;
1302 FilePath first_missing_path;
1303 GURL last_parent_dir_url;
1304 FindMissingDirectoryResult result =
1305 FindFirstMissingParentDirectory(directory_path,
1306 &last_parent_dir_url,
1307 &first_missing_path);
1308 switch (result) {
1309 case FOUND_INVALID: {
1310 if (!callback.is_null()) {
1311 MessageLoop::current()->PostTask(FROM_HERE,
1312 base::Bind(callback, DRIVE_FILE_ERROR_NOT_FOUND));
1313 }
1314
1315 return;
1316 }
1317 case DIRECTORY_ALREADY_PRESENT: {
1318 if (!callback.is_null()) {
1319 MessageLoop::current()->PostTask(FROM_HERE,
1320 base::Bind(callback,
1321 is_exclusive ? DRIVE_FILE_ERROR_EXISTS :
1322 DRIVE_FILE_OK));
1323 }
1324
1325 return;
1326 }
1327 case FOUND_MISSING: {
1328 // There is a missing folder to be created here, move on with the rest of
1329 // this function.
1330 break;
1331 }
1332 default: {
1333 NOTREACHED();
1334 break;
1335 }
1336 }
1337
1338 // Do we have a parent directory here as well? We can't then create target
1339 // directory if this is not a recursive operation.
1340 if (directory_path != first_missing_path && !is_recursive) {
1341 if (!callback.is_null()) {
1342 MessageLoop::current()->PostTask(FROM_HERE,
1343 base::Bind(callback, DRIVE_FILE_ERROR_NOT_FOUND));
1344 }
1345 return;
1346 }
1347
1348 drive_service_->CreateDirectory(
1349 last_parent_dir_url,
1350 first_missing_path.BaseName().value(),
1351 base::Bind(&GDataFileSystem::OnCreateDirectoryCompleted,
1352 ui_weak_ptr_,
1353 CreateDirectoryParams(
1354 first_missing_path,
1355 directory_path,
1356 is_exclusive,
1357 is_recursive,
1358 callback)));
1359 }
1360
1361 void GDataFileSystem::CreateFile(const FilePath& file_path,
1362 bool is_exclusive,
1363 const FileOperationCallback& callback) {
1364 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
1365 BrowserThread::CurrentlyOn(BrowserThread::IO));
1366 DCHECK(!callback.is_null());
1367
1368 RunTaskOnUIThread(base::Bind(&GDataFileSystem::CreateFileOnUIThread,
1369 ui_weak_ptr_,
1370 file_path,
1371 is_exclusive,
1372 CreateRelayCallback(callback)));
1373 }
1374
1375 void GDataFileSystem::CreateFileOnUIThread(
1376 const FilePath& file_path,
1377 bool is_exclusive,
1378 const FileOperationCallback& callback) {
1379 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1380 DCHECK(!callback.is_null());
1381
1382 // First, checks the existence of a file at |file_path|.
1383 resource_metadata_->GetEntryInfoByPath(
1384 file_path,
1385 base::Bind(&GDataFileSystem::OnGetEntryInfoForCreateFile,
1386 ui_weak_ptr_,
1387 file_path,
1388 is_exclusive,
1389 callback));
1390 }
1391
1392 void GDataFileSystem::OnGetEntryInfoForCreateFile(
1393 const FilePath& file_path,
1394 bool is_exclusive,
1395 const FileOperationCallback& callback,
1396 DriveFileError result,
1397 scoped_ptr<DriveEntryProto> entry_proto) {
1398 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1399 DCHECK(!callback.is_null());
1400
1401 // The |file_path| is invalid. It is an error.
1402 if (result != DRIVE_FILE_ERROR_NOT_FOUND &&
1403 result != DRIVE_FILE_OK) {
1404 callback.Run(result);
1405 return;
1406 }
1407
1408 // An entry already exists at |file_path|.
1409 if (result == DRIVE_FILE_OK) {
1410 DCHECK(entry_proto.get());
1411 // If an exclusive mode is requested, or the entry is not a regular file,
1412 // it is an error.
1413 if (is_exclusive ||
1414 entry_proto->file_info().is_directory() ||
1415 entry_proto->file_specific_info().is_hosted_document()) {
1416 callback.Run(DRIVE_FILE_ERROR_EXISTS);
1417 return;
1418 }
1419
1420 // Otherwise nothing more to do. Succeeded.
1421 callback.Run(DRIVE_FILE_OK);
1422 return;
1423 }
1424
1425 // No entry found at |file_path|. Let's create a brand new file.
1426 // For now, it is implemented by uploading an empty file (/dev/null).
1427 // TODO(kinaba): http://crbug.com/135143. Implement in a nicer way.
1428 TransferRegularFile(FilePath(kEmptyFilePath), file_path, callback);
1429 }
1430
1431 void GDataFileSystem::GetFileByPath(
1432 const FilePath& file_path,
1433 const GetFileCallback& get_file_callback,
1434 const GetContentCallback& get_content_callback) {
1435 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
1436 BrowserThread::CurrentlyOn(BrowserThread::IO));
1437 RunTaskOnUIThread(
1438 base::Bind(&GDataFileSystem::GetFileByPathOnUIThread,
1439 ui_weak_ptr_,
1440 file_path,
1441 CreateRelayCallback(get_file_callback),
1442 CreateRelayCallback(get_content_callback)));
1443 }
1444
1445 void GDataFileSystem::GetFileByPathOnUIThread(
1446 const FilePath& file_path,
1447 const GetFileCallback& get_file_callback,
1448 const GetContentCallback& get_content_callback) {
1449 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1450
1451 resource_metadata_->GetEntryInfoByPath(
1452 file_path,
1453 base::Bind(&GDataFileSystem::OnGetEntryInfoCompleteForGetFileByPath,
1454 ui_weak_ptr_,
1455 file_path,
1456 CreateRelayCallback(get_file_callback),
1457 CreateRelayCallback(get_content_callback)));
1458 }
1459
1460 void GDataFileSystem::OnGetEntryInfoCompleteForGetFileByPath(
1461 const FilePath& file_path,
1462 const GetFileCallback& get_file_callback,
1463 const GetContentCallback& get_content_callback,
1464 DriveFileError error,
1465 scoped_ptr<DriveEntryProto> entry_proto) {
1466 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1467
1468 // If |error| == PLATFORM_FILE_OK then |entry_proto| must be valid.
1469 DCHECK(error != DRIVE_FILE_OK ||
1470 (entry_proto.get() && !entry_proto->resource_id().empty()));
1471 GetResolvedFileByPath(file_path,
1472 get_file_callback,
1473 get_content_callback,
1474 error,
1475 entry_proto.get());
1476 }
1477
1478 void GDataFileSystem::GetResolvedFileByPath(
1479 const FilePath& file_path,
1480 const GetFileCallback& get_file_callback,
1481 const GetContentCallback& get_content_callback,
1482 DriveFileError error,
1483 const DriveEntryProto* entry_proto) {
1484 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1485
1486 if (entry_proto && !entry_proto->has_file_specific_info())
1487 error = DRIVE_FILE_ERROR_NOT_FOUND;
1488
1489 if (error != DRIVE_FILE_OK) {
1490 if (!get_file_callback.is_null()) {
1491 MessageLoop::current()->PostTask(
1492 FROM_HERE,
1493 base::Bind(get_file_callback,
1494 DRIVE_FILE_ERROR_NOT_FOUND,
1495 FilePath(),
1496 std::string(),
1497 REGULAR_FILE));
1498 }
1499 return;
1500 }
1501
1502 // For a hosted document, we create a special JSON file to represent the
1503 // document instead of fetching the document content in one of the exported
1504 // formats. The JSON file contains the edit URL and resource ID of the
1505 // document.
1506 if (entry_proto->file_specific_info().is_hosted_document()) {
1507 DriveFileError* error =
1508 new DriveFileError(DRIVE_FILE_OK);
1509 FilePath* temp_file_path = new FilePath;
1510 std::string* mime_type = new std::string;
1511 DriveFileType* file_type = new DriveFileType(REGULAR_FILE);
1512 util::PostBlockingPoolSequencedTaskAndReply(
1513 FROM_HERE,
1514 blocking_task_runner_,
1515 base::Bind(&CreateDocumentJsonFileOnBlockingPool,
1516 cache_->GetCacheDirectoryPath(
1517 DriveCache::CACHE_TYPE_TMP_DOCUMENTS),
1518 GURL(entry_proto->file_specific_info().alternate_url()),
1519 entry_proto->resource_id(),
1520 error,
1521 temp_file_path,
1522 mime_type,
1523 file_type),
1524 base::Bind(&RunGetFileCallbackHelper,
1525 get_file_callback,
1526 base::Owned(error),
1527 base::Owned(temp_file_path),
1528 base::Owned(mime_type),
1529 base::Owned(file_type)));
1530 return;
1531 }
1532
1533 // Returns absolute path of the file if it were cached or to be cached.
1534 FilePath local_tmp_path = cache_->GetCacheFilePath(
1535 entry_proto->resource_id(),
1536 entry_proto->file_specific_info().file_md5(),
1537 DriveCache::CACHE_TYPE_TMP,
1538 DriveCache::CACHED_FILE_FROM_SERVER);
1539 cache_->GetFileOnUIThread(
1540 entry_proto->resource_id(),
1541 entry_proto->file_specific_info().file_md5(),
1542 base::Bind(
1543 &GDataFileSystem::OnGetFileFromCache,
1544 ui_weak_ptr_,
1545 GetFileFromCacheParams(
1546 file_path,
1547 local_tmp_path,
1548 GURL(entry_proto->content_url()),
1549 entry_proto->resource_id(),
1550 entry_proto->file_specific_info().file_md5(),
1551 entry_proto->file_specific_info().content_mime_type(),
1552 get_file_callback,
1553 get_content_callback)));
1554 }
1555
1556 void GDataFileSystem::GetFileByResourceId(
1557 const std::string& resource_id,
1558 const GetFileCallback& get_file_callback,
1559 const GetContentCallback& get_content_callback) {
1560 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
1561 BrowserThread::CurrentlyOn(BrowserThread::IO));
1562 RunTaskOnUIThread(
1563 base::Bind(&GDataFileSystem::GetFileByResourceIdOnUIThread,
1564 ui_weak_ptr_,
1565 resource_id,
1566 CreateRelayCallback(get_file_callback),
1567 CreateRelayCallback(get_content_callback)));
1568 }
1569
1570 void GDataFileSystem::GetFileByResourceIdOnUIThread(
1571 const std::string& resource_id,
1572 const GetFileCallback& get_file_callback,
1573 const GetContentCallback& get_content_callback) {
1574 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1575
1576 resource_metadata_->GetEntryByResourceIdAsync(resource_id,
1577 base::Bind(&GDataFileSystem::GetFileByEntryOnUIThread,
1578 ui_weak_ptr_,
1579 get_file_callback,
1580 get_content_callback));
1581 }
1582
1583 void GDataFileSystem::GetFileByEntryOnUIThread(
1584 const GetFileCallback& get_file_callback,
1585 const GetContentCallback& get_content_callback,
1586 DriveEntry* entry) {
1587 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1588
1589 FilePath file_path;
1590 if (entry) {
1591 DriveFile* file = entry->AsDriveFile();
1592 if (file)
1593 file_path = file->GetFilePath();
1594 }
1595
1596 // Report an error immediately if the file for the resource ID is not
1597 // found.
1598 if (file_path.empty()) {
1599 if (!get_file_callback.is_null()) {
1600 base::MessageLoopProxy::current()->PostTask(
1601 FROM_HERE,
1602 base::Bind(get_file_callback,
1603 DRIVE_FILE_ERROR_NOT_FOUND,
1604 FilePath(),
1605 std::string(),
1606 REGULAR_FILE));
1607 }
1608 return;
1609 }
1610
1611 GetFileByPath(file_path, get_file_callback, get_content_callback);
1612 }
1613
1614 void GDataFileSystem::OnGetFileFromCache(const GetFileFromCacheParams& params,
1615 DriveFileError error,
1616 const std::string& resource_id,
1617 const std::string& md5,
1618 const FilePath& cache_file_path) {
1619 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1620
1621 // Have we found the file in cache? If so, return it back to the caller.
1622 if (error == DRIVE_FILE_OK) {
1623 if (!params.get_file_callback.is_null()) {
1624 params.get_file_callback.Run(error,
1625 cache_file_path,
1626 params.mime_type,
1627 REGULAR_FILE);
1628 }
1629 return;
1630 }
1631
1632 // If cache file is not found, try to download the file from the server
1633 // instead. This logic is rather complicated but here's how this works:
1634 //
1635 // Retrieve fresh file metadata from server. We will extract file size and
1636 // content url from there (we want to make sure used content url is not
1637 // stale).
1638 //
1639 // Check if we have enough space, based on the expected file size.
1640 // - if we don't have enough space, try to free up the disk space
1641 // - if we still don't have enough space, return "no space" error
1642 // - if we have enough space, start downloading the file from the server
1643 drive_service_->GetDocumentEntry(
1644 resource_id,
1645 base::Bind(&GDataFileSystem::OnGetDocumentEntry,
1646 ui_weak_ptr_,
1647 cache_file_path,
1648 GetFileFromCacheParams(params.virtual_file_path,
1649 params.local_tmp_path,
1650 params.content_url,
1651 params.resource_id,
1652 params.md5,
1653 params.mime_type,
1654 params.get_file_callback,
1655 params.get_content_callback)));
1656 }
1657
1658 void GDataFileSystem::OnGetDocumentEntry(const FilePath& cache_file_path,
1659 const GetFileFromCacheParams& params,
1660 GDataErrorCode status,
1661 scoped_ptr<base::Value> data) {
1662 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1663
1664 DriveFileError error = util::GDataToDriveFileError(status);
1665
1666 scoped_ptr<DriveEntry> fresh_entry;
1667 if (error == DRIVE_FILE_OK) {
1668 scoped_ptr<DocumentEntry> doc_entry(DocumentEntry::ExtractAndParse(*data));
1669 if (doc_entry.get())
1670 fresh_entry.reset(resource_metadata_->FromDocumentEntry(*doc_entry));
1671 if (!fresh_entry.get() || !fresh_entry->AsDriveFile()) {
1672 LOG(ERROR) << "Got invalid entry from server for " << params.resource_id;
1673 error = DRIVE_FILE_ERROR_FAILED;
1674 }
1675 }
1676
1677 if (error != DRIVE_FILE_OK) {
1678 if (!params.get_file_callback.is_null()) {
1679 params.get_file_callback.Run(error,
1680 cache_file_path,
1681 params.mime_type,
1682 REGULAR_FILE);
1683 }
1684 return;
1685 }
1686
1687 GURL content_url = fresh_entry->content_url();
1688 int64 file_size = fresh_entry->file_info().size;
1689
1690 DCHECK_EQ(params.resource_id, fresh_entry->resource_id());
1691 scoped_ptr<DriveFile> fresh_entry_as_file(
1692 fresh_entry.release()->AsDriveFile());
1693 resource_metadata_->RefreshFile(fresh_entry_as_file.Pass());
1694
1695 bool* has_enough_space = new bool(false);
1696 util::PostBlockingPoolSequencedTaskAndReply(
1697 FROM_HERE,
1698 blocking_task_runner_,
1699 base::Bind(&DriveCache::FreeDiskSpaceIfNeededFor,
1700 base::Unretained(cache_),
1701 file_size,
1702 has_enough_space),
1703 base::Bind(&GDataFileSystem::StartDownloadFileIfEnoughSpace,
1704 ui_weak_ptr_,
1705 params,
1706 content_url,
1707 cache_file_path,
1708 base::Owned(has_enough_space)));
1709 }
1710
1711 void GDataFileSystem::StartDownloadFileIfEnoughSpace(
1712 const GetFileFromCacheParams& params,
1713 const GURL& content_url,
1714 const FilePath& cache_file_path,
1715 bool* has_enough_space) {
1716 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1717
1718 if (!*has_enough_space) {
1719 // If no enough space, return PLATFORM_FILE_ERROR_NO_SPACE.
1720 if (!params.get_file_callback.is_null()) {
1721 params.get_file_callback.Run(DRIVE_FILE_ERROR_NO_SPACE,
1722 cache_file_path,
1723 params.mime_type,
1724 REGULAR_FILE);
1725 }
1726 return;
1727 }
1728
1729 // We have enough disk space. Start downloading the file.
1730 drive_service_->DownloadFile(
1731 params.virtual_file_path,
1732 params.local_tmp_path,
1733 content_url,
1734 base::Bind(&GDataFileSystem::OnFileDownloaded,
1735 ui_weak_ptr_,
1736 params),
1737 params.get_content_callback);
1738 }
1739
1740 void GDataFileSystem::GetEntryInfoByPath(const FilePath& file_path,
1741 const GetEntryInfoCallback& callback) {
1742 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
1743 BrowserThread::CurrentlyOn(BrowserThread::IO));
1744 DCHECK(!callback.is_null());
1745
1746 RunTaskOnUIThread(
1747 base::Bind(&GDataFileSystem::GetEntryInfoByPathOnUIThread,
1748 ui_weak_ptr_,
1749 file_path,
1750 CreateRelayCallback(callback)));
1751 }
1752
1753 void GDataFileSystem::GetEntryInfoByPathOnUIThread(
1754 const FilePath& file_path,
1755 const GetEntryInfoCallback& callback) {
1756 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1757 DCHECK(!callback.is_null());
1758
1759 LoadFeedIfNeeded(
1760 base::Bind(&GDataFileSystem::GetEntryInfoByPathOnUIThreadAfterLoad,
1761 ui_weak_ptr_,
1762 file_path,
1763 callback));
1764 }
1765
1766 void GDataFileSystem::GetEntryInfoByPathOnUIThreadAfterLoad(
1767 const FilePath& file_path,
1768 const GetEntryInfoCallback& callback,
1769 DriveFileError error) {
1770 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1771 DCHECK(!callback.is_null());
1772
1773 if (error != DRIVE_FILE_OK) {
1774 callback.Run(error, scoped_ptr<DriveEntryProto>());
1775 return;
1776 }
1777
1778 resource_metadata_->GetEntryInfoByPath(
1779 file_path,
1780 base::Bind(&GDataFileSystem::GetEntryInfoByPathOnUIThreadAfterGetEntry,
1781 ui_weak_ptr_,
1782 callback));
1783 }
1784
1785 void GDataFileSystem::GetEntryInfoByPathOnUIThreadAfterGetEntry(
1786 const GetEntryInfoCallback& callback,
1787 DriveFileError error,
1788 scoped_ptr<DriveEntryProto> entry_proto) {
1789 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1790 DCHECK(!callback.is_null());
1791
1792 if (error != DRIVE_FILE_OK) {
1793 callback.Run(error, scoped_ptr<DriveEntryProto>());
1794 return;
1795 }
1796 DCHECK(entry_proto.get());
1797
1798 CheckLocalModificationAndRun(entry_proto.Pass(), callback);
1799 }
1800
1801 void GDataFileSystem::ReadDirectoryByPath(
1802 const FilePath& file_path,
1803 const ReadDirectoryWithSettingCallback& callback) {
1804 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
1805 BrowserThread::CurrentlyOn(BrowserThread::IO));
1806 DCHECK(!callback.is_null());
1807
1808 RunTaskOnUIThread(
1809 base::Bind(&GDataFileSystem::ReadDirectoryByPathOnUIThread,
1810 ui_weak_ptr_,
1811 file_path,
1812 CreateRelayCallback(callback)));
1813 }
1814
1815 void GDataFileSystem::ReadDirectoryByPathOnUIThread(
1816 const FilePath& file_path,
1817 const ReadDirectoryWithSettingCallback& callback) {
1818 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1819 DCHECK(!callback.is_null());
1820
1821 LoadFeedIfNeeded(
1822 base::Bind(&GDataFileSystem::ReadDirectoryByPathOnUIThreadAfterLoad,
1823 ui_weak_ptr_,
1824 file_path,
1825 callback));
1826 }
1827
1828 void GDataFileSystem::ReadDirectoryByPathOnUIThreadAfterLoad(
1829 const FilePath& file_path,
1830 const ReadDirectoryWithSettingCallback& callback,
1831 DriveFileError error) {
1832 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1833 DCHECK(!callback.is_null());
1834
1835 if (error != DRIVE_FILE_OK) {
1836 callback.Run(error,
1837 hide_hosted_docs_,
1838 scoped_ptr<DriveEntryProtoVector>());
1839 return;
1840 }
1841
1842 resource_metadata_->ReadDirectoryByPath(
1843 file_path,
1844 base::Bind(&GDataFileSystem::ReadDirectoryByPathOnUIThreadAfterRead,
1845 ui_weak_ptr_,
1846 callback));
1847 }
1848
1849 void GDataFileSystem::ReadDirectoryByPathOnUIThreadAfterRead(
1850 const ReadDirectoryWithSettingCallback& callback,
1851 DriveFileError error,
1852 scoped_ptr<DriveEntryProtoVector> entries) {
1853 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1854 DCHECK(!callback.is_null());
1855
1856 if (error != DRIVE_FILE_OK) {
1857 callback.Run(error,
1858 hide_hosted_docs_,
1859 scoped_ptr<DriveEntryProtoVector>());
1860 return;
1861 }
1862 DCHECK(entries.get()); // This is valid for emptry directories too.
1863
1864 callback.Run(DRIVE_FILE_OK, hide_hosted_docs_, entries.Pass());
1865 }
1866
1867 void GDataFileSystem::RequestDirectoryRefresh(const FilePath& file_path) {
1868 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
1869 BrowserThread::CurrentlyOn(BrowserThread::IO));
1870 RunTaskOnUIThread(
1871 base::Bind(&GDataFileSystem::RequestDirectoryRefreshOnUIThread,
1872 ui_weak_ptr_,
1873 file_path));
1874 }
1875
1876 void GDataFileSystem::RequestDirectoryRefreshOnUIThread(
1877 const FilePath& file_path) {
1878 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1879
1880 // Make sure the destination directory exists.
1881 resource_metadata_->GetEntryInfoByPath(
1882 file_path,
1883 base::Bind(
1884 &GDataFileSystem::RequestDirectoryRefreshOnUIThreadAfterGetEntryInfo,
1885 ui_weak_ptr_,
1886 file_path));
1887 }
1888
1889 void GDataFileSystem::RequestDirectoryRefreshOnUIThreadAfterGetEntryInfo(
1890 const FilePath& file_path,
1891 DriveFileError error,
1892 scoped_ptr<DriveEntryProto> entry_proto) {
1893 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1894
1895 if (error != DRIVE_FILE_OK ||
1896 !entry_proto->file_info().is_directory()) {
1897 LOG(ERROR) << "Directory entry not found: " << file_path.value();
1898 return;
1899 }
1900
1901 feed_loader_->LoadDirectoryFromServer(
1902 resource_metadata_->origin(),
1903 entry_proto->resource_id(),
1904 base::Bind(&GDataFileSystem::OnRequestDirectoryRefresh,
1905 ui_weak_ptr_,
1906 file_path));
1907 }
1908
1909 void GDataFileSystem::OnRequestDirectoryRefresh(
1910 const FilePath& directory_path,
1911 GetDocumentsParams* params,
1912 DriveFileError error) {
1913 DCHECK(params);
1914 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1915
1916 if (error != DRIVE_FILE_OK) {
1917 LOG(ERROR) << "Failed to refresh directory: " << directory_path.value()
1918 << ": " << error;
1919 return;
1920 }
1921
1922 int64 unused_delta_feed_changestamp = 0;
1923 FeedToFileResourceMapUmaStats unused_uma_stats;
1924 FileResourceIdMap file_map;
1925 GDataWapiFeedProcessor feed_processor(resource_metadata_.get());
1926 error = feed_processor.FeedToFileResourceMap(
1927 *params->feed_list,
1928 &file_map,
1929 &unused_delta_feed_changestamp,
1930 &unused_uma_stats);
1931 if (error != DRIVE_FILE_OK) {
1932 LOG(ERROR) << "Failed to convert feed: " << directory_path.value()
1933 << ": " << error;
1934 return;
1935 }
1936
1937 resource_metadata_->RefreshDirectory(
1938 params->directory_resource_id,
1939 file_map,
1940 base::Bind(&GDataFileSystem::OnDirectoryChangeFileMoveCallback,
1941 ui_weak_ptr_));
1942 }
1943
1944 void GDataFileSystem::UpdateFileByResourceId(
1945 const std::string& resource_id,
1946 const FileOperationCallback& callback) {
1947 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
1948 BrowserThread::CurrentlyOn(BrowserThread::IO));
1949 DCHECK(!callback.is_null());
1950
1951 RunTaskOnUIThread(
1952 base::Bind(&GDataFileSystem::UpdateFileByResourceIdOnUIThread,
1953 ui_weak_ptr_,
1954 resource_id,
1955 CreateRelayCallback(callback)));
1956 }
1957
1958 void GDataFileSystem::UpdateFileByResourceIdOnUIThread(
1959 const std::string& resource_id,
1960 const FileOperationCallback& callback) {
1961 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1962 DCHECK(!callback.is_null());
1963
1964 // TODO(satorux): GetEntryInfoByResourceId() is called twice for
1965 // UpdateFileByResourceIdOnUIThread(). crbug.com/143873
1966 resource_metadata_->GetEntryInfoByResourceId(
1967 resource_id,
1968 base::Bind(&GDataFileSystem::UpdateFileByEntryInfo,
1969 ui_weak_ptr_,
1970 callback));
1971 }
1972
1973 void GDataFileSystem::UpdateFileByEntryInfo(
1974 const FileOperationCallback& callback,
1975 DriveFileError error,
1976 const FilePath& /* dive_file_path */,
1977 scoped_ptr<DriveEntryProto> entry_proto) {
1978 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
1979 DCHECK(!callback.is_null());
1980
1981 if (error != DRIVE_FILE_OK) {
1982 callback.Run(error);
1983 return;
1984 }
1985
1986 DCHECK(entry_proto.get());
1987 if (entry_proto->file_info().is_directory()) {
1988 callback.Run(DRIVE_FILE_ERROR_NOT_FOUND);
1989 return;
1990 }
1991
1992 cache_->GetFileOnUIThread(
1993 entry_proto->resource_id(),
1994 entry_proto->file_specific_info().file_md5(),
1995 base::Bind(&GDataFileSystem::OnGetFileCompleteForUpdateFile,
1996 ui_weak_ptr_,
1997 callback));
1998 }
1999
2000 void GDataFileSystem::OnGetFileCompleteForUpdateFile(
2001 const FileOperationCallback& callback,
2002 DriveFileError error,
2003 const std::string& resource_id,
2004 const std::string& /* md5 */,
2005 const FilePath& cache_file_path) {
2006 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2007 DCHECK(!callback.is_null());
2008
2009 if (error != DRIVE_FILE_OK) {
2010 callback.Run(error);
2011 return;
2012 }
2013
2014 // Gets the size of the cache file. Since the file is locally modified, the
2015 // file size information stored in DriveEntry is not correct.
2016 DriveFileError* get_size_error = new DriveFileError(DRIVE_FILE_ERROR_FAILED);
2017 int64* file_size = new int64(-1);
2018 util::PostBlockingPoolSequencedTaskAndReply(
2019 FROM_HERE,
2020 blocking_task_runner_,
2021 base::Bind(&GetLocalFileSizeOnBlockingPool,
2022 cache_file_path,
2023 get_size_error,
2024 file_size),
2025 base::Bind(&GDataFileSystem::OnGetFileSizeCompleteForUpdateFile,
2026 ui_weak_ptr_,
2027 callback,
2028 resource_id,
2029 cache_file_path,
2030 base::Owned(get_size_error),
2031 base::Owned(file_size)));
2032 }
2033
2034 void GDataFileSystem::OnGetFileSizeCompleteForUpdateFile(
2035 const FileOperationCallback& callback,
2036 const std::string& resource_id,
2037 const FilePath& cache_file_path,
2038 DriveFileError* error,
2039 int64* file_size) {
2040 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2041 DCHECK(!callback.is_null());
2042
2043 if (*error != DRIVE_FILE_OK) {
2044 callback.Run(*error);
2045 return;
2046 }
2047
2048 // TODO(satorux): GetEntryInfoByResourceId() is called twice for
2049 // UpdateFileByResourceIdOnUIThread(). crbug.com/143873
2050 resource_metadata_->GetEntryInfoByResourceId(
2051 resource_id,
2052 base::Bind(&GDataFileSystem::OnGetFileCompleteForUpdateFileByEntry,
2053 ui_weak_ptr_,
2054 callback,
2055 *file_size,
2056 cache_file_path));
2057 }
2058
2059 void GDataFileSystem::OnGetFileCompleteForUpdateFileByEntry(
2060 const FileOperationCallback& callback,
2061 int64 file_size,
2062 const FilePath& cache_file_path,
2063 DriveFileError error,
2064 const FilePath& drive_file_path,
2065 scoped_ptr<DriveEntryProto> entry_proto) {
2066 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2067 DCHECK(!callback.is_null());
2068
2069 if (error != DRIVE_FILE_OK) {
2070 callback.Run(error);
2071 return;
2072 }
2073
2074 DCHECK(entry_proto.get());
2075 if (entry_proto->file_info().is_directory()) {
2076 callback.Run(DRIVE_FILE_ERROR_NOT_FOUND);
2077 return;
2078 }
2079
2080 uploader_->UploadExistingFile(
2081 GURL(entry_proto->upload_url()),
2082 drive_file_path,
2083 cache_file_path,
2084 file_size,
2085 entry_proto->file_specific_info().content_mime_type(),
2086 base::Bind(&GDataFileSystem::OnUpdatedFileUploaded,
2087 ui_weak_ptr_,
2088 callback));
2089 }
2090
2091 void GDataFileSystem::OnUpdatedFileUploaded(
2092 const FileOperationCallback& callback,
2093 DriveFileError error,
2094 scoped_ptr<UploadFileInfo> upload_file_info) {
2095 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2096 DCHECK(upload_file_info.get());
2097
2098 if (error != DRIVE_FILE_OK) {
2099 if (!callback.is_null())
2100 callback.Run(error);
2101 return;
2102 }
2103
2104 AddUploadedFile(UPLOAD_EXISTING_FILE,
2105 upload_file_info->gdata_path.DirName(),
2106 upload_file_info->entry.Pass(),
2107 upload_file_info->file_path,
2108 DriveCache::FILE_OPERATION_MOVE,
2109 base::Bind(&OnAddUploadFileCompleted, callback, error));
2110 }
2111
2112 void GDataFileSystem::GetAvailableSpace(
2113 const GetAvailableSpaceCallback& callback) {
2114 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
2115 BrowserThread::CurrentlyOn(BrowserThread::IO));
2116 RunTaskOnUIThread(base::Bind(&GDataFileSystem::GetAvailableSpaceOnUIThread,
2117 ui_weak_ptr_,
2118 CreateRelayCallback(callback)));
2119 }
2120
2121 void GDataFileSystem::GetAvailableSpaceOnUIThread(
2122 const GetAvailableSpaceCallback& callback) {
2123 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2124 DCHECK(!callback.is_null());
2125
2126 drive_service_->GetAccountMetadata(
2127 gdata::util::IsDriveV2ApiEnabled() ?
2128 base::Bind(&GDataFileSystem::OnGetAboutResource,
2129 ui_weak_ptr_,
2130 callback) :
2131 base::Bind(&GDataFileSystem::OnGetAvailableSpace,
2132 ui_weak_ptr_,
2133 callback));
2134 }
2135
2136 void GDataFileSystem::OnGetAvailableSpace(
2137 const GetAvailableSpaceCallback& callback,
2138 GDataErrorCode status,
2139 scoped_ptr<base::Value> data) {
2140 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2141 DCHECK(!callback.is_null());
2142
2143 DriveFileError error = util::GDataToDriveFileError(status);
2144 if (error != DRIVE_FILE_OK) {
2145 callback.Run(error, -1, -1);
2146 return;
2147 }
2148
2149 scoped_ptr<AccountMetadataFeed> feed;
2150 if (data.get())
2151 feed = AccountMetadataFeed::CreateFrom(*data);
2152 if (!feed.get()) {
2153 callback.Run(DRIVE_FILE_ERROR_FAILED, -1, -1);
2154 return;
2155 }
2156
2157 callback.Run(DRIVE_FILE_OK,
2158 feed->quota_bytes_total(),
2159 feed->quota_bytes_used());
2160 }
2161
2162 void GDataFileSystem::OnGetAboutResource(
2163 const GetAvailableSpaceCallback& callback,
2164 GDataErrorCode status,
2165 scoped_ptr<base::Value> resource_json) {
2166 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2167 DCHECK(!callback.is_null());
2168
2169 DriveFileError error = util::GDataToDriveFileError(status);
2170 if (error != DRIVE_FILE_OK) {
2171 callback.Run(error, -1, -1);
2172 return;
2173 }
2174
2175 scoped_ptr<AboutResource> about;
2176 if (resource_json.get())
2177 about = AboutResource::CreateFrom(*resource_json);
2178
2179 if (!about.get()) {
2180 callback.Run(DRIVE_FILE_ERROR_FAILED, -1, -1);
2181 return;
2182 }
2183
2184 callback.Run(DRIVE_FILE_OK,
2185 about->quota_bytes_total(),
2186 about->quota_bytes_used());
2187 }
2188
2189 void GDataFileSystem::OnCreateDirectoryCompleted(
2190 const CreateDirectoryParams& params,
2191 GDataErrorCode status,
2192 scoped_ptr<base::Value> data) {
2193 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2194
2195 DriveFileError error = util::GDataToDriveFileError(status);
2196 if (error != DRIVE_FILE_OK) {
2197 if (!params.callback.is_null())
2198 params.callback.Run(error);
2199
2200 return;
2201 }
2202
2203 base::DictionaryValue* dict_value = NULL;
2204 base::Value* created_entry = NULL;
2205 if (data.get() && data->GetAsDictionary(&dict_value) && dict_value)
2206 dict_value->Get("entry", &created_entry);
2207 error = AddNewDirectory(params.created_directory_path.DirName(),
2208 created_entry);
2209
2210 if (error != DRIVE_FILE_OK) {
2211 if (!params.callback.is_null())
2212 params.callback.Run(error);
2213
2214 return;
2215 }
2216
2217 // Not done yet with recursive directory creation?
2218 if (params.target_directory_path != params.created_directory_path &&
2219 params.is_recursive) {
2220 CreateDirectory(params.target_directory_path,
2221 params.is_exclusive,
2222 params.is_recursive,
2223 params.callback);
2224 return;
2225 }
2226
2227 if (!params.callback.is_null()) {
2228 // Finally done with the create request.
2229 params.callback.Run(DRIVE_FILE_OK);
2230 }
2231 }
2232
2233 void GDataFileSystem::OnSearch(const SearchCallback& callback,
2234 GetDocumentsParams* params,
2235 DriveFileError error) {
2236 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2237
2238 if (error != DRIVE_FILE_OK) {
2239 if (!callback.is_null())
2240 callback.Run(error, GURL(), scoped_ptr<std::vector<SearchResultInfo> >());
2241 return;
2242 }
2243
2244 // The search results will be returned using virtual directory.
2245 // The directory is not really part of the file system, so it has no parent or
2246 // root.
2247 std::vector<SearchResultInfo>* results(new std::vector<SearchResultInfo>());
2248
2249 DCHECK_EQ(1u, params->feed_list->size());
2250 DocumentFeed* feed = params->feed_list->at(0);
2251
2252 // TODO(tbarzic): Limit total number of returned results for the query.
2253 GURL next_feed;
2254 feed->GetNextFeedURL(&next_feed);
2255
2256 if (feed->entries().empty()) {
2257 scoped_ptr<std::vector<SearchResultInfo> > result_vec(results);
2258 if (!callback.is_null())
2259 callback.Run(error, next_feed, result_vec.Pass());
2260 return;
2261 }
2262
2263 // Go through all entires generated by the feed and add them to the search
2264 // result directory.
2265 for (size_t i = 0; i < feed->entries().size(); ++i) {
2266 DocumentEntry* doc = const_cast<DocumentEntry*>(feed->entries()[i]);
2267 scoped_ptr<DriveEntry> entry(resource_metadata_->FromDocumentEntry(*doc));
2268
2269 if (!entry.get())
2270 continue;
2271
2272 DCHECK_EQ(doc->resource_id(), entry->resource_id());
2273 DCHECK(!entry->is_deleted());
2274
2275 std::string entry_resource_id = entry->resource_id();
2276
2277 // This will do nothing if the entry is not already present in file system.
2278 if (entry->AsDriveFile()) {
2279 scoped_ptr<DriveFile> entry_as_file(entry.release()->AsDriveFile());
2280 resource_metadata_->RefreshFile(entry_as_file.Pass());
2281 // We shouldn't use entry object after this point.
2282 DCHECK(!entry.get());
2283 }
2284
2285 // We will need information about result entry to create info for callback.
2286 // We can't use |entry| anymore, so we have to refetch entry from file
2287 // system. Also, |entry| doesn't have file path set before |RefreshFile|
2288 // call, so we can't get file path from there.
2289 resource_metadata_->GetEntryByResourceIdAsync(entry_resource_id,
2290 base::Bind(&AddEntryToSearchResults,
2291 results,
2292 callback,
2293 base::Bind(&GDataFileSystem::CheckForUpdates, ui_weak_ptr_),
2294 error,
2295 i+1 == feed->entries().size(),
2296 next_feed));
2297 }
2298 }
2299
2300 void GDataFileSystem::Search(const std::string& search_query,
2301 const GURL& next_feed,
2302 const SearchCallback& callback) {
2303 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
2304 BrowserThread::CurrentlyOn(BrowserThread::IO));
2305 RunTaskOnUIThread(base::Bind(&GDataFileSystem::SearchAsyncOnUIThread,
2306 ui_weak_ptr_,
2307 search_query,
2308 next_feed,
2309 CreateRelayCallback(callback)));
2310 }
2311
2312 void GDataFileSystem::SearchAsyncOnUIThread(
2313 const std::string& search_query,
2314 const GURL& next_feed,
2315 const SearchCallback& callback) {
2316 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2317
2318 feed_loader_->SearchFromServer(
2319 resource_metadata_->origin(),
2320 search_query,
2321 next_feed,
2322 base::Bind(&GDataFileSystem::OnSearch, ui_weak_ptr_, callback));
2323 }
2324
2325 void GDataFileSystem::OnDirectoryChanged(const FilePath& directory_path) {
2326 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2327
2328 FOR_EACH_OBSERVER(GDataFileSystemInterface::Observer, observers_,
2329 OnDirectoryChanged(directory_path));
2330 }
2331
2332 void GDataFileSystem::OnDocumentFeedFetched(int num_accumulated_entries) {
2333 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2334
2335 FOR_EACH_OBSERVER(GDataFileSystemInterface::Observer, observers_,
2336 OnDocumentFeedFetched(num_accumulated_entries));
2337 }
2338
2339 void GDataFileSystem::OnFeedFromServerLoaded() {
2340 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2341
2342 FOR_EACH_OBSERVER(GDataFileSystemInterface::Observer, observers_,
2343 OnFeedFromServerLoaded());
2344 }
2345
2346 void GDataFileSystem::LoadRootFeedFromCacheForTesting() {
2347 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2348
2349 feed_loader_->LoadFromCache(
2350 false, // should_load_from_server.
2351 FileOperationCallback());
2352 }
2353
2354 DriveFileError GDataFileSystem::UpdateFromFeedForTesting(
2355 const std::vector<DocumentFeed*>& feed_list,
2356 int64 start_changestamp,
2357 int64 root_feed_changestamp) {
2358 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2359
2360 return feed_loader_->UpdateFromFeed(feed_list,
2361 start_changestamp,
2362 root_feed_changestamp);
2363 }
2364
2365 void GDataFileSystem::OnFilePathUpdated(const FileOperationCallback& callback,
2366 DriveFileError error,
2367 const FilePath& /* file_path */) {
2368 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2369 if (!callback.is_null())
2370 callback.Run(error);
2371 }
2372
2373 void GDataFileSystem::OnCopyDocumentCompleted(
2374 const FilePath& dir_path,
2375 const FileOperationCallback& callback,
2376 GDataErrorCode status,
2377 scoped_ptr<base::Value> data) {
2378 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2379 DCHECK(!callback.is_null());
2380
2381 DriveFileError error = util::GDataToDriveFileError(status);
2382 if (error != DRIVE_FILE_OK) {
2383 callback.Run(error);
2384 return;
2385 }
2386
2387 scoped_ptr<DocumentEntry> doc_entry(DocumentEntry::ExtractAndParse(*data));
2388 if (!doc_entry.get()) {
2389 callback.Run(DRIVE_FILE_ERROR_FAILED);
2390 return;
2391 }
2392
2393 DriveEntry* entry = resource_metadata_->FromDocumentEntry(*doc_entry);
2394 if (!entry) {
2395 callback.Run(DRIVE_FILE_ERROR_FAILED);
2396 return;
2397 }
2398
2399 // |entry| was added in the root directory on the server, so we should
2400 // first add it to |root_| to mirror the state and then move it to the
2401 // destination directory by MoveEntryFromRootDirectory().
2402 resource_metadata_->AddEntryToDirectory(
2403 resource_metadata_->root(),
2404 entry,
2405 base::Bind(&GDataFileSystem::MoveEntryFromRootDirectory,
2406 ui_weak_ptr_,
2407 dir_path,
2408 callback));
2409 }
2410
2411 void GDataFileSystem::OnMoveEntryFromRootDirectoryCompleted(
2412 const FileOperationCallback& callback,
2413 const FilePath& file_path,
2414 const FilePath& dir_path,
2415 GDataErrorCode status,
2416 const GURL& document_url) {
2417 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2418 DCHECK(!callback.is_null());
2419
2420 DriveFileError error = util::GDataToDriveFileError(status);
2421 if (error == DRIVE_FILE_OK) {
2422 DriveEntry* entry = resource_metadata_->FindEntryByPathSync(file_path);
2423 if (entry) {
2424 DCHECK_EQ(resource_metadata_->root(), entry->parent());
2425 resource_metadata_->MoveEntryToDirectory(
2426 dir_path,
2427 entry,
2428 base::Bind(
2429 &GDataFileSystem::NotifyAndRunFileOperationCallback,
2430 ui_weak_ptr_,
2431 callback));
2432 return;
2433 } else {
2434 error = DRIVE_FILE_ERROR_NOT_FOUND;
2435 }
2436 }
2437
2438 callback.Run(error);
2439 }
2440
2441 void GDataFileSystem::OnRemovedDocument(
2442 const FileOperationCallback& callback,
2443 const FilePath& file_path,
2444 GDataErrorCode status,
2445 const GURL& document_url) {
2446 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2447
2448 DriveFileError error = util::GDataToDriveFileError(status);
2449
2450 if (error == DRIVE_FILE_OK)
2451 error = RemoveEntryAndCacheLocally(file_path);
2452
2453 if (!callback.is_null()) {
2454 callback.Run(error);
2455 }
2456 }
2457
2458 void GDataFileSystem::OnFileDownloaded(
2459 const GetFileFromCacheParams& params,
2460 GDataErrorCode status,
2461 const GURL& content_url,
2462 const FilePath& downloaded_file_path) {
2463 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2464
2465 // If user cancels download of a pinned-but-not-fetched file, mark file as
2466 // unpinned so that we do not sync the file again.
2467 if (status == GDATA_CANCELLED) {
2468 cache_->GetCacheEntryOnUIThread(
2469 params.resource_id,
2470 params.md5,
2471 base::Bind(&GDataFileSystem::UnpinIfPinned,
2472 ui_weak_ptr_,
2473 params.resource_id,
2474 params.md5));
2475 }
2476
2477 // At this point, the disk can be full or nearly full for several reasons:
2478 // - The expected file size was incorrect and the file was larger
2479 // - There was an in-flight download operation and it used up space
2480 // - The disk became full for some user actions we cannot control
2481 // (ex. the user might have downloaded a large file from a regular web site)
2482 //
2483 // If we don't have enough space, we return PLATFORM_FILE_ERROR_NO_SPACE,
2484 // and try to free up space, even if the file was downloaded successfully.
2485 bool* has_enough_space = new bool(false);
2486 util::PostBlockingPoolSequencedTaskAndReply(
2487 FROM_HERE,
2488 blocking_task_runner_,
2489 base::Bind(&DriveCache::FreeDiskSpaceIfNeededFor,
2490 base::Unretained(cache_),
2491 0,
2492 has_enough_space),
2493 base::Bind(&GDataFileSystem::OnFileDownloadedAndSpaceChecked,
2494 ui_weak_ptr_,
2495 params,
2496 status,
2497 content_url,
2498 downloaded_file_path,
2499 base::Owned(has_enough_space)));
2500 }
2501
2502 void GDataFileSystem::UnpinIfPinned(
2503 const std::string& resource_id,
2504 const std::string& md5,
2505 bool success,
2506 const DriveCacheEntry& cache_entry) {
2507 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2508 // TODO(hshi): http://crbug.com/127138 notify when file properties change.
2509 // This allows file manager to clear the "Available offline" checkbox.
2510 if (success && cache_entry.is_pinned())
2511 cache_->UnpinOnUIThread(resource_id, md5, CacheOperationCallback());
2512 }
2513
2514 void GDataFileSystem::OnFileDownloadedAndSpaceChecked(
2515 const GetFileFromCacheParams& params,
2516 GDataErrorCode status,
2517 const GURL& content_url,
2518 const FilePath& downloaded_file_path,
2519 bool* has_enough_space) {
2520 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2521
2522 DriveFileError error = util::GDataToDriveFileError(status);
2523
2524 // Make sure that downloaded file is properly stored in cache. We don't have
2525 // to wait for this operation to finish since the user can already use the
2526 // downloaded file.
2527 if (error == DRIVE_FILE_OK) {
2528 if (*has_enough_space) {
2529 cache_->StoreOnUIThread(
2530 params.resource_id,
2531 params.md5,
2532 downloaded_file_path,
2533 DriveCache::FILE_OPERATION_MOVE,
2534 base::Bind(&GDataFileSystem::OnDownloadStoredToCache,
2535 ui_weak_ptr_));
2536 } else {
2537 // If we don't have enough space, remove the downloaded file, and
2538 // report "no space" error.
2539 util::PostBlockingPoolSequencedTask(
2540 FROM_HERE,
2541 blocking_task_runner_,
2542 base::Bind(base::IgnoreResult(&file_util::Delete),
2543 downloaded_file_path,
2544 false /* recursive*/));
2545 error = DRIVE_FILE_ERROR_NO_SPACE;
2546 }
2547 }
2548
2549 if (!params.get_file_callback.is_null()) {
2550 params.get_file_callback.Run(error,
2551 downloaded_file_path,
2552 params.mime_type,
2553 REGULAR_FILE);
2554 }
2555 }
2556
2557 void GDataFileSystem::OnDownloadStoredToCache(DriveFileError error,
2558 const std::string& resource_id,
2559 const std::string& md5) {
2560 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2561 // Nothing much to do here for now.
2562 }
2563
2564 void GDataFileSystem::RenameEntryLocally(
2565 const FilePath& file_path,
2566 const FilePath::StringType& new_name,
2567 const FileMoveCallback& callback,
2568 GDataErrorCode status,
2569 const GURL& document_url) {
2570 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2571
2572 const DriveFileError error = util::GDataToDriveFileError(status);
2573 if (error != DRIVE_FILE_OK) {
2574 if (!callback.is_null())
2575 callback.Run(error, FilePath());
2576 return;
2577 }
2578
2579 DriveEntry* entry = resource_metadata_->FindEntryByPathSync(file_path);
2580 if (!entry) {
2581 if (!callback.is_null())
2582 callback.Run(DRIVE_FILE_ERROR_NOT_FOUND, FilePath());
2583 return;
2584 }
2585
2586 DCHECK(entry->parent());
2587 entry->set_title(new_name);
2588 // After changing the title of the entry, call MoveEntryToDirectory() to
2589 // remove the entry from its parent directory and then add it back in order to
2590 // go through the file name de-duplication.
2591 // TODO(achuith/satorux/zel): This code is fragile. The title has been
2592 // changed, but not the file_name. MoveEntryToDirectory calls RemoveChild to
2593 // remove the child based on the old file_name, and then re-adds the child by
2594 // first assigning the new title to file_name. http://crbug.com/30157
2595 resource_metadata_->MoveEntryToDirectory(
2596 entry->parent()->GetFilePath(),
2597 entry,
2598 base::Bind(&GDataFileSystem::NotifyAndRunFileMoveCallback,
2599 ui_weak_ptr_,
2600 callback));
2601 }
2602
2603 void GDataFileSystem::MoveEntryToRootDirectoryLocally(
2604 const FileMoveCallback& callback,
2605 const FilePath& file_path,
2606 const FilePath& dir_path,
2607 GDataErrorCode status,
2608 const GURL& document_url) {
2609 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2610 DCHECK(!callback.is_null());
2611
2612 const DriveFileError error = util::GDataToDriveFileError(status);
2613 if (error != DRIVE_FILE_OK) {
2614 callback.Run(error, FilePath());
2615 return;
2616 }
2617
2618 DriveEntry* entry = resource_metadata_->FindEntryByPathSync(file_path);
2619 if (!entry) {
2620 callback.Run(DRIVE_FILE_ERROR_NOT_FOUND, FilePath());
2621 return;
2622 }
2623
2624 resource_metadata_->MoveEntryToDirectory(
2625 resource_metadata_->root()->GetFilePath(),
2626 entry,
2627 base::Bind(&GDataFileSystem::NotifyAndRunFileMoveCallback,
2628 ui_weak_ptr_,
2629 callback));
2630 }
2631
2632 void GDataFileSystem::NotifyAndRunFileMoveCallback(
2633 const FileMoveCallback& callback,
2634 DriveFileError error,
2635 const FilePath& moved_file_path) {
2636 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2637
2638 if (error == DRIVE_FILE_OK)
2639 OnDirectoryChanged(moved_file_path.DirName());
2640
2641 if (!callback.is_null())
2642 callback.Run(error, moved_file_path);
2643 }
2644
2645 void GDataFileSystem::NotifyAndRunFileOperationCallback(
2646 const FileOperationCallback& callback,
2647 DriveFileError error,
2648 const FilePath& moved_file_path) {
2649 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2650 DCHECK(!callback.is_null());
2651
2652 if (error == DRIVE_FILE_OK)
2653 OnDirectoryChanged(moved_file_path.DirName());
2654
2655 callback.Run(error);
2656 }
2657
2658 void GDataFileSystem::OnDirectoryChangeFileMoveCallback(
2659 DriveFileError error,
2660 const FilePath& directory_path) {
2661 if (error == DRIVE_FILE_OK)
2662 OnDirectoryChanged(directory_path);
2663 }
2664
2665 DriveFileError GDataFileSystem::RemoveEntryAndCacheLocally(
2666 const FilePath& file_path) {
2667 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2668
2669 std::string resource_id;
2670 DriveFileError error = RemoveEntryLocally(file_path, &resource_id);
2671 if (error != DRIVE_FILE_OK)
2672 return error;
2673
2674 // If resource_id is not empty, remove its corresponding file from cache.
2675 if (!resource_id.empty())
2676 cache_->RemoveOnUIThread(resource_id, CacheOperationCallback());
2677
2678 return DRIVE_FILE_OK;
2679 }
2680
2681 void GDataFileSystem::RemoveStaleEntryOnUpload(
2682 const std::string& resource_id,
2683 DriveDirectory* parent_dir,
2684 const FileMoveCallback& callback,
2685 DriveEntry* existing_entry) {
2686 if (existing_entry &&
2687 // This should always match, but just in case.
2688 existing_entry->parent() == parent_dir) {
2689 resource_metadata_->RemoveEntryFromParent(existing_entry, callback);
2690 } else {
2691 callback.Run(DRIVE_FILE_ERROR_NOT_FOUND, FilePath());
2692 LOG(ERROR) << "Entry for the existing file not found: " << resource_id;
2693 }
2694 }
2695
2696 void GDataFileSystem::NotifyFileSystemMounted() {
2697 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2698
2699 DVLOG(1) << "File System is mounted";
2700 // Notify the observers that the file system is mounted.
2701 FOR_EACH_OBSERVER(GDataFileSystemInterface::Observer, observers_,
2702 OnFileSystemMounted());
2703 }
2704
2705 void GDataFileSystem::NotifyFileSystemToBeUnmounted() {
2706 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2707
2708 DVLOG(1) << "File System is to be unmounted";
2709 // Notify the observers that the file system is being unmounted.
2710 FOR_EACH_OBSERVER(GDataFileSystemInterface::Observer, observers_,
2711 OnFileSystemBeingUnmounted());
2712 }
2713
2714 void GDataFileSystem::NotifyInitialLoadFinishedAndRun(
2715 const FileOperationCallback& callback,
2716 DriveFileError error) {
2717 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2718 DCHECK(!callback.is_null());
2719
2720 // Notify the observers that root directory has been initialized.
2721 FOR_EACH_OBSERVER(GDataFileSystemInterface::Observer, observers_,
2722 OnInitialLoadFinished());
2723
2724 callback.Run(error);
2725 }
2726
2727 DriveFileError GDataFileSystem::AddNewDirectory(
2728 const FilePath& directory_path, base::Value* entry_value) {
2729 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2730
2731 if (!entry_value)
2732 return DRIVE_FILE_ERROR_FAILED;
2733
2734 scoped_ptr<DocumentEntry> doc_entry(DocumentEntry::CreateFrom(*entry_value));
2735
2736 if (!doc_entry.get())
2737 return DRIVE_FILE_ERROR_FAILED;
2738
2739 // Find parent directory element within the cached file system snapshot.
2740 DriveEntry* entry = resource_metadata_->FindEntryByPathSync(directory_path);
2741 if (!entry)
2742 return DRIVE_FILE_ERROR_FAILED;
2743
2744 // Check if parent is a directory since in theory since this is a callback
2745 // something could in the meantime have nuked the parent dir and created a
2746 // file with the exact same name.
2747 DriveDirectory* parent_dir = entry->AsDriveDirectory();
2748 if (!parent_dir)
2749 return DRIVE_FILE_ERROR_FAILED;
2750
2751 DriveEntry* new_entry =
2752 resource_metadata_->FromDocumentEntry(*doc_entry);
2753 if (!new_entry)
2754 return DRIVE_FILE_ERROR_FAILED;
2755
2756 resource_metadata_->AddEntryToDirectory(
2757 parent_dir,
2758 new_entry,
2759 base::Bind(&GDataFileSystem::NotifyAndRunFileMoveCallback,
2760 ui_weak_ptr_,
2761 FileMoveCallback()));
2762 return DRIVE_FILE_OK;
2763 }
2764
2765 GDataFileSystem::FindMissingDirectoryResult
2766 GDataFileSystem::FindFirstMissingParentDirectory(
2767 const FilePath& directory_path,
2768 GURL* last_dir_content_url,
2769 FilePath* first_missing_parent_path) {
2770 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2771
2772 // Let's find which how deep is the existing directory structure and
2773 // get the first element that's missing.
2774 std::vector<FilePath::StringType> path_parts;
2775 directory_path.GetComponents(&path_parts);
2776 FilePath current_path;
2777
2778 for (std::vector<FilePath::StringType>::const_iterator iter =
2779 path_parts.begin();
2780 iter != path_parts.end(); ++iter) {
2781 current_path = current_path.Append(*iter);
2782 DriveEntry* entry = resource_metadata_->FindEntryByPathSync(current_path);
2783 if (entry) {
2784 if (entry->file_info().is_directory) {
2785 *last_dir_content_url = entry->content_url();
2786 } else {
2787 // Huh, the segment found is a file not a directory?
2788 return FOUND_INVALID;
2789 }
2790 } else {
2791 *first_missing_parent_path = current_path;
2792 return FOUND_MISSING;
2793 }
2794 }
2795 return DIRECTORY_ALREADY_PRESENT;
2796 }
2797
2798 DriveFileError GDataFileSystem::RemoveEntryLocally(
2799 const FilePath& file_path, std::string* resource_id) {
2800 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2801
2802 resource_id->clear();
2803
2804 // Find directory element within the cached file system snapshot.
2805 DriveEntry* entry = resource_metadata_->FindEntryByPathSync(file_path);
2806
2807 if (!entry)
2808 return DRIVE_FILE_ERROR_NOT_FOUND;
2809
2810 // You can't remove root element.
2811 if (!entry->parent())
2812 return DRIVE_FILE_ERROR_ACCESS_DENIED;
2813
2814 // If it's a file (only files have resource id), get its resource id so that
2815 // we can remove it after releasing the auto lock.
2816 if (entry->AsDriveFile())
2817 *resource_id = entry->AsDriveFile()->resource_id();
2818
2819 resource_metadata_->RemoveEntryFromParent(
2820 entry,
2821 base::Bind(&GDataFileSystem::OnDirectoryChangeFileMoveCallback,
2822 ui_weak_ptr_));
2823 return DRIVE_FILE_OK;
2824 }
2825
2826 void GDataFileSystem::AddUploadedFile(
2827 UploadMode upload_mode,
2828 const FilePath& virtual_dir_path,
2829 scoped_ptr<DocumentEntry> entry,
2830 const FilePath& file_content_path,
2831 DriveCache::FileOperationType cache_operation,
2832 const base::Closure& callback) {
2833 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2834
2835 // Post a task to the same thread, rather than calling it here, as
2836 // AddUploadedFile() is asynchronous.
2837 base::MessageLoopProxy::current()->PostTask(
2838 FROM_HERE,
2839 base::Bind(&GDataFileSystem::AddUploadedFileOnUIThread,
2840 ui_weak_ptr_,
2841 upload_mode,
2842 virtual_dir_path,
2843 base::Passed(&entry),
2844 file_content_path,
2845 cache_operation,
2846 callback));
2847 }
2848
2849 void GDataFileSystem::AddUploadedFileOnUIThread(
2850 UploadMode upload_mode,
2851 const FilePath& virtual_dir_path,
2852 scoped_ptr<DocumentEntry> entry,
2853 const FilePath& file_content_path,
2854 DriveCache::FileOperationType cache_operation,
2855 const base::Closure& callback) {
2856 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2857
2858 // ScopedClosureRunner ensures that the specified callback is always invoked
2859 // upon return or passed on.
2860 base::ScopedClosureRunner callback_runner(callback);
2861
2862 if (!entry.get()) {
2863 NOTREACHED();
2864 return;
2865 }
2866
2867 DriveEntry* dir_entry = resource_metadata_->FindEntryByPathSync(
2868 virtual_dir_path);
2869 if (!dir_entry)
2870 return;
2871
2872 DriveDirectory* parent_dir = dir_entry->AsDriveDirectory();
2873 if (!parent_dir)
2874 return;
2875
2876 scoped_ptr<DriveEntry> new_entry(
2877 resource_metadata_->FromDocumentEntry(*entry));
2878 if (!new_entry.get())
2879 return;
2880
2881 const std::string& resource_id = new_entry->resource_id();
2882 AddUploadedFileParams* params =
2883 new AddUploadedFileParams(upload_mode,
2884 parent_dir,
2885 new_entry.Pass(),
2886 file_content_path,
2887 cache_operation,
2888 callback_runner.Release());
2889
2890 const FileMoveCallback file_move_callback =
2891 base::Bind(&GDataFileSystem::ContinueAddUploadedFile,
2892 ui_weak_ptr_, params);
2893
2894 if (upload_mode == UPLOAD_EXISTING_FILE) {
2895 // Remove an existing entry, which should be present.
2896 resource_metadata_->GetEntryByResourceIdAsync(
2897 resource_id,
2898 base::Bind(&GDataFileSystem::RemoveStaleEntryOnUpload,
2899 ui_weak_ptr_,
2900 resource_id,
2901 parent_dir,
2902 file_move_callback));
2903 } else {
2904 file_move_callback.Run(DRIVE_FILE_OK, FilePath());
2905 }
2906 }
2907
2908 void GDataFileSystem::ContinueAddUploadedFile(
2909 AddUploadedFileParams* params,
2910 DriveFileError error,
2911 const FilePath& file_path) {
2912 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2913 DCHECK_EQ(DRIVE_FILE_OK, error);
2914 DCHECK(params->new_entry.get());
2915 DriveFile* file = params->new_entry->AsDriveFile();
2916 DCHECK(file);
2917
2918 params->resource_id = file->resource_id();
2919 params->md5 = file->file_md5();
2920 resource_metadata_->AddEntryToDirectory(
2921 params->parent_dir,
2922 params->new_entry.release(),
2923 base::Bind(&GDataFileSystem::NotifyAndRunFileMoveCallback,
2924 ui_weak_ptr_,
2925 base::Bind(&GDataFileSystem::AddUploadedFileToCache,
2926 ui_weak_ptr_,
2927 base::Owned(params))));
2928 }
2929
2930 void GDataFileSystem::AddUploadedFileToCache(
2931 AddUploadedFileParams* params,
2932 DriveFileError error,
2933 const FilePath& file_path) {
2934 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2935
2936 if (params->upload_mode == UPLOAD_NEW_FILE) {
2937 // Add the file to the cache if we have uploaded a new file.
2938 cache_->StoreOnUIThread(params->resource_id,
2939 params->md5,
2940 params->file_content_path,
2941 params->cache_operation,
2942 base::Bind(&OnCacheUpdatedForAddUploadedFile,
2943 params->callback));
2944 } else if (params->upload_mode == UPLOAD_EXISTING_FILE) {
2945 // Clear the dirty bit if we have updated an existing file.
2946 cache_->ClearDirtyOnUIThread(params->resource_id,
2947 params->md5,
2948 base::Bind(&OnCacheUpdatedForAddUploadedFile,
2949 params->callback));
2950 } else {
2951 NOTREACHED() << "Unexpected upload mode: " << params->upload_mode;
2952 // Shouldn't reach here, so the line below should not make much sense, but
2953 // since calling |callback| exactly once is our obligation, we'd better call
2954 // it for not to clutter further more.
2955 params->callback.Run();
2956 }
2957 }
2958
2959 void GDataFileSystem::UpdateEntryData(const std::string& resource_id,
2960 const std::string& md5,
2961 scoped_ptr<DocumentEntry> entry,
2962 const FilePath& file_content_path,
2963 const base::Closure& callback) {
2964 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2965
2966 // Post a task to the same thread, rather than calling it here, as
2967 // UpdateEntryData() is asynchronous.
2968 base::MessageLoopProxy::current()->PostTask(
2969 FROM_HERE,
2970 base::Bind(&GDataFileSystem::UpdateEntryDataOnUIThread,
2971 ui_weak_ptr_,
2972 resource_id,
2973 md5,
2974 base::Passed(&entry),
2975 file_content_path,
2976 callback));
2977 }
2978
2979 void GDataFileSystem::UpdateEntryDataOnUIThread(
2980 const std::string& resource_id,
2981 const std::string& md5,
2982 scoped_ptr<DocumentEntry> entry,
2983 const FilePath& file_content_path,
2984 const base::Closure& callback) {
2985 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
2986
2987 scoped_ptr<DriveFile> new_entry(
2988 resource_metadata_->FromDocumentEntry(*entry)->AsDriveFile());
2989 if (!new_entry.get()) {
2990 return;
2991 }
2992
2993 resource_metadata_->RefreshFile(new_entry.Pass());
2994
2995 // Add the file to the cache if we have uploaded a new file.
2996 cache_->StoreOnUIThread(resource_id,
2997 md5,
2998 file_content_path,
2999 DriveCache::FILE_OPERATION_MOVE,
3000 base::Bind(&OnCacheUpdatedForAddUploadedFile,
3001 callback));
3002 }
3003
3004 void GDataFileSystem::Observe(int type,
3005 const content::NotificationSource& source,
3006 const content::NotificationDetails& details) {
3007 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
3008
3009 if (type == chrome::NOTIFICATION_PREF_CHANGED) {
3010 PrefService* pref_service = profile_->GetPrefs();
3011 std::string* pref_name = content::Details<std::string>(details).ptr();
3012 if (*pref_name == prefs::kDisableGDataHostedFiles) {
3013 SetHideHostedDocuments(
3014 pref_service->GetBoolean(prefs::kDisableGDataHostedFiles));
3015 }
3016 } else {
3017 NOTREACHED();
3018 }
3019 }
3020
3021 void GDataFileSystem::SetHideHostedDocuments(bool hide) {
3022 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
3023
3024 if (hide == hide_hosted_docs_)
3025 return;
3026
3027 hide_hosted_docs_ = hide;
3028 const FilePath root_path = resource_metadata_->root()->GetFilePath();
3029
3030 // Kick off directory refresh when this setting changes.
3031 FOR_EACH_OBSERVER(GDataFileSystemInterface::Observer, observers_,
3032 OnDirectoryChanged(root_path));
3033 }
3034
3035 //============= GDataFileSystem: internal helper functions =====================
3036
3037 void GDataFileSystem::InitializePreferenceObserver() {
3038 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
3039
3040 pref_registrar_.reset(new PrefChangeRegistrar());
3041 pref_registrar_->Init(profile_->GetPrefs());
3042 pref_registrar_->Add(prefs::kDisableGDataHostedFiles, this);
3043 }
3044
3045 void GDataFileSystem::OpenFile(const FilePath& file_path,
3046 const OpenFileCallback& callback) {
3047 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
3048 BrowserThread::CurrentlyOn(BrowserThread::IO));
3049 RunTaskOnUIThread(base::Bind(&GDataFileSystem::OpenFileOnUIThread,
3050 ui_weak_ptr_,
3051 file_path,
3052 CreateRelayCallback(callback)));
3053 }
3054
3055 void GDataFileSystem::OpenFileOnUIThread(const FilePath& file_path,
3056 const OpenFileCallback& callback) {
3057 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
3058
3059 // If the file is already opened, it cannot be opened again before closed.
3060 // This is for avoiding simultaneous modification to the file, and moreover
3061 // to avoid an inconsistent cache state (suppose an operation sequence like
3062 // Open->Open->modify->Close->modify->Close; the second modify may not be
3063 // synchronized to the server since it is already Closed on the cache).
3064 if (open_files_.find(file_path) != open_files_.end()) {
3065 MessageLoop::current()->PostTask(
3066 FROM_HERE,
3067 base::Bind(callback, DRIVE_FILE_ERROR_IN_USE, FilePath()));
3068 return;
3069 }
3070 open_files_.insert(file_path);
3071
3072 resource_metadata_->GetEntryInfoByPath(
3073 file_path,
3074 base::Bind(&GDataFileSystem::OnGetEntryInfoCompleteForOpenFile,
3075 ui_weak_ptr_,
3076 file_path,
3077 base::Bind(&GDataFileSystem::OnOpenFileFinished,
3078 ui_weak_ptr_,
3079 file_path,
3080 callback)));
3081 }
3082
3083 void GDataFileSystem::OnGetEntryInfoCompleteForOpenFile(
3084 const FilePath& file_path,
3085 const OpenFileCallback& callback,
3086 DriveFileError error,
3087 scoped_ptr<DriveEntryProto> entry_proto) {
3088 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
3089
3090 if (entry_proto.get() && !entry_proto->has_file_specific_info())
3091 error = DRIVE_FILE_ERROR_NOT_FOUND;
3092
3093 if (error == DRIVE_FILE_OK) {
3094 if (entry_proto->file_specific_info().file_md5().empty() ||
3095 entry_proto->file_specific_info().is_hosted_document()) {
3096 // No support for opening a directory or hosted document.
3097 error = DRIVE_FILE_ERROR_INVALID_OPERATION;
3098 }
3099 }
3100
3101 if (error != DRIVE_FILE_OK) {
3102 if (!callback.is_null())
3103 callback.Run(error, FilePath());
3104 return;
3105 }
3106
3107 DCHECK(!entry_proto->resource_id().empty());
3108 GetResolvedFileByPath(
3109 file_path,
3110 base::Bind(&GDataFileSystem::OnGetFileCompleteForOpenFile,
3111 ui_weak_ptr_,
3112 callback,
3113 GetFileCompleteForOpenParams(
3114 entry_proto->resource_id(),
3115 entry_proto->file_specific_info().file_md5())),
3116 GetContentCallback(),
3117 error,
3118 entry_proto.get());
3119 }
3120
3121 void GDataFileSystem::OnGetFileCompleteForOpenFile(
3122 const OpenFileCallback& callback,
3123 const GetFileCompleteForOpenParams& entry_proto,
3124 DriveFileError error,
3125 const FilePath& file_path,
3126 const std::string& mime_type,
3127 DriveFileType file_type) {
3128 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
3129
3130 if (error != DRIVE_FILE_OK) {
3131 if (!callback.is_null())
3132 callback.Run(error, FilePath());
3133 return;
3134 }
3135
3136 // OpenFileOnUIThread ensures that the file is a regular file.
3137 DCHECK_EQ(REGULAR_FILE, file_type);
3138
3139 cache_->MarkDirtyOnUIThread(
3140 entry_proto.resource_id,
3141 entry_proto.md5,
3142 base::Bind(&GDataFileSystem::OnMarkDirtyInCacheCompleteForOpenFile,
3143 ui_weak_ptr_,
3144 callback));
3145 }
3146
3147 void GDataFileSystem::OnMarkDirtyInCacheCompleteForOpenFile(
3148 const OpenFileCallback& callback,
3149 DriveFileError error,
3150 const std::string& resource_id,
3151 const std::string& md5,
3152 const FilePath& cache_file_path) {
3153 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
3154
3155 if (!callback.is_null())
3156 callback.Run(error, cache_file_path);
3157 }
3158
3159 void GDataFileSystem::OnOpenFileFinished(const FilePath& file_path,
3160 const OpenFileCallback& callback,
3161 DriveFileError result,
3162 const FilePath& cache_file_path) {
3163 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
3164
3165 // All the invocation of |callback| from operations initiated from OpenFile
3166 // must go through here. Removes the |file_path| from the remembered set when
3167 // the file was not successfully opened.
3168 if (result != DRIVE_FILE_OK)
3169 open_files_.erase(file_path);
3170
3171 if (!callback.is_null())
3172 callback.Run(result, cache_file_path);
3173 }
3174
3175 void GDataFileSystem::CloseFile(const FilePath& file_path,
3176 const FileOperationCallback& callback) {
3177 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
3178 BrowserThread::CurrentlyOn(BrowserThread::IO));
3179 DCHECK(!callback.is_null());
3180
3181 RunTaskOnUIThread(base::Bind(&GDataFileSystem::CloseFileOnUIThread,
3182 ui_weak_ptr_,
3183 file_path,
3184 CreateRelayCallback(callback)));
3185 }
3186
3187 void GDataFileSystem::CloseFileOnUIThread(
3188 const FilePath& file_path,
3189 const FileOperationCallback& callback) {
3190 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
3191 DCHECK(!callback.is_null());
3192
3193 if (open_files_.find(file_path) == open_files_.end()) {
3194 // The file is not being opened.
3195 MessageLoop::current()->PostTask(
3196 FROM_HERE,
3197 base::Bind(callback, DRIVE_FILE_ERROR_NOT_FOUND));
3198 return;
3199 }
3200
3201 // Step 1 of CloseFile: Get resource_id and md5 for |file_path|.
3202 resource_metadata_->GetEntryInfoByPath(
3203 file_path,
3204 base::Bind(&GDataFileSystem::CloseFileOnUIThreadAfterGetEntryInfo,
3205 ui_weak_ptr_,
3206 file_path,
3207 base::Bind(&GDataFileSystem::CloseFileOnUIThreadFinalize,
3208 ui_weak_ptr_,
3209 file_path,
3210 callback)));
3211 }
3212
3213 void GDataFileSystem::CloseFileOnUIThreadAfterGetEntryInfo(
3214 const FilePath& file_path,
3215 const FileOperationCallback& callback,
3216 DriveFileError error,
3217 scoped_ptr<DriveEntryProto> entry_proto) {
3218 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
3219 DCHECK(!callback.is_null());
3220
3221 if (entry_proto.get() && !entry_proto->has_file_specific_info())
3222 error = DRIVE_FILE_ERROR_NOT_FOUND;
3223
3224 if (error != DRIVE_FILE_OK) {
3225 callback.Run(error);
3226 return;
3227 }
3228
3229 // Step 2 of CloseFile: Commit the modification in cache. This will trigger
3230 // background upload.
3231 // TODO(benchan,kinaba): Call ClearDirtyInCache instead of CommitDirtyInCache
3232 // if the file has not been modified. Come up with a way to detect the
3233 // intactness effectively, or provide a method for user to declare it when
3234 // calling CloseFile().
3235 cache_->CommitDirtyOnUIThread(
3236 entry_proto->resource_id(),
3237 entry_proto->file_specific_info().file_md5(),
3238 base::Bind(&GDataFileSystem::CloseFileOnUIThreadAfterCommitDirtyInCache,
3239 ui_weak_ptr_,
3240 callback));
3241 }
3242
3243 void GDataFileSystem::CloseFileOnUIThreadAfterCommitDirtyInCache(
3244 const FileOperationCallback& callback,
3245 DriveFileError error,
3246 const std::string& /* resource_id */,
3247 const std::string& /* md5 */) {
3248 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
3249 DCHECK(!callback.is_null());
3250
3251 callback.Run(error);
3252 }
3253
3254 void GDataFileSystem::CloseFileOnUIThreadFinalize(
3255 const FilePath& file_path,
3256 const FileOperationCallback& callback,
3257 DriveFileError result) {
3258 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
3259 DCHECK(!callback.is_null());
3260
3261 // Step 3 of CloseFile.
3262 // All the invocation of |callback| from operations initiated from CloseFile
3263 // must go through here. Removes the |file_path| from the remembered set so
3264 // that subsequent operations can open the file again.
3265 open_files_.erase(file_path);
3266
3267 // Then invokes the user-supplied callback function.
3268 callback.Run(result);
3269 }
3270
3271 void GDataFileSystem::CheckLocalModificationAndRun(
3272 scoped_ptr<DriveEntryProto> entry_proto,
3273 const GetEntryInfoCallback& callback) {
3274 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
3275 DCHECK(entry_proto.get());
3276 DCHECK(!callback.is_null());
3277
3278 // For entries that will never be cached, use the original entry info as is.
3279 if (!entry_proto->has_file_specific_info() ||
3280 entry_proto->file_specific_info().is_hosted_document()) {
3281 callback.Run(DRIVE_FILE_OK, entry_proto.Pass());
3282 return;
3283 }
3284
3285 // Checks if the file is cached and modified locally.
3286 const std::string resource_id = entry_proto->resource_id();
3287 const std::string md5 = entry_proto->file_specific_info().file_md5();
3288 cache_->GetCacheEntryOnUIThread(
3289 resource_id,
3290 md5,
3291 base::Bind(
3292 &GDataFileSystem::CheckLocalModificationAndRunAfterGetCacheEntry,
3293 ui_weak_ptr_, base::Passed(&entry_proto), callback));
3294 }
3295
3296 void GDataFileSystem::CheckLocalModificationAndRunAfterGetCacheEntry(
3297 scoped_ptr<DriveEntryProto> entry_proto,
3298 const GetEntryInfoCallback& callback,
3299 bool success,
3300 const DriveCacheEntry& cache_entry) {
3301 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
3302 DCHECK(!callback.is_null());
3303
3304 // When no dirty cache is found, use the original entry info as is.
3305 if (!success || !cache_entry.is_dirty()) {
3306 callback.Run(DRIVE_FILE_OK, entry_proto.Pass());
3307 return;
3308 }
3309
3310 // Gets the cache file path.
3311 const std::string& resource_id = entry_proto->resource_id();
3312 const std::string& md5 = entry_proto->file_specific_info().file_md5();
3313 cache_->GetFileOnUIThread(
3314 resource_id,
3315 md5,
3316 base::Bind(
3317 &GDataFileSystem::CheckLocalModificationAndRunAfterGetCacheFile,
3318 ui_weak_ptr_, base::Passed(&entry_proto), callback));
3319 }
3320
3321 void GDataFileSystem::CheckLocalModificationAndRunAfterGetCacheFile(
3322 scoped_ptr<DriveEntryProto> entry_proto,
3323 const GetEntryInfoCallback& callback,
3324 DriveFileError error,
3325 const std::string& resource_id,
3326 const std::string& md5,
3327 const FilePath& local_cache_path) {
3328 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
3329 DCHECK(!callback.is_null());
3330
3331 // When no dirty cache is found, use the original entry info as is.
3332 if (error != DRIVE_FILE_OK) {
3333 callback.Run(DRIVE_FILE_OK, entry_proto.Pass());
3334 return;
3335 }
3336
3337 // If the cache is dirty, obtain the file info from the cache file itself.
3338 base::PlatformFileInfo* file_info = new base::PlatformFileInfo;
3339 bool* get_file_info_result = new bool(false);
3340 util::PostBlockingPoolSequencedTaskAndReply(
3341 FROM_HERE,
3342 blocking_task_runner_,
3343 base::Bind(&GetFileInfoOnBlockingPool,
3344 local_cache_path,
3345 base::Unretained(file_info),
3346 base::Unretained(get_file_info_result)),
3347 base::Bind(&GDataFileSystem::CheckLocalModificationAndRunAfterGetFileInfo,
3348 ui_weak_ptr_,
3349 base::Passed(&entry_proto),
3350 callback,
3351 base::Owned(file_info),
3352 base::Owned(get_file_info_result)));
3353 }
3354
3355 void GDataFileSystem::CheckLocalModificationAndRunAfterGetFileInfo(
3356 scoped_ptr<DriveEntryProto> entry_proto,
3357 const GetEntryInfoCallback& callback,
3358 base::PlatformFileInfo* file_info,
3359 bool* get_file_info_result) {
3360 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
3361 DCHECK(!callback.is_null());
3362
3363 if (!*get_file_info_result) {
3364 callback.Run(DRIVE_FILE_ERROR_NOT_FOUND, scoped_ptr<DriveEntryProto>());
3365 return;
3366 }
3367
3368 PlatformFileInfoProto entry_file_info;
3369 DriveEntry::ConvertPlatformFileInfoToProto(*file_info, &entry_file_info);
3370 *entry_proto->mutable_file_info() = entry_file_info;
3371 callback.Run(DRIVE_FILE_OK, entry_proto.Pass());
3372 }
3373
3374 } // namespace gdata
OLDNEW
« no previous file with comments | « chrome/browser/chromeos/gdata/gdata_file_system.h ('k') | chrome/browser/chromeos/gdata/gdata_file_system_interface.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698