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) { |