Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / media_galleries / win / mtp_device_delegate_impl_win.cc
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 "components/storage_monitor/storage_monitor.h"
31 #include "content/public/browser/browser_thread.h"
32 #include "storage/common/fileapi/file_system_util.h"
33
34 namespace {
35
36 // Gets the details of the MTP partition storage specified by the
37 // |storage_path| on the UI thread. Returns true if the storage details are
38 // valid and returns false otherwise.
39 bool GetStorageInfoOnUIThread(const base::string16& storage_path,
40                               base::string16* pnp_device_id,
41                               base::string16* storage_object_id) {
42   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
43   DCHECK(!storage_path.empty());
44   DCHECK(pnp_device_id);
45   DCHECK(storage_object_id);
46   base::string16 storage_device_id;
47   base::RemoveChars(storage_path, L"\\\\", &storage_device_id);
48   DCHECK(!storage_device_id.empty());
49   // TODO(gbillock): Take the StorageMonitor as an argument.
50   storage_monitor::StorageMonitor* monitor =
51       storage_monitor::StorageMonitor::GetInstance();
52   DCHECK(monitor);
53   return monitor->GetMTPStorageInfoFromDeviceId(
54       base::UTF16ToUTF8(storage_device_id), pnp_device_id, storage_object_id);
55 }
56
57 // Returns the object id of the file object specified by the |file_path|,
58 // e.g. if the |file_path| is "\\MTP:StorageSerial:SID-{1001,,192}:125\DCIM"
59 // and |device_info.registered_device_path_| is
60 // "\\MTP:StorageSerial:SID-{1001,,192}:125", this function returns the
61 // identifier of the "DCIM" folder object.
62 //
63 // Returns an empty string if the device is detached while the request is in
64 // progress or when the |file_path| is invalid.
65 base::string16 GetFileObjectIdFromPathOnBlockingPoolThread(
66     const MTPDeviceDelegateImplWin::StorageDeviceInfo& device_info,
67     const base::FilePath& file_path) {
68   base::ThreadRestrictions::AssertIOAllowed();
69   DCHECK(!file_path.empty());
70   IPortableDevice* device =
71       PortableDeviceMapService::GetInstance()->GetPortableDevice(
72           device_info.registered_device_path);
73   if (!device)
74     return base::string16();
75
76   if (device_info.registered_device_path == file_path.value())
77     return device_info.storage_object_id;
78
79   base::FilePath relative_path;
80   if (!base::FilePath(device_info.registered_device_path).AppendRelativePath(
81           file_path, &relative_path))
82     return base::string16();
83
84   std::vector<base::string16> path_components;
85   relative_path.GetComponents(&path_components);
86   DCHECK(!path_components.empty());
87   base::string16 parent_id(device_info.storage_object_id);
88   base::string16 file_object_id;
89   for (size_t i = 0; i < path_components.size(); ++i) {
90     file_object_id =
91         media_transfer_protocol::GetObjectIdFromName(device, parent_id,
92                                                      path_components[i]);
93     if (file_object_id.empty())
94       break;
95     parent_id = file_object_id;
96   }
97   return file_object_id;
98 }
99
100 // Returns a pointer to a new instance of AbstractFileEnumerator for the given
101 // |root| directory. Called on a blocking pool thread.
102 scoped_ptr<MTPDeviceObjectEnumerator>
103 CreateFileEnumeratorOnBlockingPoolThread(
104     const MTPDeviceDelegateImplWin::StorageDeviceInfo& device_info,
105     const base::FilePath& root) {
106   base::ThreadRestrictions::AssertIOAllowed();
107   DCHECK(!device_info.registered_device_path.empty());
108   DCHECK(!root.empty());
109   IPortableDevice* device =
110       PortableDeviceMapService::GetInstance()->GetPortableDevice(
111           device_info.registered_device_path);
112   if (!device)
113     return scoped_ptr<MTPDeviceObjectEnumerator>();
114
115   base::string16 object_id =
116       GetFileObjectIdFromPathOnBlockingPoolThread(device_info, root);
117   if (object_id.empty())
118     return scoped_ptr<MTPDeviceObjectEnumerator>();
119
120   MTPDeviceObjectEntries entries;
121   if (!media_transfer_protocol::GetDirectoryEntries(device, object_id,
122                                                     &entries) ||
123       entries.empty())
124     return scoped_ptr<MTPDeviceObjectEnumerator>();
125
126   return scoped_ptr<MTPDeviceObjectEnumerator>(
127       new MTPDeviceObjectEnumerator(entries));
128 }
129
130 // Opens the device for communication on a blocking pool thread.
131 // |pnp_device_id| specifies the PnP device id.
132 // |registered_device_path| specifies the registered file system root path for
133 // the given device.
134 bool OpenDeviceOnBlockingPoolThread(
135     const base::string16& pnp_device_id,
136     const base::string16& registered_device_path) {
137   base::ThreadRestrictions::AssertIOAllowed();
138   DCHECK(!pnp_device_id.empty());
139   DCHECK(!registered_device_path.empty());
140   base::win::ScopedComPtr<IPortableDevice> device =
141       media_transfer_protocol::OpenDevice(pnp_device_id);
142   bool init_succeeded = device.get() != NULL;
143   if (init_succeeded) {
144     PortableDeviceMapService::GetInstance()->AddPortableDevice(
145         registered_device_path, device.get());
146   }
147   return init_succeeded;
148 }
149
150 // Gets the |file_path| details from the MTP device specified by the
151 // |device_info.registered_device_path|. On success, |error| is set to
152 // base::File::FILE_OK and fills in |file_info|. On failure, |error| is set
153 // to corresponding platform file error and |file_info| is not set.
154 base::File::Error GetFileInfoOnBlockingPoolThread(
155     const MTPDeviceDelegateImplWin::StorageDeviceInfo& device_info,
156     const base::FilePath& file_path,
157     base::File::Info* file_info) {
158   base::ThreadRestrictions::AssertIOAllowed();
159   DCHECK(!device_info.registered_device_path.empty());
160   DCHECK(!file_path.empty());
161   DCHECK(file_info);
162   IPortableDevice* device =
163       PortableDeviceMapService::GetInstance()->GetPortableDevice(
164           device_info.registered_device_path);
165   if (!device)
166     return base::File::FILE_ERROR_FAILED;
167
168   base::string16 object_id =
169       GetFileObjectIdFromPathOnBlockingPoolThread(device_info, file_path);
170   if (object_id.empty())
171     return base::File::FILE_ERROR_FAILED;
172   return media_transfer_protocol::GetFileEntryInfo(device, object_id,
173                                                    file_info);
174 }
175
176 // Reads the |root| directory file entries on a blocking pool thread. On
177 // success, |error| is set to base::File::FILE_OK and |entries| contains the
178 // directory file entries. On failure, |error| is set to platform file error
179 // and |entries| is not set.
180 base::File::Error ReadDirectoryOnBlockingPoolThread(
181     const MTPDeviceDelegateImplWin::StorageDeviceInfo& device_info,
182     const base::FilePath& root,
183     storage::AsyncFileUtil::EntryList* entries) {
184   base::ThreadRestrictions::AssertIOAllowed();
185   DCHECK(!root.empty());
186   DCHECK(entries);
187   base::File::Info file_info;
188   base::File::Error error = GetFileInfoOnBlockingPoolThread(device_info, root,
189                                                             &file_info);
190   if (error != base::File::FILE_OK)
191     return error;
192
193   if (!file_info.is_directory)
194     return base::File::FILE_ERROR_NOT_A_DIRECTORY;
195
196   base::FilePath current;
197   scoped_ptr<MTPDeviceObjectEnumerator> file_enum =
198       CreateFileEnumeratorOnBlockingPoolThread(device_info, root);
199   if (!file_enum)
200     return error;
201
202   while (!(current = file_enum->Next()).empty()) {
203     storage::DirectoryEntry entry;
204     entry.is_directory = file_enum->IsDirectory();
205     entry.name = storage::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::File::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::File::Error 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::File::FILE_ERROR_FAILED;
230
231   base::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::File::FILE_ERROR_FAILED;
236
237   base::File::Info file_info;
238   base::File::Error error =
239       GetFileInfoOnBlockingPoolThread(
240           device_info,
241           file_details->request_info().device_file_path,
242           &file_info);
243   if (error != base::File::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::File::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 base::string16& registered_device_path) {
291   base::ThreadRestrictions::AssertIOAllowed();
292   PortableDeviceMapService::GetInstance()->RemovePortableDevice(
293       registered_device_path);
294 }
295
296 }  // namespace
297
298 // Used by CreateMTPDeviceAsyncDelegate() to create the MTP device
299 // delegate on the IO thread.
300 void OnGetStorageInfoCreateDelegate(
301     const base::string16& device_location,
302     const CreateMTPDeviceAsyncDelegateCallback& callback,
303     base::string16* pnp_device_id,
304     base::string16* storage_object_id,
305     bool succeeded) {
306   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
307   DCHECK(pnp_device_id);
308   DCHECK(storage_object_id);
309   if (!succeeded)
310     return;
311   callback.Run(new MTPDeviceDelegateImplWin(device_location,
312                                             *pnp_device_id,
313                                             *storage_object_id));
314 }
315
316 void CreateMTPDeviceAsyncDelegate(
317     const base::string16& device_location,
318     const CreateMTPDeviceAsyncDelegateCallback& callback) {
319   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
320   DCHECK(!device_location.empty());
321   base::string16* pnp_device_id = new base::string16;
322   base::string16* storage_object_id = new base::string16;
323   content::BrowserThread::PostTaskAndReplyWithResult<bool>(
324       content::BrowserThread::UI,
325       FROM_HERE,
326       base::Bind(&GetStorageInfoOnUIThread,
327                  device_location,
328                  base::Unretained(pnp_device_id),
329                  base::Unretained(storage_object_id)),
330       base::Bind(&OnGetStorageInfoCreateDelegate,
331                  device_location,
332                  callback,
333                  base::Owned(pnp_device_id),
334                  base::Owned(storage_object_id)));
335 }
336
337 // MTPDeviceDelegateImplWin ---------------------------------------------------
338
339 MTPDeviceDelegateImplWin::StorageDeviceInfo::StorageDeviceInfo(
340     const base::string16& pnp_device_id,
341     const base::string16& registered_device_path,
342     const base::string16& storage_object_id)
343     : pnp_device_id(pnp_device_id),
344       registered_device_path(registered_device_path),
345       storage_object_id(storage_object_id) {
346   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
347 }
348
349 MTPDeviceDelegateImplWin::PendingTaskInfo::PendingTaskInfo(
350     const tracked_objects::Location& location,
351     const base::Callback<base::File::Error(void)>& task,
352     const base::Callback<void(base::File::Error)>& reply)
353     : location(location),
354       task(task),
355       reply(reply) {
356 }
357
358 MTPDeviceDelegateImplWin::MTPDeviceDelegateImplWin(
359     const base::string16& registered_device_path,
360     const base::string16& pnp_device_id,
361     const base::string16& storage_object_id)
362     : storage_device_info_(pnp_device_id, registered_device_path,
363                            storage_object_id),
364       init_state_(UNINITIALIZED),
365       media_task_runner_(MediaFileSystemBackend::MediaTaskRunner()),
366       task_in_progress_(false),
367       weak_ptr_factory_(this) {
368   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
369   DCHECK(!registered_device_path.empty());
370   DCHECK(!pnp_device_id.empty());
371   DCHECK(!storage_object_id.empty());
372   DCHECK(media_task_runner_.get());
373 }
374
375 MTPDeviceDelegateImplWin::~MTPDeviceDelegateImplWin() {
376   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
377 }
378
379 void MTPDeviceDelegateImplWin::GetFileInfo(
380     const base::FilePath& file_path,
381     const GetFileInfoSuccessCallback& success_callback,
382     const ErrorCallback& error_callback) {
383   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
384   DCHECK(!file_path.empty());
385   base::File::Info* file_info = new base::File::Info;
386   EnsureInitAndRunTask(
387       PendingTaskInfo(FROM_HERE,
388                       base::Bind(&GetFileInfoOnBlockingPoolThread,
389                                  storage_device_info_,
390                                  file_path,
391                                  base::Unretained(file_info)),
392                       base::Bind(&MTPDeviceDelegateImplWin::OnGetFileInfo,
393                                  weak_ptr_factory_.GetWeakPtr(),
394                                  success_callback,
395                                  error_callback,
396                                  base::Owned(file_info))));
397 }
398
399 void MTPDeviceDelegateImplWin::ReadDirectory(
400     const base::FilePath& root,
401     const ReadDirectorySuccessCallback& success_callback,
402     const ErrorCallback& error_callback) {
403   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
404   DCHECK(!root.empty());
405   storage::AsyncFileUtil::EntryList* entries =
406       new storage::AsyncFileUtil::EntryList;
407   EnsureInitAndRunTask(
408       PendingTaskInfo(FROM_HERE,
409                       base::Bind(&ReadDirectoryOnBlockingPoolThread,
410                                  storage_device_info_,
411                                  root,
412                                  base::Unretained(entries)),
413                       base::Bind(&MTPDeviceDelegateImplWin::OnDidReadDirectory,
414                                  weak_ptr_factory_.GetWeakPtr(),
415                                  success_callback,
416                                  error_callback,
417                                  base::Owned(entries))));
418 }
419
420 void MTPDeviceDelegateImplWin::CreateSnapshotFile(
421     const base::FilePath& device_file_path,
422     const base::FilePath& snapshot_file_path,
423     const CreateSnapshotFileSuccessCallback& success_callback,
424     const ErrorCallback& error_callback) {
425   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
426   DCHECK(!device_file_path.empty());
427   DCHECK(!snapshot_file_path.empty());
428   scoped_ptr<SnapshotFileDetails> file_details(
429       new SnapshotFileDetails(SnapshotRequestInfo(device_file_path,
430                                                   snapshot_file_path,
431                                                   success_callback,
432                                                   error_callback)));
433   // Passing a raw SnapshotFileDetails* to the blocking pool is safe, because
434   // it is owned by |file_details| in the reply callback.
435   EnsureInitAndRunTask(
436       PendingTaskInfo(FROM_HERE,
437                       base::Bind(&GetFileStreamOnBlockingPoolThread,
438                                  storage_device_info_,
439                                  file_details.get()),
440                       base::Bind(&MTPDeviceDelegateImplWin::OnGetFileStream,
441                                  weak_ptr_factory_.GetWeakPtr(),
442                                  base::Passed(&file_details))));
443 }
444
445 bool MTPDeviceDelegateImplWin::IsStreaming() {
446   return false;
447 }
448
449 void MTPDeviceDelegateImplWin::ReadBytes(
450     const base::FilePath& device_file_path,
451     const scoped_refptr<net::IOBuffer>& buf,
452     int64 offset,
453     int buf_len,
454     const ReadBytesSuccessCallback& success_callback,
455     const ErrorCallback& error_callback) {
456   NOTREACHED();
457 }
458
459 void MTPDeviceDelegateImplWin::CancelPendingTasksAndDeleteDelegate() {
460   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
461   PortableDeviceMapService::GetInstance()->MarkPortableDeviceForDeletion(
462       storage_device_info_.registered_device_path);
463   media_task_runner_->PostTask(
464       FROM_HERE,
465       base::Bind(&DeletePortableDeviceOnBlockingPoolThread,
466                  storage_device_info_.registered_device_path));
467   while (!pending_tasks_.empty())
468     pending_tasks_.pop();
469   delete this;
470 }
471
472 void MTPDeviceDelegateImplWin::EnsureInitAndRunTask(
473     const PendingTaskInfo& task_info) {
474   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
475   if ((init_state_ == INITIALIZED) && !task_in_progress_) {
476     DCHECK(pending_tasks_.empty());
477     DCHECK(!current_snapshot_details_.get());
478     base::PostTaskAndReplyWithResult(media_task_runner_,
479                                      task_info.location,
480                                      task_info.task,
481                                      task_info.reply);
482     task_in_progress_ = true;
483     return;
484   }
485
486   pending_tasks_.push(task_info);
487   if (init_state_ == UNINITIALIZED) {
488     init_state_ = PENDING_INIT;
489     base::PostTaskAndReplyWithResult(
490         media_task_runner_,
491         FROM_HERE,
492         base::Bind(&OpenDeviceOnBlockingPoolThread,
493                    storage_device_info_.pnp_device_id,
494                    storage_device_info_.registered_device_path),
495         base::Bind(&MTPDeviceDelegateImplWin::OnInitCompleted,
496                    weak_ptr_factory_.GetWeakPtr()));
497     task_in_progress_ = true;
498   }
499 }
500
501 void MTPDeviceDelegateImplWin::WriteDataChunkIntoSnapshotFile() {
502   DCHECK(current_snapshot_details_.get());
503   base::PostTaskAndReplyWithResult(
504       media_task_runner_,
505       FROM_HERE,
506       base::Bind(&WriteDataChunkIntoSnapshotFileOnBlockingPoolThread,
507                  *current_snapshot_details_),
508       base::Bind(&MTPDeviceDelegateImplWin::OnWroteDataChunkIntoSnapshotFile,
509                  weak_ptr_factory_.GetWeakPtr(),
510                  current_snapshot_details_->request_info().snapshot_file_path));
511 }
512
513 void MTPDeviceDelegateImplWin::ProcessNextPendingRequest() {
514   DCHECK(!task_in_progress_);
515   if (pending_tasks_.empty())
516     return;
517   const PendingTaskInfo& task_info = pending_tasks_.front();
518   task_in_progress_ = true;
519   base::PostTaskAndReplyWithResult(media_task_runner_,
520                                    task_info.location,
521                                    task_info.task,
522                                    task_info.reply);
523   pending_tasks_.pop();
524 }
525
526 void MTPDeviceDelegateImplWin::OnInitCompleted(bool succeeded) {
527   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
528   init_state_ = succeeded ? INITIALIZED : UNINITIALIZED;
529   task_in_progress_ = false;
530   ProcessNextPendingRequest();
531 }
532
533 void MTPDeviceDelegateImplWin::OnGetFileInfo(
534     const GetFileInfoSuccessCallback& success_callback,
535     const ErrorCallback& error_callback,
536     base::File::Info* file_info,
537     base::File::Error error) {
538   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
539   DCHECK(file_info);
540   if (error == base::File::FILE_OK)
541     success_callback.Run(*file_info);
542   else
543     error_callback.Run(error);
544   task_in_progress_ = false;
545   ProcessNextPendingRequest();
546 }
547
548 void MTPDeviceDelegateImplWin::OnDidReadDirectory(
549     const ReadDirectorySuccessCallback& success_callback,
550     const ErrorCallback& error_callback,
551     storage::AsyncFileUtil::EntryList* file_list,
552     base::File::Error error) {
553   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
554   DCHECK(file_list);
555   if (error == base::File::FILE_OK)
556     success_callback.Run(*file_list, false /*no more entries*/);
557   else
558     error_callback.Run(error);
559   task_in_progress_ = false;
560   ProcessNextPendingRequest();
561 }
562
563 void MTPDeviceDelegateImplWin::OnGetFileStream(
564     scoped_ptr<SnapshotFileDetails> file_details,
565     base::File::Error error) {
566   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
567   DCHECK(file_details);
568   DCHECK(!file_details->request_info().device_file_path.empty());
569   DCHECK(!file_details->request_info().snapshot_file_path.empty());
570   DCHECK(!current_snapshot_details_.get());
571   if (error != base::File::FILE_OK) {
572     file_details->request_info().error_callback.Run(error);
573     task_in_progress_ = false;
574     ProcessNextPendingRequest();
575     return;
576   }
577   DCHECK(file_details->file_info().size == 0 ||
578          file_details->device_file_stream());
579   current_snapshot_details_.reset(file_details.release());
580   WriteDataChunkIntoSnapshotFile();
581 }
582
583 void MTPDeviceDelegateImplWin::OnWroteDataChunkIntoSnapshotFile(
584     const base::FilePath& snapshot_file_path,
585     DWORD bytes_written) {
586   DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
587   DCHECK(!snapshot_file_path.empty());
588   if (!current_snapshot_details_.get())
589     return;
590   DCHECK_EQ(
591       current_snapshot_details_->request_info().snapshot_file_path.value(),
592       snapshot_file_path.value());
593
594   bool succeeded = false;
595   bool should_continue = false;
596   if (current_snapshot_details_->file_info().size > 0) {
597     if (current_snapshot_details_->AddBytesWritten(bytes_written)) {
598       if (current_snapshot_details_->IsSnapshotFileWriteComplete()) {
599         succeeded = true;
600       } else {
601         should_continue = true;
602       }
603     }
604   } else {
605     // Handle empty files.
606     DCHECK_EQ(0U, bytes_written);
607     succeeded = true;
608   }
609
610   if (should_continue) {
611     WriteDataChunkIntoSnapshotFile();
612     return;
613   }
614   if (succeeded) {
615     current_snapshot_details_->request_info().success_callback.Run(
616         current_snapshot_details_->file_info(),
617         current_snapshot_details_->request_info().snapshot_file_path);
618   } else {
619     current_snapshot_details_->request_info().error_callback.Run(
620         base::File::FILE_ERROR_FAILED);
621   }
622   task_in_progress_ = false;
623   current_snapshot_details_.reset();
624   ProcessNextPendingRequest();
625 }