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