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