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