| Index: chrome/browser/media_galleries/linux/mtp_device_delegate_impl_linux.cc
|
| diff --git a/chrome/browser/media_galleries/linux/mtp_device_delegate_impl_linux.cc b/chrome/browser/media_galleries/linux/mtp_device_delegate_impl_linux.cc
|
| index 86370e7ecd49c4b5896052ec3eaf4af049f6ffd8..f816c9e2cc57821a5b7a590d6ac6b3b8dbeb4baf 100644
|
| --- a/chrome/browser/media_galleries/linux/mtp_device_delegate_impl_linux.cc
|
| +++ b/chrome/browser/media_galleries/linux/mtp_device_delegate_impl_linux.cc
|
| @@ -5,16 +5,19 @@
|
| #include "chrome/browser/media_galleries/linux/mtp_device_delegate_impl_linux.h"
|
|
|
| #include <algorithm>
|
| +#include <set>
|
| +#include <vector>
|
|
|
| #include "base/bind.h"
|
| -#include "base/files/file_path.h"
|
| #include "base/numerics/safe_conversions.h"
|
| +#include "base/strings/string_number_conversions.h"
|
| +#include "base/strings/string_split.h"
|
| #include "base/strings/string_util.h"
|
| #include "chrome/browser/media_galleries/linux/mtp_device_task_helper.h"
|
| #include "chrome/browser/media_galleries/linux/mtp_device_task_helper_map_service.h"
|
| #include "chrome/browser/media_galleries/linux/snapshot_file_details.h"
|
| -#include "content/public/browser/browser_thread.h"
|
| #include "net/base/io_buffer.h"
|
| +#include "third_party/cros_system_api/dbus/service_constants.h"
|
|
|
| namespace {
|
|
|
| @@ -80,7 +83,7 @@ void OpenStorageOnUIThread(
|
| task_helper->OpenStorage(storage_name, reply_callback);
|
| }
|
|
|
| -// Enumerates the |root| directory file entries.
|
| +// Enumerates the |dir_id| directory file entries.
|
| //
|
| // Called on the UI thread to dispatch the request to the
|
| // MediaTransferProtocolManager.
|
| @@ -91,7 +94,7 @@ void OpenStorageOnUIThread(
|
| // |success_callback| and |error_callback| runs on the IO thread.
|
| void ReadDirectoryOnUIThread(
|
| const std::string& storage_name,
|
| - const std::string& root,
|
| + uint32 dir_id,
|
| const base::Callback<
|
| void(const fileapi::AsyncFileUtil::EntryList&)>& success_callback,
|
| const base::Callback<void(base::File::Error)>& error_callback) {
|
| @@ -100,7 +103,7 @@ void ReadDirectoryOnUIThread(
|
| GetDeviceTaskHelperForStorage(storage_name);
|
| if (!task_helper)
|
| return;
|
| - task_helper->ReadDirectoryByPath(root, success_callback, error_callback);
|
| + task_helper->ReadDirectoryById(dir_id, success_callback, error_callback);
|
| }
|
|
|
| // Gets the |file_path| details.
|
| @@ -114,15 +117,15 @@ void ReadDirectoryOnUIThread(
|
| // |success_callback| and |error_callback| runs on the IO thread.
|
| void GetFileInfoOnUIThread(
|
| const std::string& storage_name,
|
| - const std::string& file_path,
|
| - const base::Callback<void(const base::File::Info&)>& success_callback,
|
| - const base::Callback<void(base::File::Error)>& error_callback) {
|
| + uint32 file_id,
|
| + const MTPDeviceAsyncDelegate::GetFileInfoSuccessCallback& success_callback,
|
| + const MTPDeviceAsyncDelegate::ErrorCallback& error_callback) {
|
| DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
|
| MTPDeviceTaskHelper* task_helper =
|
| GetDeviceTaskHelperForStorage(storage_name);
|
| if (!task_helper)
|
| return;
|
| - task_helper->GetFileInfoByPath(file_path, success_callback, error_callback);
|
| + task_helper->GetFileInfoById(file_id, success_callback, error_callback);
|
| }
|
|
|
| // Copies the contents of |device_file_path| to |snapshot_file_path|.
|
| @@ -188,20 +191,128 @@ void CloseStorageAndDestroyTaskHelperOnUIThread(
|
| } // namespace
|
|
|
| MTPDeviceDelegateImplLinux::PendingTaskInfo::PendingTaskInfo(
|
| + const base::FilePath& path,
|
| + content::BrowserThread::ID thread_id,
|
| const tracked_objects::Location& location,
|
| const base::Closure& task)
|
| - : location(location),
|
| + : path(path),
|
| + thread_id(thread_id),
|
| + location(location),
|
| task(task) {
|
| }
|
|
|
| MTPDeviceDelegateImplLinux::PendingTaskInfo::~PendingTaskInfo() {
|
| }
|
|
|
| +// Represents a file on the MTP device.
|
| +// Lives on the IO thread.
|
| +class MTPDeviceDelegateImplLinux::MTPFileNode {
|
| + public:
|
| + MTPFileNode(uint32 file_id,
|
| + MTPFileNode* parent,
|
| + FileIdToMTPFileNodeMap* file_id_to_node_map);
|
| + ~MTPFileNode();
|
| +
|
| + const MTPFileNode* GetChild(const std::string& name) const;
|
| +
|
| + void EnsureChildExists(const std::string& name, uint32 id);
|
| +
|
| + // Clears all the children, except those in |children_to_keep|.
|
| + void ClearNonexistentChildren(
|
| + const std::set<std::string>& children_to_keep);
|
| +
|
| + bool DeleteChild(uint32 file_id);
|
| +
|
| + uint32 file_id() const { return file_id_; }
|
| + MTPFileNode* parent() { return parent_; }
|
| +
|
| + private:
|
| + // Container for holding a node's children.
|
| + typedef base::ScopedPtrHashMap<std::string, MTPFileNode> ChildNodes;
|
| +
|
| + const uint32 file_id_;
|
| + ChildNodes children_;
|
| + MTPFileNode* const parent_;
|
| + FileIdToMTPFileNodeMap* file_id_to_node_map_;
|
| +
|
| + DISALLOW_COPY_AND_ASSIGN(MTPFileNode);
|
| +};
|
| +
|
| +MTPDeviceDelegateImplLinux::MTPFileNode::MTPFileNode(
|
| + uint32 file_id,
|
| + MTPFileNode* parent,
|
| + FileIdToMTPFileNodeMap* file_id_to_node_map)
|
| + : file_id_(file_id),
|
| + parent_(parent),
|
| + file_id_to_node_map_(file_id_to_node_map) {
|
| + DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
| + DCHECK(file_id_to_node_map_);
|
| + DCHECK(!ContainsKey(*file_id_to_node_map_, file_id_));
|
| + (*file_id_to_node_map_)[file_id_] = this;
|
| +}
|
| +
|
| +MTPDeviceDelegateImplLinux::MTPFileNode::~MTPFileNode() {
|
| + DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
| + size_t erased = file_id_to_node_map_->erase(file_id_);
|
| + DCHECK_EQ(1U, erased);
|
| +}
|
| +
|
| +const MTPDeviceDelegateImplLinux::MTPFileNode*
|
| +MTPDeviceDelegateImplLinux::MTPFileNode::GetChild(
|
| + const std::string& name) const {
|
| + DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
| + return children_.get(name);
|
| +}
|
| +
|
| +void MTPDeviceDelegateImplLinux::MTPFileNode::EnsureChildExists(
|
| + const std::string& name,
|
| + uint32 id) {
|
| + DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
| + const MTPFileNode* child = GetChild(name);
|
| + if (child && child->file_id() == id)
|
| + return;
|
| +
|
| + children_.set(
|
| + name,
|
| + make_scoped_ptr(new MTPFileNode(id, this, file_id_to_node_map_)));
|
| +}
|
| +
|
| +void MTPDeviceDelegateImplLinux::MTPFileNode::ClearNonexistentChildren(
|
| + const std::set<std::string>& children_to_keep) {
|
| + DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
| + std::set<std::string> children_to_erase;
|
| + for (ChildNodes::const_iterator it = children_.begin();
|
| + it != children_.end(); ++it) {
|
| + if (ContainsKey(children_to_keep, it->first))
|
| + continue;
|
| + children_to_erase.insert(it->first);
|
| + }
|
| + for (std::set<std::string>::iterator it = children_to_erase.begin();
|
| + it != children_to_erase.end(); ++it) {
|
| + children_.take_and_erase(*it);
|
| + }
|
| +}
|
| +
|
| +bool MTPDeviceDelegateImplLinux::MTPFileNode::DeleteChild(uint32 file_id) {
|
| + DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
| + for (ChildNodes::iterator it = children_.begin();
|
| + it != children_.end(); ++it) {
|
| + if (it->second->file_id() == file_id) {
|
| + children_.erase(it);
|
| + return true;
|
| + }
|
| + }
|
| + return false;
|
| +}
|
| +
|
| MTPDeviceDelegateImplLinux::MTPDeviceDelegateImplLinux(
|
| const std::string& device_location)
|
| : init_state_(UNINITIALIZED),
|
| task_in_progress_(false),
|
| device_path_(device_location),
|
| + root_node_(new MTPFileNode(mtpd::kRootFileId,
|
| + NULL,
|
| + &file_id_to_node_map_)),
|
| weak_ptr_factory_(this) {
|
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
| DCHECK(!device_path_.empty());
|
| @@ -219,17 +330,16 @@ void MTPDeviceDelegateImplLinux::GetFileInfo(
|
| const ErrorCallback& error_callback) {
|
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
| DCHECK(!file_path.empty());
|
| - base::Closure call_closure =
|
| - base::Bind(&GetFileInfoOnUIThread,
|
| - storage_name_,
|
| - GetDeviceRelativePath(device_path_, file_path),
|
| - base::Bind(&MTPDeviceDelegateImplLinux::OnDidGetFileInfo,
|
| - weak_ptr_factory_.GetWeakPtr(),
|
| - success_callback),
|
| - base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
|
| - weak_ptr_factory_.GetWeakPtr(),
|
| - error_callback));
|
| - EnsureInitAndRunTask(PendingTaskInfo(FROM_HERE, call_closure));
|
| + base::Closure closure =
|
| + base::Bind(&MTPDeviceDelegateImplLinux::GetFileInfoInternal,
|
| + weak_ptr_factory_.GetWeakPtr(),
|
| + file_path,
|
| + success_callback,
|
| + error_callback);
|
| + EnsureInitAndRunTask(PendingTaskInfo(file_path,
|
| + content::BrowserThread::IO,
|
| + FROM_HERE,
|
| + closure));
|
| }
|
|
|
| void MTPDeviceDelegateImplLinux::ReadDirectory(
|
| @@ -238,23 +348,16 @@ void MTPDeviceDelegateImplLinux::ReadDirectory(
|
| const ErrorCallback& error_callback) {
|
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
| DCHECK(!root.empty());
|
| - std::string device_file_relative_path = GetDeviceRelativePath(device_path_,
|
| - root);
|
| - base::Closure call_closure =
|
| - base::Bind(
|
| - &GetFileInfoOnUIThread,
|
| - storage_name_,
|
| - device_file_relative_path,
|
| - base::Bind(
|
| - &MTPDeviceDelegateImplLinux::OnDidGetFileInfoToReadDirectory,
|
| - weak_ptr_factory_.GetWeakPtr(),
|
| - device_file_relative_path,
|
| - success_callback,
|
| - error_callback),
|
| - base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
|
| - weak_ptr_factory_.GetWeakPtr(),
|
| - error_callback));
|
| - EnsureInitAndRunTask(PendingTaskInfo(FROM_HERE, call_closure));
|
| + base::Closure closure =
|
| + base::Bind(&MTPDeviceDelegateImplLinux::ReadDirectoryInternal,
|
| + weak_ptr_factory_.GetWeakPtr(),
|
| + root,
|
| + success_callback,
|
| + error_callback);
|
| + EnsureInitAndRunTask(PendingTaskInfo(root,
|
| + content::BrowserThread::IO,
|
| + FROM_HERE,
|
| + closure));
|
| }
|
|
|
| void MTPDeviceDelegateImplLinux::CreateSnapshotFile(
|
| @@ -265,26 +368,17 @@ void MTPDeviceDelegateImplLinux::CreateSnapshotFile(
|
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
| DCHECK(!device_file_path.empty());
|
| DCHECK(!local_path.empty());
|
| - std::string device_file_relative_path =
|
| - GetDeviceRelativePath(device_path_, device_file_path);
|
| - scoped_ptr<SnapshotRequestInfo> request_info(
|
| - new SnapshotRequestInfo(device_file_relative_path,
|
| - local_path,
|
| - success_callback,
|
| - error_callback));
|
| - base::Closure call_closure =
|
| - base::Bind(
|
| - &GetFileInfoOnUIThread,
|
| - storage_name_,
|
| - device_file_relative_path,
|
| - base::Bind(
|
| - &MTPDeviceDelegateImplLinux::OnDidGetFileInfoToCreateSnapshotFile,
|
| - weak_ptr_factory_.GetWeakPtr(),
|
| - base::Passed(&request_info)),
|
| - base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
|
| - weak_ptr_factory_.GetWeakPtr(),
|
| - error_callback));
|
| - EnsureInitAndRunTask(PendingTaskInfo(FROM_HERE, call_closure));
|
| + base::Closure closure =
|
| + base::Bind(&MTPDeviceDelegateImplLinux::CreateSnapshotFileInternal,
|
| + weak_ptr_factory_.GetWeakPtr(),
|
| + device_file_path,
|
| + local_path,
|
| + success_callback,
|
| + error_callback);
|
| + EnsureInitAndRunTask(PendingTaskInfo(device_file_path,
|
| + content::BrowserThread::IO,
|
| + FROM_HERE,
|
| + closure));
|
| }
|
|
|
| bool MTPDeviceDelegateImplLinux::IsStreaming() {
|
| @@ -298,20 +392,19 @@ void MTPDeviceDelegateImplLinux::ReadBytes(
|
| const ErrorCallback& error_callback) {
|
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
| DCHECK(!device_file_path.empty());
|
| - std::string device_file_relative_path =
|
| - GetDeviceRelativePath(device_path_, device_file_path);
|
| -
|
| - ReadBytesRequest request(
|
| - device_file_relative_path, buf, offset, buf_len,
|
| - base::Bind(&MTPDeviceDelegateImplLinux::OnDidReadBytes,
|
| - weak_ptr_factory_.GetWeakPtr(), success_callback),
|
| - base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
|
| - weak_ptr_factory_.GetWeakPtr(), error_callback));
|
| -
|
| - base::Closure call_closure =
|
| - base::Bind(base::Bind(&ReadBytesOnUIThread, storage_name_, request));
|
| -
|
| - EnsureInitAndRunTask(PendingTaskInfo(FROM_HERE, call_closure));
|
| + base::Closure closure =
|
| + base::Bind(&MTPDeviceDelegateImplLinux::ReadBytesInternal,
|
| + weak_ptr_factory_.GetWeakPtr(),
|
| + device_file_path,
|
| + make_scoped_refptr(buf),
|
| + offset,
|
| + buf_len,
|
| + success_callback,
|
| + error_callback);
|
| + EnsureInitAndRunTask(PendingTaskInfo(device_file_path,
|
| + content::BrowserThread::IO,
|
| + FROM_HERE,
|
| + closure));
|
| }
|
|
|
| void MTPDeviceDelegateImplLinux::CancelPendingTasksAndDeleteDelegate() {
|
| @@ -324,17 +417,159 @@ void MTPDeviceDelegateImplLinux::CancelPendingTasksAndDeleteDelegate() {
|
| delete this;
|
| }
|
|
|
| +void MTPDeviceDelegateImplLinux::GetFileInfoInternal(
|
| + const base::FilePath& file_path,
|
| + const GetFileInfoSuccessCallback& success_callback,
|
| + const ErrorCallback& error_callback) {
|
| + DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
| +
|
| + uint32 file_id;
|
| + if (CachedPathToId(file_path, &file_id)) {
|
| + GetFileInfoSuccessCallback success_callback_wrapper =
|
| + base::Bind(&MTPDeviceDelegateImplLinux::OnDidGetFileInfo,
|
| + weak_ptr_factory_.GetWeakPtr(),
|
| + success_callback);
|
| + ErrorCallback error_callback_wrapper =
|
| + base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
|
| + weak_ptr_factory_.GetWeakPtr(),
|
| + error_callback,
|
| + file_id);
|
| +
|
| +
|
| + base::Closure closure = base::Bind(&GetFileInfoOnUIThread,
|
| + storage_name_,
|
| + file_id,
|
| + success_callback_wrapper,
|
| + error_callback_wrapper);
|
| + EnsureInitAndRunTask(PendingTaskInfo(base::FilePath(),
|
| + content::BrowserThread::UI,
|
| + FROM_HERE,
|
| + closure));
|
| + } else {
|
| + error_callback.Run(base::File::FILE_ERROR_NOT_FOUND);
|
| + }
|
| + PendingRequestDone();
|
| +}
|
| +
|
| +void MTPDeviceDelegateImplLinux::ReadDirectoryInternal(
|
| + const base::FilePath& root,
|
| + const ReadDirectorySuccessCallback& success_callback,
|
| + const ErrorCallback& error_callback) {
|
| + DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
| +
|
| + uint32 dir_id;
|
| + if (CachedPathToId(root, &dir_id)) {
|
| + GetFileInfoSuccessCallback success_callback_wrapper =
|
| + base::Bind(&MTPDeviceDelegateImplLinux::OnDidGetFileInfoToReadDirectory,
|
| + weak_ptr_factory_.GetWeakPtr(),
|
| + dir_id,
|
| + success_callback,
|
| + error_callback);
|
| + ErrorCallback error_callback_wrapper =
|
| + base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
|
| + weak_ptr_factory_.GetWeakPtr(),
|
| + error_callback,
|
| + dir_id);
|
| + base::Closure closure = base::Bind(&GetFileInfoOnUIThread,
|
| + storage_name_,
|
| + dir_id,
|
| + success_callback_wrapper,
|
| + error_callback_wrapper);
|
| + EnsureInitAndRunTask(PendingTaskInfo(base::FilePath(),
|
| + content::BrowserThread::UI,
|
| + FROM_HERE,
|
| + closure));
|
| + } else {
|
| + error_callback.Run(base::File::FILE_ERROR_NOT_FOUND);
|
| + }
|
| + PendingRequestDone();
|
| +}
|
| +
|
| +void MTPDeviceDelegateImplLinux::CreateSnapshotFileInternal(
|
| + const base::FilePath& device_file_path,
|
| + const base::FilePath& local_path,
|
| + const CreateSnapshotFileSuccessCallback& success_callback,
|
| + const ErrorCallback& error_callback) {
|
| + DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
| +
|
| + uint32 file_id;
|
| + if (CachedPathToId(device_file_path, &file_id)) {
|
| + scoped_ptr<SnapshotRequestInfo> request_info(
|
| + new SnapshotRequestInfo(file_id,
|
| + local_path,
|
| + success_callback,
|
| + error_callback));
|
| + GetFileInfoSuccessCallback success_callback_wrapper =
|
| + base::Bind(
|
| + &MTPDeviceDelegateImplLinux::OnDidGetFileInfoToCreateSnapshotFile,
|
| + weak_ptr_factory_.GetWeakPtr(),
|
| + base::Passed(&request_info));
|
| + ErrorCallback error_callback_wrapper =
|
| + base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
|
| + weak_ptr_factory_.GetWeakPtr(),
|
| + error_callback,
|
| + file_id);
|
| + base::Closure closure = base::Bind(&GetFileInfoOnUIThread,
|
| + storage_name_,
|
| + file_id,
|
| + success_callback_wrapper,
|
| + error_callback_wrapper);
|
| + EnsureInitAndRunTask(PendingTaskInfo(base::FilePath(),
|
| + content::BrowserThread::UI,
|
| + FROM_HERE,
|
| + closure));
|
| + } else {
|
| + error_callback.Run(base::File::FILE_ERROR_NOT_FOUND);
|
| + }
|
| + PendingRequestDone();
|
| +}
|
| +
|
| +void MTPDeviceDelegateImplLinux::ReadBytesInternal(
|
| + const base::FilePath& device_file_path,
|
| + net::IOBuffer* buf, int64 offset, int buf_len,
|
| + const ReadBytesSuccessCallback& success_callback,
|
| + const ErrorCallback& error_callback) {
|
| + DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
| +
|
| + uint32 file_id;
|
| + if (CachedPathToId(device_file_path, &file_id)) {
|
| + ReadBytesRequest request(
|
| + file_id, buf, offset, buf_len,
|
| + base::Bind(&MTPDeviceDelegateImplLinux::OnDidReadBytes,
|
| + weak_ptr_factory_.GetWeakPtr(),
|
| + success_callback),
|
| + base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
|
| + weak_ptr_factory_.GetWeakPtr(),
|
| + error_callback,
|
| + file_id));
|
| +
|
| + base::Closure closure =
|
| + base::Bind(base::Bind(&ReadBytesOnUIThread, storage_name_, request));
|
| + EnsureInitAndRunTask(PendingTaskInfo(base::FilePath(),
|
| + content::BrowserThread::UI,
|
| + FROM_HERE,
|
| + closure));
|
| + } else {
|
| + error_callback.Run(base::File::FILE_ERROR_NOT_FOUND);
|
| + }
|
| + PendingRequestDone();
|
| +}
|
| +
|
| void MTPDeviceDelegateImplLinux::EnsureInitAndRunTask(
|
| const PendingTaskInfo& task_info) {
|
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
| if ((init_state_ == INITIALIZED) && !task_in_progress_) {
|
| - task_in_progress_ = true;
|
| - content::BrowserThread::PostTask(content::BrowserThread::UI,
|
| - task_info.location,
|
| - task_info.task);
|
| + RunTask(task_info);
|
| return;
|
| }
|
| - pending_tasks_.push(task_info);
|
| +
|
| + // Only *Internal functions have empty paths. Since they are the continuation
|
| + // of the current running task, they get to cut in line.
|
| + if (task_info.path.empty())
|
| + pending_tasks_.push_front(task_info);
|
| + else
|
| + pending_tasks_.push_back(task_info);
|
| +
|
| if (init_state_ == UNINITIALIZED) {
|
| init_state_ = PENDING_INIT;
|
| task_in_progress_ = true;
|
| @@ -348,6 +583,29 @@ void MTPDeviceDelegateImplLinux::EnsureInitAndRunTask(
|
| }
|
| }
|
|
|
| +void MTPDeviceDelegateImplLinux::RunTask(const PendingTaskInfo& task_info) {
|
| + DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
| + DCHECK_EQ(INITIALIZED, init_state_);
|
| + DCHECK(!task_in_progress_);
|
| + task_in_progress_ = true;
|
| +
|
| + bool need_to_check_cache = !task_info.path.empty();
|
| + if (need_to_check_cache) {
|
| + base::FilePath uncached_path =
|
| + NextUncachedPathComponent(task_info.path, task_info.cached_path);
|
| + if (!uncached_path.empty()) {
|
| + // Save the current task and do a cache lookup first.
|
| + pending_tasks_.push_front(task_info);
|
| + FillFileCache(uncached_path);
|
| + return;
|
| + }
|
| + }
|
| +
|
| + content::BrowserThread::PostTask(task_info.thread_id,
|
| + task_info.location,
|
| + task_info.task);
|
| +}
|
| +
|
| void MTPDeviceDelegateImplLinux::WriteDataIntoSnapshotFile(
|
| const base::File::Info& file_info) {
|
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
| @@ -355,7 +613,7 @@ void MTPDeviceDelegateImplLinux::WriteDataIntoSnapshotFile(
|
| DCHECK_GT(file_info.size, 0);
|
| DCHECK(task_in_progress_);
|
| SnapshotRequestInfo request_info(
|
| - current_snapshot_request_info_->device_file_path,
|
| + current_snapshot_request_info_->file_id,
|
| current_snapshot_request_info_->snapshot_file_path,
|
| base::Bind(
|
| &MTPDeviceDelegateImplLinux::OnDidWriteDataIntoSnapshotFile,
|
| @@ -385,12 +643,9 @@ void MTPDeviceDelegateImplLinux::ProcessNextPendingRequest() {
|
| if (pending_tasks_.empty())
|
| return;
|
|
|
| - task_in_progress_ = true;
|
| - const PendingTaskInfo& task_info = pending_tasks_.front();
|
| - content::BrowserThread::PostTask(content::BrowserThread::UI,
|
| - task_info.location,
|
| - task_info.task);
|
| - pending_tasks_.pop();
|
| + PendingTaskInfo task_info = pending_tasks_.front();
|
| + pending_tasks_.pop_front();
|
| + RunTask(task_info);
|
| }
|
|
|
| void MTPDeviceDelegateImplLinux::OnInitCompleted(bool succeeded) {
|
| @@ -408,7 +663,7 @@ void MTPDeviceDelegateImplLinux::OnDidGetFileInfo(
|
| }
|
|
|
| void MTPDeviceDelegateImplLinux::OnDidGetFileInfoToReadDirectory(
|
| - const std::string& root,
|
| + uint32 dir_id,
|
| const ReadDirectorySuccessCallback& success_callback,
|
| const ErrorCallback& error_callback,
|
| const base::File::Info& file_info) {
|
| @@ -416,19 +671,22 @@ void MTPDeviceDelegateImplLinux::OnDidGetFileInfoToReadDirectory(
|
| DCHECK(task_in_progress_);
|
| if (!file_info.is_directory) {
|
| return HandleDeviceFileError(error_callback,
|
| + dir_id,
|
| base::File::FILE_ERROR_NOT_A_DIRECTORY);
|
| }
|
|
|
| base::Closure task_closure =
|
| base::Bind(&ReadDirectoryOnUIThread,
|
| storage_name_,
|
| - root,
|
| + dir_id,
|
| base::Bind(&MTPDeviceDelegateImplLinux::OnDidReadDirectory,
|
| weak_ptr_factory_.GetWeakPtr(),
|
| + dir_id,
|
| success_callback),
|
| base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError,
|
| weak_ptr_factory_.GetWeakPtr(),
|
| - error_callback));
|
| + error_callback,
|
| + dir_id));
|
| content::BrowserThread::PostTask(content::BrowserThread::UI,
|
| FROM_HERE,
|
| task_closure);
|
| @@ -448,7 +706,9 @@ void MTPDeviceDelegateImplLinux::OnDidGetFileInfoToCreateSnapshotFile(
|
| error = base::File::FILE_ERROR_FAILED;
|
|
|
| if (error != base::File::FILE_OK)
|
| - return HandleDeviceFileError(snapshot_request_info->error_callback, error);
|
| + return HandleDeviceFileError(snapshot_request_info->error_callback,
|
| + snapshot_request_info->file_id,
|
| + error);
|
|
|
| base::File::Info snapshot_file_info(file_info);
|
| // Modify the last modified time to null. This prevents the time stamp
|
| @@ -465,10 +725,38 @@ void MTPDeviceDelegateImplLinux::OnDidGetFileInfoToCreateSnapshotFile(
|
| }
|
|
|
| void MTPDeviceDelegateImplLinux::OnDidReadDirectory(
|
| + uint32 dir_id,
|
| const ReadDirectorySuccessCallback& success_callback,
|
| const fileapi::AsyncFileUtil::EntryList& file_list) {
|
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
| - success_callback.Run(file_list, false /*no more entries*/);
|
| +
|
| + FileIdToMTPFileNodeMap::iterator it = file_id_to_node_map_.find(dir_id);
|
| + DCHECK(it != file_id_to_node_map_.end());
|
| + MTPFileNode* dir_node = it->second;
|
| + std::set<std::string> children_to_keep;
|
| +
|
| + fileapi::AsyncFileUtil::EntryList normalized_file_list;
|
| + for (size_t i = 0; i < file_list.size(); ++i) {
|
| + normalized_file_list.push_back(file_list[i]);
|
| + fileapi::DirectoryEntry& entry = normalized_file_list.back();
|
| +
|
| + // |entry.name| has the file id encoded in it. Decode here.
|
| + size_t separator_idx = entry.name.find_last_of(',');
|
| + DCHECK_NE(std::string::npos, separator_idx);
|
| + std::string file_id_str = entry.name.substr(separator_idx);
|
| + file_id_str = file_id_str.substr(1); // Get rid of the comma.
|
| + uint32 file_id = 0;
|
| + bool ret = base::StringToUint(file_id_str, &file_id);
|
| + DCHECK(ret);
|
| + entry.name = entry.name.substr(0, separator_idx);
|
| +
|
| + // Refresh the in memory tree.
|
| + dir_node->EnsureChildExists(entry.name, file_id);
|
| + children_to_keep.insert(entry.name);
|
| + }
|
| + dir_node->ClearNonexistentChildren(children_to_keep);
|
| +
|
| + success_callback.Run(normalized_file_list, false /*no more entries*/);
|
| PendingRequestDone();
|
| }
|
|
|
| @@ -500,14 +788,111 @@ void MTPDeviceDelegateImplLinux::OnDidReadBytes(
|
| PendingRequestDone();
|
| }
|
|
|
| +void MTPDeviceDelegateImplLinux::OnDidFillFileCache(
|
| + const base::FilePath& path,
|
| + const fileapi::AsyncFileUtil::EntryList& /* file_list */,
|
| + bool /* has_more */) {
|
| + DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
| + DCHECK(path.IsParent(pending_tasks_.front().path));
|
| + pending_tasks_.front().cached_path = path;
|
| +}
|
| +
|
| +void MTPDeviceDelegateImplLinux::OnFillFileCacheFailed(
|
| + base::File::Error /* error */) {
|
| + DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
| + // When filling the cache fails for the task at the front of the queue, clear
|
| + // the path of the task so it will not try to do any more caching. Instead,
|
| + // the task will just run and fail the CachedPathToId() lookup.
|
| + pending_tasks_.front().path.clear();
|
| +}
|
| +
|
| void MTPDeviceDelegateImplLinux::HandleDeviceFileError(
|
| const ErrorCallback& error_callback,
|
| + uint32 file_id,
|
| base::File::Error error) {
|
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
| +
|
| + FileIdToMTPFileNodeMap::iterator it = file_id_to_node_map_.find(file_id);
|
| + if (it != file_id_to_node_map_.end()) {
|
| + MTPFileNode* parent = it->second->parent();
|
| + if (parent) {
|
| + bool ret = parent->DeleteChild(file_id);
|
| + DCHECK(ret);
|
| + }
|
| + }
|
| error_callback.Run(error);
|
| PendingRequestDone();
|
| }
|
|
|
| +base::FilePath MTPDeviceDelegateImplLinux::NextUncachedPathComponent(
|
| + const base::FilePath& path,
|
| + const base::FilePath& cached_path) const {
|
| + DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
| + DCHECK(cached_path.empty() || cached_path.IsParent(path));
|
| +
|
| + base::FilePath uncached_path;
|
| + std::string device_relpath = GetDeviceRelativePath(device_path_, path);
|
| + if (!device_relpath.empty() && device_relpath != kRootPath) {
|
| + uncached_path = device_path_;
|
| + std::vector<std::string> device_relpath_components;
|
| + base::SplitString(device_relpath, '/', &device_relpath_components);
|
| + DCHECK(!device_relpath_components.empty());
|
| + bool all_components_cached = true;
|
| + const MTPFileNode* current_node = root_node_.get();
|
| + for (size_t i = 0; i < device_relpath_components.size(); ++i) {
|
| + current_node = current_node->GetChild(device_relpath_components[i]);
|
| + if (!current_node) {
|
| + // With a cache miss, check if it is a genuine failure. If so, pretend
|
| + // the entire |path| is cached, so there is no further attempt to do
|
| + // more caching. The actual operation will then fail.
|
| + all_components_cached =
|
| + !cached_path.empty() && (uncached_path == cached_path);
|
| + break;
|
| + }
|
| + uncached_path = uncached_path.Append(device_relpath_components[i]);
|
| + }
|
| + if (all_components_cached)
|
| + uncached_path.clear();
|
| + }
|
| + return uncached_path;
|
| +}
|
| +
|
| +void MTPDeviceDelegateImplLinux::FillFileCache(
|
| + const base::FilePath& uncached_path) {
|
| + DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
|
| + DCHECK(task_in_progress_);
|
| +
|
| + ReadDirectorySuccessCallback success_callback =
|
| + base::Bind(&MTPDeviceDelegateImplLinux::OnDidFillFileCache,
|
| + weak_ptr_factory_.GetWeakPtr(),
|
| + uncached_path);
|
| + ErrorCallback error_callback =
|
| + base::Bind(&MTPDeviceDelegateImplLinux::OnFillFileCacheFailed,
|
| + weak_ptr_factory_.GetWeakPtr());
|
| + ReadDirectoryInternal(uncached_path, success_callback, error_callback);
|
| +}
|
| +
|
| +
|
| +bool MTPDeviceDelegateImplLinux::CachedPathToId(const base::FilePath& path,
|
| + uint32* id) const {
|
| + DCHECK(id);
|
| +
|
| + std::string device_relpath = GetDeviceRelativePath(device_path_, path);
|
| + if (device_relpath.empty())
|
| + return false;
|
| + std::vector<std::string> device_relpath_components;
|
| + if (device_relpath != kRootPath)
|
| + base::SplitString(device_relpath, '/', &device_relpath_components);
|
| + const MTPFileNode* current_node = root_node_.get();
|
| + for (size_t i = 0; i < device_relpath_components.size(); ++i) {
|
| + current_node = current_node->GetChild(device_relpath_components[i]);
|
| + if (!current_node)
|
| + return false;
|
| + }
|
| + *id = current_node->file_id();
|
| + return true;
|
| +}
|
| +
|
| void CreateMTPDeviceAsyncDelegate(
|
| const std::string& device_location,
|
| const CreateMTPDeviceAsyncDelegateCallback& callback) {
|
|
|