| Index: chrome/browser/chromeos/gdata/gdata_directory_service.cc
|
| ===================================================================
|
| --- chrome/browser/chromeos/gdata/gdata_directory_service.cc (revision 0)
|
| +++ chrome/browser/chromeos/gdata/gdata_directory_service.cc (revision 0)
|
| @@ -0,0 +1,767 @@
|
| +// Copyright (c) 2012 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 "chrome/browser/chromeos/gdata/gdata_directory_service.h"
|
| +
|
| +#include <leveldb/db.h>
|
| +
|
| +#include "base/message_loop_proxy.h"
|
| +#include "base/platform_file.h"
|
| +#include "base/string_number_conversions.h"
|
| +#include "base/string_util.h"
|
| +#include "base/stringprintf.h"
|
| +#include "base/sequenced_task_runner.h"
|
| +#include "base/tracked_objects.h"
|
| +#include "base/utf_string_conversions.h"
|
| +#include "chrome/browser/chromeos/gdata/gdata.pb.h"
|
| +#include "chrome/browser/chromeos/gdata/gdata_util.h"
|
| +#include "chrome/browser/chromeos/gdata/gdata_wapi_parser.h"
|
| +#include "content/public/browser/browser_thread.h"
|
| +#include "net/base/escape.h"
|
| +
|
| +using content::BrowserThread;
|
| +
|
| +namespace gdata {
|
| +namespace {
|
| +
|
| +// m: prefix for filesystem metadata db keys, version and largest_changestamp.
|
| +// r: prefix for resource id db keys.
|
| +const char kDBKeyLargestChangestamp[] = "m:largest_changestamp";
|
| +const char kDBKeyVersion[] = "m:version";
|
| +const char kDBKeyResourceIdPrefix[] = "r:";
|
| +
|
| +// Returns true if |proto| is a valid proto as the root directory.
|
| +// Used to reject incompatible proto.
|
| +bool IsValidRootDirectoryProto(const GDataDirectoryProto& proto) {
|
| + const GDataEntryProto& entry_proto = proto.gdata_entry();
|
| + // The title field for the root directory was originally empty, then
|
| + // changed to "gdata", then changed to "drive". Discard the proto data if
|
| + // the older formats are detected. See crbug.com/128133 for details.
|
| + if (entry_proto.title() != "drive") {
|
| + LOG(ERROR) << "Incompatible proto detected (bad title): "
|
| + << entry_proto.title();
|
| + return false;
|
| + }
|
| + // The title field for the root directory was originally empty. Discard
|
| + // the proto data if the older format is detected.
|
| + if (entry_proto.resource_id() != kGDataRootDirectoryResourceId) {
|
| + LOG(ERROR) << "Incompatible proto detected (bad resource ID): "
|
| + << entry_proto.resource_id();
|
| + return false;
|
| + }
|
| +
|
| + return true;
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| +EntryInfoResult::EntryInfoResult() : error(GDATA_FILE_ERROR_FAILED) {
|
| +}
|
| +
|
| +EntryInfoResult::~EntryInfoResult() {
|
| +}
|
| +
|
| +EntryInfoPairResult::EntryInfoPairResult() {
|
| +}
|
| +
|
| +EntryInfoPairResult::~EntryInfoPairResult() {
|
| +}
|
| +
|
| +// ResourceMetadataDB implementation.
|
| +
|
| +// Params for GDatadirectoryServiceDB::Create.
|
| +struct CreateDBParams {
|
| + CreateDBParams(const FilePath& db_path,
|
| + base::SequencedTaskRunner* blocking_task_runner)
|
| + : db_path(db_path),
|
| + blocking_task_runner(blocking_task_runner) {
|
| + }
|
| +
|
| + FilePath db_path;
|
| + scoped_refptr<base::SequencedTaskRunner> blocking_task_runner;
|
| + scoped_ptr<ResourceMetadataDB> db;
|
| + GDataDirectoryService::SerializedMap serialized_resources;
|
| +};
|
| +
|
| +// Wrapper for level db. All methods must be called on blocking thread.
|
| +class ResourceMetadataDB {
|
| + public:
|
| + ResourceMetadataDB(const FilePath& db_path,
|
| + base::SequencedTaskRunner* blocking_task_runner);
|
| +
|
| + // Initializes the database.
|
| + void Init();
|
| +
|
| + // Reads the database into |serialized_resources|.
|
| + void Read(GDataDirectoryService::SerializedMap* serialized_resources);
|
| +
|
| + // Saves |serialized_resources| to the database.
|
| + void Save(const GDataDirectoryService::SerializedMap& serialized_resources);
|
| +
|
| + private:
|
| + // Clears the database.
|
| + void Clear();
|
| +
|
| + scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_;
|
| + scoped_ptr<leveldb::DB> level_db_;
|
| + FilePath db_path_;
|
| +};
|
| +
|
| +ResourceMetadataDB::ResourceMetadataDB(const FilePath& db_path,
|
| + base::SequencedTaskRunner* blocking_task_runner)
|
| + : blocking_task_runner_(blocking_task_runner),
|
| + db_path_(db_path) {
|
| + DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
|
| +}
|
| +
|
| +// Creates, initializes and reads from the database.
|
| +// This must be defined after ResourceMetadataDB and CreateDBParams.
|
| +static void CreateResourceMetadataDBOnBlockingPool(
|
| + CreateDBParams* params) {
|
| + DCHECK(params->blocking_task_runner->RunsTasksOnCurrentThread());
|
| + DCHECK(!params->db_path.empty());
|
| +
|
| + params->db.reset(new ResourceMetadataDB(params->db_path,
|
| + params->blocking_task_runner));
|
| + params->db->Init();
|
| + params->db->Read(¶ms->serialized_resources);
|
| +}
|
| +
|
| +void ResourceMetadataDB::Init() {
|
| + DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
|
| + DCHECK(!db_path_.empty());
|
| +
|
| + DVLOG(1) << "Init " << db_path_.value();
|
| +
|
| + leveldb::DB* level_db = NULL;
|
| + leveldb::Options options;
|
| + options.create_if_missing = true;
|
| + leveldb::Status db_status = leveldb::DB::Open(options, db_path_.value(),
|
| + &level_db);
|
| + DCHECK(level_db);
|
| + DCHECK(db_status.ok());
|
| + level_db_.reset(level_db);
|
| +}
|
| +
|
| +void ResourceMetadataDB::Read(
|
| + GDataDirectoryService::SerializedMap* serialized_resources) {
|
| + DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
|
| + DCHECK(serialized_resources);
|
| + DVLOG(1) << "Read " << db_path_.value();
|
| +
|
| + scoped_ptr<leveldb::Iterator> iter(level_db_->NewIterator(
|
| + leveldb::ReadOptions()));
|
| + for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
|
| + DVLOG(1) << "Read, resource " << iter->key().ToString();
|
| + serialized_resources->insert(std::make_pair(iter->key().ToString(),
|
| + iter->value().ToString()));
|
| + }
|
| +}
|
| +
|
| +void ResourceMetadataDB::Save(
|
| + const GDataDirectoryService::SerializedMap& serialized_resources) {
|
| + DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread());
|
| +
|
| + Clear();
|
| + for (GDataDirectoryService::SerializedMap::const_iterator iter =
|
| + serialized_resources.begin();
|
| + iter != serialized_resources.end(); ++iter) {
|
| + DVLOG(1) << "Saving resource " << iter->first << " to db";
|
| + leveldb::Status status = level_db_->Put(leveldb::WriteOptions(),
|
| + leveldb::Slice(iter->first),
|
| + leveldb::Slice(iter->second));
|
| + if (!status.ok()) {
|
| + LOG(ERROR) << "leveldb Put failed of " << iter->first
|
| + << ", with " << status.ToString();
|
| + NOTREACHED();
|
| + }
|
| + }
|
| +}
|
| +
|
| +void ResourceMetadataDB::Clear() {
|
| + level_db_.reset();
|
| + leveldb::DestroyDB(db_path_.value(), leveldb::Options());
|
| + Init();
|
| +}
|
| +
|
| +// GDataDirectoryService class implementation.
|
| +
|
| +GDataDirectoryService::GDataDirectoryService()
|
| + : blocking_task_runner_(NULL),
|
| + serialized_size_(0),
|
| + largest_changestamp_(0),
|
| + origin_(UNINITIALIZED),
|
| + weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
|
| + root_.reset(CreateGDataDirectory());
|
| + if (!util::IsDriveV2ApiEnabled())
|
| + InitializeRootEntry(kGDataRootDirectoryResourceId);
|
| +}
|
| +
|
| +GDataDirectoryService::~GDataDirectoryService() {
|
| + ClearRoot();
|
| +
|
| + // Ensure db is closed on the blocking pool.
|
| + if (blocking_task_runner_ && directory_service_db_.get())
|
| + blocking_task_runner_->DeleteSoon(FROM_HERE,
|
| + directory_service_db_.release());
|
| +}
|
| +
|
| +GDataEntry* GDataDirectoryService::FromDocumentEntry(DocumentEntry* doc) {
|
| + DCHECK(doc);
|
| + GDataEntry* entry = NULL;
|
| + if (doc->is_folder())
|
| + entry = CreateGDataDirectory();
|
| + else if (doc->is_hosted_document() || doc->is_file())
|
| + entry = CreateGDataFile();
|
| +
|
| + if (entry)
|
| + entry->InitFromDocumentEntry(doc);
|
| + return entry;
|
| +}
|
| +
|
| +GDataFile* GDataDirectoryService::CreateGDataFile() {
|
| + return new GDataFile(this);
|
| +}
|
| +
|
| +GDataDirectory* GDataDirectoryService::CreateGDataDirectory() {
|
| + return new GDataDirectory(this);
|
| +}
|
| +
|
| +void GDataDirectoryService::InitializeRootEntry(const std::string& root_id) {
|
| + root_.reset(CreateGDataDirectory());
|
| + root_->set_title(kGDataRootDirectory);
|
| + root_->SetBaseNameFromTitle();
|
| + root_->set_resource_id(root_id);
|
| + AddEntryToResourceMap(root_.get());
|
| +}
|
| +
|
| +void GDataDirectoryService::ClearRoot() {
|
| + // Note that children have a reference to root_,
|
| + // so we need to delete them here.
|
| + root_->RemoveChildren();
|
| + RemoveEntryFromResourceMap(root_.get());
|
| + DCHECK(resource_map_.empty());
|
| + resource_map_.clear();
|
| + root_.reset();
|
| +}
|
| +
|
| +void GDataDirectoryService::AddEntryToDirectory(
|
| + GDataDirectory* directory,
|
| + GDataEntry* new_entry,
|
| + const FileMoveCallback& callback) {
|
| + DCHECK(directory);
|
| + DCHECK(new_entry);
|
| + DCHECK(!callback.is_null());
|
| +
|
| + directory->AddEntry(new_entry);
|
| + DVLOG(1) << "AddEntryToDirectory " << new_entry->GetFilePath().value();
|
| + base::MessageLoopProxy::current()->PostTask(FROM_HERE,
|
| + base::Bind(callback, GDATA_FILE_OK, new_entry->GetFilePath()));
|
| +}
|
| +
|
| +void GDataDirectoryService::MoveEntryToDirectory(
|
| + const FilePath& directory_path,
|
| + GDataEntry* entry,
|
| + const FileMoveCallback& callback) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + DCHECK(entry);
|
| + DCHECK(!callback.is_null());
|
| +
|
| + if (entry->parent())
|
| + entry->parent()->RemoveChild(entry);
|
| +
|
| + GDataEntry* destination = FindEntryByPathSync(directory_path);
|
| + FilePath moved_file_path;
|
| + GDataFileError error = GDATA_FILE_ERROR_FAILED;
|
| + if (!destination) {
|
| + error = GDATA_FILE_ERROR_NOT_FOUND;
|
| + } else if (!destination->AsGDataDirectory()) {
|
| + error = GDATA_FILE_ERROR_NOT_A_DIRECTORY;
|
| + } else {
|
| + destination->AsGDataDirectory()->AddEntry(entry);
|
| + moved_file_path = entry->GetFilePath();
|
| + error = GDATA_FILE_OK;
|
| + }
|
| + DVLOG(1) << "MoveEntryToDirectory " << moved_file_path.value();
|
| + base::MessageLoopProxy::current()->PostTask(
|
| + FROM_HERE, base::Bind(callback, error, moved_file_path));
|
| +}
|
| +
|
| +void GDataDirectoryService::RemoveEntryFromParent(
|
| + GDataEntry* entry,
|
| + const FileMoveCallback& callback) {
|
| + GDataDirectory* parent = entry->parent();
|
| + DCHECK(parent);
|
| + DCHECK(!callback.is_null());
|
| + DVLOG(1) << "RemoveEntryFromParent " << entry->GetFilePath().value();
|
| +
|
| + parent->RemoveEntry(entry);
|
| + base::MessageLoopProxy::current()->PostTask(FROM_HERE,
|
| + base::Bind(callback, GDATA_FILE_OK, parent->GetFilePath()));
|
| +}
|
| +
|
| +void GDataDirectoryService::AddEntryToResourceMap(GDataEntry* entry) {
|
| + // GDataFileSystem has already locked.
|
| + DVLOG(1) << "AddEntryToResourceMap " << entry->resource_id();
|
| + resource_map_.insert(std::make_pair(entry->resource_id(), entry));
|
| +}
|
| +
|
| +void GDataDirectoryService::RemoveEntryFromResourceMap(GDataEntry* entry) {
|
| + // GDataFileSystem has already locked.
|
| + resource_map_.erase(entry->resource_id());
|
| +}
|
| +
|
| +GDataEntry* GDataDirectoryService::FindEntryByPathSync(
|
| + const FilePath& file_path) {
|
| + if (file_path == root_->GetFilePath())
|
| + return root_.get();
|
| +
|
| + std::vector<FilePath::StringType> components;
|
| + file_path.GetComponents(&components);
|
| + GDataDirectory* current_dir = root_.get();
|
| +
|
| + for (size_t i = 1; i < components.size() && current_dir; ++i) {
|
| + GDataEntry* entry = current_dir->FindChild(components[i]);
|
| + if (!entry)
|
| + return NULL;
|
| +
|
| + if (i == components.size() - 1) // Last component.
|
| + return entry;
|
| + else
|
| + current_dir = entry->AsGDataDirectory();
|
| + }
|
| + return NULL;
|
| +}
|
| +
|
| +void GDataDirectoryService::FindEntryByPathAndRunSync(
|
| + const FilePath& search_file_path,
|
| + const FindEntryCallback& callback) {
|
| + GDataEntry* entry = FindEntryByPathSync(search_file_path);
|
| + callback.Run(entry ? GDATA_FILE_OK : GDATA_FILE_ERROR_NOT_FOUND, entry);
|
| +}
|
| +
|
| +GDataEntry* GDataDirectoryService::GetEntryByResourceId(
|
| + const std::string& resource) {
|
| + // GDataFileSystem has already locked.
|
| + ResourceMap::const_iterator iter = resource_map_.find(resource);
|
| + return iter == resource_map_.end() ? NULL : iter->second;
|
| +}
|
| +
|
| +void GDataDirectoryService::GetEntryByResourceIdAsync(
|
| + const std::string& resource_id,
|
| + const GetEntryByResourceIdCallback& callback) {
|
| + GDataEntry* entry = GetEntryByResourceId(resource_id);
|
| + callback.Run(entry);
|
| +}
|
| +
|
| +void GDataDirectoryService::GetEntryInfoByPath(
|
| + const FilePath& path,
|
| + const GetEntryInfoCallback& callback) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + DCHECK(!callback.is_null());
|
| +
|
| + scoped_ptr<GDataEntryProto> entry_proto;
|
| + GDataFileError error = GDATA_FILE_ERROR_FAILED;
|
| +
|
| + GDataEntry* entry = FindEntryByPathSync(path);
|
| + if (entry) {
|
| + entry_proto.reset(new GDataEntryProto);
|
| + entry->ToProtoFull(entry_proto.get());
|
| + error = GDATA_FILE_OK;
|
| + } else {
|
| + error = GDATA_FILE_ERROR_NOT_FOUND;
|
| + }
|
| +
|
| + base::MessageLoopProxy::current()->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(callback, error, base::Passed(&entry_proto)));
|
| +}
|
| +
|
| +void GDataDirectoryService::ReadDirectoryByPath(
|
| + const FilePath& path,
|
| + const ReadDirectoryCallback& callback) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + DCHECK(!callback.is_null());
|
| +
|
| + scoped_ptr<GDataEntryProtoVector> entries;
|
| + GDataFileError error = GDATA_FILE_ERROR_FAILED;
|
| +
|
| + GDataEntry* entry = FindEntryByPathSync(path);
|
| + if (entry && entry->AsGDataDirectory()) {
|
| + entries = entry->AsGDataDirectory()->ToProtoVector();
|
| + error = GDATA_FILE_OK;
|
| + } else if (entry && !entry->AsGDataDirectory()) {
|
| + error = GDATA_FILE_ERROR_NOT_A_DIRECTORY;
|
| + } else {
|
| + error = GDATA_FILE_ERROR_NOT_FOUND;
|
| + }
|
| +
|
| + base::MessageLoopProxy::current()->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(callback, error, base::Passed(&entries)));
|
| +}
|
| +
|
| +void GDataDirectoryService::GetEntryInfoPairByPaths(
|
| + const FilePath& first_path,
|
| + const FilePath& second_path,
|
| + const GetEntryInfoPairCallback& callback) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + DCHECK(!callback.is_null());
|
| +
|
| + // Get the first entry.
|
| + GetEntryInfoByPath(
|
| + first_path,
|
| + base::Bind(&GDataDirectoryService::GetEntryInfoPairByPathsAfterGetFirst,
|
| + weak_ptr_factory_.GetWeakPtr(),
|
| + first_path,
|
| + second_path,
|
| + callback));
|
| +}
|
| +
|
| +void GDataDirectoryService::RefreshFile(scoped_ptr<GDataFile> fresh_file) {
|
| + DCHECK(fresh_file.get());
|
| +
|
| + // Need to get a reference here because Passed() could get evaluated first.
|
| + const std::string& resource_id = fresh_file->resource_id();
|
| + GetEntryByResourceIdAsync(
|
| + resource_id,
|
| + base::Bind(&GDataDirectoryService::RefreshFileInternal,
|
| + base::Passed(&fresh_file)));
|
| +}
|
| +
|
| +// static
|
| +void GDataDirectoryService::RefreshFileInternal(
|
| + scoped_ptr<GDataFile> fresh_file,
|
| + GDataEntry* old_entry) {
|
| + GDataDirectory* entry_parent = old_entry ? old_entry->parent() : NULL;
|
| + if (entry_parent) {
|
| + DCHECK_EQ(fresh_file->resource_id(), old_entry->resource_id());
|
| + DCHECK(old_entry->AsGDataFile());
|
| +
|
| + entry_parent->RemoveEntry(old_entry);
|
| + entry_parent->AddEntry(fresh_file.release());
|
| + }
|
| +}
|
| +
|
| +void GDataDirectoryService::RefreshDirectory(
|
| + const std::string& directory_resource_id,
|
| + const ResourceMap& file_map,
|
| + const FileMoveCallback& callback) {
|
| + DCHECK(!callback.is_null());
|
| + GetEntryByResourceIdAsync(
|
| + directory_resource_id,
|
| + base::Bind(&GDataDirectoryService::RefreshDirectoryInternal,
|
| + file_map,
|
| + callback));
|
| +}
|
| +
|
| +// static
|
| +void GDataDirectoryService::RefreshDirectoryInternal(
|
| + const ResourceMap& file_map,
|
| + const FileMoveCallback& callback,
|
| + GDataEntry* directory_entry) {
|
| + DCHECK(!callback.is_null());
|
| +
|
| + if (!directory_entry) {
|
| + callback.Run(GDATA_FILE_ERROR_NOT_FOUND, FilePath());
|
| + return;
|
| + }
|
| +
|
| + GDataDirectory* directory = directory_entry->AsGDataDirectory();
|
| + if (!directory) {
|
| + callback.Run(GDATA_FILE_ERROR_NOT_A_DIRECTORY, FilePath());
|
| + return;
|
| + }
|
| +
|
| + directory->RemoveChildFiles();
|
| + // Add files from file_map.
|
| + for (ResourceMap::const_iterator it = file_map.begin();
|
| + it != file_map.end(); ++it) {
|
| + scoped_ptr<GDataEntry> entry(it->second);
|
| + // Skip if it's not a file (i.e. directory).
|
| + if (!entry->AsGDataFile())
|
| + continue;
|
| + directory->AddEntry(entry.release());
|
| + }
|
| +
|
| + callback.Run(GDATA_FILE_OK, directory->GetFilePath());
|
| +}
|
| +
|
| +void GDataDirectoryService::InitFromDB(
|
| + const FilePath& db_path,
|
| + base::SequencedTaskRunner* blocking_task_runner,
|
| + const FileOperationCallback& callback) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + DCHECK(!db_path.empty());
|
| + DCHECK(blocking_task_runner);
|
| +
|
| + if (directory_service_db_.get()) {
|
| + if (!callback.is_null())
|
| + callback.Run(GDATA_FILE_ERROR_FAILED);
|
| + return;
|
| + }
|
| +
|
| + blocking_task_runner_ = blocking_task_runner;
|
| +
|
| + DVLOG(1) << "InitFromDB " << db_path.value();
|
| +
|
| + CreateDBParams* create_params =
|
| + new CreateDBParams(db_path, blocking_task_runner);
|
| + blocking_task_runner_->PostTaskAndReply(
|
| + FROM_HERE,
|
| + base::Bind(&CreateResourceMetadataDBOnBlockingPool,
|
| + create_params),
|
| + base::Bind(&GDataDirectoryService::InitResourceMap,
|
| + weak_ptr_factory_.GetWeakPtr(),
|
| + base::Owned(create_params),
|
| + callback));
|
| +}
|
| +
|
| +void GDataDirectoryService::InitResourceMap(
|
| + CreateDBParams* create_params,
|
| + const FileOperationCallback& callback) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + DCHECK(create_params);
|
| + DCHECK(!directory_service_db_.get());
|
| +
|
| + SerializedMap* serialized_resources = &create_params->serialized_resources;
|
| + directory_service_db_ = create_params->db.Pass();
|
| + if (serialized_resources->empty()) {
|
| + origin_ = INITIALIZING;
|
| + if (!callback.is_null())
|
| + callback.Run(GDATA_FILE_ERROR_NOT_FOUND);
|
| + return;
|
| + }
|
| +
|
| + ClearRoot();
|
| +
|
| + // Version check.
|
| + int32 version = 0;
|
| + SerializedMap::iterator iter = serialized_resources->find(kDBKeyVersion);
|
| + if (iter == serialized_resources->end() ||
|
| + !base::StringToInt(iter->second, &version) ||
|
| + version != kProtoVersion) {
|
| + if (!callback.is_null())
|
| + callback.Run(GDATA_FILE_ERROR_FAILED);
|
| + return;
|
| + }
|
| + serialized_resources->erase(iter);
|
| +
|
| + // Get the largest changestamp.
|
| + iter = serialized_resources->find(kDBKeyLargestChangestamp);
|
| + if (iter == serialized_resources->end() ||
|
| + !base::StringToInt64(iter->second, &largest_changestamp_)) {
|
| + NOTREACHED() << "Could not find/parse largest_changestamp";
|
| + if (!callback.is_null())
|
| + callback.Run(GDATA_FILE_ERROR_FAILED);
|
| + return;
|
| + } else {
|
| + DVLOG(1) << "InitResourceMap largest_changestamp_" << largest_changestamp_;
|
| + serialized_resources->erase(iter);
|
| + }
|
| +
|
| + ResourceMap resource_map;
|
| + for (SerializedMap::const_iterator iter = serialized_resources->begin();
|
| + iter != serialized_resources->end(); ++iter) {
|
| + if (iter->first.find(kDBKeyResourceIdPrefix) != 0) {
|
| + NOTREACHED() << "Incorrect prefix for db key " << iter->first;
|
| + continue;
|
| + }
|
| +
|
| + const std::string resource_id =
|
| + iter->first.substr(strlen(kDBKeyResourceIdPrefix));
|
| + scoped_ptr<GDataEntry> entry = FromProtoString(iter->second);
|
| + if (entry.get()) {
|
| + DVLOG(1) << "Inserting resource " << resource_id
|
| + << " into resource_map";
|
| + resource_map.insert(std::make_pair(resource_id, entry.release()));
|
| + } else {
|
| + NOTREACHED() << "Failed to parse GDataEntry for resource " << resource_id;
|
| + }
|
| + }
|
| +
|
| + // Fix up parent-child relations.
|
| + for (ResourceMap::iterator iter = resource_map.begin();
|
| + iter != resource_map.end(); ++iter) {
|
| + GDataEntry* entry = iter->second;
|
| + ResourceMap::iterator parent_it =
|
| + resource_map.find(entry->parent_resource_id());
|
| + if (parent_it != resource_map.end()) {
|
| + GDataDirectory* parent = parent_it->second->AsGDataDirectory();
|
| + if (parent) {
|
| + DVLOG(1) << "Adding " << entry->resource_id()
|
| + << " as a child of " << parent->resource_id();
|
| + parent->AddEntry(entry);
|
| + } else {
|
| + NOTREACHED() << "Parent is not a directory " << parent->resource_id();
|
| + }
|
| + } else if (entry->resource_id() == kGDataRootDirectoryResourceId) {
|
| + root_.reset(entry->AsGDataDirectory());
|
| + DCHECK(root_.get());
|
| + AddEntryToResourceMap(root_.get());
|
| + } else {
|
| + NOTREACHED() << "Missing parent id " << entry->parent_resource_id()
|
| + << " for resource " << entry->resource_id();
|
| + }
|
| + }
|
| +
|
| + DCHECK(root_.get());
|
| + DCHECK_EQ(resource_map.size(), resource_map_.size());
|
| + DCHECK_EQ(resource_map.size(), serialized_resources->size());
|
| +
|
| + origin_ = FROM_CACHE;
|
| +
|
| + if (!callback.is_null())
|
| + callback.Run(GDATA_FILE_OK);
|
| +}
|
| +
|
| +void GDataDirectoryService::SaveToDB() {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| +
|
| + if (!blocking_task_runner_ || !directory_service_db_.get()) {
|
| + NOTREACHED();
|
| + return;
|
| + }
|
| +
|
| + size_t serialized_size = 0;
|
| + SerializedMap serialized_resources;
|
| + for (ResourceMap::const_iterator iter = resource_map_.begin();
|
| + iter != resource_map_.end(); ++iter) {
|
| + GDataEntryProto proto;
|
| + iter->second->ToProtoFull(&proto);
|
| + std::string serialized_string;
|
| + const bool ok = proto.SerializeToString(&serialized_string);
|
| + DCHECK(ok);
|
| + if (ok) {
|
| + serialized_resources.insert(
|
| + std::make_pair(std::string(kDBKeyResourceIdPrefix) + iter->first,
|
| + serialized_string));
|
| + serialized_size += serialized_string.size();
|
| + }
|
| + }
|
| +
|
| + serialized_resources.insert(std::make_pair(kDBKeyVersion,
|
| + base::IntToString(kProtoVersion)));
|
| + serialized_resources.insert(std::make_pair(kDBKeyLargestChangestamp,
|
| + base::IntToString(largest_changestamp_)));
|
| + set_last_serialized(base::Time::Now());
|
| + set_serialized_size(serialized_size);
|
| +
|
| + blocking_task_runner_->PostTask(
|
| + FROM_HERE,
|
| + base::Bind(&ResourceMetadataDB::Save,
|
| + base::Unretained(directory_service_db_.get()),
|
| + serialized_resources));
|
| +}
|
| +
|
| +void GDataDirectoryService::SerializeToString(
|
| + std::string* serialized_proto) const {
|
| + GDataRootDirectoryProto proto;
|
| + root_->ToProto(proto.mutable_gdata_directory());
|
| + proto.set_largest_changestamp(largest_changestamp_);
|
| + proto.set_version(kProtoVersion);
|
| +
|
| + const bool ok = proto.SerializeToString(serialized_proto);
|
| + DCHECK(ok);
|
| +}
|
| +
|
| +bool GDataDirectoryService::ParseFromString(
|
| + const std::string& serialized_proto) {
|
| + GDataRootDirectoryProto proto;
|
| + if (!proto.ParseFromString(serialized_proto))
|
| + return false;
|
| +
|
| + if (proto.version() != kProtoVersion) {
|
| + LOG(ERROR) << "Incompatible proto detected (incompatible version): "
|
| + << proto.version();
|
| + return false;
|
| + }
|
| +
|
| + if (!IsValidRootDirectoryProto(proto.gdata_directory()))
|
| + return false;
|
| +
|
| + if (!root_->FromProto(proto.gdata_directory()))
|
| + return false;
|
| +
|
| + origin_ = FROM_CACHE;
|
| + largest_changestamp_ = proto.largest_changestamp();
|
| +
|
| + return true;
|
| +}
|
| +
|
| +scoped_ptr<GDataEntry> GDataDirectoryService::FromProtoString(
|
| + const std::string& serialized_proto) {
|
| + GDataEntryProto entry_proto;
|
| + if (!entry_proto.ParseFromString(serialized_proto))
|
| + return scoped_ptr<GDataEntry>();
|
| +
|
| + scoped_ptr<GDataEntry> entry;
|
| + if (entry_proto.file_info().is_directory()) {
|
| + entry.reset(CreateGDataDirectory());
|
| + // Call GDataEntry::FromProto instead of GDataDirectory::FromProto because
|
| + // the proto does not include children.
|
| + if (!entry->FromProto(entry_proto)) {
|
| + NOTREACHED() << "FromProto (directory) failed";
|
| + entry.reset();
|
| + }
|
| + } else {
|
| + scoped_ptr<GDataFile> file(CreateGDataFile());
|
| + // Call GDataFile::FromProto.
|
| + if (file->FromProto(entry_proto)) {
|
| + entry.reset(file.release());
|
| + } else {
|
| + NOTREACHED() << "FromProto (file) failed";
|
| + }
|
| + }
|
| + return entry.Pass();
|
| +}
|
| +
|
| +void GDataDirectoryService::GetEntryInfoPairByPathsAfterGetFirst(
|
| + const FilePath& first_path,
|
| + const FilePath& second_path,
|
| + const GetEntryInfoPairCallback& callback,
|
| + GDataFileError error,
|
| + scoped_ptr<GDataEntryProto> entry_proto) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + DCHECK(!callback.is_null());
|
| +
|
| + scoped_ptr<EntryInfoPairResult> result(new EntryInfoPairResult);
|
| + result->first.path = first_path;
|
| + result->first.error = error;
|
| + result->first.proto = entry_proto.Pass();
|
| +
|
| + // If the first one is not found, don't continue.
|
| + if (error != GDATA_FILE_OK) {
|
| + callback.Run(result.Pass());
|
| + return;
|
| + }
|
| +
|
| + // Get the second entry.
|
| + GetEntryInfoByPath(
|
| + second_path,
|
| + base::Bind(&GDataDirectoryService::GetEntryInfoPairByPathsAfterGetSecond,
|
| + weak_ptr_factory_.GetWeakPtr(),
|
| + second_path,
|
| + callback,
|
| + base::Passed(&result)));
|
| +}
|
| +
|
| +void GDataDirectoryService::GetEntryInfoPairByPathsAfterGetSecond(
|
| + const FilePath& second_path,
|
| + const GetEntryInfoPairCallback& callback,
|
| + scoped_ptr<EntryInfoPairResult> result,
|
| + GDataFileError error,
|
| + scoped_ptr<GDataEntryProto> entry_proto) {
|
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
|
| + DCHECK(!callback.is_null());
|
| + DCHECK(result.get());
|
| +
|
| + result->second.path = second_path;
|
| + result->second.error = error;
|
| + result->second.proto = entry_proto.Pass();
|
| +
|
| + callback.Run(result.Pass());
|
| +}
|
| +
|
| +} // namespace gdata
|
|
|
| Property changes on: chrome/browser/chromeos/gdata/gdata_directory_service.cc
|
| ___________________________________________________________________
|
| Added: svn:eol-style
|
| + LF
|
|
|
|
|