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 "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"
25 using content::BrowserThread;
30 typedef base::Callback<void(FileError, ScopedVector<ChangeList>)>
33 class ChangeListLoader::FeedFetcher {
35 virtual ~FeedFetcher() {}
36 virtual void Run(const FeedFetcherCallback& callback) = 0;
41 // Fetches all the (currently available) resource entries from the server.
42 class FullFeedFetcher : public ChangeListLoader::FeedFetcher {
44 explicit FullFeedFetcher(JobScheduler* scheduler)
45 : scheduler_(scheduler),
46 weak_ptr_factory_(this) {
49 virtual ~FullFeedFetcher() {
52 virtual void Run(const FeedFetcherCallback& callback) OVERRIDE {
53 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
54 DCHECK(!callback.is_null());
56 // Rememeber the time stamp for usage stats.
57 start_time_ = base::TimeTicks::Now();
59 // This is full resource list fetch.
60 scheduler_->GetAllResourceList(
61 base::Bind(&FullFeedFetcher::OnFileListFetched,
62 weak_ptr_factory_.GetWeakPtr(), callback));
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());
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_);
80 FileError error = GDataToFileError(status);
81 if (error != FILE_ERROR_OK) {
82 callback.Run(error, ScopedVector<ChangeList>());
86 DCHECK(resource_list);
87 change_lists_.push_back(new ChangeList(*resource_list));
90 if (resource_list->GetNextFeedURL(&next_url) && !next_url.is_empty()) {
91 // There is the remaining result so fetch it.
92 scheduler_->GetRemainingFileList(
94 base::Bind(&FullFeedFetcher::OnFileListFetched,
95 weak_ptr_factory_.GetWeakPtr(), callback));
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_);
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
106 callback.Run(FILE_ERROR_OK, change_lists_.Pass());
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);
116 // Fetches the delta changes since |start_change_id|.
117 class DeltaFeedFetcher : public ChangeListLoader::FeedFetcher {
119 DeltaFeedFetcher(JobScheduler* scheduler, int64 start_change_id)
120 : scheduler_(scheduler),
121 start_change_id_(start_change_id),
122 weak_ptr_factory_(this) {
125 virtual ~DeltaFeedFetcher() {
128 virtual void Run(const FeedFetcherCallback& callback) OVERRIDE {
129 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
130 DCHECK(!callback.is_null());
132 scheduler_->GetChangeList(
134 base::Bind(&DeltaFeedFetcher::OnChangeListFetched,
135 weak_ptr_factory_.GetWeakPtr(), callback));
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());
146 FileError error = GDataToFileError(status);
147 if (error != FILE_ERROR_OK) {
148 callback.Run(error, ScopedVector<ChangeList>());
152 DCHECK(resource_list);
153 change_lists_.push_back(new ChangeList(*resource_list));
156 if (resource_list->GetNextFeedURL(&next_url) && !next_url.is_empty()) {
157 // There is the remaining result so fetch it.
158 scheduler_->GetRemainingChangeList(
160 base::Bind(&DeltaFeedFetcher::OnChangeListFetched,
161 weak_ptr_factory_.GetWeakPtr(), callback));
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
168 callback.Run(FILE_ERROR_OK, change_lists_.Pass());
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);
178 // Fetches the resource entries in the directory with |directory_resource_id|.
179 class FastFetchFeedFetcher : public ChangeListLoader::FeedFetcher {
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) {
192 virtual ~FastFetchFeedFetcher() {
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));
204 StartGetResourceListInDirectory(callback);
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());
215 FileError error = GDataToFileError(status);
216 if (error != FILE_ERROR_OK) {
217 callback.Run(error, ScopedVector<ChangeList>());
221 root_folder_id_ = about_resource->root_folder_id();
222 StartGetResourceListInDirectory(callback);
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());
231 // We use WAPI's GetResourceListInDirectory even if Drive API v2 is
232 // enabled. This is the short term work around of the performance
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;
243 scheduler_->GetResourceListInDirectoryByWapi(
245 base::Bind(&FastFetchFeedFetcher::OnResourceListFetched,
246 weak_ptr_factory_.GetWeakPtr(), callback));
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());
256 FileError error = GDataToFileError(status);
257 if (error != FILE_ERROR_OK) {
258 callback.Run(error, ScopedVector<ChangeList>());
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);
270 if (resource_list->GetNextFeedURL(&next_url) && !next_url.is_empty()) {
271 // There is the remaining result so fetch it.
272 scheduler_->GetRemainingResourceList(
274 base::Bind(&FastFetchFeedFetcher::OnResourceListFetched,
275 weak_ptr_factory_.GetWeakPtr(), callback));
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
282 callback.Run(FILE_ERROR_OK, change_lists_.Pass());
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()));
297 (*parent_resource_ids)[i] = FixResourceId((*parent_resource_ids)[i]);
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);
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);
318 ChangeListLoader::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),
329 weak_ptr_factory_(this) {
332 ChangeListLoader::~ChangeListLoader() {
333 STLDeleteElements(&fast_fetch_feed_fetcher_set_);
336 bool 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();
342 void ChangeListLoader::AddObserver(ChangeListLoaderObserver* observer) {
343 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
344 observers_.AddObserver(observer);
347 void ChangeListLoader::RemoveObserver(ChangeListLoaderObserver* observer) {
348 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
349 observers_.RemoveObserver(observer);
352 void ChangeListLoader::CheckForUpdates(const FileOperationCallback& callback) {
353 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
354 DCHECK(!callback.is_null());
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;
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);
372 void ChangeListLoader::LoadIfNeeded(
373 const DirectoryFetchInfo& directory_fetch_info,
374 const FileOperationCallback& callback) {
375 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
376 DCHECK(!callback.is_null());
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
383 if (loaded_ && (directory_fetch_info.empty() || !IsRefreshing())) {
384 base::MessageLoopProxy::current()->PostTask(
386 base::Bind(callback, FILE_ERROR_OK));
389 Load(directory_fetch_info, callback);
392 void ChangeListLoader::Load(const DirectoryFetchInfo& directory_fetch_info,
393 const FileOperationCallback& callback) {
394 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
395 DCHECK(!callback.is_null());
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());
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);
405 // If loading task for |resource_id| is already running, do nothing.
406 if (pending_load_callback_[resource_id].size() > 1)
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));
417 // Check the current status of local metadata, and start loading if needed.
418 base::PostTaskAndReplyWithResult(
419 blocking_task_runner_,
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));
429 void ChangeListLoader::DoInitialLoad(
430 const DirectoryFetchInfo& directory_fetch_info,
431 int64 local_changestamp) {
432 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
434 if (local_changestamp > 0) {
435 // The local data is usable. Flush callbacks to tell loading was successful.
436 OnChangeListLoadComplete(FILE_ERROR_OK);
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));
447 LoadFromServerIfNeeded(directory_fetch_info, local_changestamp);
450 void ChangeListLoader::DoUpdateLoad(
451 const DirectoryFetchInfo& directory_fetch_info,
452 int64 local_changestamp) {
453 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
455 if (directory_fetch_info.empty()) {
456 LoadFromServerIfNeeded(directory_fetch_info, local_changestamp);
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,
471 base::Bind(&ChangeListLoader::OnDirectoryLoadComplete,
472 weak_ptr_factory_.GetWeakPtr(),
473 directory_fetch_info));
477 void ChangeListLoader::OnChangeListLoadComplete(FileError error) {
478 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
480 if (!loaded_ && error == FILE_ERROR_OK) {
482 FOR_EACH_OBSERVER(ChangeListLoaderObserver,
484 OnInitialLoadComplete());
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(
493 base::Bind(callbacks[i], error));
496 pending_load_callback_.clear();
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_));
506 void ChangeListLoader::OnDirectoryLoadComplete(
507 const DirectoryFetchInfo& directory_fetch_info,
509 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
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(
523 base::Bind(callbacks[i], error));
525 pending_load_callback_.erase(it);
529 void ChangeListLoader::LoadFromServerIfNeeded(
530 const DirectoryFetchInfo& directory_fetch_info,
531 int64 local_changestamp) {
532 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
534 // First fetch the latest changestamp to see if there were any new changes
536 scheduler_->GetAboutResource(
537 base::Bind(&ChangeListLoader::LoadFromServerIfNeededAfterGetAbout,
538 weak_ptr_factory_.GetWeakPtr(),
539 directory_fetch_info,
543 void 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));
550 FileError error = GDataToFileError(status);
551 if (error != FILE_ERROR_OK) {
552 OnChangeListLoadComplete(error);
556 DCHECK(about_resource);
557 last_known_remote_changestamp_ = about_resource->largest_change_id();
558 root_folder_id_ = about_resource->root_folder_id();
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;
567 // No changes detected, tell the client that the loading was successful.
568 OnChangeListLoadComplete(FILE_ERROR_OK);
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);
578 // If the caller is interested in a particular directory, start loading the
580 CheckChangestampAndLoadDirectoryIfNeeded(
581 directory_fetch_info,
584 &ChangeListLoader::LoadFromServerIfNeededAfterLoadDirectory,
585 weak_ptr_factory_.GetWeakPtr(),
586 directory_fetch_info,
587 base::Passed(&about_resource),
592 void ChangeListLoader::LoadFromServerIfNeededAfterLoadDirectory(
593 const DirectoryFetchInfo& directory_fetch_info,
594 scoped_ptr<google_apis::AboutResource> about_resource,
595 int64 start_changestamp,
597 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
598 DCHECK(about_resource);
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);
606 LoadChangeListFromServer(about_resource.Pass(), start_changestamp);
609 void 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);
616 bool is_delta_update = start_changestamp != 0;
618 // Set up feed fetcher.
619 if (is_delta_update) {
620 change_feed_fetcher_.reset(
621 new DeltaFeedFetcher(scheduler_, start_changestamp));
623 change_feed_fetcher_.reset(new FullFeedFetcher(scheduler_));
626 change_feed_fetcher_->Run(
627 base::Bind(&ChangeListLoader::LoadChangeListFromServerAfterLoadChangeList,
628 weak_ptr_factory_.GetWeakPtr(),
629 base::Passed(&about_resource),
633 void ChangeListLoader::LoadChangeListFromServerAfterLoadChangeList(
634 scoped_ptr<google_apis::AboutResource> about_resource,
635 bool is_delta_update,
637 ScopedVector<ChangeList> change_lists) {
638 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
639 DCHECK(about_resource);
641 // Delete the fetcher first.
642 change_feed_fetcher_.reset();
644 if (error != FILE_ERROR_OK) {
645 OnChangeListLoadComplete(error);
649 UpdateFromChangeList(
650 about_resource.Pass(),
653 base::Bind(&ChangeListLoader::LoadChangeListFromServerAfterUpdate,
654 weak_ptr_factory_.GetWeakPtr()));
657 void ChangeListLoader::LoadChangeListFromServerAfterUpdate() {
658 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
660 OnChangeListLoadComplete(FILE_ERROR_OK);
662 FOR_EACH_OBSERVER(ChangeListLoaderObserver,
664 OnLoadFromServerComplete());
667 void 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());
674 int64 directory_changestamp = std::max(directory_fetch_info.changestamp(),
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());
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);
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);
704 void 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();
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);
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()),
731 ::DoLoadGrandRootDirectoryFromServerAfterGetResourceEntryByPath,
732 weak_ptr_factory_.GetWeakPtr(),
733 directory_fetch_info,
738 FastFetchFeedFetcher* fetcher = new FastFetchFeedFetcher(
741 directory_fetch_info.resource_id(),
743 fast_fetch_feed_fetcher_set_.insert(fetcher);
745 base::Bind(&ChangeListLoader::DoLoadDirectoryFromServerAfterLoad,
746 weak_ptr_factory_.GetWeakPtr(),
747 directory_fetch_info,
753 ChangeListLoader::DoLoadGrandRootDirectoryFromServerAfterGetResourceEntryByPath(
754 const DirectoryFetchInfo& directory_fetch_info,
755 const FileOperationCallback& callback,
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);
763 if (error == FILE_ERROR_OK) {
764 // MyDrive root already exists. Just return success.
765 callback.Run(FILE_ERROR_OK);
769 // Fetch root resource id from the server.
770 scheduler_->GetAboutResource(
773 ::DoLoadGrandRootDirectoryFromServerAfterGetAboutResource,
774 weak_ptr_factory_.GetWeakPtr(),
775 directory_fetch_info,
779 void 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);
790 FileError error = GDataToFileError(status);
791 if (error != FILE_ERROR_OK) {
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_,
802 base::Bind(&ResourceMetadata::AddEntry,
803 base::Unretained(resource_metadata_),
804 util::CreateMyDriveRootEntry(root_resource_id),
806 base::Bind(&ChangeListLoader::DoLoadDirectoryFromServerAfterAddMyDrive,
807 weak_ptr_factory_.GetWeakPtr(),
808 directory_fetch_info,
810 base::Owned(local_id)));
813 void ChangeListLoader::DoLoadDirectoryFromServerAfterAddMyDrive(
814 const DirectoryFetchInfo& directory_fetch_info,
815 const FileOperationCallback& callback,
816 std::string* local_id,
818 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
819 DCHECK(!callback.is_null());
820 DCHECK_EQ(directory_fetch_info.resource_id(),
821 util::kDriveGrandRootSpecialResourceId);
823 const base::FilePath changed_directory_path(util::GetDriveGrandRootPath());
824 DoLoadDirectoryFromServerAfterRefresh(directory_fetch_info,
826 &changed_directory_path,
830 void ChangeListLoader::DoLoadDirectoryFromServerAfterLoad(
831 const DirectoryFetchInfo& directory_fetch_info,
832 const FileOperationCallback& callback,
833 FeedFetcher* fetcher,
835 ScopedVector<ChangeList> change_lists) {
836 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
837 DCHECK(!callback.is_null());
838 DCHECK(!directory_fetch_info.empty());
840 // Delete the fetcher.
841 fast_fetch_feed_fetcher_set_.erase(fetcher);
844 if (error != FILE_ERROR_OK) {
845 LOG(ERROR) << "Failed to load directory: "
846 << directory_fetch_info.resource_id()
847 << ": " << FileErrorToString(error);
852 base::FilePath* directory_path = new base::FilePath;
853 base::PostTaskAndReplyWithResult(
854 blocking_task_runner_,
856 base::Bind(&ChangeListProcessor::RefreshDirectory,
858 directory_fetch_info,
859 base::Passed(&change_lists),
861 base::Bind(&ChangeListLoader::DoLoadDirectoryFromServerAfterRefresh,
862 weak_ptr_factory_.GetWeakPtr(),
863 directory_fetch_info,
865 base::Owned(directory_path)));
868 void ChangeListLoader::DoLoadDirectoryFromServerAfterRefresh(
869 const DirectoryFetchInfo& directory_fetch_info,
870 const FileOperationCallback& callback,
871 const base::FilePath* directory_path,
873 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
874 DCHECK(!callback.is_null());
876 DVLOG(1) << "Directory loaded: " << directory_fetch_info.ToString();
878 // Also notify the observers.
879 if (error == FILE_ERROR_OK) {
880 FOR_EACH_OBSERVER(ChangeListLoaderObserver, observers_,
881 OnDirectoryChanged(*directory_path));
885 void 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);
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;
900 util::Log(logging::LOG_INFO,
901 "Apply change lists (is delta: %d)",
903 base::PostTaskAndReplyWithResult(
904 blocking_task_runner_,
906 base::Bind(&ChangeListProcessor::Apply,
907 base::Unretained(change_list_processor),
908 base::Passed(&about_resource),
909 base::Passed(&change_lists),
911 base::Bind(&ChangeListLoader::UpdateFromChangeListAfterApply,
912 weak_ptr_factory_.GetWeakPtr(),
913 base::Owned(change_list_processor),
914 should_notify_changed_directories,
919 void ChangeListLoader::UpdateFromChangeListAfterApply(
920 ChangeListProcessor* change_list_processor,
921 bool should_notify_changed_directories,
922 base::Time start_time,
923 const base::Closure& callback,
925 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
926 DCHECK(change_list_processor);
927 DCHECK(!callback.is_null());
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());
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();
939 FOR_EACH_OBSERVER(ChangeListLoaderObserver, observers_,
940 OnDirectoryChanged(*dir_iter));
947 } // namespace internal