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/drive/change_list_loader.h" 6 7 #include <set> 8 9 #include "base/callback.h" 10 #include "base/callback_helpers.h" 11 #include "base/metrics/histogram.h" 12 #include "base/strings/string_number_conversions.h" 13 #include "chrome/browser/chromeos/drive/change_list_loader_observer.h" 14 #include "chrome/browser/chromeos/drive/change_list_processor.h" 15 #include "chrome/browser/chromeos/drive/file_system_util.h" 16 #include "chrome/browser/chromeos/drive/job_scheduler.h" 17 #include "chrome/browser/chromeos/drive/logging.h" 18 #include "chrome/browser/chromeos/drive/resource_metadata.h" 19 #include "chrome/browser/drive/drive_api_util.h" 20 #include "chrome/browser/google_apis/drive_api_parser.h" 21 #include "content/public/browser/browser_thread.h" 22 #include "url/gurl.h" 23 24 using content::BrowserThread; 25 26 namespace drive { 27 namespace internal { 28 29 ChangeListLoader::ChangeListLoader( 30 base::SequencedTaskRunner* blocking_task_runner, 31 ResourceMetadata* resource_metadata, 32 JobScheduler* scheduler) 33 : blocking_task_runner_(blocking_task_runner), 34 resource_metadata_(resource_metadata), 35 scheduler_(scheduler), 36 last_known_remote_changestamp_(0), 37 loaded_(false), 38 weak_ptr_factory_(this) { 39 } 40 41 ChangeListLoader::~ChangeListLoader() { 42 } 43 44 bool ChangeListLoader::IsRefreshing() const { 45 // Callback for change list loading is stored in pending_load_callback_[""]. 46 // It is non-empty if and only if there is an in-flight loading operation. 47 return pending_load_callback_.find("") != pending_load_callback_.end(); 48 } 49 50 void ChangeListLoader::AddObserver(ChangeListLoaderObserver* observer) { 51 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 52 observers_.AddObserver(observer); 53 } 54 55 void ChangeListLoader::RemoveObserver(ChangeListLoaderObserver* observer) { 56 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 57 observers_.RemoveObserver(observer); 58 } 59 60 void ChangeListLoader::CheckForUpdates(const FileOperationCallback& callback) { 61 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 62 DCHECK(!callback.is_null()); 63 64 if (IsRefreshing()) { 65 // There is in-flight loading. So keep the callback here, and check for 66 // updates when the in-flight loading is completed. 67 pending_update_check_callback_ = callback; 68 return; 69 } 70 71 if (loaded_) { 72 // We only start to check for updates iff the load is done. 73 // I.e., we ignore checking updates if not loaded to avoid starting the 74 // load without user's explicit interaction (such as opening Drive). 75 util::Log(logging::LOG_INFO, "Checking for updates"); 76 Load(DirectoryFetchInfo(), callback); 77 } 78 } 79 80 void ChangeListLoader::LoadIfNeeded( 81 const DirectoryFetchInfo& directory_fetch_info, 82 const FileOperationCallback& callback) { 83 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 84 DCHECK(!callback.is_null()); 85 86 // If the resource metadata has been already loaded, for normal change list 87 // fetch (= empty directory_fetch_info), we have nothing to do. For "fast 88 // fetch", we need to schedule a fetching if a refresh is currently 89 // running, because we don't want to wait a possibly large delta change 90 // list to arrive. 91 if (loaded_ && (directory_fetch_info.empty() || !IsRefreshing())) { 92 base::MessageLoopProxy::current()->PostTask( 93 FROM_HERE, 94 base::Bind(callback, FILE_ERROR_OK)); 95 return; 96 } 97 Load(directory_fetch_info, callback); 98 } 99 100 void ChangeListLoader::LoadDirectoryFromServer( 101 const std::string& directory_resource_id, 102 const FileOperationCallback& callback) { 103 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 104 DCHECK(!callback.is_null()); 105 106 scheduler_->GetAboutResource( 107 base::Bind(&ChangeListLoader::LoadDirectoryFromServerAfterGetAbout, 108 weak_ptr_factory_.GetWeakPtr(), 109 directory_resource_id, 110 callback)); 111 } 112 113 void ChangeListLoader::Load(const DirectoryFetchInfo& directory_fetch_info, 114 const FileOperationCallback& callback) { 115 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 116 DCHECK(!callback.is_null()); 117 118 // Check if this is the first time this ChangeListLoader do loading. 119 // Note: IsRefreshing() depends on pending_load_callback_ so check in advance. 120 const bool is_initial_load = (!loaded_ && !IsRefreshing()); 121 122 // Register the callback function to be called when it is loaded. 123 const std::string& resource_id = directory_fetch_info.resource_id(); 124 pending_load_callback_[resource_id].push_back(callback); 125 126 // If loading task for |resource_id| is already running, do nothing. 127 if (pending_load_callback_[resource_id].size() > 1) 128 return; 129 130 // For initial loading, even for directory fetching, we do load the full 131 // resource list from the server to sync up. So we register a dummy 132 // callback to indicate that update for full hierarchy is running. 133 if (is_initial_load && !resource_id.empty()) { 134 pending_load_callback_[""].push_back( 135 base::Bind(&util::EmptyFileOperationCallback)); 136 } 137 138 // Check the current status of local metadata, and start loading if needed. 139 resource_metadata_->GetLargestChangestampOnUIThread( 140 base::Bind(is_initial_load ? &ChangeListLoader::DoInitialLoad 141 : &ChangeListLoader::DoUpdateLoad, 142 weak_ptr_factory_.GetWeakPtr(), 143 directory_fetch_info)); 144 } 145 146 void ChangeListLoader::DoInitialLoad( 147 const DirectoryFetchInfo& directory_fetch_info, 148 int64 local_changestamp) { 149 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 150 151 if (local_changestamp > 0) { 152 // The local data is usable. Flush callbacks to tell loading was successful. 153 OnChangeListLoadComplete(FILE_ERROR_OK); 154 155 // Continues to load from server in background. 156 // Put dummy callbacks to indicate that fetching is still continuing. 157 pending_load_callback_[directory_fetch_info.resource_id()].push_back( 158 base::Bind(&util::EmptyFileOperationCallback)); 159 if (!directory_fetch_info.empty()) { 160 pending_load_callback_[""].push_back( 161 base::Bind(&util::EmptyFileOperationCallback)); 162 } 163 } 164 LoadFromServerIfNeeded(directory_fetch_info, local_changestamp); 165 } 166 167 void ChangeListLoader::DoUpdateLoad( 168 const DirectoryFetchInfo& directory_fetch_info, 169 int64 local_changestamp) { 170 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 171 172 if (directory_fetch_info.empty()) { 173 LoadFromServerIfNeeded(directory_fetch_info, local_changestamp); 174 } else { 175 // Note: CheckChangestampAndLoadDirectoryIfNeeded regards 176 // last_know_remote_changestamp_ as the remote changestamp. To be precise, 177 // we need to call GetAboutResource() here, as we do in other places like 178 // LoadFromServerIfNeeded or LoadFromDirectory. However, 179 // - It is costly to do GetAboutResource HTTP request every time. 180 // - The chance using an old value is small; it only happens when 181 // LoadIfNeeded is called during one GetAboutResource roundtrip time 182 // of a change list fetching. 183 // - Even if the value is old, it just marks the directory as older. It may 184 // trigger one future unnecessary re-fetch, but it'll never lose data. 185 CheckChangestampAndLoadDirectoryIfNeeded( 186 directory_fetch_info, 187 local_changestamp, 188 base::Bind(&ChangeListLoader::OnDirectoryLoadComplete, 189 weak_ptr_factory_.GetWeakPtr(), 190 directory_fetch_info)); 191 } 192 } 193 194 void ChangeListLoader::OnChangeListLoadComplete(FileError error) { 195 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 196 197 if (!loaded_ && error == FILE_ERROR_OK) { 198 loaded_ = true; 199 FOR_EACH_OBSERVER(ChangeListLoaderObserver, 200 observers_, 201 OnInitialLoadComplete()); 202 } 203 204 for (LoadCallbackMap::iterator it = pending_load_callback_.begin(); 205 it != pending_load_callback_.end(); ++it) { 206 const std::vector<FileOperationCallback>& callbacks = it->second; 207 for (size_t i = 0; i < callbacks.size(); ++i) { 208 base::MessageLoopProxy::current()->PostTask( 209 FROM_HERE, 210 base::Bind(callbacks[i], error)); 211 } 212 } 213 pending_load_callback_.clear(); 214 215 // If there is pending update check, try to load the change from the server 216 // again, because there may exist an update during the completed loading. 217 if (!pending_update_check_callback_.is_null()) { 218 Load(DirectoryFetchInfo(), 219 base::ResetAndReturn(&pending_update_check_callback_)); 220 } 221 } 222 223 void ChangeListLoader::OnDirectoryLoadComplete( 224 const DirectoryFetchInfo& directory_fetch_info, 225 FileError error) { 226 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 227 228 util::Log(logging::LOG_INFO, 229 "Fast-fetch complete: %s => %s", 230 directory_fetch_info.ToString().c_str(), 231 FileErrorToString(error).c_str()); 232 const std::string& resource_id = directory_fetch_info.resource_id(); 233 LoadCallbackMap::iterator it = pending_load_callback_.find(resource_id); 234 if (it != pending_load_callback_.end()) { 235 DVLOG(1) << "Running callback for " << resource_id; 236 const std::vector<FileOperationCallback>& callbacks = it->second; 237 for (size_t i = 0; i < callbacks.size(); ++i) { 238 base::MessageLoopProxy::current()->PostTask( 239 FROM_HERE, 240 base::Bind(callbacks[i], error)); 241 } 242 pending_load_callback_.erase(it); 243 } 244 } 245 246 void ChangeListLoader::LoadFromServerIfNeeded( 247 const DirectoryFetchInfo& directory_fetch_info, 248 int64 local_changestamp) { 249 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 250 251 // First fetch the latest changestamp to see if there were any new changes 252 // there at all. 253 scheduler_->GetAboutResource( 254 base::Bind(&ChangeListLoader::LoadFromServerIfNeededAfterGetAbout, 255 weak_ptr_factory_.GetWeakPtr(), 256 directory_fetch_info, 257 local_changestamp)); 258 } 259 260 void ChangeListLoader::LoadFromServerIfNeededAfterGetAbout( 261 const DirectoryFetchInfo& directory_fetch_info, 262 int64 local_changestamp, 263 google_apis::GDataErrorCode status, 264 scoped_ptr<google_apis::AboutResource> about_resource) { 265 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 266 DCHECK_EQ(GDataToFileError(status) == FILE_ERROR_OK, 267 about_resource.get() != NULL); 268 269 if (GDataToFileError(status) == FILE_ERROR_OK) { 270 DCHECK(about_resource); 271 last_known_remote_changestamp_ = about_resource->largest_change_id(); 272 } 273 274 int64 remote_changestamp = 275 about_resource ? about_resource->largest_change_id() : 0; 276 if (remote_changestamp > 0 && local_changestamp >= remote_changestamp) { 277 if (local_changestamp > remote_changestamp) { 278 LOG(WARNING) << "Local resource metadata is fresher than server, local = " 279 << local_changestamp 280 << ", server = " 281 << remote_changestamp; 282 } 283 284 // No changes detected, tell the client that the loading was successful. 285 OnChangeListLoadComplete(FILE_ERROR_OK); 286 return; 287 } 288 289 int64 start_changestamp = local_changestamp > 0 ? local_changestamp + 1 : 0; 290 if (start_changestamp == 0 && !about_resource.get()) { 291 // Full update needs AboutResource. If this is a full update, we should 292 // just give up. Note that to exit from the change list loading, we 293 // always have to flush the pending callback tasks via 294 // OnChangeListLoadComplete. 295 OnChangeListLoadComplete(FILE_ERROR_FAILED); 296 return; 297 } 298 299 if (directory_fetch_info.empty()) { 300 // If the caller is not interested in a particular directory, just start 301 // loading the change list. 302 LoadChangeListFromServer(about_resource.Pass(), start_changestamp); 303 } else { 304 // If the caller is interested in a particular directory, start loading the 305 // directory first. 306 CheckChangestampAndLoadDirectoryIfNeeded( 307 directory_fetch_info, 308 local_changestamp, 309 base::Bind( 310 &ChangeListLoader::LoadFromServerIfNeededAfterLoadDirectory, 311 weak_ptr_factory_.GetWeakPtr(), 312 directory_fetch_info, 313 base::Passed(&about_resource), 314 start_changestamp)); 315 } 316 } 317 318 void ChangeListLoader::LoadFromServerIfNeededAfterLoadDirectory( 319 const DirectoryFetchInfo& directory_fetch_info, 320 scoped_ptr<google_apis::AboutResource> about_resource, 321 int64 start_changestamp, 322 FileError error) { 323 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 324 325 if (error == FILE_ERROR_OK) { 326 // The directory fast-fetch succeeded. Runs the callbacks waiting for the 327 // directory loading. If failed, do not flush so they're run after the 328 // change list loading is complete. 329 OnDirectoryLoadComplete(directory_fetch_info, FILE_ERROR_OK); 330 } 331 LoadChangeListFromServer(about_resource.Pass(), start_changestamp); 332 } 333 334 void ChangeListLoader::LoadChangeListFromServer( 335 scoped_ptr<google_apis::AboutResource> about_resource, 336 int64 start_changestamp) { 337 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 338 339 bool is_delta_update = start_changestamp != 0; 340 const LoadChangeListCallback& completion_callback = 341 base::Bind(&ChangeListLoader::LoadChangeListFromServerAfterLoadChangeList, 342 weak_ptr_factory_.GetWeakPtr(), 343 base::Passed(&about_resource), 344 is_delta_update); 345 base::TimeTicks start_time = base::TimeTicks::Now(); 346 if (is_delta_update) { 347 scheduler_->GetChangeList( 348 start_changestamp, 349 base::Bind(&ChangeListLoader::OnGetChangeList, 350 weak_ptr_factory_.GetWeakPtr(), 351 base::Passed(ScopedVector<ChangeList>()), 352 completion_callback, 353 start_time)); 354 } else { 355 // This is full resource list fetch. 356 scheduler_->GetAllResourceList( 357 base::Bind(&ChangeListLoader::OnGetChangeList, 358 weak_ptr_factory_.GetWeakPtr(), 359 base::Passed(ScopedVector<ChangeList>()), 360 completion_callback, 361 start_time)); 362 } 363 } 364 365 void ChangeListLoader::OnGetChangeList( 366 ScopedVector<ChangeList> change_lists, 367 const LoadChangeListCallback& callback, 368 base::TimeTicks start_time, 369 google_apis::GDataErrorCode status, 370 scoped_ptr<google_apis::ResourceList> resource_list) { 371 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 372 DCHECK(!callback.is_null()); 373 374 // Looks the UMA stats we take here is useless as many methods use this 375 // callback. crbug.com/229407 376 if (change_lists.empty()) { 377 UMA_HISTOGRAM_TIMES("Drive.InitialFeedLoadTime", 378 base::TimeTicks::Now() - start_time); 379 } 380 381 FileError error = GDataToFileError(status); 382 if (error != FILE_ERROR_OK) { 383 callback.Run(ScopedVector<ChangeList>(), error); 384 return; 385 } 386 387 // Add the current change list to the list of collected lists. 388 DCHECK(resource_list); 389 change_lists.push_back(new ChangeList(*resource_list)); 390 391 GURL next_url; 392 if (resource_list->GetNextFeedURL(&next_url) && 393 !next_url.is_empty()) { 394 // There is the remaining result so fetch it. 395 scheduler_->ContinueGetResourceList( 396 next_url, 397 base::Bind(&ChangeListLoader::OnGetChangeList, 398 weak_ptr_factory_.GetWeakPtr(), 399 base::Passed(&change_lists), 400 callback, 401 start_time)); 402 return; 403 } 404 405 // This UMA stats looks also different from what we want. crbug.com/229407 406 UMA_HISTOGRAM_TIMES("Drive.EntireFeedLoadTime", 407 base::TimeTicks::Now() - start_time); 408 409 // Run the callback so the client can process the retrieved change lists. 410 callback.Run(change_lists.Pass(), FILE_ERROR_OK); 411 } 412 413 void ChangeListLoader::LoadChangeListFromServerAfterLoadChangeList( 414 scoped_ptr<google_apis::AboutResource> about_resource, 415 bool is_delta_update, 416 ScopedVector<ChangeList> change_lists, 417 FileError error) { 418 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 419 420 if (error != FILE_ERROR_OK) { 421 OnChangeListLoadComplete(error); 422 return; 423 } 424 425 UpdateFromChangeList( 426 about_resource.Pass(), 427 change_lists.Pass(), 428 is_delta_update, 429 base::Bind(&ChangeListLoader::LoadChangeListFromServerAfterUpdate, 430 weak_ptr_factory_.GetWeakPtr())); 431 } 432 433 void ChangeListLoader::LoadChangeListFromServerAfterUpdate() { 434 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 435 436 OnChangeListLoadComplete(FILE_ERROR_OK); 437 438 FOR_EACH_OBSERVER(ChangeListLoaderObserver, 439 observers_, 440 OnLoadFromServerComplete()); 441 } 442 443 void ChangeListLoader::LoadDirectoryFromServerAfterGetAbout( 444 const std::string& directory_resource_id, 445 const FileOperationCallback& callback, 446 google_apis::GDataErrorCode status, 447 scoped_ptr<google_apis::AboutResource> about_resource) { 448 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 449 DCHECK(!callback.is_null()); 450 451 if (GDataToFileError(status) == FILE_ERROR_OK) 452 last_known_remote_changestamp_ = about_resource->largest_change_id(); 453 454 DoLoadDirectoryFromServer( 455 DirectoryFetchInfo(directory_resource_id, last_known_remote_changestamp_), 456 callback); 457 } 458 459 void ChangeListLoader::CheckChangestampAndLoadDirectoryIfNeeded( 460 const DirectoryFetchInfo& directory_fetch_info, 461 int64 local_changestamp, 462 const FileOperationCallback& callback) { 463 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 464 DCHECK(!directory_fetch_info.empty()); 465 466 int64 directory_changestamp = std::max(directory_fetch_info.changestamp(), 467 local_changestamp); 468 469 // We may not fetch from the server at all if the local metadata is new 470 // enough, but we log this message here, so "Fast-fetch start" and 471 // "Fast-fetch complete" always match. 472 // TODO(satorux): Distinguish the "not fetching at all" case. 473 util::Log(logging::LOG_INFO, 474 "Fast-fetch start: %s; Server changestamp: %s", 475 directory_fetch_info.ToString().c_str(), 476 base::Int64ToString(last_known_remote_changestamp_).c_str()); 477 478 // If the directory's changestamp is up-to-date, just schedule to run the 479 // callback, as there is no need to fetch the directory. 480 // Note that |last_known_remote_changestamp_| is 0 when it is not received 481 // yet. In that case we conservatively assume that we need to fetch. 482 if (last_known_remote_changestamp_ > 0 && 483 directory_changestamp >= last_known_remote_changestamp_) { 484 callback.Run(FILE_ERROR_OK); 485 return; 486 } 487 488 // Start fetching the directory content, and mark it with the changestamp 489 // |last_known_remote_changestamp_|. 490 DirectoryFetchInfo new_directory_fetch_info( 491 directory_fetch_info.resource_id(), 492 std::max(directory_changestamp, last_known_remote_changestamp_)); 493 DoLoadDirectoryFromServer(new_directory_fetch_info, callback); 494 } 495 496 void ChangeListLoader::DoLoadDirectoryFromServer( 497 const DirectoryFetchInfo& directory_fetch_info, 498 const FileOperationCallback& callback) { 499 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 500 DCHECK(!callback.is_null()); 501 DCHECK(!directory_fetch_info.empty()); 502 DVLOG(1) << "Start loading directory: " << directory_fetch_info.ToString(); 503 504 if (directory_fetch_info.resource_id() == 505 util::kDriveOtherDirSpecialResourceId) { 506 // Load for a <other> directory is meaningless in the server. 507 // Let it succeed and use what we have locally. 508 callback.Run(FILE_ERROR_OK); 509 return; 510 } 511 512 if (directory_fetch_info.resource_id() == 513 util::kDriveGrandRootSpecialResourceId) { 514 // Load for a grand root directory means slightly different from other 515 // directories. It should have two directories; <other> and mydrive root. 516 // <other> directory should always exist, but mydrive root should be 517 // created by root resource id retrieved from the server. 518 // Here, we check if mydrive root exists, and if not, create it. 519 resource_metadata_->GetResourceEntryByPathOnUIThread( 520 base::FilePath(util::GetDriveMyDriveRootPath()), 521 base::Bind( 522 &ChangeListLoader 523 ::DoLoadGrandRootDirectoryFromServerAfterGetResourceEntryByPath, 524 weak_ptr_factory_.GetWeakPtr(), 525 directory_fetch_info, 526 callback)); 527 return; 528 } 529 530 const LoadChangeListCallback& completion_callback = 531 base::Bind(&ChangeListLoader::DoLoadDirectoryFromServerAfterLoad, 532 weak_ptr_factory_.GetWeakPtr(), 533 directory_fetch_info, 534 callback); 535 base::TimeTicks start_time = base::TimeTicks::Now(); 536 scheduler_->GetResourceListInDirectory( 537 directory_fetch_info.resource_id(), 538 base::Bind(&ChangeListLoader::OnGetChangeList, 539 weak_ptr_factory_.GetWeakPtr(), 540 base::Passed(ScopedVector<ChangeList>()), 541 completion_callback, 542 start_time)); 543 } 544 545 void 546 ChangeListLoader::DoLoadGrandRootDirectoryFromServerAfterGetResourceEntryByPath( 547 const DirectoryFetchInfo& directory_fetch_info, 548 const FileOperationCallback& callback, 549 FileError error, 550 scoped_ptr<ResourceEntry> entry) { 551 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 552 DCHECK(!callback.is_null()); 553 DCHECK_EQ(directory_fetch_info.resource_id(), 554 util::kDriveGrandRootSpecialResourceId); 555 556 if (error == FILE_ERROR_OK) { 557 // MyDrive root already exists. Just return success. 558 callback.Run(FILE_ERROR_OK); 559 return; 560 } 561 562 // Fetch root resource id from the server. 563 scheduler_->GetAboutResource( 564 base::Bind( 565 &ChangeListLoader 566 ::DoLoadGrandRootDirectoryFromServerAfterGetAboutResource, 567 weak_ptr_factory_.GetWeakPtr(), 568 directory_fetch_info, 569 callback)); 570 } 571 572 void ChangeListLoader::DoLoadGrandRootDirectoryFromServerAfterGetAboutResource( 573 const DirectoryFetchInfo& directory_fetch_info, 574 const FileOperationCallback& callback, 575 google_apis::GDataErrorCode status, 576 scoped_ptr<google_apis::AboutResource> about_resource) { 577 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 578 DCHECK(!callback.is_null()); 579 DCHECK_EQ(directory_fetch_info.resource_id(), 580 util::kDriveGrandRootSpecialResourceId); 581 582 FileError error = GDataToFileError(status); 583 if (error != FILE_ERROR_OK) { 584 callback.Run(error); 585 return; 586 } 587 588 // Build entry map for grand root directory, which has two entries; 589 // "/drive/root" and "/drive/other". 590 ResourceEntryMap grand_root_entry_map; 591 const std::string& root_resource_id = about_resource->root_folder_id(); 592 grand_root_entry_map[root_resource_id] = 593 util::CreateMyDriveRootEntry(root_resource_id); 594 grand_root_entry_map[util::kDriveOtherDirSpecialResourceId] = 595 util::CreateOtherDirEntry(); 596 resource_metadata_->RefreshDirectoryOnUIThread( 597 directory_fetch_info, 598 grand_root_entry_map, 599 base::Bind(&ChangeListLoader::DoLoadDirectoryFromServerAfterRefresh, 600 weak_ptr_factory_.GetWeakPtr(), 601 directory_fetch_info, 602 callback)); 603 } 604 605 void ChangeListLoader::DoLoadDirectoryFromServerAfterLoad( 606 const DirectoryFetchInfo& directory_fetch_info, 607 const FileOperationCallback& callback, 608 ScopedVector<ChangeList> change_lists, 609 FileError error) { 610 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 611 DCHECK(!callback.is_null()); 612 DCHECK(!directory_fetch_info.empty()); 613 614 if (error != FILE_ERROR_OK) { 615 LOG(ERROR) << "Failed to load directory: " 616 << directory_fetch_info.resource_id() 617 << ": " << FileErrorToString(error); 618 callback.Run(error); 619 return; 620 } 621 622 ChangeListProcessor::ResourceEntryMap entry_map; 623 ChangeListProcessor::ConvertToMap(change_lists.Pass(), &entry_map, NULL); 624 resource_metadata_->RefreshDirectoryOnUIThread( 625 directory_fetch_info, 626 entry_map, 627 base::Bind(&ChangeListLoader::DoLoadDirectoryFromServerAfterRefresh, 628 weak_ptr_factory_.GetWeakPtr(), 629 directory_fetch_info, 630 callback)); 631 } 632 633 void ChangeListLoader::DoLoadDirectoryFromServerAfterRefresh( 634 const DirectoryFetchInfo& directory_fetch_info, 635 const FileOperationCallback& callback, 636 FileError error, 637 const base::FilePath& directory_path) { 638 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 639 DCHECK(!callback.is_null()); 640 641 DVLOG(1) << "Directory loaded: " << directory_fetch_info.ToString(); 642 callback.Run(error); 643 // Also notify the observers. 644 if (error == FILE_ERROR_OK) { 645 FOR_EACH_OBSERVER(ChangeListLoaderObserver, observers_, 646 OnDirectoryChanged(directory_path)); 647 } 648 } 649 650 void ChangeListLoader::UpdateFromChangeList( 651 scoped_ptr<google_apis::AboutResource> about_resource, 652 ScopedVector<ChangeList> change_lists, 653 bool is_delta_update, 654 const base::Closure& callback) { 655 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 656 DCHECK(!callback.is_null()); 657 658 ChangeListProcessor* change_list_processor = 659 new ChangeListProcessor(resource_metadata_); 660 // Don't send directory content change notification while performing 661 // the initial content retrieval. 662 const bool should_notify_changed_directories = is_delta_update; 663 664 util::Log(logging::LOG_INFO, 665 "Apply change lists (is delta: %d)", 666 is_delta_update); 667 blocking_task_runner_->PostTaskAndReply( 668 FROM_HERE, 669 base::Bind(&ChangeListProcessor::Apply, 670 base::Unretained(change_list_processor), 671 base::Passed(&about_resource), 672 base::Passed(&change_lists), 673 is_delta_update), 674 base::Bind(&ChangeListLoader::UpdateFromChangeListAfterApply, 675 weak_ptr_factory_.GetWeakPtr(), 676 base::Owned(change_list_processor), 677 should_notify_changed_directories, 678 base::Time::Now(), 679 callback)); 680 } 681 682 void ChangeListLoader::UpdateFromChangeListAfterApply( 683 ChangeListProcessor* change_list_processor, 684 bool should_notify_changed_directories, 685 base::Time start_time, 686 const base::Closure& callback) { 687 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 688 DCHECK(change_list_processor); 689 DCHECK(!callback.is_null()); 690 691 const base::TimeDelta elapsed = base::Time::Now() - start_time; 692 util::Log(logging::LOG_INFO, 693 "Change lists applied (elapsed time: %sms)", 694 base::Int64ToString(elapsed.InMilliseconds()).c_str()); 695 696 if (should_notify_changed_directories) { 697 for (std::set<base::FilePath>::iterator dir_iter = 698 change_list_processor->changed_dirs().begin(); 699 dir_iter != change_list_processor->changed_dirs().end(); 700 ++dir_iter) { 701 FOR_EACH_OBSERVER(ChangeListLoaderObserver, observers_, 702 OnDirectoryChanged(*dir_iter)); 703 } 704 } 705 706 callback.Run(); 707 } 708 709 } // namespace internal 710 } // namespace drive 711