Home | History | Annotate | Download | only in win
      1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 //
      5 // MTPDeviceDelegateImplWin implementation.
      6 
      7 #include "chrome/browser/media_galleries/win/mtp_device_delegate_impl_win.h"
      8 
      9 #include <portabledevice.h>
     10 
     11 #include <vector>
     12 
     13 #include "base/bind.h"
     14 #include "base/files/file_path.h"
     15 #include "base/logging.h"
     16 #include "base/memory/ref_counted.h"
     17 #include "base/sequenced_task_runner.h"
     18 #include "base/stl_util.h"
     19 #include "base/strings/string_split.h"
     20 #include "base/strings/string_util.h"
     21 #include "base/strings/utf_string_conversions.h"
     22 #include "base/task_runner_util.h"
     23 #include "base/threading/thread_restrictions.h"
     24 #include "chrome/browser/media_galleries/fileapi/media_file_system_backend.h"
     25 #include "chrome/browser/media_galleries/win/mtp_device_object_entry.h"
     26 #include "chrome/browser/media_galleries/win/mtp_device_object_enumerator.h"
     27 #include "chrome/browser/media_galleries/win/mtp_device_operations_util.h"
     28 #include "chrome/browser/media_galleries/win/portable_device_map_service.h"
     29 #include "chrome/browser/media_galleries/win/snapshot_file_details.h"
     30 #include "chrome/browser/storage_monitor/storage_monitor.h"
     31 #include "content/public/browser/browser_thread.h"
     32 #include "webkit/common/fileapi/file_system_util.h"
     33 
     34 namespace chrome {
     35 
     36 namespace {
     37 
     38 // Gets the details of the MTP partition storage specified by the
     39 // |storage_path| on the UI thread. Returns true if the storage details are
     40 // valid and returns false otherwise.
     41 bool GetStorageInfoOnUIThread(const string16& storage_path,
     42                               string16* pnp_device_id,
     43                               string16* storage_object_id) {
     44   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
     45   DCHECK(!storage_path.empty());
     46   DCHECK(pnp_device_id);
     47   DCHECK(storage_object_id);
     48   string16 storage_device_id;
     49   RemoveChars(storage_path, L"\\\\", &storage_device_id);
     50   DCHECK(!storage_device_id.empty());
     51   // TODO(gbillock): Take the StorageMonitor as an argument.
     52   StorageMonitor* monitor = StorageMonitor::GetInstance();
     53   DCHECK(monitor);
     54   return monitor->GetMTPStorageInfoFromDeviceId(
     55       UTF16ToUTF8(storage_device_id), pnp_device_id, storage_object_id);
     56 }
     57 
     58 // Returns the object id of the file object specified by the |file_path|,
     59 // e.g. if the |file_path| is "\\MTP:StorageSerial:SID-{1001,,192}:125\DCIM"
     60 // and |device_info.registered_device_path_| is
     61 // "\\MTP:StorageSerial:SID-{1001,,192}:125", this function returns the
     62 // identifier of the "DCIM" folder object.
     63 //
     64 // Returns an empty string if the device is detached while the request is in
     65 // progress or when the |file_path| is invalid.
     66 string16 GetFileObjectIdFromPathOnBlockingPoolThread(
     67     const MTPDeviceDelegateImplWin::StorageDeviceInfo& device_info,
     68     const base::FilePath& file_path) {
     69   base::ThreadRestrictions::AssertIOAllowed();
     70   DCHECK(!file_path.empty());
     71   IPortableDevice* device =
     72       PortableDeviceMapService::GetInstance()->GetPortableDevice(
     73           device_info.registered_device_path);
     74   if (!device)
     75     return string16();
     76 
     77   if (device_info.registered_device_path == file_path.value())
     78     return device_info.storage_object_id;
     79 
     80   base::FilePath relative_path;
     81   if (!base::FilePath(device_info.registered_device_path).AppendRelativePath(
     82           file_path, &relative_path))
     83     return string16();
     84 
     85   std::vector<string16> path_components;
     86   relative_path.GetComponents(&path_components);
     87   DCHECK(!path_components.empty());
     88   string16 parent_id(device_info.storage_object_id);
     89   string16 file_object_id;
     90   for (size_t i = 0; i < path_components.size(); ++i) {
     91     file_object_id =
     92         media_transfer_protocol::GetObjectIdFromName(device, parent_id,
     93                                                      path_components[i]);
     94     if (file_object_id.empty())
     95       break;
     96     parent_id = file_object_id;
     97   }
     98   return file_object_id;
     99 }
    100 
    101 // Returns a pointer to a new instance of AbstractFileEnumerator for the given
    102 // |root| directory. Called on a blocking pool thread.
    103 scoped_ptr<fileapi::FileSystemFileUtil::AbstractFileEnumerator>
    104 CreateFileEnumeratorOnBlockingPoolThread(
    105     const MTPDeviceDelegateImplWin::StorageDeviceInfo& device_info,
    106     const base::FilePath& root) {
    107   base::ThreadRestrictions::AssertIOAllowed();
    108   DCHECK(!device_info.registered_device_path.empty());
    109   DCHECK(!root.empty());
    110   scoped_ptr<fileapi::FileSystemFileUtil::AbstractFileEnumerator>
    111       file_enumerator(new fileapi::FileSystemFileUtil::EmptyFileEnumerator());
    112   IPortableDevice* device =
    113       PortableDeviceMapService::GetInstance()->GetPortableDevice(
    114           device_info.registered_device_path);
    115   if (!device)
    116     return file_enumerator.Pass();
    117 
    118   string16 object_id = GetFileObjectIdFromPathOnBlockingPoolThread(device_info,
    119                                                                    root);
    120   if (object_id.empty())
    121     return file_enumerator.Pass();
    122 
    123   MTPDeviceObjectEntries entries;
    124   if (!media_transfer_protocol::GetDirectoryEntries(device, object_id,
    125                                                     &entries) ||
    126       entries.empty())
    127     return file_enumerator.Pass();
    128 
    129   file_enumerator.reset(new MTPDeviceObjectEnumerator(entries));
    130   return file_enumerator.Pass();
    131 }
    132 
    133 // Opens the device for communication on a blocking pool thread.
    134 // |pnp_device_id| specifies the PnP device id.
    135 // |registered_device_path| specifies the registered file system root path for
    136 // the given device.
    137 bool OpenDeviceOnBlockingPoolThread(const string16& pnp_device_id,
    138                                     const string16& registered_device_path) {
    139   base::ThreadRestrictions::AssertIOAllowed();
    140   DCHECK(!pnp_device_id.empty());
    141   DCHECK(!registered_device_path.empty());
    142   base::win::ScopedComPtr<IPortableDevice> device =
    143       media_transfer_protocol::OpenDevice(pnp_device_id);
    144   bool init_succeeded = device.get() != NULL;
    145   if (init_succeeded) {
    146     PortableDeviceMapService::GetInstance()->AddPortableDevice(
    147         registered_device_path, device.get());
    148   }
    149   return init_succeeded;
    150 }
    151 
    152 // Gets the |file_path| details from the MTP device specified by the
    153 // |device_info.registered_device_path|. On success, |error| is set to
    154 // base::PLATFORM_FILE_OK and fills in |file_info|. On failure, |error| is set
    155 // to corresponding platform file error and |file_info| is not set.
    156 base::PlatformFileError GetFileInfoOnBlockingPoolThread(
    157     const MTPDeviceDelegateImplWin::StorageDeviceInfo& device_info,
    158     const base::FilePath& file_path,
    159     base::PlatformFileInfo* file_info) {
    160   base::ThreadRestrictions::AssertIOAllowed();
    161   DCHECK(!device_info.registered_device_path.empty());
    162   DCHECK(!file_path.empty());
    163   DCHECK(file_info);
    164   IPortableDevice* device =
    165       PortableDeviceMapService::GetInstance()->GetPortableDevice(
    166           device_info.registered_device_path);
    167   if (!device)
    168     return base::PLATFORM_FILE_ERROR_FAILED;
    169 
    170   string16 object_id = GetFileObjectIdFromPathOnBlockingPoolThread(device_info,
    171                                                                    file_path);
    172   if (object_id.empty())
    173     return base::PLATFORM_FILE_ERROR_FAILED;
    174   return media_transfer_protocol::GetFileEntryInfo(device, object_id,
    175                                                    file_info);
    176 }
    177 
    178 // Reads the |root| directory file entries on a blocking pool thread. On
    179 // success, |error| is set to base::PLATFORM_FILE_OK and |entries| contains the
    180 // directory file entries. On failure, |error| is set to platform file error
    181 // and |entries| is not set.
    182 base::PlatformFileError ReadDirectoryOnBlockingPoolThread(
    183     const MTPDeviceDelegateImplWin::StorageDeviceInfo& device_info,
    184     const base::FilePath& root,
    185     fileapi::AsyncFileUtil::EntryList* entries) {
    186   base::ThreadRestrictions::AssertIOAllowed();
    187   DCHECK(!root.empty());
    188   DCHECK(entries);
    189   base::PlatformFileInfo file_info;
    190   base::PlatformFileError error = GetFileInfoOnBlockingPoolThread(device_info,
    191                                                                   root,
    192                                                                   &file_info);
    193   if (error != base::PLATFORM_FILE_OK)
    194     return error;
    195 
    196   if (!file_info.is_directory)
    197     return base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY;
    198 
    199   base::FilePath current;
    200   scoped_ptr<fileapi::FileSystemFileUtil::AbstractFileEnumerator> file_enum =
    201       CreateFileEnumeratorOnBlockingPoolThread(device_info, root);
    202   while (!(current = file_enum->Next()).empty()) {
    203     fileapi::DirectoryEntry entry;
    204     entry.is_directory = file_enum->IsDirectory();
    205     entry.name = fileapi::VirtualPath::BaseName(current).value();
    206     entry.size = file_enum->Size();
    207     entry.last_modified_time = file_enum->LastModifiedTime();
    208     entries->push_back(entry);
    209   }
    210   return error;
    211 }
    212 
    213 // Gets the device file stream object on a blocking pool thread.
    214 // |device_info| contains the device storage partition details.
    215 // On success, returns base::PLATFORM_FILE_OK and file stream details are set in
    216 // |file_details|. On failure, returns a platform file error and file stream
    217 // details are not set in |file_details|.
    218 base::PlatformFileError GetFileStreamOnBlockingPoolThread(
    219     const MTPDeviceDelegateImplWin::StorageDeviceInfo& device_info,
    220     SnapshotFileDetails* file_details) {
    221   base::ThreadRestrictions::AssertIOAllowed();
    222   DCHECK(file_details);
    223   DCHECK(!file_details->request_info().device_file_path.empty());
    224   DCHECK(!file_details->request_info().snapshot_file_path.empty());
    225   IPortableDevice* device =
    226       PortableDeviceMapService::GetInstance()->GetPortableDevice(
    227           device_info.registered_device_path);
    228   if (!device)
    229     return base::PLATFORM_FILE_ERROR_FAILED;
    230 
    231   string16 file_object_id =
    232       GetFileObjectIdFromPathOnBlockingPoolThread(
    233           device_info, file_details->request_info().device_file_path);
    234   if (file_object_id.empty())
    235     return base::PLATFORM_FILE_ERROR_FAILED;
    236 
    237   base::PlatformFileInfo file_info;
    238   base::PlatformFileError error =
    239       GetFileInfoOnBlockingPoolThread(
    240           device_info,
    241           file_details->request_info().device_file_path,
    242           &file_info);
    243   if (error != base::PLATFORM_FILE_OK)
    244     return error;
    245 
    246   DWORD optimal_transfer_size = 0;
    247   base::win::ScopedComPtr<IStream> file_stream;
    248   if (file_info.size > 0) {
    249     HRESULT hr = media_transfer_protocol::GetFileStreamForObject(
    250         device,
    251         file_object_id,
    252         file_stream.Receive(),
    253         &optimal_transfer_size);
    254     if (hr != S_OK)
    255       return base::PLATFORM_FILE_ERROR_FAILED;
    256   }
    257 
    258   // LocalFileStreamReader is used to read the contents of the snapshot file.
    259   // Snapshot file modification time does not match the last modified time
    260   // of the original media file. Therefore, set the last modified time to null
    261   // in order to avoid the verification in LocalFileStreamReader.
    262   //
    263   // Users will use HTML5 FileSystem Entry getMetadata() interface to get the
    264   // actual last modified time of the media file.
    265   file_info.last_modified = base::Time();
    266 
    267   DCHECK(file_info.size == 0 || optimal_transfer_size > 0U);
    268   file_details->set_file_info(file_info);
    269   file_details->set_device_file_stream(file_stream);
    270   file_details->set_optimal_transfer_size(optimal_transfer_size);
    271   return error;
    272 }
    273 
    274 // Copies the data chunk from device file to the snapshot file based on the
    275 // parameters specified by |file_details|.
    276 // Returns the total number of bytes written to the snapshot file for non-empty
    277 // files, or 0 on failure. For empty files, just return 0.
    278 DWORD WriteDataChunkIntoSnapshotFileOnBlockingPoolThread(
    279     const SnapshotFileDetails& file_details) {
    280   base::ThreadRestrictions::AssertIOAllowed();
    281   if (file_details.file_info().size == 0)
    282     return 0;
    283   return media_transfer_protocol::CopyDataChunkToLocalFile(
    284       file_details.device_file_stream(),
    285       file_details.request_info().snapshot_file_path,
    286       file_details.optimal_transfer_size());
    287 }
    288 
    289 void DeletePortableDeviceOnBlockingPoolThread(
    290     const string16& registered_device_path) {
    291   base::ThreadRestrictions::AssertIOAllowed();
    292   PortableDeviceMapService::GetInstance()->RemovePortableDevice(
    293       registered_device_path);
    294 }
    295 
    296 
    297 }  // namespace
    298 
    299 // Used by CreateMTPDeviceAsyncDelegate() to create the MTP device
    300 // delegate on the IO thread.
    301 void OnGetStorageInfoCreateDelegate(
    302     const string16& device_location,
    303     const CreateMTPDeviceAsyncDelegateCallback& callback,
    304     string16* pnp_device_id,
    305     string16* storage_object_id,
    306     bool succeeded) {
    307   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    308   DCHECK(pnp_device_id);
    309   DCHECK(storage_object_id);
    310   if (!succeeded)
    311     return;
    312   callback.Run(new MTPDeviceDelegateImplWin(device_location,
    313                                             *pnp_device_id,
    314                                             *storage_object_id));
    315 }
    316 
    317 void CreateMTPDeviceAsyncDelegate(
    318     const string16& device_location,
    319     const CreateMTPDeviceAsyncDelegateCallback& callback) {
    320   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    321   DCHECK(!device_location.empty());
    322   string16* pnp_device_id = new string16;
    323   string16* storage_object_id = new string16;
    324   content::BrowserThread::PostTaskAndReplyWithResult<bool>(
    325       content::BrowserThread::UI,
    326       FROM_HERE,
    327       base::Bind(&GetStorageInfoOnUIThread,
    328                  device_location,
    329                  base::Unretained(pnp_device_id),
    330                  base::Unretained(storage_object_id)),
    331       base::Bind(&OnGetStorageInfoCreateDelegate,
    332                  device_location,
    333                  callback,
    334                  base::Owned(pnp_device_id),
    335                  base::Owned(storage_object_id)));
    336 }
    337 
    338 // MTPDeviceDelegateImplWin ---------------------------------------------------
    339 
    340 MTPDeviceDelegateImplWin::StorageDeviceInfo::StorageDeviceInfo(
    341     const string16& pnp_device_id,
    342     const string16& registered_device_path,
    343     const string16& storage_object_id)
    344     : pnp_device_id(pnp_device_id),
    345       registered_device_path(registered_device_path),
    346       storage_object_id(storage_object_id) {
    347   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    348 }
    349 
    350 MTPDeviceDelegateImplWin::PendingTaskInfo::PendingTaskInfo(
    351     const tracked_objects::Location& location,
    352     const base::Callback<base::PlatformFileError(void)>& task,
    353     const base::Callback<void(base::PlatformFileError)>& reply)
    354     : location(location),
    355       task(task),
    356       reply(reply) {
    357 }
    358 
    359 MTPDeviceDelegateImplWin::MTPDeviceDelegateImplWin(
    360     const string16& registered_device_path,
    361     const string16& pnp_device_id,
    362     const string16& storage_object_id)
    363     : storage_device_info_(pnp_device_id, registered_device_path,
    364                            storage_object_id),
    365       init_state_(UNINITIALIZED),
    366       media_task_runner_(MediaFileSystemBackend::MediaTaskRunner()),
    367       task_in_progress_(false),
    368       weak_ptr_factory_(this) {
    369   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    370   DCHECK(!registered_device_path.empty());
    371   DCHECK(!pnp_device_id.empty());
    372   DCHECK(!storage_object_id.empty());
    373   DCHECK(media_task_runner_.get());
    374 }
    375 
    376 MTPDeviceDelegateImplWin::~MTPDeviceDelegateImplWin() {
    377   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    378 }
    379 
    380 void MTPDeviceDelegateImplWin::GetFileInfo(
    381     const base::FilePath& file_path,
    382     const GetFileInfoSuccessCallback& success_callback,
    383     const ErrorCallback& error_callback) {
    384   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    385   DCHECK(!file_path.empty());
    386   base::PlatformFileInfo* file_info = new base::PlatformFileInfo;
    387   EnsureInitAndRunTask(
    388       PendingTaskInfo(FROM_HERE,
    389                       base::Bind(&GetFileInfoOnBlockingPoolThread,
    390                                  storage_device_info_,
    391                                  file_path,
    392                                  base::Unretained(file_info)),
    393                       base::Bind(&MTPDeviceDelegateImplWin::OnGetFileInfo,
    394                                  weak_ptr_factory_.GetWeakPtr(),
    395                                  success_callback,
    396                                  error_callback,
    397                                  base::Owned(file_info))));
    398 }
    399 
    400 void MTPDeviceDelegateImplWin::ReadDirectory(
    401     const base::FilePath& root,
    402     const ReadDirectorySuccessCallback& success_callback,
    403     const ErrorCallback& error_callback) {
    404   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    405   DCHECK(!root.empty());
    406   fileapi::AsyncFileUtil::EntryList* entries =
    407       new fileapi::AsyncFileUtil::EntryList;
    408   EnsureInitAndRunTask(
    409       PendingTaskInfo(FROM_HERE,
    410                       base::Bind(&ReadDirectoryOnBlockingPoolThread,
    411                                  storage_device_info_,
    412                                  root,
    413                                  base::Unretained(entries)),
    414                       base::Bind(&MTPDeviceDelegateImplWin::OnDidReadDirectory,
    415                                  weak_ptr_factory_.GetWeakPtr(),
    416                                  success_callback,
    417                                  error_callback,
    418                                  base::Owned(entries))));
    419 }
    420 
    421 void MTPDeviceDelegateImplWin::CreateSnapshotFile(
    422     const base::FilePath& device_file_path,
    423     const base::FilePath& snapshot_file_path,
    424     const CreateSnapshotFileSuccessCallback& success_callback,
    425     const ErrorCallback& error_callback) {
    426   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    427   DCHECK(!device_file_path.empty());
    428   DCHECK(!snapshot_file_path.empty());
    429   scoped_ptr<SnapshotFileDetails> file_details(
    430       new SnapshotFileDetails(SnapshotRequestInfo(device_file_path,
    431                                                   snapshot_file_path,
    432                                                   success_callback,
    433                                                   error_callback)));
    434   // Passing a raw SnapshotFileDetails* to the blocking pool is safe, because
    435   // it is owned by |file_details| in the reply callback.
    436   EnsureInitAndRunTask(
    437       PendingTaskInfo(FROM_HERE,
    438                       base::Bind(&GetFileStreamOnBlockingPoolThread,
    439                                  storage_device_info_,
    440                                  file_details.get()),
    441                       base::Bind(&MTPDeviceDelegateImplWin::OnGetFileStream,
    442                                  weak_ptr_factory_.GetWeakPtr(),
    443                                  base::Passed(&file_details))));
    444 }
    445 
    446 void MTPDeviceDelegateImplWin::CancelPendingTasksAndDeleteDelegate() {
    447   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    448   PortableDeviceMapService::GetInstance()->MarkPortableDeviceForDeletion(
    449       storage_device_info_.registered_device_path);
    450   media_task_runner_->PostTask(
    451       FROM_HERE,
    452       base::Bind(&DeletePortableDeviceOnBlockingPoolThread,
    453                  storage_device_info_.registered_device_path));
    454   while (!pending_tasks_.empty())
    455     pending_tasks_.pop();
    456   delete this;
    457 }
    458 
    459 void MTPDeviceDelegateImplWin::EnsureInitAndRunTask(
    460     const PendingTaskInfo& task_info) {
    461   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    462   if ((init_state_ == INITIALIZED) && !task_in_progress_) {
    463     DCHECK(pending_tasks_.empty());
    464     DCHECK(!current_snapshot_details_.get());
    465     base::PostTaskAndReplyWithResult(media_task_runner_,
    466                                      task_info.location,
    467                                      task_info.task,
    468                                      task_info.reply);
    469     task_in_progress_ = true;
    470     return;
    471   }
    472 
    473   pending_tasks_.push(task_info);
    474   if (init_state_ == UNINITIALIZED) {
    475     init_state_ = PENDING_INIT;
    476     base::PostTaskAndReplyWithResult(
    477         media_task_runner_,
    478         FROM_HERE,
    479         base::Bind(&OpenDeviceOnBlockingPoolThread,
    480                    storage_device_info_.pnp_device_id,
    481                    storage_device_info_.registered_device_path),
    482         base::Bind(&MTPDeviceDelegateImplWin::OnInitCompleted,
    483                    weak_ptr_factory_.GetWeakPtr()));
    484     task_in_progress_ = true;
    485   }
    486 }
    487 
    488 void MTPDeviceDelegateImplWin::WriteDataChunkIntoSnapshotFile() {
    489   DCHECK(current_snapshot_details_.get());
    490   base::PostTaskAndReplyWithResult(
    491       media_task_runner_,
    492       FROM_HERE,
    493       base::Bind(&WriteDataChunkIntoSnapshotFileOnBlockingPoolThread,
    494                  *current_snapshot_details_),
    495       base::Bind(&MTPDeviceDelegateImplWin::OnWroteDataChunkIntoSnapshotFile,
    496                  weak_ptr_factory_.GetWeakPtr(),
    497                  current_snapshot_details_->request_info().snapshot_file_path));
    498 }
    499 
    500 void MTPDeviceDelegateImplWin::ProcessNextPendingRequest() {
    501   DCHECK(!task_in_progress_);
    502   if (pending_tasks_.empty())
    503     return;
    504   const PendingTaskInfo& task_info = pending_tasks_.front();
    505   task_in_progress_ = true;
    506   base::PostTaskAndReplyWithResult(media_task_runner_,
    507                                    task_info.location,
    508                                    task_info.task,
    509                                    task_info.reply);
    510   pending_tasks_.pop();
    511 }
    512 
    513 void MTPDeviceDelegateImplWin::OnInitCompleted(bool succeeded) {
    514   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    515   init_state_ = succeeded ? INITIALIZED : UNINITIALIZED;
    516   task_in_progress_ = false;
    517   ProcessNextPendingRequest();
    518 }
    519 
    520 void MTPDeviceDelegateImplWin::OnGetFileInfo(
    521     const GetFileInfoSuccessCallback& success_callback,
    522     const ErrorCallback& error_callback,
    523     base::PlatformFileInfo* file_info,
    524     base::PlatformFileError error) {
    525   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    526   DCHECK(file_info);
    527   if (error == base::PLATFORM_FILE_OK)
    528     success_callback.Run(*file_info);
    529   else
    530     error_callback.Run(error);
    531   task_in_progress_ = false;
    532   ProcessNextPendingRequest();
    533 }
    534 
    535 void MTPDeviceDelegateImplWin::OnDidReadDirectory(
    536     const ReadDirectorySuccessCallback& success_callback,
    537     const ErrorCallback& error_callback,
    538     fileapi::AsyncFileUtil::EntryList* file_list,
    539     base::PlatformFileError error) {
    540   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    541   DCHECK(file_list);
    542   if (error == base::PLATFORM_FILE_OK)
    543     success_callback.Run(*file_list, false /*no more entries*/);
    544   else
    545     error_callback.Run(error);
    546   task_in_progress_ = false;
    547   ProcessNextPendingRequest();
    548 }
    549 
    550 void MTPDeviceDelegateImplWin::OnGetFileStream(
    551     scoped_ptr<SnapshotFileDetails> file_details,
    552     base::PlatformFileError error) {
    553   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    554   DCHECK(file_details);
    555   DCHECK(!file_details->request_info().device_file_path.empty());
    556   DCHECK(!file_details->request_info().snapshot_file_path.empty());
    557   DCHECK(!current_snapshot_details_.get());
    558   if (error != base::PLATFORM_FILE_OK) {
    559     file_details->request_info().error_callback.Run(error);
    560     task_in_progress_ = false;
    561     ProcessNextPendingRequest();
    562     return;
    563   }
    564   DCHECK(file_details->file_info().size == 0 ||
    565          file_details->device_file_stream());
    566   current_snapshot_details_.reset(file_details.release());
    567   WriteDataChunkIntoSnapshotFile();
    568 }
    569 
    570 void MTPDeviceDelegateImplWin::OnWroteDataChunkIntoSnapshotFile(
    571     const base::FilePath& snapshot_file_path,
    572     DWORD bytes_written) {
    573   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    574   DCHECK(!snapshot_file_path.empty());
    575   if (!current_snapshot_details_.get())
    576     return;
    577   DCHECK_EQ(
    578       current_snapshot_details_->request_info().snapshot_file_path.value(),
    579       snapshot_file_path.value());
    580 
    581   bool succeeded = false;
    582   bool should_continue = false;
    583   if (current_snapshot_details_->file_info().size > 0) {
    584     if (current_snapshot_details_->AddBytesWritten(bytes_written)) {
    585       if (current_snapshot_details_->IsSnapshotFileWriteComplete()) {
    586         succeeded = true;
    587       } else {
    588         should_continue = true;
    589       }
    590     }
    591   } else {
    592     // Handle empty files.
    593     DCHECK_EQ(0U, bytes_written);
    594     succeeded = true;
    595   }
    596 
    597   if (should_continue) {
    598     WriteDataChunkIntoSnapshotFile();
    599     return;
    600   }
    601   if (succeeded) {
    602     current_snapshot_details_->request_info().success_callback.Run(
    603         current_snapshot_details_->file_info(),
    604         current_snapshot_details_->request_info().snapshot_file_path);
    605   } else {
    606     current_snapshot_details_->request_info().error_callback.Run(
    607         base::PLATFORM_FILE_ERROR_FAILED);
    608   }
    609   task_in_progress_ = false;
    610   current_snapshot_details_.reset();
    611   ProcessNextPendingRequest();
    612 }
    613 
    614 }  // namespace chrome
    615