1// Copyright 2013 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/file_system/search_operation.h"
6
7#include <string>
8#include <vector>
9
10#include "base/bind.h"
11#include "base/bind_helpers.h"
12#include "base/callback.h"
13#include "base/task_runner_util.h"
14#include "chrome/browser/chromeos/drive/change_list_loader.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_entry_conversion.h"
18#include "chrome/browser/chromeos/drive/resource_metadata.h"
19#include "chrome/browser/drive/drive_api_util.h"
20#include "content/public/browser/browser_thread.h"
21#include "google_apis/drive/drive_api_parser.h"
22#include "url/gurl.h"
23
24using content::BrowserThread;
25
26namespace drive {
27namespace file_system {
28namespace {
29
30// Computes the path of each item in |file_list| returned from the server
31// and stores to |result|, by using |resource_metadata|. If the metadata is not
32// up-to-date and did not contain an item, adds the item to "drive/other" for
33// temporally assigning a path.
34FileError ResolveSearchResultOnBlockingPool(
35    internal::ResourceMetadata* resource_metadata,
36    scoped_ptr<google_apis::FileList> file_list,
37    std::vector<SearchResultInfo>* result) {
38  DCHECK(resource_metadata);
39  DCHECK(result);
40
41  const ScopedVector<google_apis::FileResource>& entries = file_list->items();
42  result->reserve(entries.size());
43  for (size_t i = 0; i < entries.size(); ++i) {
44    std::string local_id;
45    FileError error = resource_metadata->GetIdByResourceId(
46        entries[i]->file_id(), &local_id);
47
48    ResourceEntry entry;
49    if (error == FILE_ERROR_OK)
50      error = resource_metadata->GetResourceEntryById(local_id, &entry);
51
52    if (error == FILE_ERROR_NOT_FOUND) {
53      std::string original_parent_id;
54      if (!ConvertFileResourceToResourceEntry(*entries[i], &entry,
55                                              &original_parent_id))
56        continue;  // Skip non-file entries.
57
58      // The result is absent in local resource metadata. This can happen if
59      // the metadata is not synced to the latest server state yet. In that
60      // case, we temporarily add the file to the special "drive/other"
61      // directory in order to assign a path, which is needed to access the
62      // file through FileSystem API.
63      //
64      // It will be moved to the right place when the metadata gets synced
65      // in normal loading process in ChangeListProcessor.
66      entry.set_parent_local_id(util::kDriveOtherDirLocalId);
67      error = resource_metadata->AddEntry(entry, &local_id);
68    }
69    if (error != FILE_ERROR_OK)
70      return error;
71    base::FilePath path;
72    error = resource_metadata->GetFilePath(local_id, &path);
73    if (error != FILE_ERROR_OK)
74      return error;
75    result->push_back(SearchResultInfo(path, entry.file_info().is_directory()));
76  }
77
78  return FILE_ERROR_OK;
79}
80
81}  // namespace
82
83SearchOperation::SearchOperation(
84    base::SequencedTaskRunner* blocking_task_runner,
85    JobScheduler* scheduler,
86    internal::ResourceMetadata* metadata,
87    internal::LoaderController* loader_controller)
88    : blocking_task_runner_(blocking_task_runner),
89      scheduler_(scheduler),
90      metadata_(metadata),
91      loader_controller_(loader_controller),
92      weak_ptr_factory_(this) {
93}
94
95SearchOperation::~SearchOperation() {
96}
97
98void SearchOperation::Search(const std::string& search_query,
99                             const GURL& next_link,
100                             const SearchCallback& callback) {
101  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
102  DCHECK(!callback.is_null());
103
104  if (next_link.is_empty()) {
105    // This is first request for the |search_query|.
106    scheduler_->Search(
107        search_query,
108        base::Bind(&SearchOperation::SearchAfterGetFileList,
109                   weak_ptr_factory_.GetWeakPtr(), callback));
110  } else {
111    // There is the remaining result so fetch it.
112    scheduler_->GetRemainingFileList(
113        next_link,
114        base::Bind(&SearchOperation::SearchAfterGetFileList,
115                   weak_ptr_factory_.GetWeakPtr(), callback));
116  }
117}
118
119void SearchOperation::SearchAfterGetFileList(
120    const SearchCallback& callback,
121    google_apis::GDataErrorCode gdata_error,
122    scoped_ptr<google_apis::FileList> file_list) {
123  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
124  DCHECK(!callback.is_null());
125
126  FileError error = GDataToFileError(gdata_error);
127  if (error != FILE_ERROR_OK) {
128    callback.Run(error, GURL(), scoped_ptr<std::vector<SearchResultInfo> >());
129    return;
130  }
131
132  DCHECK(file_list);
133
134  GURL next_url = file_list->next_link();
135
136  scoped_ptr<std::vector<SearchResultInfo> > result(
137      new std::vector<SearchResultInfo>);
138  if (file_list->items().empty()) {
139    // Short cut. If the resource entry is empty, we don't need to refresh
140    // the resource metadata.
141    callback.Run(FILE_ERROR_OK, next_url, result.Pass());
142    return;
143  }
144
145  // ResolveSearchResultOnBlockingPool() may add entries newly created on the
146  // server to the local metadata.
147  // This may race with sync tasks so we should ask LoaderController here.
148  std::vector<SearchResultInfo>* result_ptr = result.get();
149  loader_controller_->ScheduleRun(base::Bind(
150      base::IgnoreResult(
151          &base::PostTaskAndReplyWithResult<FileError, FileError>),
152      blocking_task_runner_,
153      FROM_HERE,
154      base::Bind(&ResolveSearchResultOnBlockingPool,
155                 metadata_,
156                 base::Passed(&file_list),
157                 result_ptr),
158      base::Bind(&SearchOperation::SearchAfterResolveSearchResult,
159                 weak_ptr_factory_.GetWeakPtr(),
160                 callback,
161                 next_url,
162                 base::Passed(&result))));
163}
164
165void SearchOperation::SearchAfterResolveSearchResult(
166    const SearchCallback& callback,
167    const GURL& next_link,
168    scoped_ptr<std::vector<SearchResultInfo> > result,
169    FileError error) {
170  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
171  DCHECK(!callback.is_null());
172  DCHECK(result);
173
174  if (error != FILE_ERROR_OK) {
175    callback.Run(error, GURL(), scoped_ptr<std::vector<SearchResultInfo> >());
176    return;
177  }
178
179  callback.Run(error, next_link, result.Pass());
180}
181
182}  // namespace file_system
183}  // namespace drive
184