Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / chromeos / drive / change_list_loader.cc
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/chromeos/drive/change_list_loader.h"
6
7 #include <set>
8
9 #include "base/callback.h"
10 #include "base/callback_helpers.h"
11 #include "base/metrics/histogram.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/time/time.h"
14 #include "chrome/browser/chromeos/drive/change_list_loader_observer.h"
15 #include "chrome/browser/chromeos/drive/change_list_processor.h"
16 #include "chrome/browser/chromeos/drive/file_system_util.h"
17 #include "chrome/browser/chromeos/drive/job_scheduler.h"
18 #include "chrome/browser/chromeos/drive/resource_metadata.h"
19 #include "chrome/browser/drive/drive_api_util.h"
20 #include "chrome/browser/drive/drive_service_interface.h"
21 #include "chrome/browser/drive/event_logger.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "google_apis/drive/drive_api_parser.h"
24 #include "url/gurl.h"
25
26 using content::BrowserThread;
27
28 namespace drive {
29 namespace internal {
30
31 typedef base::Callback<void(FileError, ScopedVector<ChangeList>)>
32     FeedFetcherCallback;
33
34 class ChangeListLoader::FeedFetcher {
35  public:
36   virtual ~FeedFetcher() {}
37   virtual void Run(const FeedFetcherCallback& callback) = 0;
38 };
39
40 namespace {
41
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) {
48   }
49
50   virtual ~FullFeedFetcher() {
51   }
52
53   virtual void Run(const FeedFetcherCallback& callback) OVERRIDE {
54     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
55     DCHECK(!callback.is_null());
56
57     // Remember the time stamp for usage stats.
58     start_time_ = base::TimeTicks::Now();
59
60     // This is full resource list fetch.
61     scheduler_->GetAllResourceList(
62         base::Bind(&FullFeedFetcher::OnFileListFetched,
63                    weak_ptr_factory_.GetWeakPtr(), callback));
64   }
65
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());
73
74     FileError error = GDataToFileError(status);
75     if (error != FILE_ERROR_OK) {
76       callback.Run(error, ScopedVector<ChangeList>());
77       return;
78     }
79
80     DCHECK(resource_list);
81     change_lists_.push_back(new ChangeList(*resource_list));
82
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;
91     }
92
93     UMA_HISTOGRAM_LONG_TIMES("Drive.FullFeedLoadTime",
94                              base::TimeTicks::Now() - start_time_);
95
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());
100   }
101
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);
107 };
108
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) {
116   }
117
118   virtual ~DeltaFeedFetcher() {
119   }
120
121   virtual void Run(const FeedFetcherCallback& callback) OVERRIDE {
122     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
123     DCHECK(!callback.is_null());
124
125     scheduler_->GetChangeList(
126         start_change_id_,
127         base::Bind(&DeltaFeedFetcher::OnChangeListFetched,
128                    weak_ptr_factory_.GetWeakPtr(), callback));
129   }
130
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());
138
139     FileError error = GDataToFileError(status);
140     if (error != FILE_ERROR_OK) {
141       callback.Run(error, ScopedVector<ChangeList>());
142       return;
143     }
144
145     DCHECK(resource_list);
146     change_lists_.push_back(new ChangeList(*resource_list));
147
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;
156     }
157
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());
162   }
163
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);
169 };
170
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) {
183   }
184
185   virtual ~FastFetchFeedFetcher() {
186   }
187
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());
193
194     // Remember the time stamp for usage stats.
195     start_time_ = base::TimeTicks::Now();
196
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.
200
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;
207     }
208
209     scheduler_->GetResourceListInDirectoryByWapi(
210         resource_id,
211         base::Bind(&FastFetchFeedFetcher::OnResourceListFetched,
212                    weak_ptr_factory_.GetWeakPtr(), callback));
213   }
214
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());
222
223     FileError error = GDataToFileError(status);
224     if (error != FILE_ERROR_OK) {
225       callback.Run(error, ScopedVector<ChangeList>());
226       return;
227     }
228
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);
235
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;
244     }
245
246     UMA_HISTOGRAM_TIMES("Drive.DirectoryFeedLoadTime",
247                         base::TimeTicks::Now() - start_time_);
248
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());
253   }
254
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()));
266
267       (*parent_resource_ids)[i] = FixResourceId((*parent_resource_ids)[i]);
268     }
269   }
270
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);
275   }
276
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);
285 };
286
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;
295
296   entry.set_resource_id(about_resource.root_folder_id());
297   return resource_metadata->RefreshEntry(entry);
298 }
299
300 }  // namespace
301
302 LoaderController::LoaderController()
303     : lock_count_(0),
304       weak_ptr_factory_(this) {
305   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
306 }
307
308 LoaderController::~LoaderController() {
309   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
310 }
311
312 scoped_ptr<base::ScopedClosureRunner> LoaderController::GetLock() {
313   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
314
315   ++lock_count_;
316   return make_scoped_ptr(new base::ScopedClosureRunner(
317       base::Bind(&LoaderController::Unlock,
318                  weak_ptr_factory_.GetWeakPtr())));
319 }
320
321 void LoaderController::ScheduleRun(const base::Closure& task) {
322   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
323   DCHECK(!task.is_null());
324
325   if (lock_count_ > 0) {
326     pending_tasks_.push_back(task);
327   } else {
328     task.Run();
329   }
330 }
331
332 void LoaderController::Unlock() {
333   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
334   DCHECK_LT(0, lock_count_);
335
336   if (--lock_count_ > 0)
337     return;
338
339   std::vector<base::Closure> tasks;
340   tasks.swap(pending_tasks_);
341   for (size_t i = 0; i < tasks.size(); ++i)
342     tasks[i].Run();
343 }
344
345 AboutResourceLoader::AboutResourceLoader(JobScheduler* scheduler)
346     : scheduler_(scheduler),
347       weak_ptr_factory_(this) {
348 }
349
350 AboutResourceLoader::~AboutResourceLoader() {}
351
352 void AboutResourceLoader::GetAboutResource(
353     const google_apis::AboutResourceCallback& callback) {
354   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
355   DCHECK(!callback.is_null());
356
357   if (cached_about_resource_) {
358     base::MessageLoopProxy::current()->PostTask(
359         FROM_HERE,
360         base::Bind(
361             callback,
362             google_apis::HTTP_NO_CONTENT,
363             base::Passed(scoped_ptr<google_apis::AboutResource>(
364                 new google_apis::AboutResource(*cached_about_resource_)))));
365   } else {
366     UpdateAboutResource(callback);
367   }
368 }
369
370 void AboutResourceLoader::UpdateAboutResource(
371     const google_apis::AboutResourceCallback& callback) {
372   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
373   DCHECK(!callback.is_null());
374
375   scheduler_->GetAboutResource(
376       base::Bind(&AboutResourceLoader::UpdateAboutResourceAfterGetAbout,
377                  weak_ptr_factory_.GetWeakPtr(),
378                  callback));
379 }
380
381 void AboutResourceLoader::UpdateAboutResourceAfterGetAbout(
382     const google_apis::AboutResourceCallback& callback,
383     google_apis::GDataErrorCode status,
384     scoped_ptr<google_apis::AboutResource> about_resource) {
385   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
386   DCHECK(!callback.is_null());
387   FileError error = GDataToFileError(status);
388
389   if (error == FILE_ERROR_OK) {
390     if (cached_about_resource_ &&
391         cached_about_resource_->largest_change_id() >
392         about_resource->largest_change_id()) {
393       LOG(WARNING) << "Local cached about resource is fresher than server, "
394                    << "local = " << cached_about_resource_->largest_change_id()
395                    << ", server = " << about_resource->largest_change_id();
396     }
397
398     cached_about_resource_.reset(
399         new google_apis::AboutResource(*about_resource));
400   }
401
402   callback.Run(status, about_resource.Pass());
403 }
404
405 ChangeListLoader::ChangeListLoader(
406     EventLogger* logger,
407     base::SequencedTaskRunner* blocking_task_runner,
408     ResourceMetadata* resource_metadata,
409     JobScheduler* scheduler,
410     DriveServiceInterface* drive_service,
411     AboutResourceLoader* about_resource_loader,
412     LoaderController* loader_controller)
413     : logger_(logger),
414       blocking_task_runner_(blocking_task_runner),
415       resource_metadata_(resource_metadata),
416       scheduler_(scheduler),
417       drive_service_(drive_service),
418       about_resource_loader_(about_resource_loader),
419       loader_controller_(loader_controller),
420       loaded_(false),
421       weak_ptr_factory_(this) {
422 }
423
424 ChangeListLoader::~ChangeListLoader() {
425   STLDeleteElements(&fast_fetch_feed_fetcher_set_);
426 }
427
428 bool ChangeListLoader::IsRefreshing() const {
429   // Callback for change list loading is stored in pending_load_callback_[""].
430   // It is non-empty if and only if there is an in-flight loading operation.
431   return pending_load_callback_.find("") != pending_load_callback_.end();
432 }
433
434 void ChangeListLoader::AddObserver(ChangeListLoaderObserver* observer) {
435   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
436   observers_.AddObserver(observer);
437 }
438
439 void ChangeListLoader::RemoveObserver(ChangeListLoaderObserver* observer) {
440   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
441   observers_.RemoveObserver(observer);
442 }
443
444 void ChangeListLoader::CheckForUpdates(const FileOperationCallback& callback) {
445   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
446   DCHECK(!callback.is_null());
447
448   if (IsRefreshing()) {
449     // There is in-flight loading. So keep the callback here, and check for
450     // updates when the in-flight loading is completed.
451     pending_update_check_callback_ = callback;
452     return;
453   }
454
455   if (loaded_) {
456     // We only start to check for updates iff the load is done.
457     // I.e., we ignore checking updates if not loaded to avoid starting the
458     // load without user's explicit interaction (such as opening Drive).
459     logger_->Log(logging::LOG_INFO, "Checking for updates");
460     Load(DirectoryFetchInfo(), callback);
461   }
462 }
463
464 void ChangeListLoader::LoadDirectoryIfNeeded(
465     const base::FilePath& directory_path,
466     const FileOperationCallback& callback) {
467   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
468   DCHECK(!callback.is_null());
469
470   // If the resource metadata has been already loaded and not refreshing, it
471   // means the local metadata is up to date.
472   if (loaded_ && !IsRefreshing()) {
473     callback.Run(FILE_ERROR_OK);
474     return;
475   }
476
477   ResourceEntry* entry = new ResourceEntry;
478   base::PostTaskAndReplyWithResult(
479       blocking_task_runner_.get(),
480       FROM_HERE,
481       base::Bind(&ResourceMetadata::GetResourceEntryByPath,
482                  base::Unretained(resource_metadata_),
483                  directory_path,
484                  entry),
485       base::Bind(&ChangeListLoader::LoadDirectoryIfNeededAfterGetEntry,
486                  weak_ptr_factory_.GetWeakPtr(),
487                  directory_path,
488                  callback,
489                  true,  // should_try_loading_parent
490                  base::Owned(entry)));
491 }
492
493 void ChangeListLoader::LoadForTesting(const FileOperationCallback& callback) {
494   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
495   DCHECK(!callback.is_null());
496
497   Load(DirectoryFetchInfo(), callback);
498 }
499
500 void ChangeListLoader::LoadDirectoryIfNeededAfterGetEntry(
501     const base::FilePath& directory_path,
502     const FileOperationCallback& callback,
503     bool should_try_loading_parent,
504     const ResourceEntry* entry,
505     FileError error) {
506   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
507   DCHECK(!callback.is_null());
508
509   // Should load grand root first if My Drive's resource ID is not set.
510   const bool mydrive_resource_id_is_empty =
511       directory_path == util::GetDriveMyDriveRootPath() &&
512       error == FILE_ERROR_OK &&
513       entry->resource_id().empty();
514   if ((error == FILE_ERROR_NOT_FOUND || mydrive_resource_id_is_empty) &&
515       should_try_loading_parent &&
516       util::GetDriveGrandRootPath().IsParent(directory_path)) {
517     // This entry may be found after loading the parent.
518     LoadDirectoryIfNeeded(
519         directory_path.DirName(),
520         base::Bind(&ChangeListLoader::LoadDirectoryIfNeededAfterLoadParent,
521                    weak_ptr_factory_.GetWeakPtr(),
522                    directory_path,
523                    callback));
524     return;
525   }
526   if (error != FILE_ERROR_OK) {
527     callback.Run(error);
528     return;
529   }
530
531   if (!entry->file_info().is_directory()) {
532     callback.Run(FILE_ERROR_NOT_A_DIRECTORY);
533     return;
534   }
535
536   // This entry does not exist on the server. Grand root is excluded as it's
537   // specially handled in LoadDirectoryFromServer().
538   if (entry->resource_id().empty() &&
539       entry->local_id() != util::kDriveGrandRootLocalId) {
540     callback.Run(FILE_ERROR_OK);
541     return;
542   }
543
544   Load(DirectoryFetchInfo(entry->local_id(),
545                           entry->resource_id(),
546                           entry->directory_specific_info().changestamp()),
547        callback);
548 }
549
550 void ChangeListLoader::LoadDirectoryIfNeededAfterLoadParent(
551     const base::FilePath& directory_path,
552     const FileOperationCallback& callback,
553     FileError error) {
554   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
555   DCHECK(!callback.is_null());
556
557   if (error != FILE_ERROR_OK) {
558     callback.Run(error);
559     return;
560   }
561
562   ResourceEntry* entry = new ResourceEntry;
563   base::PostTaskAndReplyWithResult(
564       blocking_task_runner_.get(),
565       FROM_HERE,
566       base::Bind(&ResourceMetadata::GetResourceEntryByPath,
567                  base::Unretained(resource_metadata_),
568                  directory_path,
569                  entry),
570       base::Bind(&ChangeListLoader::LoadDirectoryIfNeededAfterGetEntry,
571                  weak_ptr_factory_.GetWeakPtr(),
572                  directory_path,
573                  callback,
574                  false,  // should_try_loading_parent
575                  base::Owned(entry)));
576 }
577
578 void ChangeListLoader::Load(const DirectoryFetchInfo& directory_fetch_info,
579                             const FileOperationCallback& callback) {
580   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
581   DCHECK(!callback.is_null());
582
583   // Check if this is the first time this ChangeListLoader do loading.
584   // Note: IsRefreshing() depends on pending_load_callback_ so check in advance.
585   const bool is_initial_load = (!loaded_ && !IsRefreshing());
586
587   // Register the callback function to be called when it is loaded.
588   const std::string& local_id = directory_fetch_info.local_id();
589   pending_load_callback_[local_id].push_back(callback);
590
591   // If loading task for |resource_id| is already running, do nothing.
592   if (pending_load_callback_[local_id].size() > 1)
593     return;
594
595   // For initial loading, even for directory fetching, we do load the full
596   // resource list from the server to sync up. So we register a dummy
597   // callback to indicate that update for full hierarchy is running.
598   if (is_initial_load && !directory_fetch_info.empty()) {
599     pending_load_callback_[""].push_back(
600         base::Bind(&util::EmptyFileOperationCallback));
601   }
602
603   // Check the current status of local metadata, and start loading if needed.
604   base::PostTaskAndReplyWithResult(
605       blocking_task_runner_,
606       FROM_HERE,
607       base::Bind(&ResourceMetadata::GetLargestChangestamp,
608                  base::Unretained(resource_metadata_)),
609       base::Bind(&ChangeListLoader::LoadAfterGetLargestChangestamp,
610                  weak_ptr_factory_.GetWeakPtr(),
611                  directory_fetch_info,
612                  is_initial_load));
613 }
614
615 void ChangeListLoader::LoadAfterGetLargestChangestamp(
616     const DirectoryFetchInfo& directory_fetch_info,
617     bool is_initial_load,
618     int64 local_changestamp) {
619   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
620
621   if (is_initial_load && local_changestamp > 0) {
622     // The local data is usable. Flush callbacks to tell loading was successful.
623     OnChangeListLoadComplete(FILE_ERROR_OK);
624
625     // Continues to load from server in background.
626     // Put dummy callbacks to indicate that fetching is still continuing.
627     pending_load_callback_[directory_fetch_info.local_id()].push_back(
628         base::Bind(&util::EmptyFileOperationCallback));
629     if (!directory_fetch_info.empty()) {
630       pending_load_callback_[""].push_back(
631           base::Bind(&util::EmptyFileOperationCallback));
632     }
633   }
634
635   if (directory_fetch_info.empty()) {
636     about_resource_loader_->UpdateAboutResource(
637         base::Bind(&ChangeListLoader::LoadAfterGetAboutResource,
638                    weak_ptr_factory_.GetWeakPtr(),
639                    directory_fetch_info,
640                    is_initial_load,
641                    local_changestamp));
642   } else {
643     // Note: To be precise, we need to call UpdateAboutResource() here. However,
644     // - It is costly to do GetAboutResource HTTP request every time.
645     // - The chance using an old value is small; it only happens when
646     //   LoadIfNeeded is called during one GetAboutResource roundtrip time
647     //   of a change list fetching.
648     // - Even if the value is old, it just marks the directory as older. It may
649     //   trigger one future unnecessary re-fetch, but it'll never lose data.
650     about_resource_loader_->GetAboutResource(
651         base::Bind(&ChangeListLoader::LoadAfterGetAboutResource,
652                    weak_ptr_factory_.GetWeakPtr(),
653                    directory_fetch_info,
654                    is_initial_load,
655                    local_changestamp));
656   }
657 }
658
659 void ChangeListLoader::LoadAfterGetAboutResource(
660     const DirectoryFetchInfo& directory_fetch_info,
661     bool is_initial_load,
662     int64 local_changestamp,
663     google_apis::GDataErrorCode status,
664     scoped_ptr<google_apis::AboutResource> about_resource) {
665   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
666
667   FileError error = GDataToFileError(status);
668   if (error != FILE_ERROR_OK) {
669     if (directory_fetch_info.empty() || is_initial_load)
670       OnChangeListLoadComplete(error);
671     else
672       OnDirectoryLoadComplete(directory_fetch_info, error);
673     return;
674   }
675
676   DCHECK(about_resource);
677
678   int64 remote_changestamp = about_resource->largest_change_id();
679   int64 start_changestamp = local_changestamp > 0 ? local_changestamp + 1 : 0;
680   if (directory_fetch_info.empty()) {
681     if (local_changestamp >= remote_changestamp) {
682       if (local_changestamp > remote_changestamp) {
683         LOG(WARNING) << "Local resource metadata is fresher than server, "
684                      << "local = " << local_changestamp
685                      << ", server = " << remote_changestamp;
686       }
687
688       // No changes detected, tell the client that the loading was successful.
689       OnChangeListLoadComplete(FILE_ERROR_OK);
690       return;
691     }
692
693     // If the caller is not interested in a particular directory, just start
694     // loading the change list.
695     LoadChangeListFromServer(start_changestamp);
696   } else {
697     // If the caller is interested in a particular directory, start loading the
698     // directory first.
699     int64 directory_changestamp = std::max(directory_fetch_info.changestamp(),
700                                            local_changestamp);
701
702     // We may not fetch from the server at all if the local metadata is new
703     // enough, but we log this message here, so "Fast-fetch start" and
704     // "Fast-fetch complete" always match.
705     // TODO(satorux): Distinguish the "not fetching at all" case.
706     logger_->Log(logging::LOG_INFO,
707                  "Fast-fetch start: %s; Server changestamp: %s",
708                  directory_fetch_info.ToString().c_str(),
709                  base::Int64ToString(remote_changestamp).c_str());
710
711     // If the directory's changestamp is up-to-date, just schedule to run the
712     // callback, as there is no need to fetch the directory.
713     if (directory_changestamp >= remote_changestamp) {
714       LoadAfterLoadDirectory(directory_fetch_info, is_initial_load,
715                              start_changestamp, FILE_ERROR_OK);
716       return;
717     }
718
719     // Start fetching the directory content, and mark it with the changestamp
720     // |remote_changestamp|.
721     DirectoryFetchInfo new_directory_fetch_info(
722         directory_fetch_info.local_id(), directory_fetch_info.resource_id(),
723         remote_changestamp);
724     LoadDirectoryFromServer(
725         new_directory_fetch_info,
726         base::Bind(&ChangeListLoader::LoadAfterLoadDirectory,
727                    weak_ptr_factory_.GetWeakPtr(),
728                    directory_fetch_info,
729                    is_initial_load,
730                    start_changestamp));
731   }
732 }
733
734 void ChangeListLoader::LoadAfterLoadDirectory(
735     const DirectoryFetchInfo& directory_fetch_info,
736     bool is_initial_load,
737     int64 start_changestamp,
738     FileError error) {
739   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
740
741   OnDirectoryLoadComplete(directory_fetch_info, error);
742
743   // Continue to load change list if this is the first load.
744   if (is_initial_load)
745     LoadChangeListFromServer(start_changestamp);
746 }
747
748 void ChangeListLoader::OnChangeListLoadComplete(FileError error) {
749   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
750
751   if (!loaded_ && error == FILE_ERROR_OK) {
752     loaded_ = true;
753     FOR_EACH_OBSERVER(ChangeListLoaderObserver,
754                       observers_,
755                       OnInitialLoadComplete());
756   }
757
758   for (LoadCallbackMap::iterator it = pending_load_callback_.begin();
759        it != pending_load_callback_.end();  ++it) {
760     const std::vector<FileOperationCallback>& callbacks = it->second;
761     for (size_t i = 0; i < callbacks.size(); ++i) {
762       base::MessageLoopProxy::current()->PostTask(
763           FROM_HERE,
764           base::Bind(callbacks[i], error));
765     }
766   }
767   pending_load_callback_.clear();
768
769   // If there is pending update check, try to load the change from the server
770   // again, because there may exist an update during the completed loading.
771   if (!pending_update_check_callback_.is_null()) {
772     Load(DirectoryFetchInfo(),
773          base::ResetAndReturn(&pending_update_check_callback_));
774   }
775 }
776
777 void ChangeListLoader::OnDirectoryLoadComplete(
778     const DirectoryFetchInfo& directory_fetch_info,
779     FileError error) {
780   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
781
782   logger_->Log(logging::LOG_INFO,
783                "Fast-fetch complete: %s => %s",
784                directory_fetch_info.ToString().c_str(),
785                FileErrorToString(error).c_str());
786   const std::string& local_id = directory_fetch_info.local_id();
787   LoadCallbackMap::iterator it = pending_load_callback_.find(local_id);
788   if (it != pending_load_callback_.end()) {
789     DVLOG(1) << "Running callback for " << local_id;
790     const std::vector<FileOperationCallback>& callbacks = it->second;
791     for (size_t i = 0; i < callbacks.size(); ++i) {
792       base::MessageLoopProxy::current()->PostTask(
793           FROM_HERE,
794           base::Bind(callbacks[i], error));
795     }
796     pending_load_callback_.erase(it);
797   }
798 }
799
800 void ChangeListLoader::LoadChangeListFromServer(int64 start_changestamp) {
801   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
802   DCHECK(!change_feed_fetcher_);
803   DCHECK(about_resource_loader_->cached_about_resource());
804
805   bool is_delta_update = start_changestamp != 0;
806
807   // Set up feed fetcher.
808   if (is_delta_update) {
809     change_feed_fetcher_.reset(
810         new DeltaFeedFetcher(scheduler_, start_changestamp));
811   } else {
812     change_feed_fetcher_.reset(new FullFeedFetcher(scheduler_));
813   }
814
815   // Make a copy of cached_about_resource_ to remember at which changestamp we
816   // are fetching change list.
817   change_feed_fetcher_->Run(
818       base::Bind(&ChangeListLoader::LoadChangeListFromServerAfterLoadChangeList,
819                  weak_ptr_factory_.GetWeakPtr(),
820                  base::Passed(make_scoped_ptr(new google_apis::AboutResource(
821                      *about_resource_loader_->cached_about_resource()))),
822                  is_delta_update));
823 }
824
825 void ChangeListLoader::LoadChangeListFromServerAfterLoadChangeList(
826     scoped_ptr<google_apis::AboutResource> about_resource,
827     bool is_delta_update,
828     FileError error,
829     ScopedVector<ChangeList> change_lists) {
830   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
831   DCHECK(about_resource);
832
833   // Delete the fetcher first.
834   change_feed_fetcher_.reset();
835
836   if (error != FILE_ERROR_OK) {
837     OnChangeListLoadComplete(error);
838     return;
839   }
840
841   ChangeListProcessor* change_list_processor =
842       new ChangeListProcessor(resource_metadata_);
843   // Don't send directory content change notification while performing
844   // the initial content retrieval.
845   const bool should_notify_changed_directories = is_delta_update;
846
847   logger_->Log(logging::LOG_INFO,
848                "Apply change lists (is delta: %d)",
849                is_delta_update);
850   loader_controller_->ScheduleRun(base::Bind(
851       base::IgnoreResult(
852           &base::PostTaskAndReplyWithResult<FileError, FileError>),
853       blocking_task_runner_,
854       FROM_HERE,
855       base::Bind(&ChangeListProcessor::Apply,
856                  base::Unretained(change_list_processor),
857                  base::Passed(&about_resource),
858                  base::Passed(&change_lists),
859                  is_delta_update),
860       base::Bind(&ChangeListLoader::LoadChangeListFromServerAfterUpdate,
861                  weak_ptr_factory_.GetWeakPtr(),
862                  base::Owned(change_list_processor),
863                  should_notify_changed_directories,
864                  base::Time::Now())));
865 }
866
867 void ChangeListLoader::LoadChangeListFromServerAfterUpdate(
868     ChangeListProcessor* change_list_processor,
869     bool should_notify_changed_directories,
870     const base::Time& start_time,
871     FileError error) {
872   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
873
874   const base::TimeDelta elapsed = base::Time::Now() - start_time;
875   logger_->Log(logging::LOG_INFO,
876                "Change lists applied (elapsed time: %sms)",
877                base::Int64ToString(elapsed.InMilliseconds()).c_str());
878
879   if (should_notify_changed_directories) {
880     for (std::set<base::FilePath>::iterator dir_iter =
881             change_list_processor->changed_dirs().begin();
882         dir_iter != change_list_processor->changed_dirs().end();
883         ++dir_iter) {
884       FOR_EACH_OBSERVER(ChangeListLoaderObserver, observers_,
885                         OnDirectoryChanged(*dir_iter));
886     }
887   }
888
889   OnChangeListLoadComplete(error);
890
891   FOR_EACH_OBSERVER(ChangeListLoaderObserver,
892                     observers_,
893                     OnLoadFromServerComplete());
894 }
895
896 void ChangeListLoader::LoadDirectoryFromServer(
897     const DirectoryFetchInfo& directory_fetch_info,
898     const FileOperationCallback& callback) {
899   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
900   DCHECK(!callback.is_null());
901   DCHECK(!directory_fetch_info.empty());
902   DCHECK(about_resource_loader_->cached_about_resource());
903   DVLOG(1) << "Start loading directory: " << directory_fetch_info.ToString();
904
905   if (directory_fetch_info.local_id() == util::kDriveGrandRootLocalId) {
906     // Load for a grand root directory means slightly different from other
907     // directories. It means filling resource ID of mydrive root.
908     base::FilePath* changed_directory_path = new base::FilePath;
909     base::PostTaskAndReplyWithResult(
910         blocking_task_runner_,
911         FROM_HERE,
912         base::Bind(&RefreshMyDriveIfNeeded,
913                    resource_metadata_,
914                    google_apis::AboutResource(
915                        *about_resource_loader_->cached_about_resource())),
916         base::Bind(&ChangeListLoader::LoadDirectoryFromServerAfterRefresh,
917                    weak_ptr_factory_.GetWeakPtr(),
918                    directory_fetch_info,
919                    callback,
920                    base::Owned(changed_directory_path)));
921     return;
922   }
923
924   FastFetchFeedFetcher* fetcher = new FastFetchFeedFetcher(
925       scheduler_,
926       drive_service_,
927       directory_fetch_info.resource_id(),
928       about_resource_loader_->cached_about_resource()->root_folder_id());
929   fast_fetch_feed_fetcher_set_.insert(fetcher);
930   fetcher->Run(
931       base::Bind(&ChangeListLoader::LoadDirectoryFromServerAfterLoad,
932                  weak_ptr_factory_.GetWeakPtr(),
933                  directory_fetch_info,
934                  callback,
935                  fetcher));
936 }
937
938 void ChangeListLoader::LoadDirectoryFromServerAfterLoad(
939     const DirectoryFetchInfo& directory_fetch_info,
940     const FileOperationCallback& callback,
941     FeedFetcher* fetcher,
942     FileError error,
943     ScopedVector<ChangeList> change_lists) {
944   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
945   DCHECK(!callback.is_null());
946   DCHECK(!directory_fetch_info.empty());
947
948   // Delete the fetcher.
949   fast_fetch_feed_fetcher_set_.erase(fetcher);
950   delete fetcher;
951
952   if (error != FILE_ERROR_OK) {
953     LOG(ERROR) << "Failed to load directory: "
954                << directory_fetch_info.local_id()
955                << ": " << FileErrorToString(error);
956     callback.Run(error);
957     return;
958   }
959
960   base::FilePath* directory_path = new base::FilePath;
961   loader_controller_->ScheduleRun(base::Bind(
962       base::IgnoreResult(
963           &base::PostTaskAndReplyWithResult<FileError, FileError>),
964       blocking_task_runner_,
965       FROM_HERE,
966       base::Bind(&ChangeListProcessor::RefreshDirectory,
967                  resource_metadata_,
968                  directory_fetch_info,
969                  base::Passed(&change_lists),
970                  directory_path),
971       base::Bind(&ChangeListLoader::LoadDirectoryFromServerAfterRefresh,
972                  weak_ptr_factory_.GetWeakPtr(),
973                  directory_fetch_info,
974                  callback,
975                  base::Owned(directory_path))));
976 }
977
978 void ChangeListLoader::LoadDirectoryFromServerAfterRefresh(
979     const DirectoryFetchInfo& directory_fetch_info,
980     const FileOperationCallback& callback,
981     const base::FilePath* directory_path,
982     FileError error) {
983   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
984   DCHECK(!callback.is_null());
985
986   DVLOG(1) << "Directory loaded: " << directory_fetch_info.ToString();
987   callback.Run(error);
988   // Also notify the observers.
989   if (error == FILE_ERROR_OK && !directory_path->empty()) {
990     FOR_EACH_OBSERVER(ChangeListLoaderObserver, observers_,
991                       OnDirectoryChanged(*directory_path));
992   }
993 }
994
995 }  // namespace internal
996 }  // namespace drive