- add sources.
[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 "chrome/browser/chromeos/drive/change_list_loader_observer.h"
14 #include "chrome/browser/chromeos/drive/change_list_processor.h"
15 #include "chrome/browser/chromeos/drive/file_system_util.h"
16 #include "chrome/browser/chromeos/drive/job_scheduler.h"
17 #include "chrome/browser/chromeos/drive/logging.h"
18 #include "chrome/browser/chromeos/drive/resource_metadata.h"
19 #include "chrome/browser/drive/drive_api_util.h"
20 #include "chrome/browser/drive/drive_service_interface.h"
21 #include "chrome/browser/google_apis/drive_api_parser.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "url/gurl.h"
24
25 using content::BrowserThread;
26
27 namespace drive {
28 namespace internal {
29
30 typedef base::Callback<void(FileError, ScopedVector<ChangeList>)>
31     FeedFetcherCallback;
32
33 class ChangeListLoader::FeedFetcher {
34  public:
35   virtual ~FeedFetcher() {}
36   virtual void Run(const FeedFetcherCallback& callback) = 0;
37 };
38
39 namespace {
40
41 // Fetches all the (currently available) resource entries from the server.
42 class FullFeedFetcher : public ChangeListLoader::FeedFetcher {
43  public:
44   explicit FullFeedFetcher(JobScheduler* scheduler)
45       : scheduler_(scheduler),
46         weak_ptr_factory_(this) {
47   }
48
49   virtual ~FullFeedFetcher() {
50   }
51
52   virtual void Run(const FeedFetcherCallback& callback) OVERRIDE {
53     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
54     DCHECK(!callback.is_null());
55
56     // Rememeber the time stamp for usage stats.
57     start_time_ = base::TimeTicks::Now();
58
59     // This is full resource list fetch.
60     scheduler_->GetAllResourceList(
61         base::Bind(&FullFeedFetcher::OnFileListFetched,
62                    weak_ptr_factory_.GetWeakPtr(), callback));
63   }
64
65  private:
66   void OnFileListFetched(
67       const FeedFetcherCallback& callback,
68       google_apis::GDataErrorCode status,
69       scoped_ptr<google_apis::ResourceList> resource_list) {
70     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
71     DCHECK(!callback.is_null());
72
73     // Looks the UMA stats we take here is useless as many methods use this
74     // callback. crbug.com/229407
75     if (change_lists_.empty()) {
76       UMA_HISTOGRAM_TIMES("Drive.InitialFeedLoadTime",
77                           base::TimeTicks::Now() - start_time_);
78     }
79
80     FileError error = GDataToFileError(status);
81     if (error != FILE_ERROR_OK) {
82       callback.Run(error, ScopedVector<ChangeList>());
83       return;
84     }
85
86     DCHECK(resource_list);
87     change_lists_.push_back(new ChangeList(*resource_list));
88
89     GURL next_url;
90     if (resource_list->GetNextFeedURL(&next_url) && !next_url.is_empty()) {
91       // There is the remaining result so fetch it.
92       scheduler_->GetRemainingFileList(
93           next_url,
94           base::Bind(&FullFeedFetcher::OnFileListFetched,
95                      weak_ptr_factory_.GetWeakPtr(), callback));
96       return;
97     }
98
99     // This UMA stats looks also different from what we want. crbug.com/229407
100     UMA_HISTOGRAM_TIMES("Drive.EntireFeedLoadTime",
101                         base::TimeTicks::Now() - start_time_);
102
103     // Note: The fetcher is managed by ChangeListLoader, and the instance
104     // will be deleted in the callback. Do not touch the fields after this
105     // invocation.
106     callback.Run(FILE_ERROR_OK, change_lists_.Pass());
107   }
108
109   JobScheduler* scheduler_;
110   ScopedVector<ChangeList> change_lists_;
111   base::TimeTicks start_time_;
112   base::WeakPtrFactory<FullFeedFetcher> weak_ptr_factory_;
113   DISALLOW_COPY_AND_ASSIGN(FullFeedFetcher);
114 };
115
116 // Fetches the delta changes since |start_change_id|.
117 class DeltaFeedFetcher : public ChangeListLoader::FeedFetcher {
118  public:
119   DeltaFeedFetcher(JobScheduler* scheduler, int64 start_change_id)
120       : scheduler_(scheduler),
121         start_change_id_(start_change_id),
122         weak_ptr_factory_(this) {
123   }
124
125   virtual ~DeltaFeedFetcher() {
126   }
127
128   virtual void Run(const FeedFetcherCallback& callback) OVERRIDE {
129     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
130     DCHECK(!callback.is_null());
131
132     scheduler_->GetChangeList(
133         start_change_id_,
134         base::Bind(&DeltaFeedFetcher::OnChangeListFetched,
135                    weak_ptr_factory_.GetWeakPtr(), callback));
136   }
137
138  private:
139   void OnChangeListFetched(
140       const FeedFetcherCallback& callback,
141       google_apis::GDataErrorCode status,
142       scoped_ptr<google_apis::ResourceList> resource_list) {
143     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
144     DCHECK(!callback.is_null());
145
146     FileError error = GDataToFileError(status);
147     if (error != FILE_ERROR_OK) {
148       callback.Run(error, ScopedVector<ChangeList>());
149       return;
150     }
151
152     DCHECK(resource_list);
153     change_lists_.push_back(new ChangeList(*resource_list));
154
155     GURL next_url;
156     if (resource_list->GetNextFeedURL(&next_url) && !next_url.is_empty()) {
157       // There is the remaining result so fetch it.
158       scheduler_->GetRemainingChangeList(
159           next_url,
160           base::Bind(&DeltaFeedFetcher::OnChangeListFetched,
161                      weak_ptr_factory_.GetWeakPtr(), callback));
162       return;
163     }
164
165     // Note: The fetcher is managed by ChangeListLoader, and the instance
166     // will be deleted in the callback. Do not touch the fields after this
167     // invocation.
168     callback.Run(FILE_ERROR_OK, change_lists_.Pass());
169   }
170
171   JobScheduler* scheduler_;
172   int64 start_change_id_;
173   ScopedVector<ChangeList> change_lists_;
174   base::WeakPtrFactory<DeltaFeedFetcher> weak_ptr_factory_;
175   DISALLOW_COPY_AND_ASSIGN(DeltaFeedFetcher);
176 };
177
178 // Fetches the resource entries in the directory with |directory_resource_id|.
179 class FastFetchFeedFetcher : public ChangeListLoader::FeedFetcher {
180  public:
181   FastFetchFeedFetcher(JobScheduler* scheduler,
182                        DriveServiceInterface* drive_service,
183                        const std::string& directory_resource_id,
184                        const std::string& root_folder_id)
185       : scheduler_(scheduler),
186         drive_service_(drive_service),
187         directory_resource_id_(directory_resource_id),
188         root_folder_id_(root_folder_id),
189         weak_ptr_factory_(this) {
190   }
191
192   virtual ~FastFetchFeedFetcher() {
193   }
194
195   virtual void Run(const FeedFetcherCallback& callback) OVERRIDE {
196     if (util::IsDriveV2ApiEnabled() && root_folder_id_.empty()) {
197       // The root folder id is not available yet. Fetch from the server.
198       scheduler_->GetAboutResource(
199           base::Bind(&FastFetchFeedFetcher::RunAfterGetAboutResource,
200                      weak_ptr_factory_.GetWeakPtr(), callback));
201       return;
202     }
203
204     StartGetResourceListInDirectory(callback);
205   }
206
207  private:
208   void RunAfterGetAboutResource(
209       const FeedFetcherCallback& callback,
210       google_apis::GDataErrorCode status,
211       scoped_ptr<google_apis::AboutResource> about_resource) {
212     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
213     DCHECK(!callback.is_null());
214
215     FileError error = GDataToFileError(status);
216     if (error != FILE_ERROR_OK) {
217       callback.Run(error, ScopedVector<ChangeList>());
218       return;
219     }
220
221     root_folder_id_ = about_resource->root_folder_id();
222     StartGetResourceListInDirectory(callback);
223   }
224
225   void StartGetResourceListInDirectory(const FeedFetcherCallback& callback) {
226     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
227     DCHECK(!callback.is_null());
228     DCHECK(!directory_resource_id_.empty());
229     DCHECK(!util::IsDriveV2ApiEnabled() || !root_folder_id_.empty());
230
231     // We use WAPI's GetResourceListInDirectory even if Drive API v2 is
232     // enabled. This is the short term work around of the performance
233     // regression.
234
235     std::string resource_id = directory_resource_id_;
236     if (util::IsDriveV2ApiEnabled() &&
237         directory_resource_id_ == root_folder_id_) {
238       // GData WAPI doesn't accept the root directory id which is used in Drive
239       // API v2. So it is necessary to translate it here.
240       resource_id = util::kWapiRootDirectoryResourceId;
241     }
242
243     scheduler_->GetResourceListInDirectoryByWapi(
244         resource_id,
245         base::Bind(&FastFetchFeedFetcher::OnResourceListFetched,
246                    weak_ptr_factory_.GetWeakPtr(), callback));
247   }
248
249   void OnResourceListFetched(
250       const FeedFetcherCallback& callback,
251       google_apis::GDataErrorCode status,
252       scoped_ptr<google_apis::ResourceList> resource_list) {
253     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
254     DCHECK(!callback.is_null());
255
256     FileError error = GDataToFileError(status);
257     if (error != FILE_ERROR_OK) {
258       callback.Run(error, ScopedVector<ChangeList>());
259       return;
260     }
261
262     // Add the current change list to the list of collected lists.
263     DCHECK(resource_list);
264     ChangeList* change_list = new ChangeList(*resource_list);
265     if (util::IsDriveV2ApiEnabled())
266       FixResourceIdInChangeList(change_list);
267     change_lists_.push_back(change_list);
268
269     GURL next_url;
270     if (resource_list->GetNextFeedURL(&next_url) && !next_url.is_empty()) {
271       // There is the remaining result so fetch it.
272       scheduler_->GetRemainingResourceList(
273           next_url,
274           base::Bind(&FastFetchFeedFetcher::OnResourceListFetched,
275                      weak_ptr_factory_.GetWeakPtr(), callback));
276       return;
277     }
278
279     // Note: The fetcher is managed by ChangeListLoader, and the instance
280     // will be deleted in the callback. Do not touch the fields after this
281     // invocation.
282     callback.Run(FILE_ERROR_OK, change_lists_.Pass());
283   }
284
285   // Fixes resource IDs in |change_list| into the format that |drive_service_|
286   // can understand. Note that |change_list| contains IDs in GData WAPI format
287   // since currently we always use WAPI for fast fetch, regardless of the flag.
288   void FixResourceIdInChangeList(ChangeList* change_list) {
289     std::vector<ResourceEntry>* entries = change_list->mutable_entries();
290     std::vector<std::string>* parent_resource_ids =
291         change_list->mutable_parent_resource_ids();
292     for (size_t i = 0; i < entries->size(); ++i) {
293       ResourceEntry* entry = &(*entries)[i];
294       if (entry->has_resource_id())
295         entry->set_resource_id(FixResourceId(entry->resource_id()));
296
297       (*parent_resource_ids)[i] = FixResourceId((*parent_resource_ids)[i]);
298     }
299   }
300
301   std::string FixResourceId(const std::string& resource_id) {
302     if (resource_id == util::kWapiRootDirectoryResourceId)
303       return root_folder_id_;
304     return drive_service_->GetResourceIdCanonicalizer().Run(resource_id);
305   }
306
307   JobScheduler* scheduler_;
308   DriveServiceInterface* drive_service_;
309   std::string directory_resource_id_;
310   std::string root_folder_id_;
311   ScopedVector<ChangeList> change_lists_;
312   base::WeakPtrFactory<FastFetchFeedFetcher> weak_ptr_factory_;
313   DISALLOW_COPY_AND_ASSIGN(FastFetchFeedFetcher);
314 };
315
316 }  // namespace
317
318 ChangeListLoader::ChangeListLoader(
319     base::SequencedTaskRunner* blocking_task_runner,
320     ResourceMetadata* resource_metadata,
321     JobScheduler* scheduler,
322     DriveServiceInterface* drive_service)
323     : blocking_task_runner_(blocking_task_runner),
324       resource_metadata_(resource_metadata),
325       scheduler_(scheduler),
326       drive_service_(drive_service),
327       last_known_remote_changestamp_(0),
328       loaded_(false),
329       weak_ptr_factory_(this) {
330 }
331
332 ChangeListLoader::~ChangeListLoader() {
333   STLDeleteElements(&fast_fetch_feed_fetcher_set_);
334 }
335
336 bool ChangeListLoader::IsRefreshing() const {
337   // Callback for change list loading is stored in pending_load_callback_[""].
338   // It is non-empty if and only if there is an in-flight loading operation.
339   return pending_load_callback_.find("") != pending_load_callback_.end();
340 }
341
342 void ChangeListLoader::AddObserver(ChangeListLoaderObserver* observer) {
343   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
344   observers_.AddObserver(observer);
345 }
346
347 void ChangeListLoader::RemoveObserver(ChangeListLoaderObserver* observer) {
348   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
349   observers_.RemoveObserver(observer);
350 }
351
352 void ChangeListLoader::CheckForUpdates(const FileOperationCallback& callback) {
353   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
354   DCHECK(!callback.is_null());
355
356   if (IsRefreshing()) {
357     // There is in-flight loading. So keep the callback here, and check for
358     // updates when the in-flight loading is completed.
359     pending_update_check_callback_ = callback;
360     return;
361   }
362
363   if (loaded_) {
364     // We only start to check for updates iff the load is done.
365     // I.e., we ignore checking updates if not loaded to avoid starting the
366     // load without user's explicit interaction (such as opening Drive).
367     util::Log(logging::LOG_INFO, "Checking for updates");
368     Load(DirectoryFetchInfo(), callback);
369   }
370 }
371
372 void ChangeListLoader::LoadIfNeeded(
373     const DirectoryFetchInfo& directory_fetch_info,
374     const FileOperationCallback& callback) {
375   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
376   DCHECK(!callback.is_null());
377
378   // If the resource metadata has been already loaded, for normal change list
379   // fetch (= empty directory_fetch_info), we have nothing to do. For "fast
380   // fetch", we need to schedule a fetching if a refresh is currently
381   // running, because we don't want to wait a possibly large delta change
382   // list to arrive.
383   if (loaded_ && (directory_fetch_info.empty() || !IsRefreshing())) {
384     base::MessageLoopProxy::current()->PostTask(
385         FROM_HERE,
386         base::Bind(callback, FILE_ERROR_OK));
387     return;
388   }
389   Load(directory_fetch_info, callback);
390 }
391
392 void ChangeListLoader::Load(const DirectoryFetchInfo& directory_fetch_info,
393                             const FileOperationCallback& callback) {
394   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
395   DCHECK(!callback.is_null());
396
397   // Check if this is the first time this ChangeListLoader do loading.
398   // Note: IsRefreshing() depends on pending_load_callback_ so check in advance.
399   const bool is_initial_load = (!loaded_ && !IsRefreshing());
400
401   // Register the callback function to be called when it is loaded.
402   const std::string& resource_id = directory_fetch_info.resource_id();
403   pending_load_callback_[resource_id].push_back(callback);
404
405   // If loading task for |resource_id| is already running, do nothing.
406   if (pending_load_callback_[resource_id].size() > 1)
407     return;
408
409   // For initial loading, even for directory fetching, we do load the full
410   // resource list from the server to sync up. So we register a dummy
411   // callback to indicate that update for full hierarchy is running.
412   if (is_initial_load && !resource_id.empty()) {
413     pending_load_callback_[""].push_back(
414         base::Bind(&util::EmptyFileOperationCallback));
415   }
416
417   // Check the current status of local metadata, and start loading if needed.
418   base::PostTaskAndReplyWithResult(
419       blocking_task_runner_,
420       FROM_HERE,
421       base::Bind(&ResourceMetadata::GetLargestChangestamp,
422                  base::Unretained(resource_metadata_)),
423       base::Bind(is_initial_load ? &ChangeListLoader::DoInitialLoad
424                                  : &ChangeListLoader::DoUpdateLoad,
425                  weak_ptr_factory_.GetWeakPtr(),
426                  directory_fetch_info));
427 }
428
429 void ChangeListLoader::DoInitialLoad(
430     const DirectoryFetchInfo& directory_fetch_info,
431     int64 local_changestamp) {
432   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
433
434   if (local_changestamp > 0) {
435     // The local data is usable. Flush callbacks to tell loading was successful.
436     OnChangeListLoadComplete(FILE_ERROR_OK);
437
438     // Continues to load from server in background.
439     // Put dummy callbacks to indicate that fetching is still continuing.
440     pending_load_callback_[directory_fetch_info.resource_id()].push_back(
441         base::Bind(&util::EmptyFileOperationCallback));
442     if (!directory_fetch_info.empty()) {
443       pending_load_callback_[""].push_back(
444           base::Bind(&util::EmptyFileOperationCallback));
445     }
446   }
447   LoadFromServerIfNeeded(directory_fetch_info, local_changestamp);
448 }
449
450 void ChangeListLoader::DoUpdateLoad(
451     const DirectoryFetchInfo& directory_fetch_info,
452     int64 local_changestamp) {
453   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
454
455   if (directory_fetch_info.empty()) {
456     LoadFromServerIfNeeded(directory_fetch_info, local_changestamp);
457   } else {
458     // Note: CheckChangestampAndLoadDirectoryIfNeeded regards
459     // last_know_remote_changestamp_ as the remote changestamp. To be precise,
460     // we need to call GetAboutResource() here, as we do in other places like
461     // LoadFromServerIfNeeded or LoadFromDirectory. However,
462     // - It is costly to do GetAboutResource HTTP request every time.
463     // - The chance using an old value is small; it only happens when
464     //   LoadIfNeeded is called during one GetAboutResource roundtrip time
465     //   of a change list fetching.
466     // - Even if the value is old, it just marks the directory as older. It may
467     //   trigger one future unnecessary re-fetch, but it'll never lose data.
468     CheckChangestampAndLoadDirectoryIfNeeded(
469         directory_fetch_info,
470         local_changestamp,
471         base::Bind(&ChangeListLoader::OnDirectoryLoadComplete,
472                    weak_ptr_factory_.GetWeakPtr(),
473                    directory_fetch_info));
474   }
475 }
476
477 void ChangeListLoader::OnChangeListLoadComplete(FileError error) {
478   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
479
480   if (!loaded_ && error == FILE_ERROR_OK) {
481     loaded_ = true;
482     FOR_EACH_OBSERVER(ChangeListLoaderObserver,
483                       observers_,
484                       OnInitialLoadComplete());
485   }
486
487   for (LoadCallbackMap::iterator it = pending_load_callback_.begin();
488        it != pending_load_callback_.end();  ++it) {
489     const std::vector<FileOperationCallback>& callbacks = it->second;
490     for (size_t i = 0; i < callbacks.size(); ++i) {
491       base::MessageLoopProxy::current()->PostTask(
492           FROM_HERE,
493           base::Bind(callbacks[i], error));
494     }
495   }
496   pending_load_callback_.clear();
497
498   // If there is pending update check, try to load the change from the server
499   // again, because there may exist an update during the completed loading.
500   if (!pending_update_check_callback_.is_null()) {
501     Load(DirectoryFetchInfo(),
502          base::ResetAndReturn(&pending_update_check_callback_));
503   }
504 }
505
506 void ChangeListLoader::OnDirectoryLoadComplete(
507     const DirectoryFetchInfo& directory_fetch_info,
508     FileError error) {
509   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
510
511   util::Log(logging::LOG_INFO,
512             "Fast-fetch complete: %s => %s",
513             directory_fetch_info.ToString().c_str(),
514             FileErrorToString(error).c_str());
515   const std::string& resource_id = directory_fetch_info.resource_id();
516   LoadCallbackMap::iterator it = pending_load_callback_.find(resource_id);
517   if (it != pending_load_callback_.end()) {
518     DVLOG(1) << "Running callback for " << resource_id;
519     const std::vector<FileOperationCallback>& callbacks = it->second;
520     for (size_t i = 0; i < callbacks.size(); ++i) {
521       base::MessageLoopProxy::current()->PostTask(
522           FROM_HERE,
523           base::Bind(callbacks[i], error));
524     }
525     pending_load_callback_.erase(it);
526   }
527 }
528
529 void ChangeListLoader::LoadFromServerIfNeeded(
530     const DirectoryFetchInfo& directory_fetch_info,
531     int64 local_changestamp) {
532   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
533
534   // First fetch the latest changestamp to see if there were any new changes
535   // there at all.
536   scheduler_->GetAboutResource(
537       base::Bind(&ChangeListLoader::LoadFromServerIfNeededAfterGetAbout,
538                  weak_ptr_factory_.GetWeakPtr(),
539                  directory_fetch_info,
540                  local_changestamp));
541 }
542
543 void ChangeListLoader::LoadFromServerIfNeededAfterGetAbout(
544     const DirectoryFetchInfo& directory_fetch_info,
545     int64 local_changestamp,
546     google_apis::GDataErrorCode status,
547     scoped_ptr<google_apis::AboutResource> about_resource) {
548   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
549
550   FileError error = GDataToFileError(status);
551   if (error != FILE_ERROR_OK) {
552     OnChangeListLoadComplete(error);
553     return;
554   }
555
556   DCHECK(about_resource);
557   last_known_remote_changestamp_ = about_resource->largest_change_id();
558   root_folder_id_ = about_resource->root_folder_id();
559
560   int64 remote_changestamp = about_resource->largest_change_id();
561   if (remote_changestamp > 0 && local_changestamp >= remote_changestamp) {
562     if (local_changestamp > remote_changestamp) {
563       LOG(WARNING) << "Local resource metadata is fresher than server, local = "
564                    << local_changestamp << ", server = " << remote_changestamp;
565     }
566
567     // No changes detected, tell the client that the loading was successful.
568     OnChangeListLoadComplete(FILE_ERROR_OK);
569     return;
570   }
571
572   int64 start_changestamp = local_changestamp > 0 ? local_changestamp + 1 : 0;
573   if (directory_fetch_info.empty()) {
574     // If the caller is not interested in a particular directory, just start
575     // loading the change list.
576     LoadChangeListFromServer(about_resource.Pass(), start_changestamp);
577   } else {
578     // If the caller is interested in a particular directory, start loading the
579     // directory first.
580     CheckChangestampAndLoadDirectoryIfNeeded(
581         directory_fetch_info,
582         local_changestamp,
583         base::Bind(
584             &ChangeListLoader::LoadFromServerIfNeededAfterLoadDirectory,
585             weak_ptr_factory_.GetWeakPtr(),
586             directory_fetch_info,
587             base::Passed(&about_resource),
588             start_changestamp));
589   }
590 }
591
592 void ChangeListLoader::LoadFromServerIfNeededAfterLoadDirectory(
593     const DirectoryFetchInfo& directory_fetch_info,
594     scoped_ptr<google_apis::AboutResource> about_resource,
595     int64 start_changestamp,
596     FileError error) {
597   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
598   DCHECK(about_resource);
599
600   if (error == FILE_ERROR_OK) {
601     // The directory fast-fetch succeeded. Runs the callbacks waiting for the
602     // directory loading. If failed, do not flush so they're run after the
603     // change list loading is complete.
604     OnDirectoryLoadComplete(directory_fetch_info, FILE_ERROR_OK);
605   }
606   LoadChangeListFromServer(about_resource.Pass(), start_changestamp);
607 }
608
609 void ChangeListLoader::LoadChangeListFromServer(
610     scoped_ptr<google_apis::AboutResource> about_resource,
611     int64 start_changestamp) {
612   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
613   DCHECK(!change_feed_fetcher_);
614   DCHECK(about_resource);
615
616   bool is_delta_update = start_changestamp != 0;
617
618   // Set up feed fetcher.
619   if (is_delta_update) {
620     change_feed_fetcher_.reset(
621         new DeltaFeedFetcher(scheduler_, start_changestamp));
622   } else {
623     change_feed_fetcher_.reset(new FullFeedFetcher(scheduler_));
624   }
625
626   change_feed_fetcher_->Run(
627       base::Bind(&ChangeListLoader::LoadChangeListFromServerAfterLoadChangeList,
628                  weak_ptr_factory_.GetWeakPtr(),
629                  base::Passed(&about_resource),
630                  is_delta_update));
631 }
632
633 void ChangeListLoader::LoadChangeListFromServerAfterLoadChangeList(
634     scoped_ptr<google_apis::AboutResource> about_resource,
635     bool is_delta_update,
636     FileError error,
637     ScopedVector<ChangeList> change_lists) {
638   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
639   DCHECK(about_resource);
640
641   // Delete the fetcher first.
642   change_feed_fetcher_.reset();
643
644   if (error != FILE_ERROR_OK) {
645     OnChangeListLoadComplete(error);
646     return;
647   }
648
649   UpdateFromChangeList(
650       about_resource.Pass(),
651       change_lists.Pass(),
652       is_delta_update,
653       base::Bind(&ChangeListLoader::LoadChangeListFromServerAfterUpdate,
654                  weak_ptr_factory_.GetWeakPtr()));
655 }
656
657 void ChangeListLoader::LoadChangeListFromServerAfterUpdate() {
658   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
659
660   OnChangeListLoadComplete(FILE_ERROR_OK);
661
662   FOR_EACH_OBSERVER(ChangeListLoaderObserver,
663                     observers_,
664                     OnLoadFromServerComplete());
665 }
666
667 void ChangeListLoader::CheckChangestampAndLoadDirectoryIfNeeded(
668     const DirectoryFetchInfo& directory_fetch_info,
669     int64 local_changestamp,
670     const FileOperationCallback& callback) {
671   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
672   DCHECK(!directory_fetch_info.empty());
673
674   int64 directory_changestamp = std::max(directory_fetch_info.changestamp(),
675                                          local_changestamp);
676
677   // We may not fetch from the server at all if the local metadata is new
678   // enough, but we log this message here, so "Fast-fetch start" and
679   // "Fast-fetch complete" always match.
680   // TODO(satorux): Distinguish the "not fetching at all" case.
681   util::Log(logging::LOG_INFO,
682             "Fast-fetch start: %s; Server changestamp: %s",
683             directory_fetch_info.ToString().c_str(),
684             base::Int64ToString(last_known_remote_changestamp_).c_str());
685
686   // If the directory's changestamp is up-to-date, just schedule to run the
687   // callback, as there is no need to fetch the directory.
688   // Note that |last_known_remote_changestamp_| is 0 when it is not received
689   // yet. In that case we conservatively assume that we need to fetch.
690   if (last_known_remote_changestamp_ > 0 &&
691       directory_changestamp >= last_known_remote_changestamp_) {
692     callback.Run(FILE_ERROR_OK);
693     return;
694   }
695
696   // Start fetching the directory content, and mark it with the changestamp
697   // |last_known_remote_changestamp_|.
698   DirectoryFetchInfo new_directory_fetch_info(
699       directory_fetch_info.resource_id(),
700       std::max(directory_changestamp, last_known_remote_changestamp_));
701   DoLoadDirectoryFromServer(new_directory_fetch_info, callback);
702 }
703
704 void ChangeListLoader::DoLoadDirectoryFromServer(
705     const DirectoryFetchInfo& directory_fetch_info,
706     const FileOperationCallback& callback) {
707   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
708   DCHECK(!callback.is_null());
709   DCHECK(!directory_fetch_info.empty());
710   DVLOG(1) << "Start loading directory: " << directory_fetch_info.ToString();
711
712   if (directory_fetch_info.resource_id() ==
713           util::kDriveOtherDirSpecialResourceId) {
714     // Load for a <other> directory is meaningless in the server.
715     // Let it succeed and use what we have locally.
716     callback.Run(FILE_ERROR_OK);
717     return;
718   }
719
720   if (directory_fetch_info.resource_id() ==
721           util::kDriveGrandRootSpecialResourceId) {
722     // Load for a grand root directory means slightly different from other
723     // directories. It should have two directories; <other> and mydrive root.
724     // <other> directory should always exist, but mydrive root should be
725     // created by root resource id retrieved from the server.
726     // Here, we check if mydrive root exists, and if not, create it.
727     resource_metadata_->GetResourceEntryByPathOnUIThread(
728         base::FilePath(util::GetDriveMyDriveRootPath()),
729         base::Bind(
730             &ChangeListLoader
731                 ::DoLoadGrandRootDirectoryFromServerAfterGetResourceEntryByPath,
732             weak_ptr_factory_.GetWeakPtr(),
733             directory_fetch_info,
734             callback));
735     return;
736   }
737
738   FastFetchFeedFetcher* fetcher = new FastFetchFeedFetcher(
739       scheduler_,
740       drive_service_,
741       directory_fetch_info.resource_id(),
742       root_folder_id_);
743   fast_fetch_feed_fetcher_set_.insert(fetcher);
744   fetcher->Run(
745       base::Bind(&ChangeListLoader::DoLoadDirectoryFromServerAfterLoad,
746                  weak_ptr_factory_.GetWeakPtr(),
747                  directory_fetch_info,
748                  callback,
749                  fetcher));
750 }
751
752 void
753 ChangeListLoader::DoLoadGrandRootDirectoryFromServerAfterGetResourceEntryByPath(
754     const DirectoryFetchInfo& directory_fetch_info,
755     const FileOperationCallback& callback,
756     FileError error,
757     scoped_ptr<ResourceEntry> entry) {
758   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
759   DCHECK(!callback.is_null());
760   DCHECK_EQ(directory_fetch_info.resource_id(),
761             util::kDriveGrandRootSpecialResourceId);
762
763   if (error == FILE_ERROR_OK) {
764     // MyDrive root already exists. Just return success.
765     callback.Run(FILE_ERROR_OK);
766     return;
767   }
768
769   // Fetch root resource id from the server.
770   scheduler_->GetAboutResource(
771       base::Bind(
772           &ChangeListLoader
773               ::DoLoadGrandRootDirectoryFromServerAfterGetAboutResource,
774           weak_ptr_factory_.GetWeakPtr(),
775           directory_fetch_info,
776           callback));
777 }
778
779 void ChangeListLoader::DoLoadGrandRootDirectoryFromServerAfterGetAboutResource(
780     const DirectoryFetchInfo& directory_fetch_info,
781     const FileOperationCallback& callback,
782     google_apis::GDataErrorCode status,
783     scoped_ptr<google_apis::AboutResource> about_resource) {
784   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
785   DCHECK(!callback.is_null());
786   DCHECK(about_resource);
787   DCHECK_EQ(directory_fetch_info.resource_id(),
788             util::kDriveGrandRootSpecialResourceId);
789
790   FileError error = GDataToFileError(status);
791   if (error != FILE_ERROR_OK) {
792     callback.Run(error);
793     return;
794   }
795
796   // Add "My Drive".
797   const std::string& root_resource_id = about_resource->root_folder_id();
798   std::string* local_id = new std::string;
799   base::PostTaskAndReplyWithResult(
800       blocking_task_runner_,
801       FROM_HERE,
802       base::Bind(&ResourceMetadata::AddEntry,
803                  base::Unretained(resource_metadata_),
804                  util::CreateMyDriveRootEntry(root_resource_id),
805                  local_id),
806       base::Bind(&ChangeListLoader::DoLoadDirectoryFromServerAfterAddMyDrive,
807                  weak_ptr_factory_.GetWeakPtr(),
808                  directory_fetch_info,
809                  callback,
810                  base::Owned(local_id)));
811 }
812
813 void ChangeListLoader::DoLoadDirectoryFromServerAfterAddMyDrive(
814     const DirectoryFetchInfo& directory_fetch_info,
815     const FileOperationCallback& callback,
816     std::string* local_id,
817     FileError error) {
818   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
819   DCHECK(!callback.is_null());
820   DCHECK_EQ(directory_fetch_info.resource_id(),
821             util::kDriveGrandRootSpecialResourceId);
822
823   const base::FilePath changed_directory_path(util::GetDriveGrandRootPath());
824   DoLoadDirectoryFromServerAfterRefresh(directory_fetch_info,
825                                         callback,
826                                         &changed_directory_path,
827                                         error);
828 }
829
830 void ChangeListLoader::DoLoadDirectoryFromServerAfterLoad(
831     const DirectoryFetchInfo& directory_fetch_info,
832     const FileOperationCallback& callback,
833     FeedFetcher* fetcher,
834     FileError error,
835     ScopedVector<ChangeList> change_lists) {
836   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
837   DCHECK(!callback.is_null());
838   DCHECK(!directory_fetch_info.empty());
839
840   // Delete the fetcher.
841   fast_fetch_feed_fetcher_set_.erase(fetcher);
842   delete fetcher;
843
844   if (error != FILE_ERROR_OK) {
845     LOG(ERROR) << "Failed to load directory: "
846                << directory_fetch_info.resource_id()
847                << ": " << FileErrorToString(error);
848     callback.Run(error);
849     return;
850   }
851
852   base::FilePath* directory_path = new base::FilePath;
853   base::PostTaskAndReplyWithResult(
854       blocking_task_runner_,
855       FROM_HERE,
856       base::Bind(&ChangeListProcessor::RefreshDirectory,
857                  resource_metadata_,
858                  directory_fetch_info,
859                  base::Passed(&change_lists),
860                  directory_path),
861       base::Bind(&ChangeListLoader::DoLoadDirectoryFromServerAfterRefresh,
862                  weak_ptr_factory_.GetWeakPtr(),
863                  directory_fetch_info,
864                  callback,
865                  base::Owned(directory_path)));
866 }
867
868 void ChangeListLoader::DoLoadDirectoryFromServerAfterRefresh(
869     const DirectoryFetchInfo& directory_fetch_info,
870     const FileOperationCallback& callback,
871     const base::FilePath* directory_path,
872     FileError error) {
873   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
874   DCHECK(!callback.is_null());
875
876   DVLOG(1) << "Directory loaded: " << directory_fetch_info.ToString();
877   callback.Run(error);
878   // Also notify the observers.
879   if (error == FILE_ERROR_OK) {
880     FOR_EACH_OBSERVER(ChangeListLoaderObserver, observers_,
881                       OnDirectoryChanged(*directory_path));
882   }
883 }
884
885 void ChangeListLoader::UpdateFromChangeList(
886     scoped_ptr<google_apis::AboutResource> about_resource,
887     ScopedVector<ChangeList> change_lists,
888     bool is_delta_update,
889     const base::Closure& callback) {
890   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
891   DCHECK(!callback.is_null());
892   DCHECK(about_resource);
893
894   ChangeListProcessor* change_list_processor =
895       new ChangeListProcessor(resource_metadata_);
896   // Don't send directory content change notification while performing
897   // the initial content retrieval.
898   const bool should_notify_changed_directories = is_delta_update;
899
900   util::Log(logging::LOG_INFO,
901             "Apply change lists (is delta: %d)",
902             is_delta_update);
903   base::PostTaskAndReplyWithResult(
904       blocking_task_runner_,
905       FROM_HERE,
906       base::Bind(&ChangeListProcessor::Apply,
907                  base::Unretained(change_list_processor),
908                  base::Passed(&about_resource),
909                  base::Passed(&change_lists),
910                  is_delta_update),
911       base::Bind(&ChangeListLoader::UpdateFromChangeListAfterApply,
912                  weak_ptr_factory_.GetWeakPtr(),
913                  base::Owned(change_list_processor),
914                  should_notify_changed_directories,
915                  base::Time::Now(),
916                  callback));
917 }
918
919 void ChangeListLoader::UpdateFromChangeListAfterApply(
920     ChangeListProcessor* change_list_processor,
921     bool should_notify_changed_directories,
922     base::Time start_time,
923     const base::Closure& callback,
924     FileError error) {
925   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
926   DCHECK(change_list_processor);
927   DCHECK(!callback.is_null());
928
929   const base::TimeDelta elapsed = base::Time::Now() - start_time;
930   util::Log(logging::LOG_INFO,
931             "Change lists applied (elapsed time: %sms)",
932             base::Int64ToString(elapsed.InMilliseconds()).c_str());
933
934   if (should_notify_changed_directories) {
935     for (std::set<base::FilePath>::iterator dir_iter =
936             change_list_processor->changed_dirs().begin();
937         dir_iter != change_list_processor->changed_dirs().end();
938         ++dir_iter) {
939       FOR_EACH_OBSERVER(ChangeListLoaderObserver, observers_,
940                         OnDirectoryChanged(*dir_iter));
941     }
942   }
943
944   callback.Run();
945 }
946
947 }  // namespace internal
948 }  // namespace drive