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