- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / chromeos / extensions / file_manager / file_browser_handler_api.cc
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 // The file contains the implementation of
6 // fileBrowserHandlerInternal.selectFile extension function.
7 // When invoked, the function does the following:
8 //  - Verifies that the extension function was invoked as a result of user
9 //    gesture.
10 //  - Display 'save as' dialog using FileSelectorImpl which waits for the user
11 //    feedback.
12 //  - Once the user selects the file path (or cancels the selection),
13 //    FileSelectorImpl notifies FileBrowserHandlerInternalSelectFileFunction of
14 //    the selection result by calling FileHandlerSelectFile::OnFilePathSelected.
15 //  - If the selection was canceled,
16 //    FileBrowserHandlerInternalSelectFileFunction returns reporting failure.
17 //  - If the file path was selected, the function opens external file system
18 //    needed to create FileEntry object for the selected path
19 //    (opening file system will create file system name and root url for the
20 //    caller's external file system).
21 //  - The function grants permissions needed to read/write/create file under the
22 //    selected path. To grant permissions to the caller, caller's extension ID
23 //    has to be allowed to access the files virtual path (e.g. /Downloads/foo)
24 //    in ExternalFileSystemBackend. Additionally, the callers render
25 //    process ID has to be granted read, write and create permissions for the
26 //    selected file's full filesystem path (e.g.
27 //    /home/chronos/user/Downloads/foo) in ChildProcessSecurityPolicy.
28 //  - After the required file access permissions are granted, result object is
29 //    created and returned back.
30
31 #include "chrome/browser/chromeos/extensions/file_manager/file_browser_handler_api.h"
32
33 #include "base/bind.h"
34 #include "base/files/file_path.h"
35 #include "base/memory/scoped_ptr.h"
36 #include "base/message_loop/message_loop_proxy.h"
37 #include "base/platform_file.h"
38 #include "chrome/browser/chromeos/file_manager/fileapi_util.h"
39 #include "chrome/browser/profiles/profile.h"
40 #include "chrome/browser/ui/browser.h"
41 #include "chrome/browser/ui/browser_window.h"
42 #include "chrome/browser/ui/chrome_select_file_policy.h"
43 #include "chrome/browser/ui/tabs/tab_strip_model.h"
44 #include "chrome/common/extensions/api/file_browser_handler_internal.h"
45 #include "content/public/browser/browser_thread.h"
46 #include "content/public/browser/child_process_security_policy.h"
47 #include "content/public/browser/render_process_host.h"
48 #include "content/public/browser/render_view_host.h"
49 #include "content/public/browser/storage_partition.h"
50 #include "ui/shell_dialogs/select_file_dialog.h"
51 #include "webkit/browser/fileapi/file_system_backend.h"
52 #include "webkit/browser/fileapi/file_system_context.h"
53 #include "webkit/common/fileapi/file_system_info.h"
54 #include "webkit/common/fileapi/file_system_util.h"
55
56 using content::BrowserContext;
57 using content::BrowserThread;
58 using extensions::api::file_browser_handler_internal::FileEntryInfo;
59 using file_manager::FileSelector;
60 using file_manager::FileSelectorFactory;
61
62 namespace SelectFile =
63     extensions::api::file_browser_handler_internal::SelectFile;
64
65 namespace {
66
67 const char kNoUserGestureError[] =
68     "This method can only be called in response to user gesture, such as a "
69     "mouse click or key press.";
70
71 // Converts file extensions to a ui::SelectFileDialog::FileTypeInfo.
72 ui::SelectFileDialog::FileTypeInfo ConvertExtensionsToFileTypeInfo(
73     const std::vector<std::string>& extensions) {
74   ui::SelectFileDialog::FileTypeInfo file_type_info;
75
76   for (size_t i = 0; i < extensions.size(); ++i) {
77     base::FilePath::StringType allowed_extension =
78         base::FilePath::FromUTF8Unsafe(extensions[i]).value();
79
80     // FileTypeInfo takes a nested vector like [["htm", "html"], ["txt"]] to
81     // group equivalent extensions, but we don't use this feature here.
82     std::vector<base::FilePath::StringType> inner_vector;
83     inner_vector.push_back(allowed_extension);
84     file_type_info.extensions.push_back(inner_vector);
85   }
86
87   return file_type_info;
88 }
89
90 // File selector implementation.
91 // When |SelectFile| is invoked, it will show save as dialog and listen for user
92 // action. When user selects the file (or closes the dialog), the function's
93 // |OnFilePathSelected| method will be called with the result.
94 // SelectFile should be called only once, because the class instance takes
95 // ownership of itself after the first call. It will delete itself after the
96 // extension function is notified of file selection result.
97 // Since the extension function object is ref counted, FileSelectorImpl holds
98 // a reference to it to ensure that the extension function doesn't go away while
99 // waiting for user action. The reference is released after the function is
100 // notified of the selection result.
101 class FileSelectorImpl : public FileSelector,
102                          public ui::SelectFileDialog::Listener {
103  public:
104   explicit FileSelectorImpl();
105   virtual ~FileSelectorImpl() OVERRIDE;
106
107  protected:
108   // file_manager::FileSelectr overrides.
109   // Shows save as dialog with suggested name in window bound to |browser|.
110   // |allowed_extensions| specifies the file extensions allowed to be shown,
111   // and selected. Extensions should not include '.'.
112   //
113   // After this method is called, the selector implementation should not be
114   // deleted by the caller. It will delete itself after it receives response
115   // from SelectFielDialog.
116   virtual void SelectFile(
117       const base::FilePath& suggested_name,
118       const std::vector<std::string>& allowed_extensions,
119       Browser* browser,
120       FileBrowserHandlerInternalSelectFileFunction* function) OVERRIDE;
121
122   // ui::SelectFileDialog::Listener overrides.
123   virtual void FileSelected(const base::FilePath& path,
124                             int index,
125                             void* params) OVERRIDE;
126   virtual void MultiFilesSelected(const std::vector<base::FilePath>& files,
127                                   void* params) OVERRIDE;
128   virtual void FileSelectionCanceled(void* params) OVERRIDE;
129
130  private:
131   // Initiates and shows 'save as' dialog which will be used to prompt user to
132   // select a file path. The initial selected file name in the dialog will be
133   // set to |suggested_name|. The dialog will be bound to the tab active in
134   // |browser|.
135   // |allowed_extensions| specifies the file extensions allowed to be shown,
136   // and selected. Extensions should not include '.'.
137   //
138   // Returns boolean indicating whether the dialog has been successfully shown
139   // to the user.
140   bool StartSelectFile(const base::FilePath& suggested_name,
141                        const std::vector<std::string>& allowed_extensions,
142                        Browser* browser);
143
144   // Reacts to the user action reported by the dialog and notifies |function_|
145   // about file selection result (by calling |OnFilePathSelected()|).
146   // The |this| object is self destruct after the function is notified.
147   // |success| indicates whether user has selected the file.
148   // |selected_path| is path that was selected. It is empty if the file wasn't
149   // selected.
150   void SendResponse(bool success, const base::FilePath& selected_path);
151
152   // Dialog that is shown by selector.
153   scoped_refptr<ui::SelectFileDialog> dialog_;
154
155   // Extension function that uses the selector.
156   scoped_refptr<FileBrowserHandlerInternalSelectFileFunction> function_;
157
158   DISALLOW_COPY_AND_ASSIGN(FileSelectorImpl);
159 };
160
161 FileSelectorImpl::FileSelectorImpl() {}
162
163 FileSelectorImpl::~FileSelectorImpl() {
164   if (dialog_.get())
165     dialog_->ListenerDestroyed();
166   // Send response if needed.
167   if (function_.get())
168     SendResponse(false, base::FilePath());
169 }
170
171 void FileSelectorImpl::SelectFile(
172     const base::FilePath& suggested_name,
173     const std::vector<std::string>& allowed_extensions,
174     Browser* browser,
175     FileBrowserHandlerInternalSelectFileFunction* function) {
176   // We will hold reference to the function until it is notified of selection
177   // result.
178   function_ = function;
179
180   if (!StartSelectFile(suggested_name, allowed_extensions, browser)) {
181     // If the dialog wasn't launched, let's asynchronously report failure to the
182     // function.
183     base::MessageLoopProxy::current()->PostTask(FROM_HERE,
184         base::Bind(&FileSelectorImpl::FileSelectionCanceled,
185                    base::Unretained(this), static_cast<void*>(NULL)));
186   }
187 }
188
189 bool FileSelectorImpl::StartSelectFile(
190     const base::FilePath& suggested_name,
191     const std::vector<std::string>& allowed_extensions,
192     Browser* browser) {
193   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
194   DCHECK(!dialog_.get());
195   DCHECK(browser);
196
197   if (!browser->window())
198     return false;
199
200   content::WebContents* web_contents =
201       browser->tab_strip_model()->GetActiveWebContents();
202   if (!web_contents)
203     return false;
204
205   dialog_ = ui::SelectFileDialog::Create(
206       this, new ChromeSelectFilePolicy(web_contents));
207
208   // Convert |allowed_extensions| to ui::SelectFileDialog::FileTypeInfo.
209   ui::SelectFileDialog::FileTypeInfo allowed_file_info =
210       ConvertExtensionsToFileTypeInfo(allowed_extensions);
211   allowed_file_info.support_drive = true;
212
213   dialog_->SelectFile(ui::SelectFileDialog::SELECT_SAVEAS_FILE,
214                       string16() /* dialog title*/,
215                       suggested_name,
216                       &allowed_file_info,
217                       0 /* file type index */,
218                       std::string() /* default file extension */,
219                       browser->window()->GetNativeWindow(), NULL /* params */);
220
221   return dialog_->IsRunning(browser->window()->GetNativeWindow());
222 }
223
224 void FileSelectorImpl::FileSelected(
225     const base::FilePath& path, int index, void* params) {
226   SendResponse(true, path);
227   delete this;
228 }
229
230 void FileSelectorImpl::MultiFilesSelected(
231     const std::vector<base::FilePath>& files,
232     void* params) {
233   // Only single file should be selected in save-as dialog.
234   NOTREACHED();
235 }
236
237 void FileSelectorImpl::FileSelectionCanceled(
238     void* params) {
239   SendResponse(false, base::FilePath());
240   delete this;
241 }
242
243 void FileSelectorImpl::SendResponse(bool success,
244                                     const base::FilePath& selected_path) {
245   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
246
247   // We don't want to send multiple responses.
248   if (function_.get())
249     function_->OnFilePathSelected(success, selected_path);
250   function_ = NULL;
251 }
252
253 // FileSelectorFactory implementation.
254 class FileSelectorFactoryImpl : public FileSelectorFactory {
255  public:
256   FileSelectorFactoryImpl() {}
257   virtual ~FileSelectorFactoryImpl() {}
258
259   // FileSelectorFactory implementation.
260   // Creates new FileSelectorImplementation for the function.
261   virtual FileSelector* CreateFileSelector() const OVERRIDE {
262     return new FileSelectorImpl();
263   }
264
265  private:
266   DISALLOW_COPY_AND_ASSIGN(FileSelectorFactoryImpl);
267 };
268
269 }  // namespace
270
271 FileBrowserHandlerInternalSelectFileFunction::
272     FileBrowserHandlerInternalSelectFileFunction()
273         : file_selector_factory_(new FileSelectorFactoryImpl()),
274           user_gesture_check_enabled_(true) {
275 }
276
277 FileBrowserHandlerInternalSelectFileFunction::
278     FileBrowserHandlerInternalSelectFileFunction(
279         FileSelectorFactory* file_selector_factory,
280         bool enable_user_gesture_check)
281         : file_selector_factory_(file_selector_factory),
282           user_gesture_check_enabled_(enable_user_gesture_check) {
283   DCHECK(file_selector_factory);
284 }
285
286 FileBrowserHandlerInternalSelectFileFunction::
287     ~FileBrowserHandlerInternalSelectFileFunction() {}
288
289 bool FileBrowserHandlerInternalSelectFileFunction::RunImpl() {
290   scoped_ptr<SelectFile::Params> params(SelectFile::Params::Create(*args_));
291
292   base::FilePath suggested_name(params->selection_params.suggested_name);
293   std::vector<std::string> allowed_extensions;
294   if (params->selection_params.allowed_file_extensions.get())
295     allowed_extensions = *params->selection_params.allowed_file_extensions;
296
297   if (!user_gesture() && user_gesture_check_enabled_) {
298     error_ = kNoUserGestureError;
299     return false;
300   }
301
302   FileSelector* file_selector = file_selector_factory_->CreateFileSelector();
303   file_selector->SelectFile(suggested_name.BaseName(),
304                             allowed_extensions,
305                             GetCurrentBrowser(),
306                             this);
307   return true;
308 }
309
310 void FileBrowserHandlerInternalSelectFileFunction::OnFilePathSelected(
311     bool success,
312     const base::FilePath& full_path) {
313   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
314
315   if (!success) {
316     Respond(false);
317     return;
318   }
319
320   full_path_ = full_path;
321
322   fileapi::FileSystemInfo info =
323       fileapi::GetFileSystemInfoForChromeOS(source_url_.GetOrigin());
324   file_system_name_ = info.name;
325   file_system_root_ = info.root_url;
326
327   GrantPermissions();
328
329   Respond(true);
330 }
331
332 void FileBrowserHandlerInternalSelectFileFunction::GrantPermissions() {
333   fileapi::ExternalFileSystemBackend* external_backend =
334       file_manager::util::GetFileSystemContextForRenderViewHost(
335           GetProfile(), render_view_host())->external_backend();
336   DCHECK(external_backend);
337
338   external_backend->GetVirtualPath(full_path_, &virtual_path_);
339   DCHECK(!virtual_path_.empty());
340
341   // Grant access to this particular file to target extension. This will
342   // ensure that the target extension can access only this FS entry and
343   // prevent from traversing FS hierarchy upward.
344   external_backend->GrantFileAccessToExtension(extension_id(), virtual_path_);
345
346   // Grant access to the selected file to target extensions render view process.
347   content::ChildProcessSecurityPolicy::GetInstance()->GrantCreateReadWriteFile(
348       render_view_host()->GetProcess()->GetID(), full_path_);
349 }
350
351 void FileBrowserHandlerInternalSelectFileFunction::Respond(bool success) {
352   scoped_ptr<SelectFile::Results::Result> result(
353       new SelectFile::Results::Result());
354   result->success = success;
355
356   // If the file was selected, add 'entry' object which will be later used to
357   // create a FileEntry instance for the selected file.
358   if (success) {
359     result->entry.reset(new FileEntryInfo());
360     result->entry->file_system_name = file_system_name_;
361     result->entry->file_system_root = file_system_root_.spec();
362     result->entry->file_full_path = "/" + virtual_path_.AsUTF8Unsafe();
363     result->entry->file_is_directory = false;
364   }
365
366   results_ = SelectFile::Results::Create(*result);
367   SendResponse(true);
368 }