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.
5 #include "chrome/browser/chromeos/drive/change_list_loader.h"
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 "base/time/time.h"
14 #include "chrome/browser/chromeos/drive/change_list_loader_observer.h"
15 #include "chrome/browser/chromeos/drive/change_list_processor.h"
16 #include "chrome/browser/chromeos/drive/file_system_util.h"
17 #include "chrome/browser/chromeos/drive/job_scheduler.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/drive/event_logger.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "google_apis/drive/drive_api_parser.h"
26 using content::BrowserThread;
31 typedef base::Callback<void(FileError, ScopedVector<ChangeList>)>
34 class ChangeListLoader::FeedFetcher {
36 virtual ~FeedFetcher() {}
37 virtual void Run(const FeedFetcherCallback& callback) = 0;
42 // Fetches all the (currently available) resource entries from the server.
43 class FullFeedFetcher : public ChangeListLoader::FeedFetcher {
45 explicit FullFeedFetcher(JobScheduler* scheduler)
46 : scheduler_(scheduler),
47 weak_ptr_factory_(this) {
50 virtual ~FullFeedFetcher() {
53 virtual void Run(const FeedFetcherCallback& callback) OVERRIDE {
54 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
55 DCHECK(!callback.is_null());
57 // Remember the time stamp for usage stats.
58 start_time_ = base::TimeTicks::Now();
60 // This is full resource list fetch.
61 scheduler_->GetAllResourceList(
62 base::Bind(&FullFeedFetcher::OnFileListFetched,
63 weak_ptr_factory_.GetWeakPtr(), callback));
67 void OnFileListFetched(
68 const FeedFetcherCallback& callback,
69 google_apis::GDataErrorCode status,
70 scoped_ptr<google_apis::ResourceList> resource_list) {
71 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
72 DCHECK(!callback.is_null());
74 FileError error = GDataToFileError(status);
75 if (error != FILE_ERROR_OK) {
76 callback.Run(error, ScopedVector<ChangeList>());
80 DCHECK(resource_list);
81 change_lists_.push_back(new ChangeList(*resource_list));
84 if (resource_list->GetNextFeedURL(&next_url) && !next_url.is_empty()) {
85 // There is the remaining result so fetch it.
86 scheduler_->GetRemainingFileList(
88 base::Bind(&FullFeedFetcher::OnFileListFetched,
89 weak_ptr_factory_.GetWeakPtr(), callback));
93 UMA_HISTOGRAM_LONG_TIMES("Drive.FullFeedLoadTime",
94 base::TimeTicks::Now() - start_time_);
96 // Note: The fetcher is managed by ChangeListLoader, and the instance
97 // will be deleted in the callback. Do not touch the fields after this
99 callback.Run(FILE_ERROR_OK, change_lists_.Pass());
102 JobScheduler* scheduler_;
103 ScopedVector<ChangeList> change_lists_;
104 base::TimeTicks start_time_;
105 base::WeakPtrFactory<FullFeedFetcher> weak_ptr_factory_;
106 DISALLOW_COPY_AND_ASSIGN(FullFeedFetcher);
109 // Fetches the delta changes since |start_change_id|.
110 class DeltaFeedFetcher : public ChangeListLoader::FeedFetcher {
112 DeltaFeedFetcher(JobScheduler* scheduler, int64 start_change_id)
113 : scheduler_(scheduler),
114 start_change_id_(start_change_id),
115 weak_ptr_factory_(this) {
118 virtual ~DeltaFeedFetcher() {
121 virtual void Run(const FeedFetcherCallback& callback) OVERRIDE {
122 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
123 DCHECK(!callback.is_null());
125 scheduler_->GetChangeList(
127 base::Bind(&DeltaFeedFetcher::OnChangeListFetched,
128 weak_ptr_factory_.GetWeakPtr(), callback));
132 void OnChangeListFetched(
133 const FeedFetcherCallback& callback,
134 google_apis::GDataErrorCode status,
135 scoped_ptr<google_apis::ResourceList> resource_list) {
136 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
137 DCHECK(!callback.is_null());
139 FileError error = GDataToFileError(status);
140 if (error != FILE_ERROR_OK) {
141 callback.Run(error, ScopedVector<ChangeList>());
145 DCHECK(resource_list);
146 change_lists_.push_back(new ChangeList(*resource_list));
149 if (resource_list->GetNextFeedURL(&next_url) && !next_url.is_empty()) {
150 // There is the remaining result so fetch it.
151 scheduler_->GetRemainingChangeList(
153 base::Bind(&DeltaFeedFetcher::OnChangeListFetched,
154 weak_ptr_factory_.GetWeakPtr(), callback));
158 // Note: The fetcher is managed by ChangeListLoader, and the instance
159 // will be deleted in the callback. Do not touch the fields after this
161 callback.Run(FILE_ERROR_OK, change_lists_.Pass());
164 JobScheduler* scheduler_;
165 int64 start_change_id_;
166 ScopedVector<ChangeList> change_lists_;
167 base::WeakPtrFactory<DeltaFeedFetcher> weak_ptr_factory_;
168 DISALLOW_COPY_AND_ASSIGN(DeltaFeedFetcher);
171 // Fetches the resource entries in the directory with |directory_resource_id|.
172 class FastFetchFeedFetcher : public ChangeListLoader::FeedFetcher {
174 FastFetchFeedFetcher(JobScheduler* scheduler,
175 DriveServiceInterface* drive_service,
176 const std::string& directory_resource_id,
177 const std::string& root_folder_id)
178 : scheduler_(scheduler),
179 drive_service_(drive_service),
180 directory_resource_id_(directory_resource_id),
181 root_folder_id_(root_folder_id),
182 weak_ptr_factory_(this) {
185 virtual ~FastFetchFeedFetcher() {
188 virtual void Run(const FeedFetcherCallback& callback) OVERRIDE {
189 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
190 DCHECK(!callback.is_null());
191 DCHECK(!directory_resource_id_.empty());
192 DCHECK(util::IsDriveV2ApiEnabled() || !root_folder_id_.empty());
194 // Remember the time stamp for usage stats.
195 start_time_ = base::TimeTicks::Now();
197 // We use WAPI's GetResourceListInDirectory even if Drive API v2 is
198 // enabled. This is the short term work around of the performance
201 std::string resource_id = directory_resource_id_;
202 if (util::IsDriveV2ApiEnabled() &&
203 directory_resource_id_ == root_folder_id_) {
204 // GData WAPI doesn't accept the root directory id which is used in Drive
205 // API v2. So it is necessary to translate it here.
206 resource_id = util::kWapiRootDirectoryResourceId;
209 scheduler_->GetResourceListInDirectoryByWapi(
211 base::Bind(&FastFetchFeedFetcher::OnResourceListFetched,
212 weak_ptr_factory_.GetWeakPtr(), callback));
216 void OnResourceListFetched(
217 const FeedFetcherCallback& callback,
218 google_apis::GDataErrorCode status,
219 scoped_ptr<google_apis::ResourceList> resource_list) {
220 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
221 DCHECK(!callback.is_null());
223 FileError error = GDataToFileError(status);
224 if (error != FILE_ERROR_OK) {
225 callback.Run(error, ScopedVector<ChangeList>());
229 // Add the current change list to the list of collected lists.
230 DCHECK(resource_list);
231 ChangeList* change_list = new ChangeList(*resource_list);
232 if (util::IsDriveV2ApiEnabled())
233 FixResourceIdInChangeList(change_list);
234 change_lists_.push_back(change_list);
237 if (resource_list->GetNextFeedURL(&next_url) && !next_url.is_empty()) {
238 // There is the remaining result so fetch it.
239 scheduler_->GetRemainingResourceList(
241 base::Bind(&FastFetchFeedFetcher::OnResourceListFetched,
242 weak_ptr_factory_.GetWeakPtr(), callback));
246 UMA_HISTOGRAM_TIMES("Drive.DirectoryFeedLoadTime",
247 base::TimeTicks::Now() - start_time_);
249 // Note: The fetcher is managed by ChangeListLoader, and the instance
250 // will be deleted in the callback. Do not touch the fields after this
252 callback.Run(FILE_ERROR_OK, change_lists_.Pass());
255 // Fixes resource IDs in |change_list| into the format that |drive_service_|
256 // can understand. Note that |change_list| contains IDs in GData WAPI format
257 // since currently we always use WAPI for fast fetch, regardless of the flag.
258 void FixResourceIdInChangeList(ChangeList* change_list) {
259 std::vector<ResourceEntry>* entries = change_list->mutable_entries();
260 std::vector<std::string>* parent_resource_ids =
261 change_list->mutable_parent_resource_ids();
262 for (size_t i = 0; i < entries->size(); ++i) {
263 ResourceEntry* entry = &(*entries)[i];
264 if (entry->has_resource_id())
265 entry->set_resource_id(FixResourceId(entry->resource_id()));
267 (*parent_resource_ids)[i] = FixResourceId((*parent_resource_ids)[i]);
271 std::string FixResourceId(const std::string& resource_id) {
272 if (resource_id == util::kWapiRootDirectoryResourceId)
273 return root_folder_id_;
274 return drive_service_->GetResourceIdCanonicalizer().Run(resource_id);
277 JobScheduler* scheduler_;
278 DriveServiceInterface* drive_service_;
279 std::string directory_resource_id_;
280 std::string root_folder_id_;
281 ScopedVector<ChangeList> change_lists_;
282 base::TimeTicks start_time_;
283 base::WeakPtrFactory<FastFetchFeedFetcher> weak_ptr_factory_;
284 DISALLOW_COPY_AND_ASSIGN(FastFetchFeedFetcher);
287 FileError RefreshMyDriveIfNeeded(
288 ResourceMetadata* resource_metadata,
289 const google_apis::AboutResource& about_resource) {
291 FileError error = resource_metadata->GetResourceEntryByPath(
292 util::GetDriveMyDriveRootPath(), &entry);
293 if (error != FILE_ERROR_OK || !entry.resource_id().empty())
296 entry.set_resource_id(about_resource.root_folder_id());
297 return resource_metadata->RefreshEntry(entry);
302 LoaderController::LoaderController()
304 weak_ptr_factory_(this) {
305 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
308 LoaderController::~LoaderController() {
309 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
312 scoped_ptr<base::ScopedClosureRunner> LoaderController::GetLock() {
313 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
316 return make_scoped_ptr(new base::ScopedClosureRunner(
317 base::Bind(&LoaderController::Unlock,
318 weak_ptr_factory_.GetWeakPtr())));
321 void LoaderController::ScheduleRun(const base::Closure& task) {
322 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
323 DCHECK(!task.is_null());
325 if (lock_count_ > 0) {
326 pending_tasks_.push_back(task);
332 void LoaderController::Unlock() {
333 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
334 DCHECK_LT(0, lock_count_);
336 if (--lock_count_ > 0)
339 std::vector<base::Closure> tasks;
340 tasks.swap(pending_tasks_);
341 for (size_t i = 0; i < tasks.size(); ++i)
345 AboutResourceLoader::AboutResourceLoader(JobScheduler* scheduler)
346 : scheduler_(scheduler),
347 weak_ptr_factory_(this) {
350 AboutResourceLoader::~AboutResourceLoader() {}
352 void AboutResourceLoader::GetAboutResource(
353 const google_apis::AboutResourceCallback& callback) {
354 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
355 DCHECK(!callback.is_null());
357 if (cached_about_resource_) {
358 base::MessageLoopProxy::current()->PostTask(
362 google_apis::HTTP_NO_CONTENT,
363 base::Passed(scoped_ptr<google_apis::AboutResource>(
364 new google_apis::AboutResource(*cached_about_resource_)))));
366 UpdateAboutResource(callback);
370 void AboutResourceLoader::UpdateAboutResource(
371 const google_apis::AboutResourceCallback& callback) {
372 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
373 DCHECK(!callback.is_null());
375 scheduler_->GetAboutResource(
376 base::Bind(&AboutResourceLoader::UpdateAboutResourceAfterGetAbout,
377 weak_ptr_factory_.GetWeakPtr(),
381 void AboutResourceLoader::UpdateAboutResourceAfterGetAbout(
382 const google_apis::AboutResourceCallback& callback,
383 google_apis::GDataErrorCode status,
384 scoped_ptr<google_apis::AboutResource> about_resource) {
385 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
386 DCHECK(!callback.is_null());
387 FileError error = GDataToFileError(status);
389 if (error == FILE_ERROR_OK) {
390 if (cached_about_resource_ &&
391 cached_about_resource_->largest_change_id() >
392 about_resource->largest_change_id()) {
393 LOG(WARNING) << "Local cached about resource is fresher than server, "
394 << "local = " << cached_about_resource_->largest_change_id()
395 << ", server = " << about_resource->largest_change_id();
398 cached_about_resource_.reset(
399 new google_apis::AboutResource(*about_resource));
402 callback.Run(status, about_resource.Pass());
405 ChangeListLoader::ChangeListLoader(
407 base::SequencedTaskRunner* blocking_task_runner,
408 ResourceMetadata* resource_metadata,
409 JobScheduler* scheduler,
410 DriveServiceInterface* drive_service,
411 AboutResourceLoader* about_resource_loader,
412 LoaderController* loader_controller)
414 blocking_task_runner_(blocking_task_runner),
415 resource_metadata_(resource_metadata),
416 scheduler_(scheduler),
417 drive_service_(drive_service),
418 about_resource_loader_(about_resource_loader),
419 loader_controller_(loader_controller),
421 weak_ptr_factory_(this) {
424 ChangeListLoader::~ChangeListLoader() {
425 STLDeleteElements(&fast_fetch_feed_fetcher_set_);
428 bool ChangeListLoader::IsRefreshing() const {
429 // Callback for change list loading is stored in pending_load_callback_[""].
430 // It is non-empty if and only if there is an in-flight loading operation.
431 return pending_load_callback_.find("") != pending_load_callback_.end();
434 void ChangeListLoader::AddObserver(ChangeListLoaderObserver* observer) {
435 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
436 observers_.AddObserver(observer);
439 void ChangeListLoader::RemoveObserver(ChangeListLoaderObserver* observer) {
440 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
441 observers_.RemoveObserver(observer);
444 void ChangeListLoader::CheckForUpdates(const FileOperationCallback& callback) {
445 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
446 DCHECK(!callback.is_null());
448 if (IsRefreshing()) {
449 // There is in-flight loading. So keep the callback here, and check for
450 // updates when the in-flight loading is completed.
451 pending_update_check_callback_ = callback;
456 // We only start to check for updates iff the load is done.
457 // I.e., we ignore checking updates if not loaded to avoid starting the
458 // load without user's explicit interaction (such as opening Drive).
459 logger_->Log(logging::LOG_INFO, "Checking for updates");
460 Load(DirectoryFetchInfo(), callback);
464 void ChangeListLoader::LoadDirectoryIfNeeded(
465 const base::FilePath& directory_path,
466 const FileOperationCallback& callback) {
467 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
468 DCHECK(!callback.is_null());
470 // If the resource metadata has been already loaded and not refreshing, it
471 // means the local metadata is up to date.
472 if (loaded_ && !IsRefreshing()) {
473 callback.Run(FILE_ERROR_OK);
477 ResourceEntry* entry = new ResourceEntry;
478 base::PostTaskAndReplyWithResult(
479 blocking_task_runner_.get(),
481 base::Bind(&ResourceMetadata::GetResourceEntryByPath,
482 base::Unretained(resource_metadata_),
485 base::Bind(&ChangeListLoader::LoadDirectoryIfNeededAfterGetEntry,
486 weak_ptr_factory_.GetWeakPtr(),
489 true, // should_try_loading_parent
490 base::Owned(entry)));
493 void ChangeListLoader::LoadForTesting(const FileOperationCallback& callback) {
494 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
495 DCHECK(!callback.is_null());
497 Load(DirectoryFetchInfo(), callback);
500 void ChangeListLoader::LoadDirectoryIfNeededAfterGetEntry(
501 const base::FilePath& directory_path,
502 const FileOperationCallback& callback,
503 bool should_try_loading_parent,
504 const ResourceEntry* entry,
506 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
507 DCHECK(!callback.is_null());
509 // Should load grand root first if My Drive's resource ID is not set.
510 const bool mydrive_resource_id_is_empty =
511 directory_path == util::GetDriveMyDriveRootPath() &&
512 error == FILE_ERROR_OK &&
513 entry->resource_id().empty();
514 if ((error == FILE_ERROR_NOT_FOUND || mydrive_resource_id_is_empty) &&
515 should_try_loading_parent &&
516 util::GetDriveGrandRootPath().IsParent(directory_path)) {
517 // This entry may be found after loading the parent.
518 LoadDirectoryIfNeeded(
519 directory_path.DirName(),
520 base::Bind(&ChangeListLoader::LoadDirectoryIfNeededAfterLoadParent,
521 weak_ptr_factory_.GetWeakPtr(),
526 if (error != FILE_ERROR_OK) {
531 if (!entry->file_info().is_directory()) {
532 callback.Run(FILE_ERROR_NOT_A_DIRECTORY);
536 // This entry does not exist on the server. Grand root is excluded as it's
537 // specially handled in LoadDirectoryFromServer().
538 if (entry->resource_id().empty() &&
539 entry->local_id() != util::kDriveGrandRootLocalId) {
540 callback.Run(FILE_ERROR_OK);
544 Load(DirectoryFetchInfo(entry->local_id(),
545 entry->resource_id(),
546 entry->directory_specific_info().changestamp()),
550 void ChangeListLoader::LoadDirectoryIfNeededAfterLoadParent(
551 const base::FilePath& directory_path,
552 const FileOperationCallback& callback,
554 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
555 DCHECK(!callback.is_null());
557 if (error != FILE_ERROR_OK) {
562 ResourceEntry* entry = new ResourceEntry;
563 base::PostTaskAndReplyWithResult(
564 blocking_task_runner_.get(),
566 base::Bind(&ResourceMetadata::GetResourceEntryByPath,
567 base::Unretained(resource_metadata_),
570 base::Bind(&ChangeListLoader::LoadDirectoryIfNeededAfterGetEntry,
571 weak_ptr_factory_.GetWeakPtr(),
574 false, // should_try_loading_parent
575 base::Owned(entry)));
578 void ChangeListLoader::Load(const DirectoryFetchInfo& directory_fetch_info,
579 const FileOperationCallback& callback) {
580 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
581 DCHECK(!callback.is_null());
583 // Check if this is the first time this ChangeListLoader do loading.
584 // Note: IsRefreshing() depends on pending_load_callback_ so check in advance.
585 const bool is_initial_load = (!loaded_ && !IsRefreshing());
587 // Register the callback function to be called when it is loaded.
588 const std::string& local_id = directory_fetch_info.local_id();
589 pending_load_callback_[local_id].push_back(callback);
591 // If loading task for |resource_id| is already running, do nothing.
592 if (pending_load_callback_[local_id].size() > 1)
595 // For initial loading, even for directory fetching, we do load the full
596 // resource list from the server to sync up. So we register a dummy
597 // callback to indicate that update for full hierarchy is running.
598 if (is_initial_load && !directory_fetch_info.empty()) {
599 pending_load_callback_[""].push_back(
600 base::Bind(&util::EmptyFileOperationCallback));
603 // Check the current status of local metadata, and start loading if needed.
604 base::PostTaskAndReplyWithResult(
605 blocking_task_runner_,
607 base::Bind(&ResourceMetadata::GetLargestChangestamp,
608 base::Unretained(resource_metadata_)),
609 base::Bind(&ChangeListLoader::LoadAfterGetLargestChangestamp,
610 weak_ptr_factory_.GetWeakPtr(),
611 directory_fetch_info,
615 void ChangeListLoader::LoadAfterGetLargestChangestamp(
616 const DirectoryFetchInfo& directory_fetch_info,
617 bool is_initial_load,
618 int64 local_changestamp) {
619 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
621 if (is_initial_load && local_changestamp > 0) {
622 // The local data is usable. Flush callbacks to tell loading was successful.
623 OnChangeListLoadComplete(FILE_ERROR_OK);
625 // Continues to load from server in background.
626 // Put dummy callbacks to indicate that fetching is still continuing.
627 pending_load_callback_[directory_fetch_info.local_id()].push_back(
628 base::Bind(&util::EmptyFileOperationCallback));
629 if (!directory_fetch_info.empty()) {
630 pending_load_callback_[""].push_back(
631 base::Bind(&util::EmptyFileOperationCallback));
635 if (directory_fetch_info.empty()) {
636 about_resource_loader_->UpdateAboutResource(
637 base::Bind(&ChangeListLoader::LoadAfterGetAboutResource,
638 weak_ptr_factory_.GetWeakPtr(),
639 directory_fetch_info,
643 // Note: To be precise, we need to call UpdateAboutResource() here. However,
644 // - It is costly to do GetAboutResource HTTP request every time.
645 // - The chance using an old value is small; it only happens when
646 // LoadIfNeeded is called during one GetAboutResource roundtrip time
647 // of a change list fetching.
648 // - Even if the value is old, it just marks the directory as older. It may
649 // trigger one future unnecessary re-fetch, but it'll never lose data.
650 about_resource_loader_->GetAboutResource(
651 base::Bind(&ChangeListLoader::LoadAfterGetAboutResource,
652 weak_ptr_factory_.GetWeakPtr(),
653 directory_fetch_info,
659 void ChangeListLoader::LoadAfterGetAboutResource(
660 const DirectoryFetchInfo& directory_fetch_info,
661 bool is_initial_load,
662 int64 local_changestamp,
663 google_apis::GDataErrorCode status,
664 scoped_ptr<google_apis::AboutResource> about_resource) {
665 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
667 FileError error = GDataToFileError(status);
668 if (error != FILE_ERROR_OK) {
669 if (directory_fetch_info.empty() || is_initial_load)
670 OnChangeListLoadComplete(error);
672 OnDirectoryLoadComplete(directory_fetch_info, error);
676 DCHECK(about_resource);
678 int64 remote_changestamp = about_resource->largest_change_id();
679 int64 start_changestamp = local_changestamp > 0 ? local_changestamp + 1 : 0;
680 if (directory_fetch_info.empty()) {
681 if (local_changestamp >= remote_changestamp) {
682 if (local_changestamp > remote_changestamp) {
683 LOG(WARNING) << "Local resource metadata is fresher than server, "
684 << "local = " << local_changestamp
685 << ", server = " << remote_changestamp;
688 // No changes detected, tell the client that the loading was successful.
689 OnChangeListLoadComplete(FILE_ERROR_OK);
693 // If the caller is not interested in a particular directory, just start
694 // loading the change list.
695 LoadChangeListFromServer(start_changestamp);
697 // If the caller is interested in a particular directory, start loading the
699 int64 directory_changestamp = std::max(directory_fetch_info.changestamp(),
702 // We may not fetch from the server at all if the local metadata is new
703 // enough, but we log this message here, so "Fast-fetch start" and
704 // "Fast-fetch complete" always match.
705 // TODO(satorux): Distinguish the "not fetching at all" case.
706 logger_->Log(logging::LOG_INFO,
707 "Fast-fetch start: %s; Server changestamp: %s",
708 directory_fetch_info.ToString().c_str(),
709 base::Int64ToString(remote_changestamp).c_str());
711 // If the directory's changestamp is up-to-date, just schedule to run the
712 // callback, as there is no need to fetch the directory.
713 if (directory_changestamp >= remote_changestamp) {
714 LoadAfterLoadDirectory(directory_fetch_info, is_initial_load,
715 start_changestamp, FILE_ERROR_OK);
719 // Start fetching the directory content, and mark it with the changestamp
720 // |remote_changestamp|.
721 DirectoryFetchInfo new_directory_fetch_info(
722 directory_fetch_info.local_id(), directory_fetch_info.resource_id(),
724 LoadDirectoryFromServer(
725 new_directory_fetch_info,
726 base::Bind(&ChangeListLoader::LoadAfterLoadDirectory,
727 weak_ptr_factory_.GetWeakPtr(),
728 directory_fetch_info,
734 void ChangeListLoader::LoadAfterLoadDirectory(
735 const DirectoryFetchInfo& directory_fetch_info,
736 bool is_initial_load,
737 int64 start_changestamp,
739 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
741 OnDirectoryLoadComplete(directory_fetch_info, error);
743 // Continue to load change list if this is the first load.
745 LoadChangeListFromServer(start_changestamp);
748 void ChangeListLoader::OnChangeListLoadComplete(FileError error) {
749 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
751 if (!loaded_ && error == FILE_ERROR_OK) {
753 FOR_EACH_OBSERVER(ChangeListLoaderObserver,
755 OnInitialLoadComplete());
758 for (LoadCallbackMap::iterator it = pending_load_callback_.begin();
759 it != pending_load_callback_.end(); ++it) {
760 const std::vector<FileOperationCallback>& callbacks = it->second;
761 for (size_t i = 0; i < callbacks.size(); ++i) {
762 base::MessageLoopProxy::current()->PostTask(
764 base::Bind(callbacks[i], error));
767 pending_load_callback_.clear();
769 // If there is pending update check, try to load the change from the server
770 // again, because there may exist an update during the completed loading.
771 if (!pending_update_check_callback_.is_null()) {
772 Load(DirectoryFetchInfo(),
773 base::ResetAndReturn(&pending_update_check_callback_));
777 void ChangeListLoader::OnDirectoryLoadComplete(
778 const DirectoryFetchInfo& directory_fetch_info,
780 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
782 logger_->Log(logging::LOG_INFO,
783 "Fast-fetch complete: %s => %s",
784 directory_fetch_info.ToString().c_str(),
785 FileErrorToString(error).c_str());
786 const std::string& local_id = directory_fetch_info.local_id();
787 LoadCallbackMap::iterator it = pending_load_callback_.find(local_id);
788 if (it != pending_load_callback_.end()) {
789 DVLOG(1) << "Running callback for " << local_id;
790 const std::vector<FileOperationCallback>& callbacks = it->second;
791 for (size_t i = 0; i < callbacks.size(); ++i) {
792 base::MessageLoopProxy::current()->PostTask(
794 base::Bind(callbacks[i], error));
796 pending_load_callback_.erase(it);
800 void ChangeListLoader::LoadChangeListFromServer(int64 start_changestamp) {
801 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
802 DCHECK(!change_feed_fetcher_);
803 DCHECK(about_resource_loader_->cached_about_resource());
805 bool is_delta_update = start_changestamp != 0;
807 // Set up feed fetcher.
808 if (is_delta_update) {
809 change_feed_fetcher_.reset(
810 new DeltaFeedFetcher(scheduler_, start_changestamp));
812 change_feed_fetcher_.reset(new FullFeedFetcher(scheduler_));
815 // Make a copy of cached_about_resource_ to remember at which changestamp we
816 // are fetching change list.
817 change_feed_fetcher_->Run(
818 base::Bind(&ChangeListLoader::LoadChangeListFromServerAfterLoadChangeList,
819 weak_ptr_factory_.GetWeakPtr(),
820 base::Passed(make_scoped_ptr(new google_apis::AboutResource(
821 *about_resource_loader_->cached_about_resource()))),
825 void ChangeListLoader::LoadChangeListFromServerAfterLoadChangeList(
826 scoped_ptr<google_apis::AboutResource> about_resource,
827 bool is_delta_update,
829 ScopedVector<ChangeList> change_lists) {
830 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
831 DCHECK(about_resource);
833 // Delete the fetcher first.
834 change_feed_fetcher_.reset();
836 if (error != FILE_ERROR_OK) {
837 OnChangeListLoadComplete(error);
841 ChangeListProcessor* change_list_processor =
842 new ChangeListProcessor(resource_metadata_);
843 // Don't send directory content change notification while performing
844 // the initial content retrieval.
845 const bool should_notify_changed_directories = is_delta_update;
847 logger_->Log(logging::LOG_INFO,
848 "Apply change lists (is delta: %d)",
850 loader_controller_->ScheduleRun(base::Bind(
852 &base::PostTaskAndReplyWithResult<FileError, FileError>),
853 blocking_task_runner_,
855 base::Bind(&ChangeListProcessor::Apply,
856 base::Unretained(change_list_processor),
857 base::Passed(&about_resource),
858 base::Passed(&change_lists),
860 base::Bind(&ChangeListLoader::LoadChangeListFromServerAfterUpdate,
861 weak_ptr_factory_.GetWeakPtr(),
862 base::Owned(change_list_processor),
863 should_notify_changed_directories,
864 base::Time::Now())));
867 void ChangeListLoader::LoadChangeListFromServerAfterUpdate(
868 ChangeListProcessor* change_list_processor,
869 bool should_notify_changed_directories,
870 const base::Time& start_time,
872 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
874 const base::TimeDelta elapsed = base::Time::Now() - start_time;
875 logger_->Log(logging::LOG_INFO,
876 "Change lists applied (elapsed time: %sms)",
877 base::Int64ToString(elapsed.InMilliseconds()).c_str());
879 if (should_notify_changed_directories) {
880 for (std::set<base::FilePath>::iterator dir_iter =
881 change_list_processor->changed_dirs().begin();
882 dir_iter != change_list_processor->changed_dirs().end();
884 FOR_EACH_OBSERVER(ChangeListLoaderObserver, observers_,
885 OnDirectoryChanged(*dir_iter));
889 OnChangeListLoadComplete(error);
891 FOR_EACH_OBSERVER(ChangeListLoaderObserver,
893 OnLoadFromServerComplete());
896 void ChangeListLoader::LoadDirectoryFromServer(
897 const DirectoryFetchInfo& directory_fetch_info,
898 const FileOperationCallback& callback) {
899 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
900 DCHECK(!callback.is_null());
901 DCHECK(!directory_fetch_info.empty());
902 DCHECK(about_resource_loader_->cached_about_resource());
903 DVLOG(1) << "Start loading directory: " << directory_fetch_info.ToString();
905 if (directory_fetch_info.local_id() == util::kDriveGrandRootLocalId) {
906 // Load for a grand root directory means slightly different from other
907 // directories. It means filling resource ID of mydrive root.
908 base::FilePath* changed_directory_path = new base::FilePath;
909 base::PostTaskAndReplyWithResult(
910 blocking_task_runner_,
912 base::Bind(&RefreshMyDriveIfNeeded,
914 google_apis::AboutResource(
915 *about_resource_loader_->cached_about_resource())),
916 base::Bind(&ChangeListLoader::LoadDirectoryFromServerAfterRefresh,
917 weak_ptr_factory_.GetWeakPtr(),
918 directory_fetch_info,
920 base::Owned(changed_directory_path)));
924 FastFetchFeedFetcher* fetcher = new FastFetchFeedFetcher(
927 directory_fetch_info.resource_id(),
928 about_resource_loader_->cached_about_resource()->root_folder_id());
929 fast_fetch_feed_fetcher_set_.insert(fetcher);
931 base::Bind(&ChangeListLoader::LoadDirectoryFromServerAfterLoad,
932 weak_ptr_factory_.GetWeakPtr(),
933 directory_fetch_info,
938 void ChangeListLoader::LoadDirectoryFromServerAfterLoad(
939 const DirectoryFetchInfo& directory_fetch_info,
940 const FileOperationCallback& callback,
941 FeedFetcher* fetcher,
943 ScopedVector<ChangeList> change_lists) {
944 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
945 DCHECK(!callback.is_null());
946 DCHECK(!directory_fetch_info.empty());
948 // Delete the fetcher.
949 fast_fetch_feed_fetcher_set_.erase(fetcher);
952 if (error != FILE_ERROR_OK) {
953 LOG(ERROR) << "Failed to load directory: "
954 << directory_fetch_info.local_id()
955 << ": " << FileErrorToString(error);
960 base::FilePath* directory_path = new base::FilePath;
961 loader_controller_->ScheduleRun(base::Bind(
963 &base::PostTaskAndReplyWithResult<FileError, FileError>),
964 blocking_task_runner_,
966 base::Bind(&ChangeListProcessor::RefreshDirectory,
968 directory_fetch_info,
969 base::Passed(&change_lists),
971 base::Bind(&ChangeListLoader::LoadDirectoryFromServerAfterRefresh,
972 weak_ptr_factory_.GetWeakPtr(),
973 directory_fetch_info,
975 base::Owned(directory_path))));
978 void ChangeListLoader::LoadDirectoryFromServerAfterRefresh(
979 const DirectoryFetchInfo& directory_fetch_info,
980 const FileOperationCallback& callback,
981 const base::FilePath* directory_path,
983 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
984 DCHECK(!callback.is_null());
986 DVLOG(1) << "Directory loaded: " << directory_fetch_info.ToString();
988 // Also notify the observers.
989 if (error == FILE_ERROR_OK && !directory_path->empty()) {
990 FOR_EACH_OBSERVER(ChangeListLoaderObserver, observers_,
991 OnDirectoryChanged(*directory_path));
995 } // namespace internal