Chromium Code Reviews| Index: chrome/browser/chromeos/drive/drive_resource_metadata.cc |
| diff --git a/chrome/browser/chromeos/drive/drive_resource_metadata.cc b/chrome/browser/chromeos/drive/drive_resource_metadata.cc |
| index fe0c3e80b7f39ebf725442a5edeeb1b006852abf..ac243f3063a840053078eddfe3f8cd62ba4eebac 100644 |
| --- a/chrome/browser/chromeos/drive/drive_resource_metadata.cc |
| +++ b/chrome/browser/chromeos/drive/drive_resource_metadata.cc |
| @@ -115,7 +115,7 @@ void DriveResourceMetadata::ClearRoot() { |
| // Note that children have a reference to root_, |
| // so we need to delete them here. |
| - RemoveDirectoryChildren(root_.get()); |
| + RemoveDirectoryChildren(root_->resource_id()); |
| RemoveEntryFromResourceMap(root_->resource_id()); |
| DCHECK(resource_map_.empty()); |
| // The resource_map_ should be empty here, but to make sure for non-Debug |
| @@ -164,10 +164,11 @@ void DriveResourceMetadata::MoveEntryToDirectory( |
| DriveEntryProto* parent = entry->parent_resource_id().empty() ? NULL : |
|
kinaba
2013/03/15 05:48:08
Now, empty parent_resource_id means it is the root
hashimoto
2013/03/15 06:40:23
Changed to return DRIVE_FILE_ERROR_INVALID_OPERATI
|
| GetEntryByResourceId(entry->parent_resource_id()); |
| if (parent && parent->file_info().is_directory()) |
|
kinaba
2013/03/15 05:48:08
Similarly, non directory parent is a bug.
I think
hashimoto
2013/03/15 06:40:23
Now we no longer need to get parent.
Removed.
|
| - DetachEntryFromDirectory(parent, entry); |
| + DetachEntryFromDirectory(entry->resource_id()); |
| - AddEntryToDirectory(destination, entry); |
| - moved_file_path = GetFilePath(*entry); |
| + entry->set_parent_resource_id(destination->resource_id()); |
| + AddEntryToDirectory(entry); |
| + moved_file_path = GetFilePath(entry->resource_id()); |
| error = DRIVE_FILE_OK; |
| } |
| DVLOG(1) << "MoveEntryToDirectory " << moved_file_path.value(); |
| @@ -197,9 +198,6 @@ void DriveResourceMetadata::RenameEntry( |
| } |
| entry->set_title(new_name); |
| - |
| - DriveEntryProto* parent = GetEntryByResourceId(entry->parent_resource_id()); |
| - DCHECK(parent); |
| // After changing the title of the entry, call MoveEntryToDirectory to |
| // remove the entry from its parent directory and then add it back in order to |
| // go through the file name de-duplication. |
| @@ -207,7 +205,8 @@ void DriveResourceMetadata::RenameEntry( |
| // changed, but not the file_name. MoveEntryToDirectory calls RemoveChild to |
| // remove the child based on the old file_name, and then re-adds the child by |
| // first assigning the new title to file_name. http://crbug.com/30157 |
| - MoveEntryToDirectory(file_path, GetFilePath(*parent), callback); |
| + MoveEntryToDirectory(file_path, |
| + GetFilePath(entry->parent_resource_id()), callback); |
| } |
| void DriveResourceMetadata::RemoveEntry(const std::string& resource_id, |
| @@ -227,13 +226,12 @@ void DriveResourceMetadata::RemoveEntry(const std::string& resource_id, |
| return; |
| } |
| - DriveEntryProto* parent = GetEntryByResourceId(entry->parent_resource_id()); |
| - DCHECK(parent && parent->file_info().is_directory()); |
| - |
| - RemoveDirectoryChild(parent, entry); |
| + const base::FilePath parent_file_path = |
| + GetFilePath(entry->parent_resource_id()); |
| + RemoveDirectoryChild(entry->resource_id()); |
| base::MessageLoopProxy::current()->PostTask( |
| FROM_HERE, |
| - base::Bind(callback, DRIVE_FILE_OK, GetFilePath(*parent))); |
| + base::Bind(callback, DRIVE_FILE_OK, parent_file_path)); |
| } |
| bool DriveResourceMetadata::AddEntryToResourceMap(DriveEntryProto* entry) { |
| @@ -255,7 +253,7 @@ void DriveResourceMetadata::RemoveEntryFromResourceMap( |
| DriveEntryProto* DriveResourceMetadata::FindEntryByPathSync( |
| const base::FilePath& file_path) { |
| - if (file_path == GetFilePath(*root_)) |
| + if (file_path == GetFilePath(root_->resource_id())) |
| return root_.get(); |
| std::vector<base::FilePath::StringType> components; |
| @@ -263,7 +261,8 @@ DriveEntryProto* DriveResourceMetadata::FindEntryByPathSync( |
| DriveEntryProto* current_dir = root_.get(); |
| for (size_t i = 1; i < components.size() && current_dir; ++i) { |
| - std::string resource_id = FindDirectoryChild(current_dir, components[i]); |
| + std::string resource_id = FindDirectoryChild(current_dir->resource_id(), |
| + components[i]); |
| if (resource_id.empty()) |
| return NULL; |
| @@ -300,7 +299,7 @@ void DriveResourceMetadata::GetEntryInfoByResourceId( |
| if (entry) { |
| entry_proto.reset(new DriveEntryProto(*entry)); |
| error = DRIVE_FILE_OK; |
| - drive_file_path = GetFilePath(*entry); |
| + drive_file_path = GetFilePath(entry->resource_id()); |
| } else { |
| error = DRIVE_FILE_ERROR_NOT_FOUND; |
| } |
| @@ -343,7 +342,7 @@ void DriveResourceMetadata::ReadDirectoryByPath( |
| DriveEntryProto* entry = FindEntryByPathSync(path); |
| if (entry && entry->file_info().is_directory()) { |
| - entries = DirectoryChildrenToProtoVector(entry); |
| + entries = DirectoryChildrenToProtoVector(entry->resource_id()); |
| error = DRIVE_FILE_OK; |
| } else if (entry && !entry->file_info().is_directory()) { |
| error = DRIVE_FILE_ERROR_NOT_A_DIRECTORY; |
| @@ -396,11 +395,10 @@ void DriveResourceMetadata::RefreshEntry( |
| // Update data. |
| if (entry != root_.get()) { |
| - DriveEntryProto* old_parent = GetDirectory(entry->parent_resource_id()); |
| DriveEntryProto* new_parent = |
| GetDirectory(entry_proto.parent_resource_id()); |
| - if (!old_parent || !new_parent) { |
| + if (!new_parent) { |
| PostGetEntryInfoWithFilePathCallbackError( |
| callback, DRIVE_FILE_ERROR_NOT_FOUND); |
| return; |
| @@ -411,24 +409,25 @@ void DriveResourceMetadata::RefreshEntry( |
| // "(2)" from entries with duplicate names. If FromProto() is first |
| // called, RemoveChild() won't work correctly because of the missing |
| // suffix. |
| - DetachEntryFromDirectory(old_parent, entry); |
| + DetachEntryFromDirectory(entry->resource_id()); |
| // Note that it's safe to update the directory entry with |
| // DriveEntry::FromProto() as it won't clear children. |
| *entry = CreateEntryWithProperBaseName(entry_proto); |
| - AddEntryToDirectory(new_parent, entry); // Transfers ownership. |
| + entry->set_parent_resource_id(new_parent->resource_id()); |
|
kinaba
2013/03/15 05:48:08
Is this set_parent necessary?
At this point |entry
hashimoto
2013/03/15 06:40:23
Excellent catch.
Removed.
|
| + AddEntryToDirectory(entry); // Transfers ownership. |
| } else { |
| // root has no parent. |
| *entry = CreateEntryWithProperBaseName(entry_proto); |
| } |
| - DVLOG(1) << "RefreshEntry " << GetFilePath(*entry).value(); |
| + DVLOG(1) << "RefreshEntry " << GetFilePath(entry->resource_id()).value(); |
| // Note that base_name is not the same for new_entry and entry_proto. |
| scoped_ptr<DriveEntryProto> result_entry_proto(new DriveEntryProto(*entry)); |
| base::MessageLoopProxy::current()->PostTask( |
| FROM_HERE, |
| base::Bind(callback, |
| DRIVE_FILE_OK, |
| - GetFilePath(*entry), |
| + GetFilePath(entry->resource_id()), |
| base::Passed(&result_entry_proto))); |
| } |
| @@ -477,37 +476,33 @@ void DriveResourceMetadata::RefreshDirectory( |
| DriveEntryProto* existing_entry = |
| GetEntryByResourceId(entry_proto.resource_id()); |
| if (existing_entry) { |
| - DriveEntryProto* old_parent = |
| - GetDirectory(existing_entry->parent_resource_id()); |
| - DCHECK(old_parent && old_parent->file_info().is_directory()); |
| // See the comment in RefreshEntry() for why the existing entry is |
| // updated in this way. |
| - DetachEntryFromDirectory(old_parent, existing_entry); |
| + DetachEntryFromDirectory(existing_entry->resource_id()); |
| *existing_entry = CreateEntryWithProperBaseName(entry_proto); |
| - AddEntryToDirectory(directory, existing_entry); |
| + AddEntryToDirectory(existing_entry); |
| } else { // New entry. |
| // A new directory will have changestamp of zero, so the directory will |
| // be "fast-fetched". See crbug.com/178348 for details. |
| scoped_ptr<DriveEntryProto> new_entry( |
| new DriveEntryProto(CreateEntryWithProperBaseName(entry_proto))); |
| - AddEntryToDirectory(directory, new_entry.release()); |
| + AddEntryToDirectory(new_entry.release()); |
| } |
| } |
| // Go through the existing entries and remove deleted entries. |
| scoped_ptr<DriveEntryProtoVector> entries = |
| - DirectoryChildrenToProtoVector(directory); |
| + DirectoryChildrenToProtoVector(directory->resource_id()); |
| for (size_t i = 0; i < entries->size(); ++i) { |
| const DriveEntryProto& entry_proto = entries->at(i); |
| - if (entry_proto_map.count(entry_proto.resource_id()) == 0) { |
| - DriveEntryProto* entry = GetEntryByResourceId(entry_proto.resource_id()); |
| - RemoveDirectoryChild(directory, entry); |
| - } |
| + if (entry_proto_map.count(entry_proto.resource_id()) == 0) |
| + RemoveDirectoryChild(entry_proto.resource_id()); |
| } |
| base::MessageLoopProxy::current()->PostTask( |
| FROM_HERE, |
| - base::Bind(callback, DRIVE_FILE_OK, GetFilePath(*directory))); |
| + base::Bind(callback, DRIVE_FILE_OK, |
| + GetFilePath(directory->resource_id()))); |
| } |
| void DriveResourceMetadata::AddEntry(const DriveEntryProto& entry_proto, |
| @@ -523,10 +518,11 @@ void DriveResourceMetadata::AddEntry(const DriveEntryProto& entry_proto, |
| DriveEntryProto* added_entry = |
| new DriveEntryProto(CreateEntryWithProperBaseName(entry_proto)); |
| - AddEntryToDirectory(parent, added_entry); // Transfers ownership. |
| + AddEntryToDirectory(added_entry); // Transfers ownership. |
| base::MessageLoopProxy::current()->PostTask( |
| FROM_HERE, |
| - base::Bind(callback, DRIVE_FILE_OK, GetFilePath(*added_entry))); |
| + base::Bind(callback, DRIVE_FILE_OK, |
| + GetFilePath(added_entry->resource_id()))); |
| } |
| DriveEntryProto* DriveResourceMetadata::GetDirectory( |
| @@ -539,13 +535,13 @@ DriveEntryProto* DriveResourceMetadata::GetDirectory( |
| } |
| base::FilePath DriveResourceMetadata::GetFilePath( |
| - const DriveEntryProto& entry) { |
| + const std::string& resource_id) { |
| + DriveEntryProto* entry = GetEntryByResourceId(resource_id); |
| + DCHECK(entry); |
| base::FilePath path; |
| - DriveEntryProto* parent = entry.parent_resource_id().empty() ? NULL : |
| - GetEntryByResourceId(entry.parent_resource_id()); |
| - if (parent) |
| - path = GetFilePath(*parent); |
| - path = path.Append(entry.base_name()); |
| + if (!entry->parent_resource_id().empty()) |
| + path = GetFilePath(entry->parent_resource_id()); |
| + path = path.Append(entry->base_name()); |
| return path; |
| } |
| @@ -560,7 +556,7 @@ void DriveResourceMetadata::GetChildDirectories( |
| DriveEntryProto* directory = |
| entry && entry->file_info().is_directory() ? entry : NULL; |
| if (directory) |
| - GetDescendantDirectoryPaths(*directory, &changed_directories); |
| + GetDescendantDirectoryPaths(directory->resource_id(), &changed_directories); |
| base::MessageLoopProxy::current()->PostTask( |
| FROM_HERE, |
| @@ -568,28 +564,27 @@ void DriveResourceMetadata::GetChildDirectories( |
| } |
| void DriveResourceMetadata::GetDescendantDirectoryPaths( |
| - const DriveEntryProto& directory, |
| + const std::string& directory_resource_id, |
| std::set<base::FilePath>* child_directories) { |
| - DCHECK(directory.file_info().is_directory()); |
| - const ChildMap& children = child_maps_[directory.resource_id()]; |
| + const ChildMap& children = child_maps_[directory_resource_id]; |
| for (ChildMap::const_iterator iter = children.begin(); |
| iter != children.end(); ++iter) { |
| DriveEntryProto* entry = GetEntryByResourceId(iter->second); |
| if (entry && entry->file_info().is_directory()) { |
| - child_directories->insert(GetFilePath(*entry)); |
| - GetDescendantDirectoryPaths(*entry, child_directories); |
| + child_directories->insert(GetFilePath(entry->resource_id())); |
| + GetDescendantDirectoryPaths(entry->resource_id(), child_directories); |
| } |
| } |
| } |
| void DriveResourceMetadata::RemoveAll(const base::Closure& callback) { |
| - RemoveDirectoryChildren(root_.get()); |
| + RemoveDirectoryChildren(root_->resource_id()); |
| base::MessageLoopProxy::current()->PostTask(FROM_HERE, callback); |
| } |
| void DriveResourceMetadata::SerializeToString(std::string* serialized_proto) { |
| DriveRootDirectoryProto proto; |
| - DirectoryToProto(root_.get(), proto.mutable_drive_directory()); |
| + DirectoryToProto(root_->resource_id(), proto.mutable_drive_directory()); |
| proto.set_largest_changestamp(largest_changestamp_); |
| proto.set_version(kProtoVersion); |
| @@ -617,7 +612,14 @@ bool DriveResourceMetadata::ParseFromString( |
| proto.largest_changestamp()); |
| } |
| - ProtoToDirectory(proto.drive_directory(), root_.get()); |
| + if (proto.drive_directory().drive_entry().resource_id() != |
| + root_->resource_id()) { |
| + LOG(ERROR) << "Incompatible proto detected (incompatible root ID): " |
| + << proto.drive_directory().drive_entry().resource_id(); |
| + return false; |
| + } |
| + |
| + ProtoToDirectory(proto.drive_directory()); |
| loaded_ = true; |
| largest_changestamp_ = proto.largest_changestamp(); |
| @@ -672,12 +674,7 @@ void DriveResourceMetadata::GetEntryInfoPairByPathsAfterGetSecond( |
| callback.Run(result.Pass()); |
| } |
| -void DriveResourceMetadata::AddEntryToDirectory(DriveEntryProto* directory, |
| - DriveEntryProto* entry) { |
| - DCHECK(directory->file_info().is_directory()); |
| - DCHECK(entry->parent_resource_id().empty() || |
| - entry->parent_resource_id() == directory->resource_id()); |
| - |
| +void DriveResourceMetadata::AddEntryToDirectory(DriveEntryProto* entry) { |
| // Try to add the entry to resource map. |
| if (!AddEntryToResourceMap(entry)) { |
| LOG(WARNING) << "Duplicate resource=" << entry->resource_id() |
| @@ -696,7 +693,8 @@ void DriveResourceMetadata::AddEntryToDirectory(DriveEntryProto* directory, |
| base::FilePath full_file_name(entry->base_name()); |
| const std::string extension = full_file_name.Extension(); |
| const std::string file_name = full_file_name.RemoveExtension().value(); |
| - while (!FindDirectoryChild(directory, full_file_name.value()).empty()) { |
| + while (!FindDirectoryChild(entry->parent_resource_id(), |
| + full_file_name.value()).empty()) { |
| if (!extension.empty()) { |
| full_file_name = base::FilePath(base::StringPrintf("%s (%d)%s", |
| file_name.c_str(), |
| @@ -711,25 +709,24 @@ void DriveResourceMetadata::AddEntryToDirectory(DriveEntryProto* directory, |
| entry->set_base_name(full_file_name.value()); |
| // Setup child and parent links. |
| - child_maps_[directory->resource_id()].insert( |
| + child_maps_[entry->parent_resource_id()].insert( |
| std::make_pair(entry->base_name(), entry->resource_id())); |
| - entry->set_parent_resource_id(directory->resource_id()); |
| } |
| -void DriveResourceMetadata::RemoveDirectoryChild(DriveEntryProto* directory, |
| - DriveEntryProto* entry) { |
| - DCHECK(directory->file_info().is_directory()); |
| - DetachEntryFromDirectory(directory, entry); |
| +void DriveResourceMetadata::RemoveDirectoryChild( |
| + const std::string& child_resource_id) { |
| + DriveEntryProto* entry = GetEntryByResourceId(child_resource_id); |
| + DCHECK(entry); |
| + DetachEntryFromDirectory(child_resource_id); |
| if (entry->file_info().is_directory()) |
| - RemoveDirectoryChildren(entry); |
| + RemoveDirectoryChildren(child_resource_id); |
| delete entry; |
| } |
| std::string DriveResourceMetadata::FindDirectoryChild( |
| - DriveEntryProto* directory, |
| + const std::string& directory_resource_id, |
| const base::FilePath::StringType& file_name) { |
| - DCHECK(directory->file_info().is_directory()); |
| - const ChildMap& children = child_maps_[directory->resource_id()]; |
| + const ChildMap& children = child_maps_[directory_resource_id]; |
| DriveResourceMetadata::ChildMap::const_iterator iter = |
| children.find(file_name); |
| if (iter != children.end()) |
| @@ -738,73 +735,91 @@ std::string DriveResourceMetadata::FindDirectoryChild( |
| } |
| void DriveResourceMetadata::DetachEntryFromDirectory( |
| - DriveEntryProto* directory, |
| - DriveEntryProto* entry) { |
| - DCHECK(directory->file_info().is_directory()); |
| + const std::string& child_resource_id) { |
| + DriveEntryProto* entry = GetEntryByResourceId(child_resource_id); |
| DCHECK(entry); |
| const std::string& base_name(entry->base_name()); |
| // entry must be present in this directory. |
| - DCHECK_EQ(entry->resource_id(), FindDirectoryChild(directory, base_name)); |
| + DCHECK_EQ(entry->resource_id(), |
| + FindDirectoryChild(entry->parent_resource_id(), base_name)); |
| // Remove entry from resource map first. |
| RemoveEntryFromResourceMap(entry->resource_id()); |
| // Then delete it from tree. |
| - child_maps_[directory->resource_id()].erase(base_name); |
| + child_maps_[entry->parent_resource_id()].erase(base_name); |
| entry->set_parent_resource_id(std::string()); |
| } |
| void DriveResourceMetadata::RemoveDirectoryChildren( |
| - DriveEntryProto* directory) { |
| - DCHECK(directory->file_info().is_directory()); |
| + const std::string& directory_resource_id) { |
| DriveResourceMetadata::ChildMap* children = |
| - &child_maps_[directory->resource_id()]; |
| + &child_maps_[directory_resource_id]; |
| for (DriveResourceMetadata::ChildMap::iterator iter = children->begin(); |
| iter != children->end(); ++iter) { |
| DriveEntryProto* child = GetEntryByResourceId(iter->second); |
| DCHECK(child); |
| // Remove directories recursively. |
| if (child->file_info().is_directory()) |
| - RemoveDirectoryChildren(child); |
| + RemoveDirectoryChildren(child->resource_id()); |
| RemoveEntryFromResourceMap(iter->second); |
| delete child; |
| } |
| - child_maps_.erase(directory->resource_id()); |
| + child_maps_.erase(directory_resource_id); |
| } |
| -void DriveResourceMetadata::ProtoToDirectory(const DriveDirectoryProto& proto, |
| - DriveEntryProto* directory) { |
| +void DriveResourceMetadata::ProtoToDirectory(const DriveDirectoryProto& proto) { |
| DCHECK(proto.drive_entry().file_info().is_directory()); |
| DCHECK(!proto.drive_entry().has_file_specific_info()); |
| + DriveEntryProto* directory = |
| + GetEntryByResourceId(proto.drive_entry().resource_id()); |
| + // Allocate a new DriveEntryProto when missing. |
| + if (!directory) { |
| + // No need to care about root missing case. |
|
kinaba
2013/03/15 05:48:08
Inversely, do we need to care about non-root exist
hashimoto
2013/03/15 06:40:23
Added a DCHECK(child_maps_[proto.drive_entry().res
|
| + DCHECK(!proto.drive_entry().parent_resource_id().empty()); |
| + directory = new DriveEntryProto; |
| + } |
| + |
| + // Set entry properties. |
| *directory = CreateEntryWithProperBaseName(proto.drive_entry()); |
| + // Become a child of the parent. |
| + if (!directory->parent_resource_id().empty()) |
| + AddEntryToDirectory(directory); // Transfer ownership. |
|
kinaba
2013/03/15 05:48:08
If the "Allocate a new DriveEntryProto when missin
hashimoto
2013/03/15 06:40:23
Moved directory allocation to under the for loop f
|
| + |
| + // Add child files. |
| for (int i = 0; i < proto.child_files_size(); ++i) { |
| scoped_ptr<DriveEntryProto> file(new DriveEntryProto( |
| CreateEntryWithProperBaseName(proto.child_files(i)))); |
| - AddEntryToDirectory(directory, file.release()); |
| + DCHECK_EQ(directory->resource_id(), file->parent_resource_id()); |
| + AddEntryToDirectory(file.release()); |
| } |
| + // Add child directories recursively. |
| for (int i = 0; i < proto.child_directories_size(); ++i) { |
| - scoped_ptr<DriveEntryProto> child_dir(new DriveEntryProto); |
| - ProtoToDirectory(proto.child_directories(i), child_dir.get()); |
| - AddEntryToDirectory(directory, child_dir.release()); |
| + DCHECK_EQ(directory->resource_id(), |
| + proto.child_directories(i).drive_entry().parent_resource_id()); |
| + ProtoToDirectory(proto.child_directories(i)); |
| } |
| } |
| -void DriveResourceMetadata::DirectoryToProto(DriveEntryProto* directory, |
| - DriveDirectoryProto* proto) { |
| +void DriveResourceMetadata::DirectoryToProto( |
| + const std::string& directory_resource_id, |
| + DriveDirectoryProto* proto) { |
| + DriveEntryProto* directory = GetEntryByResourceId(directory_resource_id); |
| + DCHECK(directory); |
| *proto->mutable_drive_entry() = *directory; |
| DCHECK(proto->drive_entry().file_info().is_directory()); |
| - const ChildMap& children = child_maps_[directory->resource_id()]; |
| + const ChildMap& children = child_maps_[directory_resource_id]; |
| for (ChildMap::const_iterator iter = children.begin(); |
| iter != children.end(); ++iter) { |
| DriveEntryProto* entry = GetEntryByResourceId(iter->second); |
| DCHECK(entry); |
| if (entry->file_info().is_directory()) |
| - DirectoryToProto(entry, proto->add_child_directories()); |
| + DirectoryToProto(entry->resource_id(), proto->add_child_directories()); |
| else |
| *proto->add_child_files() = *entry; |
| } |
| @@ -812,14 +827,14 @@ void DriveResourceMetadata::DirectoryToProto(DriveEntryProto* directory, |
| scoped_ptr<DriveEntryProtoVector> |
| DriveResourceMetadata::DirectoryChildrenToProtoVector( |
| - DriveEntryProto* directory) { |
| - DCHECK(directory->file_info().is_directory()); |
| + const std::string& directory_resource_id) { |
| scoped_ptr<DriveEntryProtoVector> entries(new DriveEntryProtoVector); |
| - const ChildMap& children = child_maps_[directory->resource_id()]; |
| + const ChildMap& children = child_maps_[directory_resource_id]; |
| for (ChildMap::const_iterator iter = children.begin(); |
| iter != children.end(); ++iter) { |
| - const DriveEntryProto& proto = *GetEntryByResourceId(iter->second); |
| - entries->push_back(proto); |
| + const DriveEntryProto* child = GetEntryByResourceId(iter->second); |
| + DCHECK(child); |
| + entries->push_back(*child); |
| } |
| return entries.Pass(); |
| } |