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