File: file_browser_handler_api.cc

package info (click to toggle)
chromium-browser 41.0.2272.118-1
  • links: PTS, VCS
  • area: main
  • in suites: jessie-kfreebsd
  • size: 2,189,132 kB
  • sloc: cpp: 9,691,462; ansic: 3,341,451; python: 712,689; asm: 518,779; xml: 208,926; java: 169,820; sh: 119,353; perl: 68,907; makefile: 28,311; yacc: 13,305; objc: 11,385; tcl: 3,186; cs: 2,225; sql: 2,217; lex: 2,215; lisp: 1,349; pascal: 1,256; awk: 407; ruby: 155; sed: 53; php: 14; exp: 11
file content (374 lines) | stat: -rw-r--r-- 14,911 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

// The file contains the implementation of
// fileBrowserHandlerInternal.selectFile extension function.
// When invoked, the function does the following:
//  - Verifies that the extension function was invoked as a result of user
//    gesture.
//  - Display 'save as' dialog using FileSelectorImpl which waits for the user
//    feedback.
//  - Once the user selects the file path (or cancels the selection),
//    FileSelectorImpl notifies FileBrowserHandlerInternalSelectFileFunction of
//    the selection result by calling FileHandlerSelectFile::OnFilePathSelected.
//  - If the selection was canceled,
//    FileBrowserHandlerInternalSelectFileFunction returns reporting failure.
//  - If the file path was selected, the function opens external file system
//    needed to create FileEntry object for the selected path
//    (opening file system will create file system name and root url for the
//    caller's external file system).
//  - The function grants permissions needed to read/write/create file under the
//    selected path. To grant permissions to the caller, caller's extension ID
//    has to be allowed to access the files virtual path (e.g. /Downloads/foo)
//    in ExternalFileSystemBackend. Additionally, the callers render
//    process ID has to be granted read, write and create permissions for the
//    selected file's full filesystem path (e.g.
//    /home/chronos/user/Downloads/foo) in ChildProcessSecurityPolicy.
//  - After the required file access permissions are granted, result object is
//    created and returned back.

#include "chrome/browser/chromeos/extensions/file_manager/file_browser_handler_api.h"

#include "base/bind.h"
#include "base/files/file_path.h"
#include "base/memory/scoped_ptr.h"
#include "base/message_loop/message_loop_proxy.h"
#include "chrome/browser/chromeos/file_manager/fileapi_util.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/chrome_select_file_policy.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/common/extensions/api/file_browser_handler_internal.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/child_process_security_policy.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "storage/browser/fileapi/file_system_backend.h"
#include "storage/browser/fileapi/file_system_context.h"
#include "storage/common/fileapi/file_system_info.h"
#include "storage/common/fileapi/file_system_util.h"
#include "ui/shell_dialogs/select_file_dialog.h"

using content::BrowserThread;
using extensions::api::file_browser_handler_internal::FileEntryInfo;
using file_manager::FileSelector;
using file_manager::FileSelectorFactory;
using file_manager::util::EntryDefinition;
using file_manager::util::FileDefinition;

namespace SelectFile =
    extensions::api::file_browser_handler_internal::SelectFile;

namespace {

const char kNoUserGestureError[] =
    "This method can only be called in response to user gesture, such as a "
    "mouse click or key press.";

// Converts file extensions to a ui::SelectFileDialog::FileTypeInfo.
ui::SelectFileDialog::FileTypeInfo ConvertExtensionsToFileTypeInfo(
    const std::vector<std::string>& extensions) {
  ui::SelectFileDialog::FileTypeInfo file_type_info;

  for (size_t i = 0; i < extensions.size(); ++i) {
    base::FilePath::StringType allowed_extension =
        base::FilePath::FromUTF8Unsafe(extensions[i]).value();

    // FileTypeInfo takes a nested vector like [["htm", "html"], ["txt"]] to
    // group equivalent extensions, but we don't use this feature here.
    std::vector<base::FilePath::StringType> inner_vector;
    inner_vector.push_back(allowed_extension);
    file_type_info.extensions.push_back(inner_vector);
  }

  return file_type_info;
}

// File selector implementation.
// When |SelectFile| is invoked, it will show save as dialog and listen for user
// action. When user selects the file (or closes the dialog), the function's
// |OnFilePathSelected| method will be called with the result.
// SelectFile should be called only once, because the class instance takes
// ownership of itself after the first call. It will delete itself after the
// extension function is notified of file selection result.
// Since the extension function object is ref counted, FileSelectorImpl holds
// a reference to it to ensure that the extension function doesn't go away while
// waiting for user action. The reference is released after the function is
// notified of the selection result.
class FileSelectorImpl : public FileSelector,
                         public ui::SelectFileDialog::Listener {
 public:
  FileSelectorImpl();
  virtual ~FileSelectorImpl() override;

 protected:
  // file_manager::FileSelectr overrides.
  // Shows save as dialog with suggested name in window bound to |browser|.
  // |allowed_extensions| specifies the file extensions allowed to be shown,
  // and selected. Extensions should not include '.'.
  //
  // After this method is called, the selector implementation should not be
  // deleted by the caller. It will delete itself after it receives response
  // from SelectFielDialog.
  virtual void SelectFile(
      const base::FilePath& suggested_name,
      const std::vector<std::string>& allowed_extensions,
      Browser* browser,
      FileBrowserHandlerInternalSelectFileFunction* function) override;

  // ui::SelectFileDialog::Listener overrides.
  virtual void FileSelected(const base::FilePath& path,
                            int index,
                            void* params) override;
  virtual void MultiFilesSelected(const std::vector<base::FilePath>& files,
                                  void* params) override;
  virtual void FileSelectionCanceled(void* params) override;

 private:
  // Initiates and shows 'save as' dialog which will be used to prompt user to
  // select a file path. The initial selected file name in the dialog will be
  // set to |suggested_name|. The dialog will be bound to the tab active in
  // |browser|.
  // |allowed_extensions| specifies the file extensions allowed to be shown,
  // and selected. Extensions should not include '.'.
  //
  // Returns boolean indicating whether the dialog has been successfully shown
  // to the user.
  bool StartSelectFile(const base::FilePath& suggested_name,
                       const std::vector<std::string>& allowed_extensions,
                       Browser* browser);

  // Reacts to the user action reported by the dialog and notifies |function_|
  // about file selection result (by calling |OnFilePathSelected()|).
  // The |this| object is self destruct after the function is notified.
  // |success| indicates whether user has selected the file.
  // |selected_path| is path that was selected. It is empty if the file wasn't
  // selected.
  void SendResponse(bool success, const base::FilePath& selected_path);

  // Dialog that is shown by selector.
  scoped_refptr<ui::SelectFileDialog> dialog_;

  // Extension function that uses the selector.
  scoped_refptr<FileBrowserHandlerInternalSelectFileFunction> function_;

  DISALLOW_COPY_AND_ASSIGN(FileSelectorImpl);
};

FileSelectorImpl::FileSelectorImpl() {}

FileSelectorImpl::~FileSelectorImpl() {
  if (dialog_.get())
    dialog_->ListenerDestroyed();
  // Send response if needed.
  if (function_.get())
    SendResponse(false, base::FilePath());
}

void FileSelectorImpl::SelectFile(
    const base::FilePath& suggested_name,
    const std::vector<std::string>& allowed_extensions,
    Browser* browser,
    FileBrowserHandlerInternalSelectFileFunction* function) {
  // We will hold reference to the function until it is notified of selection
  // result.
  function_ = function;

  if (!StartSelectFile(suggested_name, allowed_extensions, browser)) {
    // If the dialog wasn't launched, let's asynchronously report failure to the
    // function.
    base::MessageLoopProxy::current()->PostTask(FROM_HERE,
        base::Bind(&FileSelectorImpl::FileSelectionCanceled,
                   base::Unretained(this), static_cast<void*>(NULL)));
  }
}

bool FileSelectorImpl::StartSelectFile(
    const base::FilePath& suggested_name,
    const std::vector<std::string>& allowed_extensions,
    Browser* browser) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  DCHECK(!dialog_.get());
  DCHECK(browser);

  if (!browser->window())
    return false;

  content::WebContents* web_contents =
      browser->tab_strip_model()->GetActiveWebContents();
  if (!web_contents)
    return false;

  dialog_ = ui::SelectFileDialog::Create(
      this, new ChromeSelectFilePolicy(web_contents));

  // Convert |allowed_extensions| to ui::SelectFileDialog::FileTypeInfo.
  ui::SelectFileDialog::FileTypeInfo allowed_file_info =
      ConvertExtensionsToFileTypeInfo(allowed_extensions);
  allowed_file_info.support_drive = true;

  dialog_->SelectFile(ui::SelectFileDialog::SELECT_SAVEAS_FILE,
                      base::string16() /* dialog title*/,
                      suggested_name,
                      &allowed_file_info,
                      0 /* file type index */,
                      std::string() /* default file extension */,
                      browser->window()->GetNativeWindow(), NULL /* params */);

  return dialog_->IsRunning(browser->window()->GetNativeWindow());
}

void FileSelectorImpl::FileSelected(
    const base::FilePath& path, int index, void* params) {
  SendResponse(true, path);
  delete this;
}

void FileSelectorImpl::MultiFilesSelected(
    const std::vector<base::FilePath>& files,
    void* params) {
  // Only single file should be selected in save-as dialog.
  NOTREACHED();
}

void FileSelectorImpl::FileSelectionCanceled(
    void* params) {
  SendResponse(false, base::FilePath());
  delete this;
}

void FileSelectorImpl::SendResponse(bool success,
                                    const base::FilePath& selected_path) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  // We don't want to send multiple responses.
  if (function_.get())
    function_->OnFilePathSelected(success, selected_path);
  function_ = NULL;
}

// FileSelectorFactory implementation.
class FileSelectorFactoryImpl : public FileSelectorFactory {
 public:
  FileSelectorFactoryImpl() {}
  virtual ~FileSelectorFactoryImpl() {}

  // FileSelectorFactory implementation.
  // Creates new FileSelectorImplementation for the function.
  virtual FileSelector* CreateFileSelector() const override {
    return new FileSelectorImpl();
  }

 private:
  DISALLOW_COPY_AND_ASSIGN(FileSelectorFactoryImpl);
};

}  // namespace

FileBrowserHandlerInternalSelectFileFunction::
    FileBrowserHandlerInternalSelectFileFunction()
        : file_selector_factory_(new FileSelectorFactoryImpl()),
          user_gesture_check_enabled_(true) {
}

FileBrowserHandlerInternalSelectFileFunction::
    FileBrowserHandlerInternalSelectFileFunction(
        FileSelectorFactory* file_selector_factory,
        bool enable_user_gesture_check)
        : file_selector_factory_(file_selector_factory),
          user_gesture_check_enabled_(enable_user_gesture_check) {
  DCHECK(file_selector_factory);
}

FileBrowserHandlerInternalSelectFileFunction::
    ~FileBrowserHandlerInternalSelectFileFunction() {}

bool FileBrowserHandlerInternalSelectFileFunction::RunAsync() {
  scoped_ptr<SelectFile::Params> params(SelectFile::Params::Create(*args_));

  base::FilePath suggested_name(params->selection_params.suggested_name);
  std::vector<std::string> allowed_extensions;
  if (params->selection_params.allowed_file_extensions.get())
    allowed_extensions = *params->selection_params.allowed_file_extensions;

  if (!user_gesture() && user_gesture_check_enabled_) {
    SetError(kNoUserGestureError);
    return false;
  }

  FileSelector* file_selector = file_selector_factory_->CreateFileSelector();
  file_selector->SelectFile(suggested_name.BaseName(),
                            allowed_extensions,
                            GetCurrentBrowser(),
                            this);
  return true;
}

void FileBrowserHandlerInternalSelectFileFunction::OnFilePathSelected(
    bool success,
    const base::FilePath& full_path) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  if (!success) {
    Respond(EntryDefinition(), false);
    return;
  }

  storage::ExternalFileSystemBackend* external_backend =
      file_manager::util::GetFileSystemContextForRenderViewHost(
          GetProfile(), render_view_host())->external_backend();
  DCHECK(external_backend);

  FileDefinition file_definition;
  file_definition.is_directory = false;

  external_backend->GetVirtualPath(full_path, &file_definition.virtual_path);
  DCHECK(!file_definition.virtual_path.empty());

  // Grant access to this particular file to target extension. This will
  // ensure that the target extension can access only this FS entry and
  // prevent from traversing FS hierarchy upward.
  external_backend->GrantFileAccessToExtension(extension_id(),
                                               file_definition.virtual_path);

  // Grant access to the selected file to target extensions render view process.
  content::ChildProcessSecurityPolicy::GetInstance()->GrantCreateReadWriteFile(
      render_view_host()->GetProcess()->GetID(), full_path);

  file_manager::util::ConvertFileDefinitionToEntryDefinition(
      GetProfile(),
      extension_id(),
      file_definition,
      base::Bind(
          &FileBrowserHandlerInternalSelectFileFunction::RespondEntryDefinition,
          this));
}

void FileBrowserHandlerInternalSelectFileFunction::RespondEntryDefinition(
    const EntryDefinition& entry_definition) {
  Respond(entry_definition, true);
}

void FileBrowserHandlerInternalSelectFileFunction::Respond(
    const EntryDefinition& entry_definition,
    bool success) {
  scoped_ptr<SelectFile::Results::Result> result(
      new SelectFile::Results::Result());
  result->success = success;

  // If the file was selected, add 'entry' object which will be later used to
  // create a FileEntry instance for the selected file.
  if (success && entry_definition.error == base::File::FILE_OK) {
    result->entry.reset(new FileEntryInfo());
    // TODO(mtomasz): Make the response fields consistent with other files.
    result->entry->file_system_name = entry_definition.file_system_name;
    result->entry->file_system_root = entry_definition.file_system_root_url;
    result->entry->file_full_path =
        "/" + entry_definition.full_path.AsUTF8Unsafe();
    result->entry->file_is_directory = entry_definition.is_directory;
  }

  results_ = SelectFile::Results::Create(*result);
  SendResponse(true);
}