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/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"
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 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
),
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
;
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
);
368 ResourceEntry
* entry
= new ResourceEntry
;
369 base::PostTaskAndReplyWithResult(
370 blocking_task_runner_
.get(),
372 base::Bind(&ResourceMetadata::GetResourceEntryByPath
,
373 base::Unretained(resource_metadata_
),
376 base::Bind(&ChangeListLoader::LoadDirectoryIfNeededAfterGetEntry
,
377 weak_ptr_factory_
.GetWeakPtr(),
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(
401 google_apis::HTTP_NO_CONTENT
,
402 base::Passed(scoped_ptr
<google_apis::AboutResource
>(
403 new google_apis::AboutResource(*cached_about_resource_
)))));
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
,
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(),
435 if (error
!= FILE_ERROR_OK
) {
440 if (!entry
->file_info().is_directory()) {
441 callback
.Run(FILE_ERROR_NOT_A_DIRECTORY
);
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
);
453 Load(DirectoryFetchInfo(entry
->local_id(),
454 entry
->resource_id(),
455 entry
->directory_specific_info().changestamp()),
459 void ChangeListLoader::LoadDirectoryIfNeededAfterLoadParent(
460 const base::FilePath
& directory_path
,
461 const FileOperationCallback
& callback
,
463 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
464 DCHECK(!callback
.is_null());
466 if (error
!= FILE_ERROR_OK
) {
471 ResourceEntry
* entry
= new ResourceEntry
;
472 base::PostTaskAndReplyWithResult(
473 blocking_task_runner_
.get(),
475 base::Bind(&ResourceMetadata::GetResourceEntryByPath
,
476 base::Unretained(resource_metadata_
),
479 base::Bind(&ChangeListLoader::LoadDirectoryIfNeededAfterGetEntry
,
480 weak_ptr_factory_
.GetWeakPtr(),
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)
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_
,
516 base::Bind(&ResourceMetadata::GetLargestChangestamp
,
517 base::Unretained(resource_metadata_
)),
518 base::Bind(&ChangeListLoader::LoadAfterGetLargestChangestamp
,
519 weak_ptr_factory_
.GetWeakPtr(),
520 directory_fetch_info
,
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
,
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
,
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
);
579 OnDirectoryLoadComplete(directory_fetch_info
, error
);
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
);
600 // If the caller is not interested in a particular directory, just start
601 // loading the change list.
602 LoadChangeListFromServer(start_changestamp
);
604 // If the caller is interested in a particular directory, start loading the
606 int64 directory_changestamp
= std::max(directory_fetch_info
.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
);
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(),
631 LoadDirectoryFromServer(
632 new_directory_fetch_info
,
633 base::Bind(&ChangeListLoader::LoadAfterLoadDirectory
,
634 weak_ptr_factory_
.GetWeakPtr(),
635 directory_fetch_info
,
641 void ChangeListLoader::LoadAfterLoadDirectory(
642 const DirectoryFetchInfo
& directory_fetch_info
,
643 bool is_initial_load
,
644 int64 start_changestamp
,
646 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
648 OnDirectoryLoadComplete(directory_fetch_info
, error
);
650 // Continue to load change list if this is the first load.
652 LoadChangeListFromServer(start_changestamp
);
655 void ChangeListLoader::OnChangeListLoadComplete(FileError error
) {
656 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
658 if (!loaded_
&& error
== FILE_ERROR_OK
) {
660 FOR_EACH_OBSERVER(ChangeListLoaderObserver
,
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(
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
,
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(
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
));
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_
))),
732 void ChangeListLoader::LoadChangeListFromServerAfterLoadChangeList(
733 scoped_ptr
<google_apis::AboutResource
> about_resource
,
734 bool is_delta_update
,
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
);
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)",
757 base::PostTaskAndReplyWithResult(
758 blocking_task_runner_
,
760 base::Bind(&ChangeListProcessor::Apply
,
761 base::Unretained(change_list_processor
),
762 base::Passed(&about_resource
),
763 base::Passed(&change_lists
),
765 base::Bind(&ChangeListLoader::LoadChangeListFromServerAfterUpdate
,
766 weak_ptr_factory_
.GetWeakPtr(),
767 base::Owned(change_list_processor
),
768 should_notify_changed_directories
,
772 void ChangeListLoader::LoadChangeListFromServerAfterUpdate(
773 ChangeListProcessor
* change_list_processor
,
774 bool should_notify_changed_directories
,
775 const base::Time
& start_time
,
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();
789 FOR_EACH_OBSERVER(ChangeListLoaderObserver
, observers_
,
790 OnDirectoryChanged(*dir_iter
));
794 OnChangeListLoadComplete(error
);
796 FOR_EACH_OBSERVER(ChangeListLoaderObserver
,
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_
,
817 base::Bind(&RefreshMyDriveIfNeeded
,
819 google_apis::AboutResource(*cached_about_resource_
)),
820 base::Bind(&ChangeListLoader::LoadDirectoryFromServerAfterRefresh
,
821 weak_ptr_factory_
.GetWeakPtr(),
822 directory_fetch_info
,
824 base::Owned(changed_directory_path
)));
828 FastFetchFeedFetcher
* fetcher
= new FastFetchFeedFetcher(
831 directory_fetch_info
.resource_id(),
832 cached_about_resource_
->root_folder_id());
833 fast_fetch_feed_fetcher_set_
.insert(fetcher
);
835 base::Bind(&ChangeListLoader::LoadDirectoryFromServerAfterLoad
,
836 weak_ptr_factory_
.GetWeakPtr(),
837 directory_fetch_info
,
842 void ChangeListLoader::LoadDirectoryFromServerAfterLoad(
843 const DirectoryFetchInfo
& directory_fetch_info
,
844 const FileOperationCallback
& callback
,
845 FeedFetcher
* fetcher
,
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
);
856 if (error
!= FILE_ERROR_OK
) {
857 LOG(ERROR
) << "Failed to load directory: "
858 << directory_fetch_info
.local_id()
859 << ": " << FileErrorToString(error
);
864 base::FilePath
* directory_path
= new base::FilePath
;
865 base::PostTaskAndReplyWithResult(
866 blocking_task_runner_
,
868 base::Bind(&ChangeListProcessor::RefreshDirectory
,
870 directory_fetch_info
,
871 base::Passed(&change_lists
),
873 base::Bind(&ChangeListLoader::LoadDirectoryFromServerAfterRefresh
,
874 weak_ptr_factory_
.GetWeakPtr(),
875 directory_fetch_info
,
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
,
885 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
886 DCHECK(!callback
.is_null());
888 DVLOG(1) << "Directory loaded: " << directory_fetch_info
.ToString();
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(),
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