search_operation.cc revision 3551c9c881056c480085172ff9840cab31610854
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/file_system_util.h"
15#include "chrome/browser/chromeos/drive/job_scheduler.h"
16#include "chrome/browser/chromeos/drive/resource_entry_conversion.h"
17#include "chrome/browser/chromeos/drive/resource_metadata.h"
18#include "chrome/browser/google_apis/gdata_wapi_parser.h"
19#include "content/public/browser/browser_thread.h"
20#include "url/gurl.h"
21
22using content::BrowserThread;
23
24namespace drive {
25namespace file_system {
26namespace {
27
28// Refreshes entries of |resource_metadata| based on |resource_list|, and
29// returns the result. Refreshed entries will be stored into |result|.
30FileError RefreshEntriesOnBlockingPool(
31    internal::ResourceMetadata* resource_metadata,
32    scoped_ptr<google_apis::ResourceList> resource_list,
33    std::vector<SearchResultInfo>* result) {
34  DCHECK(resource_metadata);
35  DCHECK(result);
36
37  const ScopedVector<google_apis::ResourceEntry>& entries =
38      resource_list->entries();
39  result->reserve(entries.size());
40  for (size_t i = 0; i < entries.size(); ++i) {
41    ResourceEntry entry;
42    if (!ConvertToResourceEntry(*entries[i], &entry))
43      continue;  // Skip non-file entries.
44
45    const std::string id = entry.resource_id();
46    FileError error = resource_metadata->RefreshEntry(id, entry);
47    if (error == FILE_ERROR_NOT_FOUND) {
48      // The result is absent in local resource metadata. This can happen if
49      // the metadata is not synced to the latest server state yet. In that
50      // case, we temporarily add the file to the special "drive/other"
51      // directory in order to assign a path, which is needed to access the
52      // file through FileSystem API.
53      //
54      // It will be moved to the right place when the metadata gets synced
55      // in normal loading process in ChangeListProcessor.
56      entry.set_parent_local_id(util::kDriveOtherDirSpecialResourceId);
57      error = resource_metadata->AddEntry(entry);
58
59      // FILE_ERROR_EXISTS may happen if we have already added the entry to
60      // "drive/other" once before. That's not an error.
61      if (error == FILE_ERROR_EXISTS)
62        error = FILE_ERROR_OK;
63    }
64    if (error == FILE_ERROR_OK)
65      error = resource_metadata->GetResourceEntryById(id, &entry);
66    if (error != FILE_ERROR_OK)
67      return error;
68    result->push_back(SearchResultInfo(resource_metadata->GetFilePath(id),
69                                       entry));
70  }
71
72  return FILE_ERROR_OK;
73}
74
75}  // namespace
76
77SearchOperation::SearchOperation(
78    base::SequencedTaskRunner* blocking_task_runner,
79    JobScheduler* scheduler,
80    internal::ResourceMetadata* metadata)
81    : blocking_task_runner_(blocking_task_runner),
82      scheduler_(scheduler),
83      metadata_(metadata),
84      weak_ptr_factory_(this) {
85}
86
87SearchOperation::~SearchOperation() {
88}
89
90void SearchOperation::Search(const std::string& search_query,
91                             const GURL& next_url,
92                             const SearchCallback& callback) {
93  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
94  DCHECK(!callback.is_null());
95
96  if (next_url.is_empty()) {
97    // This is first request for the |search_query|.
98    scheduler_->Search(
99        search_query,
100        base::Bind(&SearchOperation::SearchAfterGetResourceList,
101                   weak_ptr_factory_.GetWeakPtr(), callback));
102  } else {
103    // There is the remaining result so fetch it.
104    scheduler_->ContinueGetResourceList(
105        next_url,
106        base::Bind(&SearchOperation::SearchAfterGetResourceList,
107                   weak_ptr_factory_.GetWeakPtr(), callback));
108  }
109}
110
111void SearchOperation::SearchAfterGetResourceList(
112    const SearchCallback& callback,
113    google_apis::GDataErrorCode gdata_error,
114    scoped_ptr<google_apis::ResourceList> resource_list) {
115  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
116  DCHECK(!callback.is_null());
117
118  FileError error = GDataToFileError(gdata_error);
119  if (error != FILE_ERROR_OK) {
120    callback.Run(error, GURL(), scoped_ptr<std::vector<SearchResultInfo> >());
121    return;
122  }
123
124  DCHECK(resource_list);
125
126  GURL next_url;
127  resource_list->GetNextFeedURL(&next_url);
128
129  // The search results will be returned using virtual directory.
130  // The directory is not really part of the file system, so it has no parent or
131  // root.
132  scoped_ptr<std::vector<SearchResultInfo> > result(
133      new std::vector<SearchResultInfo>);
134  if (resource_list->entries().empty()) {
135    // Short cut. If the resource entry is empty, we don't need to refresh
136    // the resource metadata.
137    callback.Run(FILE_ERROR_OK, next_url, result.Pass());
138    return;
139  }
140
141  std::vector<SearchResultInfo>* result_ptr = result.get();
142  base::PostTaskAndReplyWithResult(
143      blocking_task_runner_.get(),
144      FROM_HERE,
145      base::Bind(&RefreshEntriesOnBlockingPool,
146                 metadata_,
147                 base::Passed(&resource_list),
148                 result_ptr),
149      base::Bind(&SearchOperation::SearchAfterRefreshEntry,
150                 weak_ptr_factory_.GetWeakPtr(),
151                 callback,
152                 next_url,
153                 base::Passed(&result)));
154}
155
156void SearchOperation::SearchAfterRefreshEntry(
157    const SearchCallback& callback,
158    const GURL& next_url,
159    scoped_ptr<std::vector<SearchResultInfo> > result,
160    FileError error) {
161  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
162  DCHECK(!callback.is_null());
163  DCHECK(result);
164
165  if (error != FILE_ERROR_OK) {
166    callback.Run(error, GURL(), scoped_ptr<std::vector<SearchResultInfo> >());
167    return;
168  }
169
170  callback.Run(error, next_url, result.Pass());
171}
172
173}  // namespace file_system
174}  // namespace drive
175