Disable view source for Developer Tools.
[chromium-blink-merge.git] / chrome / browser / chromeos / drive / change_list_loader.cc
blobd317b948c0a8ed13335f98fcf7125d5ac63ba4e1
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"
7 #include <set>
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/logging.h"
19 #include "chrome/browser/chromeos/drive/resource_metadata.h"
20 #include "chrome/browser/drive/drive_api_util.h"
21 #include "chrome/browser/drive/drive_service_interface.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "google_apis/drive/drive_api_parser.h"
24 #include "url/gurl.h"
26 using content::BrowserThread;
28 namespace drive {
29 namespace internal {
31 typedef base::Callback<void(FileError, ScopedVector<ChangeList>)>
32 FeedFetcherCallback;
34 class ChangeListLoader::FeedFetcher {
35 public:
36 virtual ~FeedFetcher() {}
37 virtual void Run(const FeedFetcherCallback& callback) = 0;
40 namespace {
42 // Fetches all the (currently available) resource entries from the server.
43 class FullFeedFetcher : public ChangeListLoader::FeedFetcher {
44 public:
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));
66 private:
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>());
77 return;
80 DCHECK(resource_list);
81 change_lists_.push_back(new ChangeList(*resource_list));
83 GURL next_url;
84 if (resource_list->GetNextFeedURL(&next_url) && !next_url.is_empty()) {
85 // There is the remaining result so fetch it.
86 scheduler_->GetRemainingFileList(
87 next_url,
88 base::Bind(&FullFeedFetcher::OnFileListFetched,
89 weak_ptr_factory_.GetWeakPtr(), callback));
90 return;
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
98 // invocation.
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 {
111 public:
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(
126 start_change_id_,
127 base::Bind(&DeltaFeedFetcher::OnChangeListFetched,
128 weak_ptr_factory_.GetWeakPtr(), callback));
131 private:
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>());
142 return;
145 DCHECK(resource_list);
146 change_lists_.push_back(new ChangeList(*resource_list));
148 GURL next_url;
149 if (resource_list->GetNextFeedURL(&next_url) && !next_url.is_empty()) {
150 // There is the remaining result so fetch it.
151 scheduler_->GetRemainingChangeList(
152 next_url,
153 base::Bind(&DeltaFeedFetcher::OnChangeListFetched,
154 weak_ptr_factory_.GetWeakPtr(), callback));
155 return;
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
160 // invocation.
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 {
173 public:
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
199 // regression.
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(
210 resource_id,
211 base::Bind(&FastFetchFeedFetcher::OnResourceListFetched,
212 weak_ptr_factory_.GetWeakPtr(), callback));
215 private:
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>());
226 return;
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);
236 GURL next_url;
237 if (resource_list->GetNextFeedURL(&next_url) && !next_url.is_empty()) {
238 // There is the remaining result so fetch it.
239 scheduler_->GetRemainingResourceList(
240 next_url,
241 base::Bind(&FastFetchFeedFetcher::OnResourceListFetched,
242 weak_ptr_factory_.GetWeakPtr(), callback));
243 return;
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
251 // invocation.
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) {
290 ResourceEntry entry;
291 FileError error = resource_metadata->GetResourceEntryByPath(
292 util::GetDriveMyDriveRootPath(), &entry);
293 if (error != FILE_ERROR_OK || !entry.resource_id().empty())
294 return error;
296 entry.set_resource_id(about_resource.root_folder_id());
297 return resource_metadata->RefreshEntry(entry);
300 } // namespace
302 ChangeListLoader::ChangeListLoader(
303 base::SequencedTaskRunner* blocking_task_runner,
304 ResourceMetadata* resource_metadata,
305 JobScheduler* scheduler,
306 DriveServiceInterface* drive_service)
307 : blocking_task_runner_(blocking_task_runner),
308 resource_metadata_(resource_metadata),
309 scheduler_(scheduler),
310 drive_service_(drive_service),
311 loaded_(false),
312 weak_ptr_factory_(this) {
315 ChangeListLoader::~ChangeListLoader() {
316 STLDeleteElements(&fast_fetch_feed_fetcher_set_);
319 bool ChangeListLoader::IsRefreshing() const {
320 // Callback for change list loading is stored in pending_load_callback_[""].
321 // It is non-empty if and only if there is an in-flight loading operation.
322 return pending_load_callback_.find("") != pending_load_callback_.end();
325 void ChangeListLoader::AddObserver(ChangeListLoaderObserver* observer) {
326 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
327 observers_.AddObserver(observer);
330 void ChangeListLoader::RemoveObserver(ChangeListLoaderObserver* observer) {
331 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
332 observers_.RemoveObserver(observer);
335 void ChangeListLoader::CheckForUpdates(const FileOperationCallback& callback) {
336 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
337 DCHECK(!callback.is_null());
339 if (IsRefreshing()) {
340 // There is in-flight loading. So keep the callback here, and check for
341 // updates when the in-flight loading is completed.
342 pending_update_check_callback_ = callback;
343 return;
346 if (loaded_) {
347 // We only start to check for updates iff the load is done.
348 // I.e., we ignore checking updates if not loaded to avoid starting the
349 // load without user's explicit interaction (such as opening Drive).
350 util::Log(logging::LOG_INFO, "Checking for updates");
351 Load(DirectoryFetchInfo(), callback);
355 void ChangeListLoader::LoadDirectoryIfNeeded(
356 const base::FilePath& directory_path,
357 const FileOperationCallback& callback) {
358 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
359 DCHECK(!callback.is_null());
361 // If the resource metadata has been already loaded and not refreshing, it
362 // means the local metadata is up to date.
363 if (loaded_ && !IsRefreshing()) {
364 callback.Run(FILE_ERROR_OK);
365 return;
368 ResourceEntry* entry = new ResourceEntry;
369 base::PostTaskAndReplyWithResult(
370 blocking_task_runner_.get(),
371 FROM_HERE,
372 base::Bind(&ResourceMetadata::GetResourceEntryByPath,
373 base::Unretained(resource_metadata_),
374 directory_path,
375 entry),
376 base::Bind(&ChangeListLoader::LoadDirectoryIfNeededAfterGetEntry,
377 weak_ptr_factory_.GetWeakPtr(),
378 directory_path,
379 callback,
380 true, // should_try_loading_parent
381 base::Owned(entry)));
384 void ChangeListLoader::LoadForTesting(const FileOperationCallback& callback) {
385 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
386 DCHECK(!callback.is_null());
388 Load(DirectoryFetchInfo(), callback);
391 void ChangeListLoader::GetAboutResource(
392 const google_apis::AboutResourceCallback& callback) {
393 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
394 DCHECK(!callback.is_null());
396 if (cached_about_resource_) {
397 base::MessageLoopProxy::current()->PostTask(
398 FROM_HERE,
399 base::Bind(
400 callback,
401 google_apis::HTTP_NO_CONTENT,
402 base::Passed(scoped_ptr<google_apis::AboutResource>(
403 new google_apis::AboutResource(*cached_about_resource_)))));
404 } else {
405 UpdateAboutResource(callback);
409 void ChangeListLoader::LoadDirectoryIfNeededAfterGetEntry(
410 const base::FilePath& directory_path,
411 const FileOperationCallback& callback,
412 bool should_try_loading_parent,
413 const ResourceEntry* entry,
414 FileError error) {
415 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
416 DCHECK(!callback.is_null());
418 // Should load grand root first if My Drive's resource ID is not set.
419 const bool mydrive_resource_id_is_empty =
420 directory_path == util::GetDriveMyDriveRootPath() &&
421 error == FILE_ERROR_OK &&
422 entry->resource_id().empty();
423 if ((error == FILE_ERROR_NOT_FOUND || mydrive_resource_id_is_empty) &&
424 should_try_loading_parent &&
425 util::GetDriveGrandRootPath().IsParent(directory_path)) {
426 // This entry may be found after loading the parent.
427 LoadDirectoryIfNeeded(
428 directory_path.DirName(),
429 base::Bind(&ChangeListLoader::LoadDirectoryIfNeededAfterLoadParent,
430 weak_ptr_factory_.GetWeakPtr(),
431 directory_path,
432 callback));
433 return;
435 if (error != FILE_ERROR_OK) {
436 callback.Run(error);
437 return;
440 if (!entry->file_info().is_directory()) {
441 callback.Run(FILE_ERROR_NOT_A_DIRECTORY);
442 return;
445 // This entry does not exist on the server. Grand root is excluded as it's
446 // specially handled in LoadDirectoryFromServer().
447 if (entry->resource_id().empty() &&
448 entry->local_id() != util::kDriveGrandRootLocalId) {
449 callback.Run(FILE_ERROR_OK);
450 return;
453 Load(DirectoryFetchInfo(entry->local_id(),
454 entry->resource_id(),
455 entry->directory_specific_info().changestamp()),
456 callback);
459 void ChangeListLoader::LoadDirectoryIfNeededAfterLoadParent(
460 const base::FilePath& directory_path,
461 const FileOperationCallback& callback,
462 FileError error) {
463 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
464 DCHECK(!callback.is_null());
466 if (error != FILE_ERROR_OK) {
467 callback.Run(error);
468 return;
471 ResourceEntry* entry = new ResourceEntry;
472 base::PostTaskAndReplyWithResult(
473 blocking_task_runner_.get(),
474 FROM_HERE,
475 base::Bind(&ResourceMetadata::GetResourceEntryByPath,
476 base::Unretained(resource_metadata_),
477 directory_path,
478 entry),
479 base::Bind(&ChangeListLoader::LoadDirectoryIfNeededAfterGetEntry,
480 weak_ptr_factory_.GetWeakPtr(),
481 directory_path,
482 callback,
483 false, // should_try_loading_parent
484 base::Owned(entry)));
487 void ChangeListLoader::Load(const DirectoryFetchInfo& directory_fetch_info,
488 const FileOperationCallback& callback) {
489 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
490 DCHECK(!callback.is_null());
492 // Check if this is the first time this ChangeListLoader do loading.
493 // Note: IsRefreshing() depends on pending_load_callback_ so check in advance.
494 const bool is_initial_load = (!loaded_ && !IsRefreshing());
496 // Register the callback function to be called when it is loaded.
497 const std::string& local_id = directory_fetch_info.local_id();
498 pending_load_callback_[local_id].push_back(callback);
500 // If loading task for |resource_id| is already running, do nothing.
501 if (pending_load_callback_[local_id].size() > 1)
502 return;
504 // For initial loading, even for directory fetching, we do load the full
505 // resource list from the server to sync up. So we register a dummy
506 // callback to indicate that update for full hierarchy is running.
507 if (is_initial_load && !directory_fetch_info.empty()) {
508 pending_load_callback_[""].push_back(
509 base::Bind(&util::EmptyFileOperationCallback));
512 // Check the current status of local metadata, and start loading if needed.
513 base::PostTaskAndReplyWithResult(
514 blocking_task_runner_,
515 FROM_HERE,
516 base::Bind(&ResourceMetadata::GetLargestChangestamp,
517 base::Unretained(resource_metadata_)),
518 base::Bind(&ChangeListLoader::LoadAfterGetLargestChangestamp,
519 weak_ptr_factory_.GetWeakPtr(),
520 directory_fetch_info,
521 is_initial_load));
524 void ChangeListLoader::LoadAfterGetLargestChangestamp(
525 const DirectoryFetchInfo& directory_fetch_info,
526 bool is_initial_load,
527 int64 local_changestamp) {
528 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
530 if (is_initial_load && local_changestamp > 0) {
531 // The local data is usable. Flush callbacks to tell loading was successful.
532 OnChangeListLoadComplete(FILE_ERROR_OK);
534 // Continues to load from server in background.
535 // Put dummy callbacks to indicate that fetching is still continuing.
536 pending_load_callback_[directory_fetch_info.local_id()].push_back(
537 base::Bind(&util::EmptyFileOperationCallback));
538 if (!directory_fetch_info.empty()) {
539 pending_load_callback_[""].push_back(
540 base::Bind(&util::EmptyFileOperationCallback));
544 if (directory_fetch_info.empty()) {
545 UpdateAboutResource(base::Bind(&ChangeListLoader::LoadAfterGetAboutResource,
546 weak_ptr_factory_.GetWeakPtr(),
547 directory_fetch_info,
548 is_initial_load,
549 local_changestamp));
550 } else {
551 // Note: To be precise, we need to call UpdateAboutResource() here. However,
552 // - It is costly to do GetAboutResource HTTP request every time.
553 // - The chance using an old value is small; it only happens when
554 // LoadIfNeeded is called during one GetAboutResource roundtrip time
555 // of a change list fetching.
556 // - Even if the value is old, it just marks the directory as older. It may
557 // trigger one future unnecessary re-fetch, but it'll never lose data.
558 GetAboutResource(base::Bind(&ChangeListLoader::LoadAfterGetAboutResource,
559 weak_ptr_factory_.GetWeakPtr(),
560 directory_fetch_info,
561 is_initial_load,
562 local_changestamp));
566 void ChangeListLoader::LoadAfterGetAboutResource(
567 const DirectoryFetchInfo& directory_fetch_info,
568 bool is_initial_load,
569 int64 local_changestamp,
570 google_apis::GDataErrorCode status,
571 scoped_ptr<google_apis::AboutResource> about_resource) {
572 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
574 FileError error = GDataToFileError(status);
575 if (error != FILE_ERROR_OK) {
576 if (directory_fetch_info.empty() || is_initial_load)
577 OnChangeListLoadComplete(error);
578 else
579 OnDirectoryLoadComplete(directory_fetch_info, error);
580 return;
583 DCHECK(about_resource);
585 int64 remote_changestamp = about_resource->largest_change_id();
586 int64 start_changestamp = local_changestamp > 0 ? local_changestamp + 1 : 0;
587 if (directory_fetch_info.empty()) {
588 if (local_changestamp >= remote_changestamp) {
589 if (local_changestamp > remote_changestamp) {
590 LOG(WARNING) << "Local resource metadata is fresher than server, "
591 << "local = " << local_changestamp
592 << ", server = " << remote_changestamp;
595 // No changes detected, tell the client that the loading was successful.
596 OnChangeListLoadComplete(FILE_ERROR_OK);
597 return;
600 // If the caller is not interested in a particular directory, just start
601 // loading the change list.
602 LoadChangeListFromServer(start_changestamp);
603 } else {
604 // If the caller is interested in a particular directory, start loading the
605 // directory first.
606 int64 directory_changestamp = std::max(directory_fetch_info.changestamp(),
607 local_changestamp);
609 // We may not fetch from the server at all if the local metadata is new
610 // enough, but we log this message here, so "Fast-fetch start" and
611 // "Fast-fetch complete" always match.
612 // TODO(satorux): Distinguish the "not fetching at all" case.
613 util::Log(logging::LOG_INFO,
614 "Fast-fetch start: %s; Server changestamp: %s",
615 directory_fetch_info.ToString().c_str(),
616 base::Int64ToString(remote_changestamp).c_str());
618 // If the directory's changestamp is up-to-date, just schedule to run the
619 // callback, as there is no need to fetch the directory.
620 if (directory_changestamp >= remote_changestamp) {
621 LoadAfterLoadDirectory(directory_fetch_info, is_initial_load,
622 start_changestamp, FILE_ERROR_OK);
623 return;
626 // Start fetching the directory content, and mark it with the changestamp
627 // |remote_changestamp|.
628 DirectoryFetchInfo new_directory_fetch_info(
629 directory_fetch_info.local_id(), directory_fetch_info.resource_id(),
630 remote_changestamp);
631 LoadDirectoryFromServer(
632 new_directory_fetch_info,
633 base::Bind(&ChangeListLoader::LoadAfterLoadDirectory,
634 weak_ptr_factory_.GetWeakPtr(),
635 directory_fetch_info,
636 is_initial_load,
637 start_changestamp));
641 void ChangeListLoader::LoadAfterLoadDirectory(
642 const DirectoryFetchInfo& directory_fetch_info,
643 bool is_initial_load,
644 int64 start_changestamp,
645 FileError error) {
646 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
648 OnDirectoryLoadComplete(directory_fetch_info, error);
650 // Continue to load change list if this is the first load.
651 if (is_initial_load)
652 LoadChangeListFromServer(start_changestamp);
655 void ChangeListLoader::OnChangeListLoadComplete(FileError error) {
656 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
658 if (!loaded_ && error == FILE_ERROR_OK) {
659 loaded_ = true;
660 FOR_EACH_OBSERVER(ChangeListLoaderObserver,
661 observers_,
662 OnInitialLoadComplete());
665 for (LoadCallbackMap::iterator it = pending_load_callback_.begin();
666 it != pending_load_callback_.end(); ++it) {
667 const std::vector<FileOperationCallback>& callbacks = it->second;
668 for (size_t i = 0; i < callbacks.size(); ++i) {
669 base::MessageLoopProxy::current()->PostTask(
670 FROM_HERE,
671 base::Bind(callbacks[i], error));
674 pending_load_callback_.clear();
676 // If there is pending update check, try to load the change from the server
677 // again, because there may exist an update during the completed loading.
678 if (!pending_update_check_callback_.is_null()) {
679 Load(DirectoryFetchInfo(),
680 base::ResetAndReturn(&pending_update_check_callback_));
684 void ChangeListLoader::OnDirectoryLoadComplete(
685 const DirectoryFetchInfo& directory_fetch_info,
686 FileError error) {
687 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
689 util::Log(logging::LOG_INFO,
690 "Fast-fetch complete: %s => %s",
691 directory_fetch_info.ToString().c_str(),
692 FileErrorToString(error).c_str());
693 const std::string& local_id = directory_fetch_info.local_id();
694 LoadCallbackMap::iterator it = pending_load_callback_.find(local_id);
695 if (it != pending_load_callback_.end()) {
696 DVLOG(1) << "Running callback for " << local_id;
697 const std::vector<FileOperationCallback>& callbacks = it->second;
698 for (size_t i = 0; i < callbacks.size(); ++i) {
699 base::MessageLoopProxy::current()->PostTask(
700 FROM_HERE,
701 base::Bind(callbacks[i], error));
703 pending_load_callback_.erase(it);
707 void ChangeListLoader::LoadChangeListFromServer(int64 start_changestamp) {
708 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
709 DCHECK(!change_feed_fetcher_);
710 DCHECK(cached_about_resource_);
712 bool is_delta_update = start_changestamp != 0;
714 // Set up feed fetcher.
715 if (is_delta_update) {
716 change_feed_fetcher_.reset(
717 new DeltaFeedFetcher(scheduler_, start_changestamp));
718 } else {
719 change_feed_fetcher_.reset(new FullFeedFetcher(scheduler_));
722 // Make a copy of cached_about_resource_ to remember at which changestamp we
723 // are fetching change list.
724 change_feed_fetcher_->Run(
725 base::Bind(&ChangeListLoader::LoadChangeListFromServerAfterLoadChangeList,
726 weak_ptr_factory_.GetWeakPtr(),
727 base::Passed(make_scoped_ptr(
728 new google_apis::AboutResource(*cached_about_resource_))),
729 is_delta_update));
732 void ChangeListLoader::LoadChangeListFromServerAfterLoadChangeList(
733 scoped_ptr<google_apis::AboutResource> about_resource,
734 bool is_delta_update,
735 FileError error,
736 ScopedVector<ChangeList> change_lists) {
737 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
738 DCHECK(about_resource);
740 // Delete the fetcher first.
741 change_feed_fetcher_.reset();
743 if (error != FILE_ERROR_OK) {
744 OnChangeListLoadComplete(error);
745 return;
748 ChangeListProcessor* change_list_processor =
749 new ChangeListProcessor(resource_metadata_);
750 // Don't send directory content change notification while performing
751 // the initial content retrieval.
752 const bool should_notify_changed_directories = is_delta_update;
754 util::Log(logging::LOG_INFO,
755 "Apply change lists (is delta: %d)",
756 is_delta_update);
757 base::PostTaskAndReplyWithResult(
758 blocking_task_runner_,
759 FROM_HERE,
760 base::Bind(&ChangeListProcessor::Apply,
761 base::Unretained(change_list_processor),
762 base::Passed(&about_resource),
763 base::Passed(&change_lists),
764 is_delta_update),
765 base::Bind(&ChangeListLoader::LoadChangeListFromServerAfterUpdate,
766 weak_ptr_factory_.GetWeakPtr(),
767 base::Owned(change_list_processor),
768 should_notify_changed_directories,
769 base::Time::Now()));
772 void ChangeListLoader::LoadChangeListFromServerAfterUpdate(
773 ChangeListProcessor* change_list_processor,
774 bool should_notify_changed_directories,
775 const base::Time& start_time,
776 FileError error) {
777 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
779 const base::TimeDelta elapsed = base::Time::Now() - start_time;
780 util::Log(logging::LOG_INFO,
781 "Change lists applied (elapsed time: %sms)",
782 base::Int64ToString(elapsed.InMilliseconds()).c_str());
784 if (should_notify_changed_directories) {
785 for (std::set<base::FilePath>::iterator dir_iter =
786 change_list_processor->changed_dirs().begin();
787 dir_iter != change_list_processor->changed_dirs().end();
788 ++dir_iter) {
789 FOR_EACH_OBSERVER(ChangeListLoaderObserver, observers_,
790 OnDirectoryChanged(*dir_iter));
794 OnChangeListLoadComplete(error);
796 FOR_EACH_OBSERVER(ChangeListLoaderObserver,
797 observers_,
798 OnLoadFromServerComplete());
801 void ChangeListLoader::LoadDirectoryFromServer(
802 const DirectoryFetchInfo& directory_fetch_info,
803 const FileOperationCallback& callback) {
804 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
805 DCHECK(!callback.is_null());
806 DCHECK(!directory_fetch_info.empty());
807 DCHECK(cached_about_resource_);
808 DVLOG(1) << "Start loading directory: " << directory_fetch_info.ToString();
810 if (directory_fetch_info.local_id() == util::kDriveGrandRootLocalId) {
811 // Load for a grand root directory means slightly different from other
812 // directories. It means filling resource ID of mydrive root.
813 base::FilePath* changed_directory_path = new base::FilePath;
814 base::PostTaskAndReplyWithResult(
815 blocking_task_runner_,
816 FROM_HERE,
817 base::Bind(&RefreshMyDriveIfNeeded,
818 resource_metadata_,
819 google_apis::AboutResource(*cached_about_resource_)),
820 base::Bind(&ChangeListLoader::LoadDirectoryFromServerAfterRefresh,
821 weak_ptr_factory_.GetWeakPtr(),
822 directory_fetch_info,
823 callback,
824 base::Owned(changed_directory_path)));
825 return;
828 FastFetchFeedFetcher* fetcher = new FastFetchFeedFetcher(
829 scheduler_,
830 drive_service_,
831 directory_fetch_info.resource_id(),
832 cached_about_resource_->root_folder_id());
833 fast_fetch_feed_fetcher_set_.insert(fetcher);
834 fetcher->Run(
835 base::Bind(&ChangeListLoader::LoadDirectoryFromServerAfterLoad,
836 weak_ptr_factory_.GetWeakPtr(),
837 directory_fetch_info,
838 callback,
839 fetcher));
842 void ChangeListLoader::LoadDirectoryFromServerAfterLoad(
843 const DirectoryFetchInfo& directory_fetch_info,
844 const FileOperationCallback& callback,
845 FeedFetcher* fetcher,
846 FileError error,
847 ScopedVector<ChangeList> change_lists) {
848 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
849 DCHECK(!callback.is_null());
850 DCHECK(!directory_fetch_info.empty());
852 // Delete the fetcher.
853 fast_fetch_feed_fetcher_set_.erase(fetcher);
854 delete fetcher;
856 if (error != FILE_ERROR_OK) {
857 LOG(ERROR) << "Failed to load directory: "
858 << directory_fetch_info.local_id()
859 << ": " << FileErrorToString(error);
860 callback.Run(error);
861 return;
864 base::FilePath* directory_path = new base::FilePath;
865 base::PostTaskAndReplyWithResult(
866 blocking_task_runner_,
867 FROM_HERE,
868 base::Bind(&ChangeListProcessor::RefreshDirectory,
869 resource_metadata_,
870 directory_fetch_info,
871 base::Passed(&change_lists),
872 directory_path),
873 base::Bind(&ChangeListLoader::LoadDirectoryFromServerAfterRefresh,
874 weak_ptr_factory_.GetWeakPtr(),
875 directory_fetch_info,
876 callback,
877 base::Owned(directory_path)));
880 void ChangeListLoader::LoadDirectoryFromServerAfterRefresh(
881 const DirectoryFetchInfo& directory_fetch_info,
882 const FileOperationCallback& callback,
883 const base::FilePath* directory_path,
884 FileError error) {
885 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
886 DCHECK(!callback.is_null());
888 DVLOG(1) << "Directory loaded: " << directory_fetch_info.ToString();
889 callback.Run(error);
890 // Also notify the observers.
891 if (error == FILE_ERROR_OK && !directory_path->empty()) {
892 FOR_EACH_OBSERVER(ChangeListLoaderObserver, observers_,
893 OnDirectoryChanged(*directory_path));
897 void ChangeListLoader::UpdateAboutResource(
898 const google_apis::AboutResourceCallback& callback) {
899 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
900 DCHECK(!callback.is_null());
902 scheduler_->GetAboutResource(
903 base::Bind(&ChangeListLoader::UpdateAboutResourceAfterGetAbout,
904 weak_ptr_factory_.GetWeakPtr(),
905 callback));
908 void ChangeListLoader::UpdateAboutResourceAfterGetAbout(
909 const google_apis::AboutResourceCallback& callback,
910 google_apis::GDataErrorCode status,
911 scoped_ptr<google_apis::AboutResource> about_resource) {
912 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
913 DCHECK(!callback.is_null());
914 FileError error = GDataToFileError(status);
916 if (error == FILE_ERROR_OK) {
917 if (cached_about_resource_ &&
918 cached_about_resource_->largest_change_id() >
919 about_resource->largest_change_id()) {
920 LOG(WARNING) << "Local cached about resource is fresher than server, "
921 << "local = " << cached_about_resource_->largest_change_id()
922 << ", server = " << about_resource->largest_change_id();
925 cached_about_resource_.reset(
926 new google_apis::AboutResource(*about_resource));
929 callback.Run(status, about_resource.Pass());
932 } // namespace internal
933 } // namespace drive