Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / media_galleries / mac / mtp_device_delegate_impl_mac.mm
1 // Copyright (c) 2012 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 #include "chrome/browser/media_galleries/mac/mtp_device_delegate_impl_mac.h"
6
7 #include <algorithm>
8
9 #include "base/mac/scoped_nsobject.h"
10 #include "base/threading/sequenced_worker_pool.h"
11 #include "components/storage_monitor/image_capture_device.h"
12 #include "components/storage_monitor/image_capture_device_manager.h"
13 #include "content/public/browser/browser_thread.h"
14 #include "storage/browser/fileapi/async_file_util.h"
15
16 namespace {
17
18 int kReadDirectoryTimeLimitSeconds = 20;
19
20 typedef MTPDeviceAsyncDelegate::CreateSnapshotFileSuccessCallback
21     CreateSnapshotFileSuccessCallback;
22 typedef MTPDeviceAsyncDelegate::ErrorCallback ErrorCallback;
23 typedef MTPDeviceAsyncDelegate::GetFileInfoSuccessCallback
24     GetFileInfoSuccessCallback;
25 typedef MTPDeviceAsyncDelegate::ReadDirectorySuccessCallback
26     ReadDirectorySuccessCallback;
27
28 }  // namespace
29
30 // This class handles the UI-thread hand-offs needed to interface
31 // with the ImageCapture library. It will forward callbacks to
32 // its delegate on the task runner with which it is created. All
33 // interactions with it are done on the UI thread, but it may be
34 // created/destroyed on another thread.
35 class MTPDeviceDelegateImplMac::DeviceListener
36     : public storage_monitor::ImageCaptureDeviceListener,
37       public base::SupportsWeakPtr<DeviceListener> {
38  public:
39   DeviceListener(MTPDeviceDelegateImplMac* delegate)
40       : delegate_(delegate) {}
41   virtual ~DeviceListener() {}
42
43   void OpenCameraSession(const std::string& device_id);
44   void CloseCameraSessionAndDelete();
45
46   void DownloadFile(const std::string& name, const base::FilePath& local_path);
47
48   // ImageCaptureDeviceListener
49   virtual void ItemAdded(const std::string& name,
50                          const base::File::Info& info) OVERRIDE;
51   virtual void NoMoreItems() OVERRIDE;
52   virtual void DownloadedFile(const std::string& name,
53                               base::File::Error error) OVERRIDE;
54   virtual void DeviceRemoved() OVERRIDE;
55
56   // Used during delegate destruction to ensure there are no more calls
57   // to the delegate by the listener.
58   virtual void ResetDelegate();
59
60  private:
61   base::scoped_nsobject<ImageCaptureDevice> camera_device_;
62
63   // Weak pointer
64   MTPDeviceDelegateImplMac* delegate_;
65
66   DISALLOW_COPY_AND_ASSIGN(DeviceListener);
67 };
68
69 void MTPDeviceDelegateImplMac::DeviceListener::OpenCameraSession(
70     const std::string& device_id) {
71   camera_device_.reset(
72       [storage_monitor::ImageCaptureDeviceManager::deviceForUUID(device_id)
73           retain]);
74   [camera_device_ setListener:AsWeakPtr()];
75   [camera_device_ open];
76 }
77
78 void MTPDeviceDelegateImplMac::DeviceListener::CloseCameraSessionAndDelete() {
79   [camera_device_ close];
80   [camera_device_ setListener:base::WeakPtr<DeviceListener>()];
81
82   delete this;
83 }
84
85 void MTPDeviceDelegateImplMac::DeviceListener::DownloadFile(
86     const std::string& name,
87     const base::FilePath& local_path) {
88   [camera_device_ downloadFile:name localPath:local_path];
89 }
90
91 void MTPDeviceDelegateImplMac::DeviceListener::ItemAdded(
92     const std::string& name,
93     const base::File::Info& info) {
94   if (delegate_)
95     delegate_->ItemAdded(name, info);
96 }
97
98 void MTPDeviceDelegateImplMac::DeviceListener::NoMoreItems() {
99   if (delegate_)
100     delegate_->NoMoreItems();
101 }
102
103 void MTPDeviceDelegateImplMac::DeviceListener::DownloadedFile(
104     const std::string& name,
105     base::File::Error error) {
106   if (delegate_)
107     delegate_->DownloadedFile(name, error);
108 }
109
110 void MTPDeviceDelegateImplMac::DeviceListener::DeviceRemoved() {
111   [camera_device_ close];
112   camera_device_.reset();
113   if (delegate_)
114     delegate_->NoMoreItems();
115 }
116
117 void MTPDeviceDelegateImplMac::DeviceListener::ResetDelegate() {
118   delegate_ = NULL;
119 }
120
121 MTPDeviceDelegateImplMac::MTPDeviceDelegateImplMac(
122     const std::string& device_id,
123     const base::FilePath::StringType& synthetic_path)
124     : device_id_(device_id),
125       root_path_(synthetic_path),
126       received_all_files_(false),
127       weak_factory_(this) {
128
129   // Make a synthetic entry for the root of the filesystem.
130   base::File::Info info;
131   info.is_directory = true;
132   file_paths_.push_back(root_path_);
133   file_info_[root_path_.value()] = info;
134
135   camera_interface_.reset(new DeviceListener(this));
136   content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
137       base::Bind(&DeviceListener::OpenCameraSession,
138                  base::Unretained(camera_interface_.get()),
139                  device_id_));
140 }
141
142 MTPDeviceDelegateImplMac::~MTPDeviceDelegateImplMac() {
143 }
144
145 namespace {
146
147 void ForwardGetFileInfo(
148     base::File::Info* info,
149     base::File::Error* error,
150     const GetFileInfoSuccessCallback& success_callback,
151     const ErrorCallback& error_callback) {
152   if (*error == base::File::FILE_OK)
153     success_callback.Run(*info);
154   else
155     error_callback.Run(*error);
156 }
157
158 }  // namespace
159
160 void MTPDeviceDelegateImplMac::GetFileInfo(
161     const base::FilePath& file_path,
162     const GetFileInfoSuccessCallback& success_callback,
163     const ErrorCallback& error_callback) {
164   base::File::Info* info = new base::File::Info;
165   base::File::Error* error = new base::File::Error;
166   // Note: ownership of these objects passed into the reply callback.
167   content::BrowserThread::PostTaskAndReply(content::BrowserThread::UI,
168       FROM_HERE,
169       base::Bind(&MTPDeviceDelegateImplMac::GetFileInfoImpl,
170                  base::Unretained(this), file_path, info, error),
171       base::Bind(&ForwardGetFileInfo,
172                  base::Owned(info), base::Owned(error),
173                  success_callback, error_callback));
174 }
175
176 void MTPDeviceDelegateImplMac::ReadDirectory(
177       const base::FilePath& root,
178       const ReadDirectorySuccessCallback& success_callback,
179       const ErrorCallback& error_callback) {
180   content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
181       base::Bind(&MTPDeviceDelegateImplMac::ReadDirectoryImpl,
182                  base::Unretained(this),
183                  root, success_callback, error_callback));
184 }
185
186 void MTPDeviceDelegateImplMac::CreateSnapshotFile(
187       const base::FilePath& device_file_path,
188       const base::FilePath& local_path,
189       const CreateSnapshotFileSuccessCallback& success_callback,
190       const ErrorCallback& error_callback) {
191   content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
192       base::Bind(&MTPDeviceDelegateImplMac::DownloadFile,
193                  base::Unretained(this),
194                  device_file_path, local_path,
195                  success_callback, error_callback));
196 }
197
198 bool MTPDeviceDelegateImplMac::IsStreaming() {
199   return false;
200 }
201
202 void MTPDeviceDelegateImplMac::ReadBytes(
203     const base::FilePath& device_file_path,
204     const scoped_refptr<net::IOBuffer>& buf,
205     int64 offset,
206     int buf_len,
207     const ReadBytesSuccessCallback& success_callback,
208     const ErrorCallback& error_callback) {
209   NOTREACHED();
210 }
211
212 void MTPDeviceDelegateImplMac::CancelPendingTasksAndDeleteDelegate() {
213   content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
214       base::Bind(&MTPDeviceDelegateImplMac::CancelAndDelete,
215                  base::Unretained(this)));
216 }
217
218 void MTPDeviceDelegateImplMac::GetFileInfoImpl(
219     const base::FilePath& file_path,
220     base::File::Info* file_info,
221     base::File::Error* error) {
222   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
223   base::hash_map<base::FilePath::StringType,
224                  base::File::Info>::const_iterator i =
225       file_info_.find(file_path.value());
226   if (i == file_info_.end()) {
227     *error = base::File::FILE_ERROR_NOT_FOUND;
228     return;
229   }
230   *file_info = i->second;
231   *error = base::File::FILE_OK;
232 }
233
234 void MTPDeviceDelegateImplMac::ReadDirectoryImpl(
235       const base::FilePath& root,
236       const ReadDirectorySuccessCallback& success_callback,
237       const ErrorCallback& error_callback) {
238   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
239
240   read_dir_transactions_.push_back(ReadDirectoryRequest(
241       root, success_callback, error_callback));
242
243   if (received_all_files_) {
244     NotifyReadDir();
245     return;
246   }
247
248   // Schedule a timeout in case the directory read doesn't complete.
249   content::BrowserThread::PostDelayedTask(
250       content::BrowserThread::UI, FROM_HERE,
251       base::Bind(&MTPDeviceDelegateImplMac::ReadDirectoryTimeout,
252                  weak_factory_.GetWeakPtr(), root),
253       base::TimeDelta::FromSeconds(kReadDirectoryTimeLimitSeconds));
254 }
255
256 void MTPDeviceDelegateImplMac::ReadDirectoryTimeout(
257     const base::FilePath& root) {
258   if (received_all_files_)
259     return;
260
261   for (ReadDirTransactionList::iterator iter = read_dir_transactions_.begin();
262        iter != read_dir_transactions_.end();) {
263     if (iter->directory != root) {
264       ++iter;
265       continue;
266     }
267     iter->error_callback.Run(base::File::FILE_ERROR_ABORT);
268     iter = read_dir_transactions_.erase(iter);
269   }
270 }
271
272 void MTPDeviceDelegateImplMac::DownloadFile(
273       const base::FilePath& device_file_path,
274       const base::FilePath& local_path,
275       const CreateSnapshotFileSuccessCallback& success_callback,
276       const ErrorCallback& error_callback) {
277   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
278
279   base::File::Error error;
280   base::File::Info info;
281   GetFileInfoImpl(device_file_path, &info, &error);
282   if (error != base::File::FILE_OK) {
283     content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE,
284                                      base::Bind(error_callback,
285                                                 error));
286     return;
287   }
288
289   base::FilePath relative_path;
290   root_path_.AppendRelativePath(device_file_path, &relative_path);
291
292   read_file_transactions_.push_back(
293       ReadFileRequest(relative_path.value(), local_path,
294                       success_callback, error_callback));
295
296   camera_interface_->DownloadFile(relative_path.value(), local_path);
297 }
298
299 void MTPDeviceDelegateImplMac::CancelAndDelete() {
300   DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
301   // Artificially pretend that we have already gotten all items we're going
302   // to get.
303   NoMoreItems();
304
305   CancelDownloads();
306
307   // Schedule the camera session to be closed and the interface deleted.
308   // This will cancel any downloads in progress.
309   camera_interface_->ResetDelegate();
310   content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
311       base::Bind(&DeviceListener::CloseCameraSessionAndDelete,
312                  base::Unretained(camera_interface_.release())));
313
314   delete this;
315 }
316
317 void MTPDeviceDelegateImplMac::CancelDownloads() {
318   // Cancel any outstanding callbacks.
319   for (ReadFileTransactionList::iterator iter = read_file_transactions_.begin();
320        iter != read_file_transactions_.end(); ++iter) {
321     content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE,
322         base::Bind(iter->error_callback,
323                    base::File::FILE_ERROR_ABORT));
324   }
325   read_file_transactions_.clear();
326
327   for (ReadDirTransactionList::iterator iter = read_dir_transactions_.begin();
328        iter != read_dir_transactions_.end(); ++iter) {
329     content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE,
330         base::Bind(iter->error_callback, base::File::FILE_ERROR_ABORT));
331   }
332   read_dir_transactions_.clear();
333 }
334
335 // Called on the UI thread by the listener
336 void MTPDeviceDelegateImplMac::ItemAdded(
337     const std::string& name, const base::File::Info& info) {
338   if (received_all_files_)
339     return;
340
341   // This kinda should go in a Join method in FilePath...
342   base::FilePath relative_path(name);
343   std::vector<base::FilePath::StringType> components;
344   relative_path.GetComponents(&components);
345   base::FilePath item_filename = root_path_;
346   for (std::vector<base::FilePath::StringType>::iterator iter =
347            components.begin();
348        iter != components.end(); ++iter) {
349     item_filename = item_filename.Append(*iter);
350   }
351
352   file_info_[item_filename.value()] = info;
353   file_paths_.push_back(item_filename);
354
355   // TODO(gbillock): Should we send new files to
356   // read_dir_transactions_ callbacks?
357 }
358
359 // Called in the UI thread by delegate.
360 void MTPDeviceDelegateImplMac::NoMoreItems() {
361   received_all_files_ = true;
362   std::sort(file_paths_.begin(), file_paths_.end());
363
364   NotifyReadDir();
365 }
366
367 void MTPDeviceDelegateImplMac::NotifyReadDir() {
368   for (ReadDirTransactionList::iterator iter = read_dir_transactions_.begin();
369        iter != read_dir_transactions_.end(); ++iter) {
370     base::FilePath read_path = iter->directory;
371     // This code assumes that the list of paths is sorted, so we skip to
372     // where we find the entry for the directory, then read out all first-level
373     // children. We then break when the DirName is greater than the read_path,
374     // as that means we've passed the subdir we're reading.
375     storage::AsyncFileUtil::EntryList entry_list;
376     bool found_path = false;
377     for (size_t i = 0; i < file_paths_.size(); ++i) {
378       if (file_paths_[i] == read_path) {
379         found_path = true;
380         continue;
381       }
382       if (!read_path.IsParent(file_paths_[i])) {
383         if (read_path < file_paths_[i].DirName())
384           break;
385         continue;
386       }
387       if (file_paths_[i].DirName() != read_path)
388         continue;
389
390       base::FilePath relative_path;
391       read_path.AppendRelativePath(file_paths_[i], &relative_path);
392       base::File::Info info = file_info_[file_paths_[i].value()];
393       storage::DirectoryEntry entry;
394       entry.name = relative_path.value();
395       entry.is_directory = info.is_directory;
396       entry.size = info.size;
397       entry.last_modified_time = info.last_modified;
398       entry_list.push_back(entry);
399     }
400
401     if (found_path) {
402       content::BrowserThread::PostTask(content::BrowserThread::IO,
403           FROM_HERE,
404           base::Bind(iter->success_callback, entry_list, false));
405     } else {
406       content::BrowserThread::PostTask(content::BrowserThread::IO,
407           FROM_HERE,
408           base::Bind(iter->error_callback,
409                      base::File::FILE_ERROR_NOT_FOUND));
410     }
411   }
412
413   read_dir_transactions_.clear();
414 }
415
416 // Invoked on UI thread from the listener.
417 void MTPDeviceDelegateImplMac::DownloadedFile(
418     const std::string& name, base::File::Error error) {
419   // If we're cancelled and deleting, we may have deleted the camera.
420   if (!camera_interface_.get())
421     return;
422
423   bool found = false;
424   ReadFileTransactionList::iterator iter = read_file_transactions_.begin();
425   for (; iter != read_file_transactions_.end(); ++iter) {
426     if (iter->request_file == name) {
427       found = true;
428       break;
429     }
430   }
431   if (!found)
432     return;
433
434   if (error != base::File::FILE_OK) {
435     content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE,
436         base::Bind(iter->error_callback, error));
437     read_file_transactions_.erase(iter);
438     return;
439   }
440
441   base::FilePath relative_path(name);
442   std::vector<base::FilePath::StringType> components;
443   relative_path.GetComponents(&components);
444   base::FilePath item_filename = root_path_;
445   for (std::vector<base::FilePath::StringType>::iterator i =
446            components.begin();
447        i != components.end(); ++i) {
448     item_filename = item_filename.Append(*i);
449   }
450
451   base::File::Info info = file_info_[item_filename.value()];
452   content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE,
453       base::Bind(iter->success_callback, info, iter->snapshot_file));
454   read_file_transactions_.erase(iter);
455 }
456
457 MTPDeviceDelegateImplMac::ReadFileRequest::ReadFileRequest(
458     const std::string& file,
459     const base::FilePath& snapshot_filename,
460     CreateSnapshotFileSuccessCallback success_cb,
461     ErrorCallback error_cb)
462     : request_file(file),
463       snapshot_file(snapshot_filename),
464       success_callback(success_cb),
465       error_callback(error_cb) {}
466
467 MTPDeviceDelegateImplMac::ReadFileRequest::ReadFileRequest() {}
468
469 MTPDeviceDelegateImplMac::ReadFileRequest::~ReadFileRequest() {}
470
471 MTPDeviceDelegateImplMac::ReadDirectoryRequest::ReadDirectoryRequest(
472     const base::FilePath& dir,
473     ReadDirectorySuccessCallback success_cb,
474     ErrorCallback error_cb)
475     : directory(dir),
476       success_callback(success_cb),
477       error_callback(error_cb) {}
478
479 MTPDeviceDelegateImplMac::ReadDirectoryRequest::~ReadDirectoryRequest() {}
480
481 void CreateMTPDeviceAsyncDelegate(
482     const base::FilePath::StringType& device_location,
483     const CreateMTPDeviceAsyncDelegateCallback& cb) {
484   std::string device_name = base::FilePath(device_location).BaseName().value();
485   std::string device_id;
486   storage_monitor::StorageInfo::Type type;
487   bool cracked = storage_monitor::StorageInfo::CrackDeviceId(
488       device_name, &type, &device_id);
489   DCHECK(cracked);
490   DCHECK_EQ(storage_monitor::StorageInfo::MAC_IMAGE_CAPTURE, type);
491
492   cb.Run(new MTPDeviceDelegateImplMac(device_id, device_location));
493 }