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