Revert of Add button to add new FSP services to Files app. (patchset #8 id:140001...
[chromium-blink-merge.git] / chrome / browser / chromeos / drive / directory_loader.cc
blob7d60858a5f412f8060e2bd807909666d964da86f
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.
5 #include "chrome/browser/chromeos/drive/directory_loader.h"
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"
23 using content::BrowserThread;
25 namespace drive {
26 namespace internal {
28 namespace {
30 // Minimum changestamp gap required to start loading directory.
31 const int kMinimumChangestampGap = 50;
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;
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;
52 // Get entry.
53 error = resource_metadata->GetResourceEntryById(local_id, entry);
54 if (error != FILE_ERROR_OK)
55 return error;
57 // Get the local changestamp.
58 return resource_metadata->GetLargestChangestamp(local_changestamp);
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;
71 if (!directory.file_info().is_directory())
72 return FILE_ERROR_NOT_A_DIRECTORY;
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;
80 // Get the directory path.
81 return resource_metadata->GetFilePath(directory_fetch_info.local_id(),
82 directory_path);
85 } // namespace
87 struct DirectoryLoader::ReadDirectoryCallbackState {
88 ReadDirectoryEntriesCallback entries_callback;
89 FileOperationCallback completion_callback;
90 std::set<std::string> sent_entry_names;
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) {
105 ~FeedFetcher() {
108 void Run(const FileOperationCallback& callback) {
109 DCHECK_CURRENTLY_ON(BrowserThread::UI);
110 DCHECK(!callback.is_null());
111 DCHECK(!directory_fetch_info_.resource_id().empty());
113 // Remember the time stamp for usage stats.
114 start_time_ = base::TimeTicks::Now();
116 loader_->scheduler_->GetFileListInDirectory(
117 directory_fetch_info_.resource_id(),
118 base::Bind(&FeedFetcher::OnFileListFetched,
119 weak_ptr_factory_.GetWeakPtr(), callback));
122 private:
123 void OnFileListFetched(const FileOperationCallback& callback,
124 google_apis::DriveApiErrorCode status,
125 scoped_ptr<google_apis::FileList> file_list) {
126 DCHECK_CURRENTLY_ON(BrowserThread::UI);
127 DCHECK(!callback.is_null());
129 FileError error = GDataToFileError(status);
130 if (error != FILE_ERROR_OK) {
131 callback.Run(error);
132 return;
135 DCHECK(file_list);
136 scoped_ptr<ChangeList> change_list(new ChangeList(*file_list));
137 GURL next_url = file_list->next_link();
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))));
157 void OnDirectoryRefreshed(
158 const FileOperationCallback& callback,
159 const GURL& next_url,
160 const std::vector<ResourceEntry>* refreshed_entries,
161 FileError error) {
162 DCHECK_CURRENTLY_ON(BrowserThread::UI);
163 DCHECK(!callback.is_null());
165 if (error != FILE_ERROR_OK) {
166 callback.Run(error);
167 return;
170 loader_->SendEntries(directory_fetch_info_.local_id(), *refreshed_entries);
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;
181 UMA_HISTOGRAM_TIMES("Drive.DirectoryFeedLoadTime",
182 base::TimeTicks::Now() - start_time_);
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);
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);
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) {
214 DirectoryLoader::~DirectoryLoader() {
215 STLDeleteElements(&fast_fetch_feed_fetcher_set_);
218 void DirectoryLoader::AddObserver(ChangeListLoaderObserver* observer) {
219 DCHECK_CURRENTLY_ON(BrowserThread::UI);
220 observers_.AddObserver(observer);
223 void DirectoryLoader::RemoveObserver(ChangeListLoaderObserver* observer) {
224 DCHECK_CURRENTLY_ON(BrowserThread::UI);
225 observers_.RemoveObserver(observer);
228 void DirectoryLoader::ReadDirectory(
229 const base::FilePath& directory_path,
230 const ReadDirectoryEntriesCallback& entries_callback,
231 const FileOperationCallback& completion_callback) {
232 DCHECK_CURRENTLY_ON(BrowserThread::UI);
233 DCHECK(!completion_callback.is_null());
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)));
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_CURRENTLY_ON(BrowserThread::UI);
260 DCHECK(!completion_callback.is_null());
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;
275 if (error != FILE_ERROR_OK) {
276 completion_callback.Run(error);
277 return;
280 if (!entry->file_info().is_directory()) {
281 completion_callback.Run(FILE_ERROR_NOT_A_DIRECTORY);
282 return;
285 DirectoryFetchInfo directory_fetch_info(
286 entry->local_id(),
287 entry->resource_id(),
288 entry->directory_specific_info().changestamp());
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);
297 // If loading task for |local_id| is already running, do nothing.
298 if (pending_load_callback_[local_id].size() > 1)
299 return;
301 about_resource_loader_->GetAboutResource(
302 base::Bind(&DirectoryLoader::ReadDirectoryAfterGetAboutResource,
303 weak_ptr_factory_.GetWeakPtr(), local_id));
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_CURRENTLY_ON(BrowserThread::UI);
312 DCHECK(!completion_callback.is_null());
314 if (error != FILE_ERROR_OK) {
315 completion_callback.Run(error);
316 return;
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)));
336 void DirectoryLoader::ReadDirectoryAfterGetAboutResource(
337 const std::string& local_id,
338 google_apis::DriveApiErrorCode status,
339 scoped_ptr<google_apis::AboutResource> about_resource) {
340 DCHECK_CURRENTLY_ON(BrowserThread::UI);
342 FileError error = GDataToFileError(status);
343 if (error != FILE_ERROR_OK) {
344 OnDirectoryLoadComplete(local_id, error);
345 return;
348 DCHECK(about_resource);
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_.get(),
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)));
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_CURRENTLY_ON(BrowserThread::UI);
378 DCHECK(about_resource);
380 if (error != FILE_ERROR_OK) {
381 OnDirectoryLoadComplete(local_id, error);
382 return;
384 // This entry does not exist on the server.
385 if (entry->resource_id().empty()) {
386 OnDirectoryLoadComplete(local_id, FILE_ERROR_OK);
387 return;
390 int64 remote_changestamp = about_resource->largest_change_id();
392 // Start loading the directory.
393 int64 directory_changestamp = std::max(
394 entry->directory_specific_info().changestamp(), *local_changestamp);
396 DirectoryFetchInfo directory_fetch_info(
397 local_id, entry->resource_id(), remote_changestamp);
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);
413 void DirectoryLoader::OnDirectoryLoadComplete(const std::string& local_id,
414 FileError error) {
415 DCHECK_CURRENTLY_ON(BrowserThread::UI);
417 LoadCallbackMap::iterator it = pending_load_callback_.find(local_id);
418 if (it == pending_load_callback_.end())
419 return;
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;
429 if (!needs_to_send_entries) {
430 OnDirectoryLoadCompleteAfterRead(local_id, NULL, FILE_ERROR_OK);
431 return;
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)));
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;
454 if (error == FILE_ERROR_OK && entries)
455 SendEntries(local_id, *entries);
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);
461 pending_load_callback_.erase(it);
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());
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;
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);
484 callback_state->entries_callback.Run(entries_to_send.Pass());
488 void DirectoryLoader::LoadDirectoryFromServer(
489 const DirectoryFetchInfo& directory_fetch_info) {
490 DCHECK_CURRENTLY_ON(BrowserThread::UI);
491 DCHECK(!directory_fetch_info.empty());
492 DVLOG(1) << "Start loading directory: " << directory_fetch_info.ToString();
494 const google_apis::AboutResource* about_resource =
495 about_resource_loader_->cached_about_resource();
496 DCHECK(about_resource);
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());
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));
515 void DirectoryLoader::LoadDirectoryFromServerAfterLoad(
516 const DirectoryFetchInfo& directory_fetch_info,
517 FeedFetcher* fetcher,
518 FileError error) {
519 DCHECK_CURRENTLY_ON(BrowserThread::UI);
520 DCHECK(!directory_fetch_info.empty());
522 // Delete the fetcher.
523 fast_fetch_feed_fetcher_set_.erase(fetcher);
524 delete fetcher;
526 logger_->Log(logging::LOG_INFO,
527 "Fast-fetch complete: %s => %s",
528 directory_fetch_info.ToString().c_str(),
529 FileErrorToString(error).c_str());
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;
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)));
555 void DirectoryLoader::LoadDirectoryFromServerAfterUpdateChangestamp(
556 const DirectoryFetchInfo& directory_fetch_info,
557 const base::FilePath* directory_path,
558 FileError error) {
559 DCHECK_CURRENTLY_ON(BrowserThread::UI);
561 DVLOG(1) << "Directory loaded: " << directory_fetch_info.ToString();
562 OnDirectoryLoadComplete(directory_fetch_info.local_id(), error);
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));
572 } // namespace internal
573 } // namespace drive