6844368e7da3eb08ded96171797e7be72a9234d0
[platform/framework/web/crosswalk.git] / src / chrome / browser / chromeos / drive / directory_loader.cc
1 // Copyright 2014 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/directory_loader.h"
6
7 #include "base/callback.h"
8 #include "base/callback_helpers.h"
9 #include "base/metrics/histogram.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/time/time.h"
12 #include "chrome/browser/chromeos/drive/change_list_loader.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/resource_metadata.h"
18 #include "chrome/browser/drive/event_logger.h"
19 #include "content/public/browser/browser_thread.h"
20 #include "google_apis/drive/drive_api_parser.h"
21 #include "url/gurl.h"
22
23 using content::BrowserThread;
24
25 namespace drive {
26 namespace internal {
27
28 namespace {
29
30 // Minimum changestamp gap required to start loading directory.
31 const int kMinimumChangestampGap = 50;
32
33 FileError CheckLocalState(ResourceMetadata* resource_metadata,
34                           const google_apis::AboutResource& about_resource,
35                           const std::string& local_id,
36                           ResourceEntry* entry,
37                           int64* local_changestamp) {
38   // Fill My Drive resource ID.
39   ResourceEntry mydrive;
40   FileError error = resource_metadata->GetResourceEntryByPath(
41       util::GetDriveMyDriveRootPath(), &mydrive);
42   if (error != FILE_ERROR_OK)
43     return error;
44
45   if (mydrive.resource_id().empty()) {
46     mydrive.set_resource_id(about_resource.root_folder_id());
47     error = resource_metadata->RefreshEntry(mydrive);
48     if (error != FILE_ERROR_OK)
49       return error;
50   }
51
52   // Get entry.
53   error = resource_metadata->GetResourceEntryById(local_id, entry);
54   if (error != FILE_ERROR_OK)
55     return error;
56
57   // Get the local changestamp.
58   return resource_metadata->GetLargestChangestamp(local_changestamp);
59 }
60
61 FileError UpdateChangestamp(ResourceMetadata* resource_metadata,
62                             const DirectoryFetchInfo& directory_fetch_info,
63                             base::FilePath* directory_path) {
64   // Update the directory changestamp.
65   ResourceEntry directory;
66   FileError error = resource_metadata->GetResourceEntryById(
67       directory_fetch_info.local_id(), &directory);
68   if (error != FILE_ERROR_OK)
69     return error;
70
71   if (!directory.file_info().is_directory())
72     return FILE_ERROR_NOT_A_DIRECTORY;
73
74   directory.mutable_directory_specific_info()->set_changestamp(
75       directory_fetch_info.changestamp());
76   error = resource_metadata->RefreshEntry(directory);
77   if (error != FILE_ERROR_OK)
78     return error;
79
80   // Get the directory path.
81   return resource_metadata->GetFilePath(directory_fetch_info.local_id(),
82                                         directory_path);
83 }
84
85 }  // namespace
86
87 struct DirectoryLoader::ReadDirectoryCallbackState {
88   ReadDirectoryEntriesCallback entries_callback;
89   FileOperationCallback completion_callback;
90   std::set<std::string> sent_entry_names;
91 };
92
93 // Fetches the resource entries in the directory with |directory_resource_id|.
94 class DirectoryLoader::FeedFetcher {
95  public:
96   FeedFetcher(DirectoryLoader* loader,
97               const DirectoryFetchInfo& directory_fetch_info,
98               const std::string& root_folder_id)
99       : loader_(loader),
100         directory_fetch_info_(directory_fetch_info),
101         root_folder_id_(root_folder_id),
102         weak_ptr_factory_(this) {
103   }
104
105   ~FeedFetcher() {
106   }
107
108   void Run(const FileOperationCallback& callback) {
109     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
110     DCHECK(!callback.is_null());
111     DCHECK(!directory_fetch_info_.resource_id().empty());
112
113     // Remember the time stamp for usage stats.
114     start_time_ = base::TimeTicks::Now();
115
116     loader_->scheduler_->GetFileListInDirectory(
117         directory_fetch_info_.resource_id(),
118         base::Bind(&FeedFetcher::OnFileListFetched,
119                    weak_ptr_factory_.GetWeakPtr(), callback));
120   }
121
122  private:
123   void OnFileListFetched(const FileOperationCallback& callback,
124                          google_apis::GDataErrorCode status,
125                          scoped_ptr<google_apis::FileList> file_list) {
126     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
127     DCHECK(!callback.is_null());
128
129     FileError error = GDataToFileError(status);
130     if (error != FILE_ERROR_OK) {
131       callback.Run(error);
132       return;
133     }
134
135     DCHECK(file_list);
136     scoped_ptr<ChangeList> change_list(new ChangeList(*file_list));
137     GURL next_url = file_list->next_link();
138
139     ResourceEntryVector* entries = new ResourceEntryVector;
140     loader_->loader_controller_->ScheduleRun(base::Bind(
141         base::IgnoreResult(
142             &base::PostTaskAndReplyWithResult<FileError, FileError>),
143         loader_->blocking_task_runner_,
144         FROM_HERE,
145         base::Bind(&ChangeListProcessor::RefreshDirectory,
146                    loader_->resource_metadata_,
147                    directory_fetch_info_,
148                    base::Passed(&change_list),
149                    entries),
150         base::Bind(&FeedFetcher::OnDirectoryRefreshed,
151                    weak_ptr_factory_.GetWeakPtr(),
152                    callback,
153                    next_url,
154                    base::Owned(entries))));
155   }
156
157   void OnDirectoryRefreshed(
158       const FileOperationCallback& callback,
159       const GURL& next_url,
160       const std::vector<ResourceEntry>* refreshed_entries,
161       FileError error) {
162     DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
163     DCHECK(!callback.is_null());
164
165     if (error != FILE_ERROR_OK) {
166       callback.Run(error);
167       return;
168     }
169
170     loader_->SendEntries(directory_fetch_info_.local_id(), *refreshed_entries);
171
172     if (!next_url.is_empty()) {
173       // There is the remaining result so fetch it.
174       loader_->scheduler_->GetRemainingFileList(
175           next_url,
176           base::Bind(&FeedFetcher::OnFileListFetched,
177                      weak_ptr_factory_.GetWeakPtr(), callback));
178       return;
179     }
180
181     UMA_HISTOGRAM_TIMES("Drive.DirectoryFeedLoadTime",
182                         base::TimeTicks::Now() - start_time_);
183
184     // Note: The fetcher is managed by DirectoryLoader, and the instance
185     // will be deleted in the callback. Do not touch the fields after this
186     // invocation.
187     callback.Run(FILE_ERROR_OK);
188   }
189
190   DirectoryLoader* loader_;
191   DirectoryFetchInfo directory_fetch_info_;
192   std::string root_folder_id_;
193   base::TimeTicks start_time_;
194   base::WeakPtrFactory<FeedFetcher> weak_ptr_factory_;
195   DISALLOW_COPY_AND_ASSIGN(FeedFetcher);
196 };
197
198 DirectoryLoader::DirectoryLoader(
199     EventLogger* logger,
200     base::SequencedTaskRunner* blocking_task_runner,
201     ResourceMetadata* resource_metadata,
202     JobScheduler* scheduler,
203     AboutResourceLoader* about_resource_loader,
204     LoaderController* loader_controller)
205     : logger_(logger),
206       blocking_task_runner_(blocking_task_runner),
207       resource_metadata_(resource_metadata),
208       scheduler_(scheduler),
209       about_resource_loader_(about_resource_loader),
210       loader_controller_(loader_controller),
211       weak_ptr_factory_(this) {
212 }
213
214 DirectoryLoader::~DirectoryLoader() {
215   STLDeleteElements(&fast_fetch_feed_fetcher_set_);
216 }
217
218 void DirectoryLoader::AddObserver(ChangeListLoaderObserver* observer) {
219   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
220   observers_.AddObserver(observer);
221 }
222
223 void DirectoryLoader::RemoveObserver(ChangeListLoaderObserver* observer) {
224   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
225   observers_.RemoveObserver(observer);
226 }
227
228 void DirectoryLoader::ReadDirectory(
229     const base::FilePath& directory_path,
230     const ReadDirectoryEntriesCallback& entries_callback,
231     const FileOperationCallback& completion_callback) {
232   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
233   DCHECK(!completion_callback.is_null());
234
235   ResourceEntry* entry = new ResourceEntry;
236   base::PostTaskAndReplyWithResult(
237       blocking_task_runner_.get(),
238       FROM_HERE,
239       base::Bind(&ResourceMetadata::GetResourceEntryByPath,
240                  base::Unretained(resource_metadata_),
241                  directory_path,
242                  entry),
243       base::Bind(&DirectoryLoader::ReadDirectoryAfterGetEntry,
244                  weak_ptr_factory_.GetWeakPtr(),
245                  directory_path,
246                  entries_callback,
247                  completion_callback,
248                  true,  // should_try_loading_parent
249                  base::Owned(entry)));
250 }
251
252 void DirectoryLoader::ReadDirectoryAfterGetEntry(
253     const base::FilePath& directory_path,
254     const ReadDirectoryEntriesCallback& entries_callback,
255     const FileOperationCallback& completion_callback,
256     bool should_try_loading_parent,
257     const ResourceEntry* entry,
258     FileError error) {
259   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
260   DCHECK(!completion_callback.is_null());
261
262   if (error == FILE_ERROR_NOT_FOUND &&
263       should_try_loading_parent &&
264       util::GetDriveGrandRootPath().IsParent(directory_path)) {
265     // This entry may be found after loading the parent.
266     ReadDirectory(directory_path.DirName(),
267                   ReadDirectoryEntriesCallback(),
268                   base::Bind(&DirectoryLoader::ReadDirectoryAfterLoadParent,
269                              weak_ptr_factory_.GetWeakPtr(),
270                              directory_path,
271                              entries_callback,
272                              completion_callback));
273     return;
274   }
275   if (error != FILE_ERROR_OK) {
276     completion_callback.Run(error);
277     return;
278   }
279
280   if (!entry->file_info().is_directory()) {
281     completion_callback.Run(FILE_ERROR_NOT_A_DIRECTORY);
282     return;
283   }
284
285   DirectoryFetchInfo directory_fetch_info(
286       entry->local_id(),
287       entry->resource_id(),
288       entry->directory_specific_info().changestamp());
289
290   // Register the callback function to be called when it is loaded.
291   const std::string& local_id = directory_fetch_info.local_id();
292   ReadDirectoryCallbackState callback_state;
293   callback_state.entries_callback = entries_callback;
294   callback_state.completion_callback = completion_callback;
295   pending_load_callback_[local_id].push_back(callback_state);
296
297   // If loading task for |local_id| is already running, do nothing.
298   if (pending_load_callback_[local_id].size() > 1)
299     return;
300
301   about_resource_loader_->GetAboutResource(
302       base::Bind(&DirectoryLoader::ReadDirectoryAfterGetAboutResource,
303                  weak_ptr_factory_.GetWeakPtr(), local_id));
304 }
305
306 void DirectoryLoader::ReadDirectoryAfterLoadParent(
307     const base::FilePath& directory_path,
308     const ReadDirectoryEntriesCallback& entries_callback,
309     const FileOperationCallback& completion_callback,
310     FileError error) {
311   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
312   DCHECK(!completion_callback.is_null());
313
314   if (error != FILE_ERROR_OK) {
315     completion_callback.Run(error);
316     return;
317   }
318
319   ResourceEntry* entry = new ResourceEntry;
320   base::PostTaskAndReplyWithResult(
321       blocking_task_runner_.get(),
322       FROM_HERE,
323       base::Bind(&ResourceMetadata::GetResourceEntryByPath,
324                  base::Unretained(resource_metadata_),
325                  directory_path,
326                  entry),
327       base::Bind(&DirectoryLoader::ReadDirectoryAfterGetEntry,
328                  weak_ptr_factory_.GetWeakPtr(),
329                  directory_path,
330                  entries_callback,
331                  completion_callback,
332                  false,  // should_try_loading_parent
333                  base::Owned(entry)));
334 }
335
336 void DirectoryLoader::ReadDirectoryAfterGetAboutResource(
337     const std::string& local_id,
338     google_apis::GDataErrorCode status,
339     scoped_ptr<google_apis::AboutResource> about_resource) {
340   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
341
342   FileError error = GDataToFileError(status);
343   if (error != FILE_ERROR_OK) {
344     OnDirectoryLoadComplete(local_id, error);
345     return;
346   }
347
348   DCHECK(about_resource);
349
350   // Check the current status of local metadata, and start loading if needed.
351   google_apis::AboutResource* about_resource_ptr = about_resource.get();
352   ResourceEntry* entry = new ResourceEntry;
353   int64* local_changestamp = new int64;
354   base::PostTaskAndReplyWithResult(
355       blocking_task_runner_,
356       FROM_HERE,
357       base::Bind(&CheckLocalState,
358                  resource_metadata_,
359                  *about_resource_ptr,
360                  local_id,
361                  entry,
362                  local_changestamp),
363       base::Bind(&DirectoryLoader::ReadDirectoryAfterCheckLocalState,
364                  weak_ptr_factory_.GetWeakPtr(),
365                  base::Passed(&about_resource),
366                  local_id,
367                  base::Owned(entry),
368                  base::Owned(local_changestamp)));
369 }
370
371 void DirectoryLoader::ReadDirectoryAfterCheckLocalState(
372     scoped_ptr<google_apis::AboutResource> about_resource,
373     const std::string& local_id,
374     const ResourceEntry* entry,
375     const int64* local_changestamp,
376     FileError error) {
377   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
378   DCHECK(about_resource);
379
380   if (error != FILE_ERROR_OK) {
381     OnDirectoryLoadComplete(local_id, error);
382     return;
383   }
384   // This entry does not exist on the server.
385   if (entry->resource_id().empty()) {
386     OnDirectoryLoadComplete(local_id, FILE_ERROR_OK);
387     return;
388   }
389
390   int64 remote_changestamp = about_resource->largest_change_id();
391
392   // Start loading the directory.
393   int64 directory_changestamp = std::max(
394       entry->directory_specific_info().changestamp(), *local_changestamp);
395
396   DirectoryFetchInfo directory_fetch_info(
397       local_id, entry->resource_id(), remote_changestamp);
398
399   // If the directory's changestamp is up-to-date or the global changestamp of
400   // the metadata DB is new enough (which means the normal changelist loading
401   // should finish very soon), just schedule to run the callback, as there is no
402   // need to fetch the directory.
403   if (directory_changestamp >= remote_changestamp ||
404       *local_changestamp + kMinimumChangestampGap > remote_changestamp) {
405     OnDirectoryLoadComplete(local_id, FILE_ERROR_OK);
406   } else {
407     // Start fetching the directory content, and mark it with the changestamp
408     // |remote_changestamp|.
409     LoadDirectoryFromServer(directory_fetch_info);
410   }
411 }
412
413 void DirectoryLoader::OnDirectoryLoadComplete(const std::string& local_id,
414                                               FileError error) {
415   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
416
417   LoadCallbackMap::iterator it = pending_load_callback_.find(local_id);
418   if (it == pending_load_callback_.end())
419     return;
420
421   // No need to read metadata when no one needs entries.
422   bool needs_to_send_entries = false;
423   for (size_t i = 0; i < it->second.size(); ++i) {
424     const ReadDirectoryCallbackState& callback_state = it->second[i];
425     if (!callback_state.entries_callback.is_null())
426       needs_to_send_entries = true;
427   }
428
429   if (!needs_to_send_entries) {
430     OnDirectoryLoadCompleteAfterRead(local_id, NULL, FILE_ERROR_OK);
431     return;
432   }
433
434   ResourceEntryVector* entries = new ResourceEntryVector;
435   base::PostTaskAndReplyWithResult(
436       blocking_task_runner_.get(),
437       FROM_HERE,
438       base::Bind(&ResourceMetadata::ReadDirectoryById,
439                  base::Unretained(resource_metadata_), local_id, entries),
440       base::Bind(&DirectoryLoader::OnDirectoryLoadCompleteAfterRead,
441                  weak_ptr_factory_.GetWeakPtr(),
442                  local_id,
443                  base::Owned(entries)));
444 }
445
446 void DirectoryLoader::OnDirectoryLoadCompleteAfterRead(
447     const std::string& local_id,
448     const ResourceEntryVector* entries,
449     FileError error) {
450   LoadCallbackMap::iterator it = pending_load_callback_.find(local_id);
451   if (it != pending_load_callback_.end()) {
452     DVLOG(1) << "Running callback for " << local_id;
453
454     if (error == FILE_ERROR_OK && entries)
455       SendEntries(local_id, *entries);
456
457     for (size_t i = 0; i < it->second.size(); ++i) {
458       const ReadDirectoryCallbackState& callback_state = it->second[i];
459       callback_state.completion_callback.Run(error);
460     }
461     pending_load_callback_.erase(it);
462   }
463 }
464
465 void DirectoryLoader::SendEntries(const std::string& local_id,
466                                   const ResourceEntryVector& entries) {
467   LoadCallbackMap::iterator it = pending_load_callback_.find(local_id);
468   DCHECK(it != pending_load_callback_.end());
469
470   for (size_t i = 0; i < it->second.size(); ++i) {
471     ReadDirectoryCallbackState* callback_state = &it->second[i];
472     if (callback_state->entries_callback.is_null())
473       continue;
474
475     // Filter out entries which were already sent.
476     scoped_ptr<ResourceEntryVector> entries_to_send(new ResourceEntryVector);
477     for (size_t i = 0; i < entries.size(); ++i) {
478       const ResourceEntry& entry = entries[i];
479       if (!callback_state->sent_entry_names.count(entry.base_name())) {
480         callback_state->sent_entry_names.insert(entry.base_name());
481         entries_to_send->push_back(entry);
482       }
483     }
484     callback_state->entries_callback.Run(entries_to_send.Pass());
485   }
486 }
487
488 void DirectoryLoader::LoadDirectoryFromServer(
489     const DirectoryFetchInfo& directory_fetch_info) {
490   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
491   DCHECK(!directory_fetch_info.empty());
492   DVLOG(1) << "Start loading directory: " << directory_fetch_info.ToString();
493
494   const google_apis::AboutResource* about_resource =
495       about_resource_loader_->cached_about_resource();
496   DCHECK(about_resource);
497
498   logger_->Log(logging::LOG_INFO,
499                "Fast-fetch start: %s; Server changestamp: %s",
500                directory_fetch_info.ToString().c_str(),
501                base::Int64ToString(
502                    about_resource->largest_change_id()).c_str());
503
504   FeedFetcher* fetcher = new FeedFetcher(this,
505                                          directory_fetch_info,
506                                          about_resource->root_folder_id());
507   fast_fetch_feed_fetcher_set_.insert(fetcher);
508   fetcher->Run(
509       base::Bind(&DirectoryLoader::LoadDirectoryFromServerAfterLoad,
510                  weak_ptr_factory_.GetWeakPtr(),
511                  directory_fetch_info,
512                  fetcher));
513 }
514
515 void DirectoryLoader::LoadDirectoryFromServerAfterLoad(
516     const DirectoryFetchInfo& directory_fetch_info,
517     FeedFetcher* fetcher,
518     FileError error) {
519   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
520   DCHECK(!directory_fetch_info.empty());
521
522   // Delete the fetcher.
523   fast_fetch_feed_fetcher_set_.erase(fetcher);
524   delete fetcher;
525
526   logger_->Log(logging::LOG_INFO,
527                "Fast-fetch complete: %s => %s",
528                directory_fetch_info.ToString().c_str(),
529                FileErrorToString(error).c_str());
530
531   if (error != FILE_ERROR_OK) {
532     LOG(ERROR) << "Failed to load directory: "
533                << directory_fetch_info.local_id()
534                << ": " << FileErrorToString(error);
535     OnDirectoryLoadComplete(directory_fetch_info.local_id(), error);
536     return;
537   }
538
539   // Update changestamp and get the directory path.
540   base::FilePath* directory_path = new base::FilePath;
541   base::PostTaskAndReplyWithResult(
542       blocking_task_runner_.get(),
543       FROM_HERE,
544       base::Bind(&UpdateChangestamp,
545                  resource_metadata_,
546                  directory_fetch_info,
547                  directory_path),
548       base::Bind(
549           &DirectoryLoader::LoadDirectoryFromServerAfterUpdateChangestamp,
550           weak_ptr_factory_.GetWeakPtr(),
551           directory_fetch_info,
552           base::Owned(directory_path)));
553 }
554
555 void DirectoryLoader::LoadDirectoryFromServerAfterUpdateChangestamp(
556     const DirectoryFetchInfo& directory_fetch_info,
557     const base::FilePath* directory_path,
558     FileError error) {
559   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
560
561   DVLOG(1) << "Directory loaded: " << directory_fetch_info.ToString();
562   OnDirectoryLoadComplete(directory_fetch_info.local_id(), error);
563
564   // Also notify the observers.
565   if (error == FILE_ERROR_OK && !directory_path->empty()) {
566     FOR_EACH_OBSERVER(ChangeListLoaderObserver,
567                       observers_,
568                       OnDirectoryReloaded(*directory_path));
569   }
570 }
571
572 }  // namespace internal
573 }  // namespace drive