Chromium Code Reviews| 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 b742eea8cf08c89944a53ca2662d18aafde5fba8..006f6398ea6de1e75f2a58da15314b373cf415fd 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 | 
| @@ -113,6 +113,33 @@ void ReadDirectoryOnUIThread( | 
| task_helper->ReadDirectory(dir_id, success_callback, error_callback); | 
| } | 
| +// Gets entry ids on |directory_id|. | 
| +// | 
| +// Called on the UI thread to dispatch the request to the | 
| +// MediaTransferProtocolManager. | 
| +// | 
| +// |storage_name| specifies the name of the storage device. | 
| +// |read_only| specifies the mode of the storage device. | 
| +// |directory_id| is the directory to be read. | 
| +// |success_callback| is called when the ReadDirectoryEntryIds request succeeds. | 
| +// |error_callback| is called when the ReadDirectoryEntryIds request fails. | 
| +// |success_callback| and |error_callback| runs on the IO thread. | 
| +void ReadDirectoryEntryIdsOnUIThread( | 
| + const std::string& storage_name, | 
| + const bool read_only, | 
| + const uint32 directory_id, | 
| + const MTPDeviceTaskHelper::ReadDirectoryEntryIdsSuccessCallback& | 
| + success_callback, | 
| + const MTPDeviceTaskHelper::ErrorCallback& error_callback) { | 
| + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 
| + MTPDeviceTaskHelper* task_helper = | 
| + GetDeviceTaskHelperForStorage(storage_name, read_only); | 
| + if (!task_helper) | 
| + return; | 
| + task_helper->ReadDirectoryEntryIds(directory_id, success_callback, | 
| + error_callback); | 
| +} | 
| + | 
| // Gets the |file_path| details. | 
| // | 
| // Called on the UI thread to dispatch the request to the | 
| @@ -214,6 +241,31 @@ void CopyFileFromLocalOnUIThread( | 
| error_callback); | 
| } | 
| +// Deletes |object_id|. | 
| +// | 
| +// Called on the UI thread to dispatch the request to the | 
| +// MediaTransferProtocolManager. | 
| +// | 
| +// |storage_name| specifies the name of the storage device. | 
| +// |read_only| specifies the mode of the storage device. | 
| +// |object_id| is the object to be deleted. | 
| +// |success_callback| is called when the object is deleted successfully. | 
| +// |error_callback| is called when it fails to delete the object. | 
| +// |success_callback| and |error_callback| runs on the IO thread. | 
| +void DeleteObjectOnUIThread( | 
| + const std::string storage_name, | 
| + const bool read_only, | 
| + const uint32 object_id, | 
| + const MTPDeviceTaskHelper::DeleteObjectSuccessCallback success_callback, | 
| + const MTPDeviceTaskHelper::ErrorCallback error_callback) { | 
| + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 
| + MTPDeviceTaskHelper* task_helper = | 
| + GetDeviceTaskHelperForStorage(storage_name, read_only); | 
| + if (!task_helper) | 
| + return; | 
| + task_helper->DeleteObject(object_id, success_callback, error_callback); | 
| +} | 
| + | 
| // Closes the device storage specified by the |storage_name| and destroys the | 
| // MTPDeviceTaskHelper object associated with the device storage. | 
| // | 
| @@ -282,6 +334,8 @@ class MTPDeviceDelegateImplLinux::MTPFileNode { | 
| bool DeleteChild(uint32 file_id); | 
| + bool HasChildren() const; | 
| + | 
| uint32 file_id() const { return file_id_; } | 
| const std::string& file_name() const { return file_name_; } | 
| MTPFileNode* parent() { return parent_; } | 
| @@ -362,6 +416,7 @@ bool MTPDeviceDelegateImplLinux::MTPFileNode::DeleteChild(uint32 file_id) { | 
| for (ChildNodes::iterator it = children_.begin(); | 
| it != children_.end(); ++it) { | 
| if (it->second->file_id() == file_id) { | 
| + DCHECK(!it->second->HasChildren()); | 
| children_.erase(it); | 
| return true; | 
| } | 
| @@ -369,6 +424,11 @@ bool MTPDeviceDelegateImplLinux::MTPFileNode::DeleteChild(uint32 file_id) { | 
| return false; | 
| } | 
| +bool MTPDeviceDelegateImplLinux::MTPFileNode::HasChildren() const { | 
| + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); | 
| + return children_.size() > 0; | 
| +} | 
| + | 
| MTPDeviceDelegateImplLinux::MTPDeviceDelegateImplLinux( | 
| const std::string& device_location, | 
| const bool read_only) | 
| @@ -494,7 +554,7 @@ void MTPDeviceDelegateImplLinux::ReadBytes( | 
| closure)); | 
| } | 
| -bool MTPDeviceDelegateImplLinux::IsReadOnly() { | 
| +bool MTPDeviceDelegateImplLinux::IsReadOnly() const { | 
| return read_only_; | 
| } | 
| @@ -520,6 +580,46 @@ void MTPDeviceDelegateImplLinux::CopyFileFromLocal( | 
| error_callback)); | 
| } | 
| +void MTPDeviceDelegateImplLinux::DeleteFile( | 
| + const base::FilePath& file_path, | 
| + const DeleteFileSuccessCallback& success_callback, | 
| + const ErrorCallback& error_callback) { | 
| + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); | 
| + DCHECK(!file_path.empty()); | 
| + | 
| + const GetFileInfoSuccessCallback& success_callback_wrapper = | 
| + base::Bind(&MTPDeviceDelegateImplLinux::DeleteFileInternal, | 
| + weak_ptr_factory_.GetWeakPtr(), file_path, success_callback, | 
| + error_callback); | 
| + | 
| + const base::Closure closure = | 
| + base::Bind(&MTPDeviceDelegateImplLinux::GetFileInfoInternal, | 
| + weak_ptr_factory_.GetWeakPtr(), file_path, | 
| + success_callback_wrapper, error_callback); | 
| + EnsureInitAndRunTask(PendingTaskInfo(file_path, content::BrowserThread::IO, | 
| + FROM_HERE, closure)); | 
| +} | 
| + | 
| +void MTPDeviceDelegateImplLinux::DeleteDirectory( | 
| + const base::FilePath& file_path, | 
| + const DeleteDirectorySuccessCallback& success_callback, | 
| + const ErrorCallback& error_callback) { | 
| + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); | 
| + DCHECK(!file_path.empty()); | 
| + | 
| + const GetFileInfoSuccessCallback& success_callback_wrapper = | 
| + base::Bind(&MTPDeviceDelegateImplLinux::DeleteDirectoryInternal, | 
| + weak_ptr_factory_.GetWeakPtr(), file_path, success_callback, | 
| + error_callback); | 
| + | 
| + const base::Closure closure = | 
| + base::Bind(&MTPDeviceDelegateImplLinux::GetFileInfoInternal, | 
| + weak_ptr_factory_.GetWeakPtr(), file_path, | 
| + success_callback_wrapper, error_callback); | 
| + EnsureInitAndRunTask(PendingTaskInfo(file_path, content::BrowserThread::IO, | 
| + FROM_HERE, closure)); | 
| +} | 
| + | 
| void MTPDeviceDelegateImplLinux::CancelPendingTasksAndDeleteDelegate() { | 
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); | 
| // To cancel all the pending tasks, destroy the MTPDeviceTaskHelper object. | 
| @@ -716,6 +816,111 @@ void MTPDeviceDelegateImplLinux::CopyFileFromLocalInternal( | 
| PendingRequestDone(); | 
| } | 
| +void MTPDeviceDelegateImplLinux::DeleteFileInternal( | 
| + const base::FilePath& file_path, | 
| + const DeleteFileSuccessCallback& success_callback, | 
| + const ErrorCallback& error_callback, | 
| + const base::File::Info& file_info) { | 
| + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); | 
| + | 
| + if (file_info.is_directory) { | 
| + error_callback.Run(base::File::FILE_ERROR_NOT_A_FILE); | 
| + } else { | 
| + uint32 file_id; | 
| + if (CachedPathToId(file_path, &file_id)) | 
| + RunDeleteObjectOnUIThread(file_id, success_callback, error_callback); | 
| + else | 
| + error_callback.Run(base::File::FILE_ERROR_NOT_FOUND); | 
| + } | 
| + | 
| + PendingRequestDone(); | 
| +} | 
| + | 
| +void MTPDeviceDelegateImplLinux::DeleteDirectoryInternal( | 
| + const base::FilePath& file_path, | 
| + const DeleteDirectorySuccessCallback& success_callback, | 
| + const ErrorCallback& error_callback, | 
| + const base::File::Info& file_info) { | 
| + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); | 
| + | 
| + if (!file_info.is_directory) { | 
| + error_callback.Run(base::File::FILE_ERROR_NOT_A_DIRECTORY); | 
| + PendingRequestDone(); | 
| + return; | 
| + } | 
| + | 
| + uint32 directory_id; | 
| + if (!CachedPathToId(file_path, &directory_id)) { | 
| + error_callback.Run(base::File::FILE_ERROR_NOT_FOUND); | 
| + PendingRequestDone(); | 
| + return; | 
| + } | 
| + | 
| + // Checks the cache first. If it has children in cache, the directory cannot | 
| + // be empty. | 
| + FileIdToMTPFileNodeMap::const_iterator it = | 
| + file_id_to_node_map_.find(directory_id); | 
| + if (it != file_id_to_node_map_.end() && it->second->HasChildren()) { | 
| + error_callback.Run(base::File::FILE_ERROR_NOT_EMPTY); | 
| + PendingRequestDone(); | 
| + return; | 
| + } | 
| + | 
| + // Since the directory can contain a file even if the cache returns it as | 
| + // empty, read the directory and confirm the directory is actually empty. | 
| + const MTPDeviceTaskHelper::ReadDirectoryEntryIdsSuccessCallback | 
| + success_callback_wrapper = | 
| + base::Bind(&MTPDeviceDelegateImplLinux:: | 
| + OnDidReadDirectoryEntryIdsToDeleteDirectory, | 
| + weak_ptr_factory_.GetWeakPtr(), directory_id, | 
| + success_callback, error_callback); | 
| + const MTPDeviceTaskHelper::ErrorCallback error_callback_wrapper = | 
| + base::Bind(&MTPDeviceDelegateImplLinux::HandleDeviceFileError, | 
| + weak_ptr_factory_.GetWeakPtr(), error_callback, directory_id); | 
| + const base::Closure closure = base::Bind( | 
| + &ReadDirectoryEntryIdsOnUIThread, storage_name_, read_only_, directory_id, | 
| + success_callback_wrapper, error_callback_wrapper); | 
| + EnsureInitAndRunTask(PendingTaskInfo( | 
| 
 
Lei Zhang
2015/03/10 00:48:06
This can also just be PostTask().
 
yawano
2015/03/10 05:01:28
If we use PostTask instead of PendingTaskInfo with
 
Lei Zhang
2015/03/10 08:12:04
If you were to use PostTask(), then you would not
 
yawano
2015/03/10 08:20:36
Acknowledged.
 
 | 
| + base::FilePath(), content::BrowserThread::UI, FROM_HERE, closure)); | 
| + PendingRequestDone(); | 
| +} | 
| + | 
| +void MTPDeviceDelegateImplLinux::OnDidReadDirectoryEntryIdsToDeleteDirectory( | 
| + const uint32 directory_id, | 
| + const DeleteDirectorySuccessCallback& success_callback, | 
| + const ErrorCallback& error_callback, | 
| + const std::vector<uint32>& file_ids) { | 
| + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); | 
| + | 
| + if (file_ids.size() > 0) | 
| + error_callback.Run(base::File::FILE_ERROR_NOT_EMPTY); | 
| + else | 
| + RunDeleteObjectOnUIThread(directory_id, success_callback, error_callback); | 
| + | 
| + PendingRequestDone(); | 
| +} | 
| + | 
| +void MTPDeviceDelegateImplLinux::RunDeleteObjectOnUIThread( | 
| + const uint32 object_id, | 
| + const DeleteObjectSuccessCallback& success_callback, | 
| + const ErrorCallback& error_callback) { | 
| + const MTPDeviceTaskHelper::DeleteObjectSuccessCallback | 
| + success_callback_wrapper = base::Bind( | 
| + &MTPDeviceDelegateImplLinux::OnDidDeleteObject, | 
| + weak_ptr_factory_.GetWeakPtr(), object_id, success_callback); | 
| + | 
| + const MTPDeviceTaskHelper::ErrorCallback error_callback_wrapper = | 
| + base::Bind(&MTPDeviceDelegateImplLinux::HandleDeleteFileOrDirectoryError, | 
| + weak_ptr_factory_.GetWeakPtr(), error_callback); | 
| + | 
| + const base::Closure closure = | 
| + base::Bind(&DeleteObjectOnUIThread, storage_name_, read_only_, object_id, | 
| + success_callback_wrapper, error_callback_wrapper); | 
| + | 
| + content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE, | 
| + closure); | 
| +} | 
| + | 
| void MTPDeviceDelegateImplLinux::EnsureInitAndRunTask( | 
| const PendingTaskInfo& task_info) { | 
| DCHECK_CURRENTLY_ON(content::BrowserThread::IO); | 
| @@ -1022,20 +1227,32 @@ void MTPDeviceDelegateImplLinux::HandleCopyFileFromLocalError( | 
| PendingRequestDone(); | 
| } | 
| +void MTPDeviceDelegateImplLinux::OnDidDeleteObject( | 
| + const uint32 object_id, | 
| + const DeleteObjectSuccessCallback success_callback) { | 
| + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); | 
| + | 
| + EvictCachedPathToId(object_id); | 
| + success_callback.Run(); | 
| + PendingRequestDone(); | 
| +} | 
| + | 
| +void MTPDeviceDelegateImplLinux::HandleDeleteFileOrDirectoryError( | 
| + const ErrorCallback& error_callback, | 
| + base::File::Error error) { | 
| + DCHECK_CURRENTLY_ON(content::BrowserThread::IO); | 
| + | 
| + error_callback.Run(error); | 
| + PendingRequestDone(); | 
| +} | 
| + | 
| 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); | 
| - } | 
| - } | 
| + EvictCachedPathToId(file_id); | 
| error_callback.Run(error); | 
| PendingRequestDone(); | 
| } | 
| @@ -1109,6 +1326,18 @@ bool MTPDeviceDelegateImplLinux::CachedPathToId(const base::FilePath& path, | 
| return true; | 
| } | 
| +void MTPDeviceDelegateImplLinux::EvictCachedPathToId(const uint32 id) { | 
| + FileIdToMTPFileNodeMap::iterator it = file_id_to_node_map_.find(id); | 
| + if (it != file_id_to_node_map_.end()) { | 
| + DCHECK(!it->second->HasChildren()); | 
| + MTPFileNode* parent = it->second->parent(); | 
| + if (parent) { | 
| + const bool ret = parent->DeleteChild(id); | 
| + DCHECK(ret); | 
| + } | 
| + } | 
| +} | 
| + | 
| void CreateMTPDeviceAsyncDelegate( | 
| const std::string& device_location, | 
| const bool read_only, |