change_list_loader.cc revision 8bcbed890bc3ce4d7a057a8f32cab53fa534672e
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/drive/drive_service_interface.h" 21#include "chrome/browser/google_apis/drive_api_parser.h" 22#include "content/public/browser/browser_thread.h" 23#include "url/gurl.h" 24 25using content::BrowserThread; 26 27namespace drive { 28namespace internal { 29 30typedef base::Callback<void(FileError, ScopedVector<ChangeList>)> 31 FeedFetcherCallback; 32 33class ChangeListLoader::FeedFetcher { 34 public: 35 virtual ~FeedFetcher() {} 36 virtual void Run(const FeedFetcherCallback& callback) = 0; 37}; 38 39namespace { 40 41// Fetches all the (currently available) resource entries from the server. 42class FullFeedFetcher : public ChangeListLoader::FeedFetcher { 43 public: 44 explicit FullFeedFetcher(JobScheduler* scheduler) 45 : scheduler_(scheduler), 46 weak_ptr_factory_(this) { 47 } 48 49 virtual ~FullFeedFetcher() { 50 } 51 52 virtual void Run(const FeedFetcherCallback& callback) OVERRIDE { 53 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 54 DCHECK(!callback.is_null()); 55 56 // Rememeber the time stamp for usage stats. 57 start_time_ = base::TimeTicks::Now(); 58 59 // This is full resource list fetch. 60 scheduler_->GetAllResourceList( 61 base::Bind(&FullFeedFetcher::OnFileListFetched, 62 weak_ptr_factory_.GetWeakPtr(), callback)); 63 } 64 65 private: 66 void OnFileListFetched( 67 const FeedFetcherCallback& callback, 68 google_apis::GDataErrorCode status, 69 scoped_ptr<google_apis::ResourceList> resource_list) { 70 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 71 DCHECK(!callback.is_null()); 72 73 // Looks the UMA stats we take here is useless as many methods use this 74 // callback. crbug.com/229407 75 if (change_lists_.empty()) { 76 UMA_HISTOGRAM_TIMES("Drive.InitialFeedLoadTime", 77 base::TimeTicks::Now() - start_time_); 78 } 79 80 FileError error = GDataToFileError(status); 81 if (error != FILE_ERROR_OK) { 82 callback.Run(error, ScopedVector<ChangeList>()); 83 return; 84 } 85 86 DCHECK(resource_list); 87 change_lists_.push_back(new ChangeList(*resource_list)); 88 89 GURL next_url; 90 if (resource_list->GetNextFeedURL(&next_url) && !next_url.is_empty()) { 91 // There is the remaining result so fetch it. 92 scheduler_->GetRemainingFileList( 93 next_url, 94 base::Bind(&FullFeedFetcher::OnFileListFetched, 95 weak_ptr_factory_.GetWeakPtr(), callback)); 96 return; 97 } 98 99 // This UMA stats looks also different from what we want. crbug.com/229407 100 UMA_HISTOGRAM_TIMES("Drive.EntireFeedLoadTime", 101 base::TimeTicks::Now() - start_time_); 102 103 // Note: The fetcher is managed by ChangeListLoader, and the instance 104 // will be deleted in the callback. Do not touch the fields after this 105 // invocation. 106 callback.Run(FILE_ERROR_OK, change_lists_.Pass()); 107 } 108 109 JobScheduler* scheduler_; 110 ScopedVector<ChangeList> change_lists_; 111 base::TimeTicks start_time_; 112 base::WeakPtrFactory<FullFeedFetcher> weak_ptr_factory_; 113 DISALLOW_COPY_AND_ASSIGN(FullFeedFetcher); 114}; 115 116// Fetches the delta changes since |start_change_id|. 117class DeltaFeedFetcher : public ChangeListLoader::FeedFetcher { 118 public: 119 DeltaFeedFetcher(JobScheduler* scheduler, int64 start_change_id) 120 : scheduler_(scheduler), 121 start_change_id_(start_change_id), 122 weak_ptr_factory_(this) { 123 } 124 125 virtual ~DeltaFeedFetcher() { 126 } 127 128 virtual void Run(const FeedFetcherCallback& callback) OVERRIDE { 129 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 130 DCHECK(!callback.is_null()); 131 132 scheduler_->GetChangeList( 133 start_change_id_, 134 base::Bind(&DeltaFeedFetcher::OnChangeListFetched, 135 weak_ptr_factory_.GetWeakPtr(), callback)); 136 } 137 138 private: 139 void OnChangeListFetched( 140 const FeedFetcherCallback& callback, 141 google_apis::GDataErrorCode status, 142 scoped_ptr<google_apis::ResourceList> resource_list) { 143 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 144 DCHECK(!callback.is_null()); 145 146 FileError error = GDataToFileError(status); 147 if (error != FILE_ERROR_OK) { 148 callback.Run(error, ScopedVector<ChangeList>()); 149 return; 150 } 151 152 DCHECK(resource_list); 153 change_lists_.push_back(new ChangeList(*resource_list)); 154 155 GURL next_url; 156 if (resource_list->GetNextFeedURL(&next_url) && !next_url.is_empty()) { 157 // There is the remaining result so fetch it. 158 scheduler_->GetRemainingChangeList( 159 next_url, 160 base::Bind(&DeltaFeedFetcher::OnChangeListFetched, 161 weak_ptr_factory_.GetWeakPtr(), callback)); 162 return; 163 } 164 165 // Note: The fetcher is managed by ChangeListLoader, and the instance 166 // will be deleted in the callback. Do not touch the fields after this 167 // invocation. 168 callback.Run(FILE_ERROR_OK, change_lists_.Pass()); 169 } 170 171 JobScheduler* scheduler_; 172 int64 start_change_id_; 173 ScopedVector<ChangeList> change_lists_; 174 base::WeakPtrFactory<DeltaFeedFetcher> weak_ptr_factory_; 175 DISALLOW_COPY_AND_ASSIGN(DeltaFeedFetcher); 176}; 177 178// Fetches the resource entries in the directory with |directory_resource_id|. 179class FastFetchFeedFetcher : public ChangeListLoader::FeedFetcher { 180 public: 181 FastFetchFeedFetcher(JobScheduler* scheduler, 182 DriveServiceInterface* drive_service, 183 const std::string& directory_resource_id, 184 const std::string& root_folder_id) 185 : scheduler_(scheduler), 186 drive_service_(drive_service), 187 directory_resource_id_(directory_resource_id), 188 root_folder_id_(root_folder_id), 189 weak_ptr_factory_(this) { 190 } 191 192 virtual ~FastFetchFeedFetcher() { 193 } 194 195 virtual void Run(const FeedFetcherCallback& callback) OVERRIDE { 196 if (util::IsDriveV2ApiEnabled() && root_folder_id_.empty()) { 197 // The root folder id is not available yet. Fetch from the server. 198 scheduler_->GetAboutResource( 199 base::Bind(&FastFetchFeedFetcher::RunAfterGetAboutResource, 200 weak_ptr_factory_.GetWeakPtr(), callback)); 201 return; 202 } 203 204 StartGetResourceListInDirectory(callback); 205 } 206 207 private: 208 void RunAfterGetAboutResource( 209 const FeedFetcherCallback& callback, 210 google_apis::GDataErrorCode status, 211 scoped_ptr<google_apis::AboutResource> about_resource) { 212 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 213 DCHECK(!callback.is_null()); 214 215 FileError error = GDataToFileError(status); 216 if (error != FILE_ERROR_OK) { 217 callback.Run(error, ScopedVector<ChangeList>()); 218 return; 219 } 220 221 root_folder_id_ = about_resource->root_folder_id(); 222 StartGetResourceListInDirectory(callback); 223 } 224 225 void StartGetResourceListInDirectory(const FeedFetcherCallback& callback) { 226 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 227 DCHECK(!callback.is_null()); 228 DCHECK(!directory_resource_id_.empty()); 229 DCHECK(!util::IsDriveV2ApiEnabled() || !root_folder_id_.empty()); 230 231 // We use WAPI's GetResourceListInDirectory even if Drive API v2 is 232 // enabled. This is the short term work around of the performance 233 // regression. 234 235 std::string resource_id = directory_resource_id_; 236 if (util::IsDriveV2ApiEnabled() && 237 directory_resource_id_ == root_folder_id_) { 238 // GData WAPI doesn't accept the root directory id which is used in Drive 239 // API v2. So it is necessary to translate it here. 240 resource_id = util::kWapiRootDirectoryResourceId; 241 } 242 243 scheduler_->GetResourceListInDirectoryByWapi( 244 resource_id, 245 base::Bind(&FastFetchFeedFetcher::OnResourceListFetched, 246 weak_ptr_factory_.GetWeakPtr(), callback)); 247 } 248 249 void OnResourceListFetched( 250 const FeedFetcherCallback& callback, 251 google_apis::GDataErrorCode status, 252 scoped_ptr<google_apis::ResourceList> resource_list) { 253 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 254 DCHECK(!callback.is_null()); 255 256 FileError error = GDataToFileError(status); 257 if (error != FILE_ERROR_OK) { 258 callback.Run(error, ScopedVector<ChangeList>()); 259 return; 260 } 261 262 // Add the current change list to the list of collected lists. 263 DCHECK(resource_list); 264 ChangeList* change_list = new ChangeList(*resource_list); 265 if (util::IsDriveV2ApiEnabled()) 266 FixResourceIdInChangeList(change_list); 267 change_lists_.push_back(change_list); 268 269 GURL next_url; 270 if (resource_list->GetNextFeedURL(&next_url) && !next_url.is_empty()) { 271 // There is the remaining result so fetch it. 272 scheduler_->GetRemainingResourceList( 273 next_url, 274 base::Bind(&FastFetchFeedFetcher::OnResourceListFetched, 275 weak_ptr_factory_.GetWeakPtr(), callback)); 276 return; 277 } 278 279 // Note: The fetcher is managed by ChangeListLoader, and the instance 280 // will be deleted in the callback. Do not touch the fields after this 281 // invocation. 282 callback.Run(FILE_ERROR_OK, change_lists_.Pass()); 283 } 284 285 // Fixes resource IDs in |change_list| into the format that |drive_service_| 286 // can understand. Note that |change_list| contains IDs in GData WAPI format 287 // since currently we always use WAPI for fast fetch, regardless of the flag. 288 void FixResourceIdInChangeList(ChangeList* change_list) { 289 std::vector<ResourceEntry>* entries = change_list->mutable_entries(); 290 std::vector<std::string>* parent_resource_ids = 291 change_list->mutable_parent_resource_ids(); 292 for (size_t i = 0; i < entries->size(); ++i) { 293 ResourceEntry* entry = &(*entries)[i]; 294 if (entry->has_resource_id()) 295 entry->set_resource_id(FixResourceId(entry->resource_id())); 296 297 (*parent_resource_ids)[i] = FixResourceId((*parent_resource_ids)[i]); 298 } 299 } 300 301 std::string FixResourceId(const std::string& resource_id) { 302 if (resource_id == util::kWapiRootDirectoryResourceId) 303 return root_folder_id_; 304 return drive_service_->GetResourceIdCanonicalizer().Run(resource_id); 305 } 306 307 JobScheduler* scheduler_; 308 DriveServiceInterface* drive_service_; 309 std::string directory_resource_id_; 310 std::string root_folder_id_; 311 ScopedVector<ChangeList> change_lists_; 312 base::WeakPtrFactory<FastFetchFeedFetcher> weak_ptr_factory_; 313 DISALLOW_COPY_AND_ASSIGN(FastFetchFeedFetcher); 314}; 315 316} // namespace 317 318ChangeListLoader::ChangeListLoader( 319 base::SequencedTaskRunner* blocking_task_runner, 320 ResourceMetadata* resource_metadata, 321 JobScheduler* scheduler, 322 DriveServiceInterface* drive_service) 323 : blocking_task_runner_(blocking_task_runner), 324 resource_metadata_(resource_metadata), 325 scheduler_(scheduler), 326 drive_service_(drive_service), 327 last_known_remote_changestamp_(0), 328 loaded_(false), 329 weak_ptr_factory_(this) { 330} 331 332ChangeListLoader::~ChangeListLoader() { 333 STLDeleteElements(&fast_fetch_feed_fetcher_set_); 334} 335 336bool ChangeListLoader::IsRefreshing() const { 337 // Callback for change list loading is stored in pending_load_callback_[""]. 338 // It is non-empty if and only if there is an in-flight loading operation. 339 return pending_load_callback_.find("") != pending_load_callback_.end(); 340} 341 342void ChangeListLoader::AddObserver(ChangeListLoaderObserver* observer) { 343 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 344 observers_.AddObserver(observer); 345} 346 347void ChangeListLoader::RemoveObserver(ChangeListLoaderObserver* observer) { 348 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 349 observers_.RemoveObserver(observer); 350} 351 352void ChangeListLoader::CheckForUpdates(const FileOperationCallback& callback) { 353 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 354 DCHECK(!callback.is_null()); 355 356 if (IsRefreshing()) { 357 // There is in-flight loading. So keep the callback here, and check for 358 // updates when the in-flight loading is completed. 359 pending_update_check_callback_ = callback; 360 return; 361 } 362 363 if (loaded_) { 364 // We only start to check for updates iff the load is done. 365 // I.e., we ignore checking updates if not loaded to avoid starting the 366 // load without user's explicit interaction (such as opening Drive). 367 util::Log(logging::LOG_INFO, "Checking for updates"); 368 Load(DirectoryFetchInfo(), callback); 369 } 370} 371 372void ChangeListLoader::LoadIfNeeded( 373 const DirectoryFetchInfo& directory_fetch_info, 374 const FileOperationCallback& callback) { 375 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 376 DCHECK(!callback.is_null()); 377 378 // If the resource metadata has been already loaded, for normal change list 379 // fetch (= empty directory_fetch_info), we have nothing to do. For "fast 380 // fetch", we need to schedule a fetching if a refresh is currently 381 // running, because we don't want to wait a possibly large delta change 382 // list to arrive. 383 if (loaded_ && (directory_fetch_info.empty() || !IsRefreshing())) { 384 base::MessageLoopProxy::current()->PostTask( 385 FROM_HERE, 386 base::Bind(callback, FILE_ERROR_OK)); 387 return; 388 } 389 Load(directory_fetch_info, callback); 390} 391 392void ChangeListLoader::Load(const DirectoryFetchInfo& directory_fetch_info, 393 const FileOperationCallback& callback) { 394 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 395 DCHECK(!callback.is_null()); 396 397 // Check if this is the first time this ChangeListLoader do loading. 398 // Note: IsRefreshing() depends on pending_load_callback_ so check in advance. 399 const bool is_initial_load = (!loaded_ && !IsRefreshing()); 400 401 // Register the callback function to be called when it is loaded. 402 const std::string& resource_id = directory_fetch_info.resource_id(); 403 pending_load_callback_[resource_id].push_back(callback); 404 405 // If loading task for |resource_id| is already running, do nothing. 406 if (pending_load_callback_[resource_id].size() > 1) 407 return; 408 409 // For initial loading, even for directory fetching, we do load the full 410 // resource list from the server to sync up. So we register a dummy 411 // callback to indicate that update for full hierarchy is running. 412 if (is_initial_load && !resource_id.empty()) { 413 pending_load_callback_[""].push_back( 414 base::Bind(&util::EmptyFileOperationCallback)); 415 } 416 417 // Check the current status of local metadata, and start loading if needed. 418 base::PostTaskAndReplyWithResult( 419 blocking_task_runner_, 420 FROM_HERE, 421 base::Bind(&ResourceMetadata::GetLargestChangestamp, 422 base::Unretained(resource_metadata_)), 423 base::Bind(is_initial_load ? &ChangeListLoader::DoInitialLoad 424 : &ChangeListLoader::DoUpdateLoad, 425 weak_ptr_factory_.GetWeakPtr(), 426 directory_fetch_info)); 427} 428 429void ChangeListLoader::DoInitialLoad( 430 const DirectoryFetchInfo& directory_fetch_info, 431 int64 local_changestamp) { 432 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 433 434 if (local_changestamp > 0) { 435 // The local data is usable. Flush callbacks to tell loading was successful. 436 OnChangeListLoadComplete(FILE_ERROR_OK); 437 438 // Continues to load from server in background. 439 // Put dummy callbacks to indicate that fetching is still continuing. 440 pending_load_callback_[directory_fetch_info.resource_id()].push_back( 441 base::Bind(&util::EmptyFileOperationCallback)); 442 if (!directory_fetch_info.empty()) { 443 pending_load_callback_[""].push_back( 444 base::Bind(&util::EmptyFileOperationCallback)); 445 } 446 } 447 LoadFromServerIfNeeded(directory_fetch_info, local_changestamp); 448} 449 450void ChangeListLoader::DoUpdateLoad( 451 const DirectoryFetchInfo& directory_fetch_info, 452 int64 local_changestamp) { 453 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 454 455 if (directory_fetch_info.empty()) { 456 LoadFromServerIfNeeded(directory_fetch_info, local_changestamp); 457 } else { 458 // Note: CheckChangestampAndLoadDirectoryIfNeeded regards 459 // last_know_remote_changestamp_ as the remote changestamp. To be precise, 460 // we need to call GetAboutResource() here, as we do in other places like 461 // LoadFromServerIfNeeded or LoadFromDirectory. However, 462 // - It is costly to do GetAboutResource HTTP request every time. 463 // - The chance using an old value is small; it only happens when 464 // LoadIfNeeded is called during one GetAboutResource roundtrip time 465 // of a change list fetching. 466 // - Even if the value is old, it just marks the directory as older. It may 467 // trigger one future unnecessary re-fetch, but it'll never lose data. 468 CheckChangestampAndLoadDirectoryIfNeeded( 469 directory_fetch_info, 470 local_changestamp, 471 base::Bind(&ChangeListLoader::OnDirectoryLoadComplete, 472 weak_ptr_factory_.GetWeakPtr(), 473 directory_fetch_info)); 474 } 475} 476 477void ChangeListLoader::OnChangeListLoadComplete(FileError error) { 478 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 479 480 if (!loaded_ && error == FILE_ERROR_OK) { 481 loaded_ = true; 482 FOR_EACH_OBSERVER(ChangeListLoaderObserver, 483 observers_, 484 OnInitialLoadComplete()); 485 } 486 487 for (LoadCallbackMap::iterator it = pending_load_callback_.begin(); 488 it != pending_load_callback_.end(); ++it) { 489 const std::vector<FileOperationCallback>& callbacks = it->second; 490 for (size_t i = 0; i < callbacks.size(); ++i) { 491 base::MessageLoopProxy::current()->PostTask( 492 FROM_HERE, 493 base::Bind(callbacks[i], error)); 494 } 495 } 496 pending_load_callback_.clear(); 497 498 // If there is pending update check, try to load the change from the server 499 // again, because there may exist an update during the completed loading. 500 if (!pending_update_check_callback_.is_null()) { 501 Load(DirectoryFetchInfo(), 502 base::ResetAndReturn(&pending_update_check_callback_)); 503 } 504} 505 506void ChangeListLoader::OnDirectoryLoadComplete( 507 const DirectoryFetchInfo& directory_fetch_info, 508 FileError error) { 509 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 510 511 util::Log(logging::LOG_INFO, 512 "Fast-fetch complete: %s => %s", 513 directory_fetch_info.ToString().c_str(), 514 FileErrorToString(error).c_str()); 515 const std::string& resource_id = directory_fetch_info.resource_id(); 516 LoadCallbackMap::iterator it = pending_load_callback_.find(resource_id); 517 if (it != pending_load_callback_.end()) { 518 DVLOG(1) << "Running callback for " << resource_id; 519 const std::vector<FileOperationCallback>& callbacks = it->second; 520 for (size_t i = 0; i < callbacks.size(); ++i) { 521 base::MessageLoopProxy::current()->PostTask( 522 FROM_HERE, 523 base::Bind(callbacks[i], error)); 524 } 525 pending_load_callback_.erase(it); 526 } 527} 528 529void ChangeListLoader::LoadFromServerIfNeeded( 530 const DirectoryFetchInfo& directory_fetch_info, 531 int64 local_changestamp) { 532 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 533 534 // First fetch the latest changestamp to see if there were any new changes 535 // there at all. 536 scheduler_->GetAboutResource( 537 base::Bind(&ChangeListLoader::LoadFromServerIfNeededAfterGetAbout, 538 weak_ptr_factory_.GetWeakPtr(), 539 directory_fetch_info, 540 local_changestamp)); 541} 542 543void ChangeListLoader::LoadFromServerIfNeededAfterGetAbout( 544 const DirectoryFetchInfo& directory_fetch_info, 545 int64 local_changestamp, 546 google_apis::GDataErrorCode status, 547 scoped_ptr<google_apis::AboutResource> about_resource) { 548 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 549 550 FileError error = GDataToFileError(status); 551 if (error != FILE_ERROR_OK) { 552 OnChangeListLoadComplete(error); 553 return; 554 } 555 556 DCHECK(about_resource); 557 last_known_remote_changestamp_ = about_resource->largest_change_id(); 558 root_folder_id_ = about_resource->root_folder_id(); 559 560 int64 remote_changestamp = about_resource->largest_change_id(); 561 if (remote_changestamp > 0 && local_changestamp >= remote_changestamp) { 562 if (local_changestamp > remote_changestamp) { 563 LOG(WARNING) << "Local resource metadata is fresher than server, local = " 564 << local_changestamp << ", server = " << remote_changestamp; 565 } 566 567 // No changes detected, tell the client that the loading was successful. 568 OnChangeListLoadComplete(FILE_ERROR_OK); 569 return; 570 } 571 572 int64 start_changestamp = local_changestamp > 0 ? local_changestamp + 1 : 0; 573 if (directory_fetch_info.empty()) { 574 // If the caller is not interested in a particular directory, just start 575 // loading the change list. 576 LoadChangeListFromServer(about_resource.Pass(), start_changestamp); 577 } else { 578 // If the caller is interested in a particular directory, start loading the 579 // directory first. 580 CheckChangestampAndLoadDirectoryIfNeeded( 581 directory_fetch_info, 582 local_changestamp, 583 base::Bind( 584 &ChangeListLoader::LoadFromServerIfNeededAfterLoadDirectory, 585 weak_ptr_factory_.GetWeakPtr(), 586 directory_fetch_info, 587 base::Passed(&about_resource), 588 start_changestamp)); 589 } 590} 591 592void ChangeListLoader::LoadFromServerIfNeededAfterLoadDirectory( 593 const DirectoryFetchInfo& directory_fetch_info, 594 scoped_ptr<google_apis::AboutResource> about_resource, 595 int64 start_changestamp, 596 FileError error) { 597 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 598 DCHECK(about_resource); 599 600 if (error == FILE_ERROR_OK) { 601 // The directory fast-fetch succeeded. Runs the callbacks waiting for the 602 // directory loading. If failed, do not flush so they're run after the 603 // change list loading is complete. 604 OnDirectoryLoadComplete(directory_fetch_info, FILE_ERROR_OK); 605 } 606 LoadChangeListFromServer(about_resource.Pass(), start_changestamp); 607} 608 609void ChangeListLoader::LoadChangeListFromServer( 610 scoped_ptr<google_apis::AboutResource> about_resource, 611 int64 start_changestamp) { 612 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 613 DCHECK(!change_feed_fetcher_); 614 DCHECK(about_resource); 615 616 bool is_delta_update = start_changestamp != 0; 617 618 // Set up feed fetcher. 619 if (is_delta_update) { 620 change_feed_fetcher_.reset( 621 new DeltaFeedFetcher(scheduler_, start_changestamp)); 622 } else { 623 change_feed_fetcher_.reset(new FullFeedFetcher(scheduler_)); 624 } 625 626 change_feed_fetcher_->Run( 627 base::Bind(&ChangeListLoader::LoadChangeListFromServerAfterLoadChangeList, 628 weak_ptr_factory_.GetWeakPtr(), 629 base::Passed(&about_resource), 630 is_delta_update)); 631} 632 633void ChangeListLoader::LoadChangeListFromServerAfterLoadChangeList( 634 scoped_ptr<google_apis::AboutResource> about_resource, 635 bool is_delta_update, 636 FileError error, 637 ScopedVector<ChangeList> change_lists) { 638 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 639 DCHECK(about_resource); 640 641 // Delete the fetcher first. 642 change_feed_fetcher_.reset(); 643 644 if (error != FILE_ERROR_OK) { 645 OnChangeListLoadComplete(error); 646 return; 647 } 648 649 UpdateFromChangeList( 650 about_resource.Pass(), 651 change_lists.Pass(), 652 is_delta_update, 653 base::Bind(&ChangeListLoader::LoadChangeListFromServerAfterUpdate, 654 weak_ptr_factory_.GetWeakPtr())); 655} 656 657void ChangeListLoader::LoadChangeListFromServerAfterUpdate() { 658 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 659 660 OnChangeListLoadComplete(FILE_ERROR_OK); 661 662 FOR_EACH_OBSERVER(ChangeListLoaderObserver, 663 observers_, 664 OnLoadFromServerComplete()); 665} 666 667void ChangeListLoader::CheckChangestampAndLoadDirectoryIfNeeded( 668 const DirectoryFetchInfo& directory_fetch_info, 669 int64 local_changestamp, 670 const FileOperationCallback& callback) { 671 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 672 DCHECK(!directory_fetch_info.empty()); 673 674 int64 directory_changestamp = std::max(directory_fetch_info.changestamp(), 675 local_changestamp); 676 677 // We may not fetch from the server at all if the local metadata is new 678 // enough, but we log this message here, so "Fast-fetch start" and 679 // "Fast-fetch complete" always match. 680 // TODO(satorux): Distinguish the "not fetching at all" case. 681 util::Log(logging::LOG_INFO, 682 "Fast-fetch start: %s; Server changestamp: %s", 683 directory_fetch_info.ToString().c_str(), 684 base::Int64ToString(last_known_remote_changestamp_).c_str()); 685 686 // If the directory's changestamp is up-to-date, just schedule to run the 687 // callback, as there is no need to fetch the directory. 688 // Note that |last_known_remote_changestamp_| is 0 when it is not received 689 // yet. In that case we conservatively assume that we need to fetch. 690 if (last_known_remote_changestamp_ > 0 && 691 directory_changestamp >= last_known_remote_changestamp_) { 692 callback.Run(FILE_ERROR_OK); 693 return; 694 } 695 696 // Start fetching the directory content, and mark it with the changestamp 697 // |last_known_remote_changestamp_|. 698 DirectoryFetchInfo new_directory_fetch_info( 699 directory_fetch_info.resource_id(), 700 std::max(directory_changestamp, last_known_remote_changestamp_)); 701 DoLoadDirectoryFromServer(new_directory_fetch_info, callback); 702} 703 704void ChangeListLoader::DoLoadDirectoryFromServer( 705 const DirectoryFetchInfo& directory_fetch_info, 706 const FileOperationCallback& callback) { 707 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 708 DCHECK(!callback.is_null()); 709 DCHECK(!directory_fetch_info.empty()); 710 DVLOG(1) << "Start loading directory: " << directory_fetch_info.ToString(); 711 712 if (directory_fetch_info.resource_id() == 713 util::kDriveOtherDirSpecialResourceId) { 714 // Load for a <other> directory is meaningless in the server. 715 // Let it succeed and use what we have locally. 716 callback.Run(FILE_ERROR_OK); 717 return; 718 } 719 720 if (directory_fetch_info.resource_id() == 721 util::kDriveGrandRootSpecialResourceId) { 722 // Load for a grand root directory means slightly different from other 723 // directories. It should have two directories; <other> and mydrive root. 724 // <other> directory should always exist, but mydrive root should be 725 // created by root resource id retrieved from the server. 726 // Here, we check if mydrive root exists, and if not, create it. 727 resource_metadata_->GetResourceEntryByPathOnUIThread( 728 base::FilePath(util::GetDriveMyDriveRootPath()), 729 base::Bind( 730 &ChangeListLoader 731 ::DoLoadGrandRootDirectoryFromServerAfterGetResourceEntryByPath, 732 weak_ptr_factory_.GetWeakPtr(), 733 directory_fetch_info, 734 callback)); 735 return; 736 } 737 738 FastFetchFeedFetcher* fetcher = new FastFetchFeedFetcher( 739 scheduler_, 740 drive_service_, 741 directory_fetch_info.resource_id(), 742 root_folder_id_); 743 fast_fetch_feed_fetcher_set_.insert(fetcher); 744 fetcher->Run( 745 base::Bind(&ChangeListLoader::DoLoadDirectoryFromServerAfterLoad, 746 weak_ptr_factory_.GetWeakPtr(), 747 directory_fetch_info, 748 callback, 749 fetcher)); 750} 751 752void 753ChangeListLoader::DoLoadGrandRootDirectoryFromServerAfterGetResourceEntryByPath( 754 const DirectoryFetchInfo& directory_fetch_info, 755 const FileOperationCallback& callback, 756 FileError error, 757 scoped_ptr<ResourceEntry> entry) { 758 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 759 DCHECK(!callback.is_null()); 760 DCHECK_EQ(directory_fetch_info.resource_id(), 761 util::kDriveGrandRootSpecialResourceId); 762 763 if (error == FILE_ERROR_OK) { 764 // MyDrive root already exists. Just return success. 765 callback.Run(FILE_ERROR_OK); 766 return; 767 } 768 769 // Fetch root resource id from the server. 770 scheduler_->GetAboutResource( 771 base::Bind( 772 &ChangeListLoader 773 ::DoLoadGrandRootDirectoryFromServerAfterGetAboutResource, 774 weak_ptr_factory_.GetWeakPtr(), 775 directory_fetch_info, 776 callback)); 777} 778 779void ChangeListLoader::DoLoadGrandRootDirectoryFromServerAfterGetAboutResource( 780 const DirectoryFetchInfo& directory_fetch_info, 781 const FileOperationCallback& callback, 782 google_apis::GDataErrorCode status, 783 scoped_ptr<google_apis::AboutResource> about_resource) { 784 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 785 DCHECK(!callback.is_null()); 786 DCHECK(about_resource); 787 DCHECK_EQ(directory_fetch_info.resource_id(), 788 util::kDriveGrandRootSpecialResourceId); 789 790 FileError error = GDataToFileError(status); 791 if (error != FILE_ERROR_OK) { 792 callback.Run(error); 793 return; 794 } 795 796 // Add "My Drive". 797 const std::string& root_resource_id = about_resource->root_folder_id(); 798 std::string* local_id = new std::string; 799 base::PostTaskAndReplyWithResult( 800 blocking_task_runner_, 801 FROM_HERE, 802 base::Bind(&ResourceMetadata::AddEntry, 803 base::Unretained(resource_metadata_), 804 util::CreateMyDriveRootEntry(root_resource_id), 805 local_id), 806 base::Bind(&ChangeListLoader::DoLoadDirectoryFromServerAfterAddMyDrive, 807 weak_ptr_factory_.GetWeakPtr(), 808 directory_fetch_info, 809 callback, 810 base::Owned(local_id))); 811} 812 813void ChangeListLoader::DoLoadDirectoryFromServerAfterAddMyDrive( 814 const DirectoryFetchInfo& directory_fetch_info, 815 const FileOperationCallback& callback, 816 std::string* local_id, 817 FileError error) { 818 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 819 DCHECK(!callback.is_null()); 820 DCHECK_EQ(directory_fetch_info.resource_id(), 821 util::kDriveGrandRootSpecialResourceId); 822 823 const base::FilePath changed_directory_path(util::GetDriveGrandRootPath()); 824 DoLoadDirectoryFromServerAfterRefresh(directory_fetch_info, 825 callback, 826 &changed_directory_path, 827 error); 828} 829 830void ChangeListLoader::DoLoadDirectoryFromServerAfterLoad( 831 const DirectoryFetchInfo& directory_fetch_info, 832 const FileOperationCallback& callback, 833 FeedFetcher* fetcher, 834 FileError error, 835 ScopedVector<ChangeList> change_lists) { 836 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 837 DCHECK(!callback.is_null()); 838 DCHECK(!directory_fetch_info.empty()); 839 840 // Delete the fetcher. 841 fast_fetch_feed_fetcher_set_.erase(fetcher); 842 delete fetcher; 843 844 if (error != FILE_ERROR_OK) { 845 LOG(ERROR) << "Failed to load directory: " 846 << directory_fetch_info.resource_id() 847 << ": " << FileErrorToString(error); 848 callback.Run(error); 849 return; 850 } 851 852 base::FilePath* directory_path = new base::FilePath; 853 base::PostTaskAndReplyWithResult( 854 blocking_task_runner_, 855 FROM_HERE, 856 base::Bind(&ChangeListProcessor::RefreshDirectory, 857 resource_metadata_, 858 directory_fetch_info, 859 base::Passed(&change_lists), 860 directory_path), 861 base::Bind(&ChangeListLoader::DoLoadDirectoryFromServerAfterRefresh, 862 weak_ptr_factory_.GetWeakPtr(), 863 directory_fetch_info, 864 callback, 865 base::Owned(directory_path))); 866} 867 868void ChangeListLoader::DoLoadDirectoryFromServerAfterRefresh( 869 const DirectoryFetchInfo& directory_fetch_info, 870 const FileOperationCallback& callback, 871 const base::FilePath* directory_path, 872 FileError error) { 873 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 874 DCHECK(!callback.is_null()); 875 876 DVLOG(1) << "Directory loaded: " << directory_fetch_info.ToString(); 877 callback.Run(error); 878 // Also notify the observers. 879 if (error == FILE_ERROR_OK) { 880 FOR_EACH_OBSERVER(ChangeListLoaderObserver, observers_, 881 OnDirectoryChanged(*directory_path)); 882 } 883} 884 885void ChangeListLoader::UpdateFromChangeList( 886 scoped_ptr<google_apis::AboutResource> about_resource, 887 ScopedVector<ChangeList> change_lists, 888 bool is_delta_update, 889 const base::Closure& callback) { 890 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 891 DCHECK(!callback.is_null()); 892 DCHECK(about_resource); 893 894 ChangeListProcessor* change_list_processor = 895 new ChangeListProcessor(resource_metadata_); 896 // Don't send directory content change notification while performing 897 // the initial content retrieval. 898 const bool should_notify_changed_directories = is_delta_update; 899 900 util::Log(logging::LOG_INFO, 901 "Apply change lists (is delta: %d)", 902 is_delta_update); 903 base::PostTaskAndReplyWithResult( 904 blocking_task_runner_, 905 FROM_HERE, 906 base::Bind(&ChangeListProcessor::Apply, 907 base::Unretained(change_list_processor), 908 base::Passed(&about_resource), 909 base::Passed(&change_lists), 910 is_delta_update), 911 base::Bind(&ChangeListLoader::UpdateFromChangeListAfterApply, 912 weak_ptr_factory_.GetWeakPtr(), 913 base::Owned(change_list_processor), 914 should_notify_changed_directories, 915 base::Time::Now(), 916 callback)); 917} 918 919void ChangeListLoader::UpdateFromChangeListAfterApply( 920 ChangeListProcessor* change_list_processor, 921 bool should_notify_changed_directories, 922 base::Time start_time, 923 const base::Closure& callback, 924 FileError error) { 925 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 926 DCHECK(change_list_processor); 927 DCHECK(!callback.is_null()); 928 929 const base::TimeDelta elapsed = base::Time::Now() - start_time; 930 util::Log(logging::LOG_INFO, 931 "Change lists applied (elapsed time: %sms)", 932 base::Int64ToString(elapsed.InMilliseconds()).c_str()); 933 934 if (should_notify_changed_directories) { 935 for (std::set<base::FilePath>::iterator dir_iter = 936 change_list_processor->changed_dirs().begin(); 937 dir_iter != change_list_processor->changed_dirs().end(); 938 ++dir_iter) { 939 FOR_EACH_OBSERVER(ChangeListLoaderObserver, observers_, 940 OnDirectoryChanged(*dir_iter)); 941 } 942 } 943 944 callback.Run(); 945} 946 947} // namespace internal 948} // namespace drive 949