blob: a0be870bf982f7d26ed10b5869973fdf2c8931ab [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "components/drive/file_system/search_operation.h"
#include <string>
#include <vector>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/callback.h"
#include "base/task_runner_util.h"
#include "components/drive/change_list_loader.h"
#include "components/drive/drive_api_util.h"
#include "components/drive/file_system_core_util.h"
#include "components/drive/job_scheduler.h"
#include "components/drive/resource_entry_conversion.h"
#include "components/drive/resource_metadata.h"
#include "google_apis/drive/drive_api_parser.h"
#include "url/gurl.h"
namespace drive {
namespace file_system {
namespace {
// Computes the path of each item in |file_list| returned from the server
// and stores to |result|, by using |resource_metadata|. If the metadata is not
// up-to-date and did not contain an item, adds the item to "drive/other" for
// temporally assigning a path.
FileError ResolveSearchResultOnBlockingPool(
internal::ResourceMetadata* resource_metadata,
scoped_ptr<google_apis::FileList> file_list,
std::vector<SearchResultInfo>* result) {
DCHECK(resource_metadata);
DCHECK(result);
const ScopedVector<google_apis::FileResource>& entries = file_list->items();
result->reserve(entries.size());
for (size_t i = 0; i < entries.size(); ++i) {
std::string local_id;
FileError error = resource_metadata->GetIdByResourceId(
entries[i]->file_id(), &local_id);
ResourceEntry entry;
if (error == FILE_ERROR_OK)
error = resource_metadata->GetResourceEntryById(local_id, &entry);
if (error == FILE_ERROR_NOT_FOUND) {
std::string original_parent_id;
if (!ConvertFileResourceToResourceEntry(*entries[i], &entry,
&original_parent_id))
continue; // Skip non-file entries.
// The result is absent in local resource metadata. This can happen if
// the metadata is not synced to the latest server state yet. In that
// case, we temporarily add the file to the special "drive/other"
// directory in order to assign a path, which is needed to access the
// file through FileSystem API.
//
// It will be moved to the right place when the metadata gets synced
// in normal loading process in ChangeListProcessor.
entry.set_parent_local_id(util::kDriveOtherDirLocalId);
error = resource_metadata->AddEntry(entry, &local_id);
}
if (error != FILE_ERROR_OK)
return error;
base::FilePath path;
error = resource_metadata->GetFilePath(local_id, &path);
if (error != FILE_ERROR_OK)
return error;
result->push_back(SearchResultInfo(path, entry.file_info().is_directory()));
}
return FILE_ERROR_OK;
}
} // namespace
SearchOperation::SearchOperation(
base::SequencedTaskRunner* blocking_task_runner,
JobScheduler* scheduler,
internal::ResourceMetadata* metadata,
internal::LoaderController* loader_controller)
: blocking_task_runner_(blocking_task_runner),
scheduler_(scheduler),
metadata_(metadata),
loader_controller_(loader_controller),
weak_ptr_factory_(this) {
}
SearchOperation::~SearchOperation() {
}
void SearchOperation::Search(const std::string& search_query,
const GURL& next_link,
const SearchCallback& callback) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(!callback.is_null());
if (next_link.is_empty()) {
// This is first request for the |search_query|.
scheduler_->Search(
search_query,
base::Bind(&SearchOperation::SearchAfterGetFileList,
weak_ptr_factory_.GetWeakPtr(), callback));
} else {
// There is the remaining result so fetch it.
scheduler_->GetRemainingFileList(
next_link,
base::Bind(&SearchOperation::SearchAfterGetFileList,
weak_ptr_factory_.GetWeakPtr(), callback));
}
}
void SearchOperation::SearchAfterGetFileList(
const SearchCallback& callback,
google_apis::DriveApiErrorCode gdata_error,
scoped_ptr<google_apis::FileList> file_list) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(!callback.is_null());
FileError error = GDataToFileError(gdata_error);
if (error != FILE_ERROR_OK) {
callback.Run(error, GURL(), scoped_ptr<std::vector<SearchResultInfo> >());
return;
}
DCHECK(file_list);
GURL next_url = file_list->next_link();
scoped_ptr<std::vector<SearchResultInfo> > result(
new std::vector<SearchResultInfo>);
if (file_list->items().empty()) {
// Short cut. If the resource entry is empty, we don't need to refresh
// the resource metadata.
callback.Run(FILE_ERROR_OK, next_url, result.Pass());
return;
}
// ResolveSearchResultOnBlockingPool() may add entries newly created on the
// server to the local metadata.
// This may race with sync tasks so we should ask LoaderController here.
std::vector<SearchResultInfo>* result_ptr = result.get();
loader_controller_->ScheduleRun(base::Bind(
base::IgnoreResult(
&base::PostTaskAndReplyWithResult<FileError, FileError>),
blocking_task_runner_,
FROM_HERE,
base::Bind(&ResolveSearchResultOnBlockingPool,
metadata_,
base::Passed(&file_list),
result_ptr),
base::Bind(&SearchOperation::SearchAfterResolveSearchResult,
weak_ptr_factory_.GetWeakPtr(),
callback,
next_url,
base::Passed(&result))));
}
void SearchOperation::SearchAfterResolveSearchResult(
const SearchCallback& callback,
const GURL& next_link,
scoped_ptr<std::vector<SearchResultInfo> > result,
FileError error) {
DCHECK(thread_checker_.CalledOnValidThread());
DCHECK(!callback.is_null());
DCHECK(result);
if (error != FILE_ERROR_OK) {
callback.Run(error, GURL(), scoped_ptr<std::vector<SearchResultInfo> >());
return;
}
callback.Run(error, next_link, result.Pass());
}
} // namespace file_system
} // namespace drive