Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / views / select_file_dialog_extension_browsertest.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 #include "chrome/browser/ui/views/select_file_dialog_extension.h"
6
7 #include "base/file_util.h"
8 #include "base/files/scoped_temp_dir.h"
9 #include "base/logging.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/path_service.h"
12 #include "base/prefs/pref_service.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "base/threading/platform_thread.h"
15 #include "build/build_config.h"
16 #include "chrome/browser/chromeos/file_manager/volume_manager.h"
17 #include "chrome/browser/extensions/component_loader.h"
18 #include "chrome/browser/extensions/extension_browsertest.h"
19 #include "chrome/browser/extensions/extension_test_message_listener.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/browser/ui/browser.h"
22 #include "chrome/browser/ui/browser_navigator.h"
23 #include "chrome/browser/ui/browser_window.h"
24 #include "chrome/common/chrome_paths.h"
25 #include "chrome/common/pref_names.h"
26 #include "content/public/browser/notification_service.h"
27 #include "content/public/browser/notification_types.h"
28 #include "content/public/browser/render_view_host.h"
29 #include "content/public/test/test_utils.h"
30 #include "ui/shell_dialogs/select_file_dialog.h"
31 #include "ui/shell_dialogs/selected_file_info.h"
32
33 using content::BrowserContext;
34
35 // Mock listener used by test below.
36 class MockSelectFileDialogListener : public ui::SelectFileDialog::Listener {
37  public:
38   MockSelectFileDialogListener()
39     : file_selected_(false),
40       canceled_(false),
41       params_(NULL) {
42   }
43
44   bool file_selected() const { return file_selected_; }
45   bool canceled() const { return canceled_; }
46   base::FilePath path() const { return path_; }
47   void* params() const { return params_; }
48
49   // ui::SelectFileDialog::Listener implementation.
50   virtual void FileSelected(const base::FilePath& path,
51                             int index,
52                             void* params) OVERRIDE {
53     file_selected_ = true;
54     path_ = path;
55     params_ = params;
56   }
57   virtual void FileSelectedWithExtraInfo(
58       const ui::SelectedFileInfo& selected_file_info,
59       int index,
60       void* params) OVERRIDE {
61     FileSelected(selected_file_info.local_path, index, params);
62   }
63   virtual void MultiFilesSelected(
64       const std::vector<base::FilePath>& files, void* params) OVERRIDE {}
65   virtual void FileSelectionCanceled(void* params) OVERRIDE {
66     canceled_ = true;
67     params_ = params;
68   }
69
70  private:
71   bool file_selected_;
72   bool canceled_;
73   base::FilePath path_;
74   void* params_;
75
76   DISALLOW_COPY_AND_ASSIGN(MockSelectFileDialogListener);
77 };
78
79 class SelectFileDialogExtensionBrowserTest : public ExtensionBrowserTest {
80  public:
81   enum DialogButtonType {
82     DIALOG_BTN_OK,
83     DIALOG_BTN_CANCEL
84   };
85
86   virtual void SetUp() OVERRIDE {
87     extensions::ComponentLoader::EnableBackgroundExtensionsForTesting();
88
89     // Create the dialog wrapper object, but don't show it yet.
90     listener_.reset(new MockSelectFileDialogListener());
91     dialog_ = new SelectFileDialogExtension(listener_.get(), NULL);
92
93     // We have to provide at least one mount point.
94     // File manager looks for "Downloads" mount point, so use this name.
95     base::FilePath tmp_path;
96     PathService::Get(base::DIR_TEMP, &tmp_path);
97     ASSERT_TRUE(tmp_dir_.CreateUniqueTempDirUnderPath(tmp_path));
98     downloads_dir_ = tmp_dir_.path().Append("Downloads");
99     base::CreateDirectory(downloads_dir_);
100
101     // Must run after our setup because it actually runs the test.
102     ExtensionBrowserTest::SetUp();
103   }
104
105   virtual void TearDown() OVERRIDE {
106     ExtensionBrowserTest::TearDown();
107
108     // Delete the dialog first, as it holds a pointer to the listener.
109     dialog_ = NULL;
110     listener_.reset();
111
112     second_dialog_ = NULL;
113     second_listener_.reset();
114   }
115
116   // Creates a file system mount point for a directory.
117   void AddMountPoint(const base::FilePath& path) {
118     EXPECT_TRUE(file_manager::VolumeManager::Get(
119         browser()->profile())->RegisterDownloadsDirectoryForTesting(path));
120     browser()->profile()->GetPrefs()->SetFilePath(
121         prefs::kDownloadDefaultDirectory, downloads_dir_);
122   }
123
124   void CheckJavascriptErrors() {
125     content::RenderViewHost* host = dialog_->GetRenderViewHost();
126     scoped_ptr<base::Value> value =
127         content::ExecuteScriptAndGetValue(host, "window.JSErrorCount");
128     int js_error_count = 0;
129     ASSERT_TRUE(value->GetAsInteger(&js_error_count));
130     ASSERT_EQ(0, js_error_count);
131   }
132
133   void OpenDialog(ui::SelectFileDialog::Type dialog_type,
134                   const base::FilePath& file_path,
135                   const gfx::NativeWindow& owning_window,
136                   const std::string& additional_message) {
137     // Spawn a dialog to open a file.  The dialog will signal that it is ready
138     // via chrome.test.sendMessage() in the extension JavaScript.
139     ExtensionTestMessageListener init_listener("worker-initialized",
140                                                false /* will_reply */);
141
142     scoped_ptr<ExtensionTestMessageListener> additional_listener;
143     if (!additional_message.empty()) {
144       additional_listener.reset(
145           new ExtensionTestMessageListener(additional_message, false));
146     }
147
148     dialog_->SelectFile(dialog_type,
149                         base::string16() /* title */,
150                         file_path,
151                         NULL /* file_types */,
152                          0 /* file_type_index */,
153                         FILE_PATH_LITERAL("") /* default_extension */,
154                         owning_window,
155                         this /* params */);
156
157     LOG(INFO) << "Waiting for JavaScript ready message.";
158     ASSERT_TRUE(init_listener.WaitUntilSatisfied());
159
160     if (additional_listener.get()) {
161       LOG(INFO) << "Waiting for JavaScript " << additional_message
162                 << " message.";
163       ASSERT_TRUE(additional_listener->WaitUntilSatisfied());
164     }
165
166     // Dialog should be running now.
167     ASSERT_TRUE(dialog_->IsRunning(owning_window));
168
169     ASSERT_NO_FATAL_FAILURE(CheckJavascriptErrors());
170   }
171
172   void TryOpeningSecondDialog(const gfx::NativeWindow& owning_window) {
173     second_listener_.reset(new MockSelectFileDialogListener());
174     second_dialog_ = new SelectFileDialogExtension(second_listener_.get(),
175                                                    NULL);
176
177     // At the moment we don't really care about dialog type, but we have to put
178     // some dialog type.
179     second_dialog_->SelectFile(ui::SelectFileDialog::SELECT_OPEN_FILE,
180                                base::string16() /* title */,
181                                base::FilePath() /* default_path */,
182                                NULL /* file_types */,
183                                0 /* file_type_index */,
184                                FILE_PATH_LITERAL("") /* default_extension */,
185                                owning_window,
186                                this /* params */);
187   }
188
189   void CloseDialog(DialogButtonType button_type,
190                    const gfx::NativeWindow& owning_window) {
191     // Inject JavaScript to click the cancel button and wait for notification
192     // that the window has closed.
193     content::WindowedNotificationObserver host_destroyed(
194         content::NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED,
195         content::NotificationService::AllSources());
196     content::RenderViewHost* host = dialog_->GetRenderViewHost();
197     base::string16 main_frame;
198     std::string button_class =
199         (button_type == DIALOG_BTN_OK) ? ".button-panel .ok" :
200                                          ".button-panel .cancel";
201     base::string16 script = base::ASCIIToUTF16(
202         "console.log(\'Test JavaScript injected.\');"
203         "document.querySelector(\'" + button_class + "\').click();");
204     // The file selection handler closes the dialog and does not return control
205     // to JavaScript, so do not wait for return values.
206     host->ExecuteJavascriptInWebFrame(main_frame, script);
207     LOG(INFO) << "Waiting for window close notification.";
208     host_destroyed.Wait();
209
210     // Dialog no longer believes it is running.
211     ASSERT_FALSE(dialog_->IsRunning(owning_window));
212   }
213
214   scoped_ptr<MockSelectFileDialogListener> listener_;
215   scoped_refptr<SelectFileDialogExtension> dialog_;
216
217   scoped_ptr<MockSelectFileDialogListener> second_listener_;
218   scoped_refptr<SelectFileDialogExtension> second_dialog_;
219
220   base::ScopedTempDir tmp_dir_;
221   base::FilePath downloads_dir_;
222 };
223
224 IN_PROC_BROWSER_TEST_F(SelectFileDialogExtensionBrowserTest, CreateAndDestroy) {
225   // Browser window must be up for us to test dialog window parent.
226   gfx::NativeWindow native_window = browser()->window()->GetNativeWindow();
227   ASSERT_TRUE(native_window != NULL);
228
229   // Before we call SelectFile, dialog is not running/visible.
230   ASSERT_FALSE(dialog_->IsRunning(native_window));
231 }
232
233 IN_PROC_BROWSER_TEST_F(SelectFileDialogExtensionBrowserTest, DestroyListener) {
234   // Some users of SelectFileDialog destroy their listener before cleaning
235   // up the dialog.  Make sure we don't crash.
236   dialog_->ListenerDestroyed();
237   listener_.reset();
238 }
239
240 // TODO(jamescook): Add a test for selecting a file for an <input type='file'/>
241 // page element, as that uses different memory management pathways.
242 // crbug.com/98791
243
244 IN_PROC_BROWSER_TEST_F(SelectFileDialogExtensionBrowserTest,
245                        SelectFileAndCancel) {
246   AddMountPoint(downloads_dir_);
247
248   gfx::NativeWindow owning_window = browser()->window()->GetNativeWindow();
249
250   // base::FilePath() for default path.
251   ASSERT_NO_FATAL_FAILURE(OpenDialog(ui::SelectFileDialog::SELECT_OPEN_FILE,
252                                      base::FilePath(), owning_window, ""));
253
254   // Press cancel button.
255   CloseDialog(DIALOG_BTN_CANCEL, owning_window);
256
257   // Listener should have been informed of the cancellation.
258   ASSERT_FALSE(listener_->file_selected());
259   ASSERT_TRUE(listener_->canceled());
260   ASSERT_EQ(this, listener_->params());
261 }
262
263 IN_PROC_BROWSER_TEST_F(SelectFileDialogExtensionBrowserTest,
264                        SelectFileAndOpen) {
265   AddMountPoint(downloads_dir_);
266
267   base::FilePath test_file =
268       downloads_dir_.AppendASCII("file_manager_test.html");
269
270   // Create an empty file to give us something to select.
271   FILE* fp = base::OpenFile(test_file, "w");
272   ASSERT_TRUE(fp != NULL);
273   ASSERT_TRUE(base::CloseFile(fp));
274
275   gfx::NativeWindow owning_window = browser()->window()->GetNativeWindow();
276
277   // Spawn a dialog to open a file.  Provide the path to the file so the dialog
278   // will automatically select it.  Ensure that the OK button is enabled by
279   // waiting for chrome.test.sendMessage('selection-change-complete').
280   // The extension starts a Web Worker to read file metadata, so it may send
281   // 'selection-change-complete' before 'worker-initialized'.  This is OK.
282   ASSERT_NO_FATAL_FAILURE(OpenDialog(ui::SelectFileDialog::SELECT_OPEN_FILE,
283                                      test_file, owning_window,
284                                      "selection-change-complete"));
285
286   // Click open button.
287   CloseDialog(DIALOG_BTN_OK, owning_window);
288
289   // Listener should have been informed that the file was opened.
290   ASSERT_TRUE(listener_->file_selected());
291   ASSERT_FALSE(listener_->canceled());
292   ASSERT_EQ(test_file, listener_->path());
293   ASSERT_EQ(this, listener_->params());
294 }
295
296 IN_PROC_BROWSER_TEST_F(SelectFileDialogExtensionBrowserTest,
297                        SelectFileAndSave) {
298   AddMountPoint(downloads_dir_);
299
300   base::FilePath test_file =
301       downloads_dir_.AppendASCII("file_manager_test.html");
302
303   gfx::NativeWindow owning_window = browser()->window()->GetNativeWindow();
304
305   // Spawn a dialog to save a file, providing a suggested path.
306   // Ensure "Save" button is enabled by waiting for notification from
307   // chrome.test.sendMessage().
308   // The extension starts a Web Worker to read file metadata, so it may send
309   // 'directory-change-complete' before 'worker-initialized'.  This is OK.
310   ASSERT_NO_FATAL_FAILURE(OpenDialog(ui::SelectFileDialog::SELECT_SAVEAS_FILE,
311                                      test_file, owning_window,
312                                      "directory-change-complete"));
313
314   // Click save button.
315   CloseDialog(DIALOG_BTN_OK, owning_window);
316
317   // Listener should have been informed that the file was selected.
318   ASSERT_TRUE(listener_->file_selected());
319   ASSERT_FALSE(listener_->canceled());
320   ASSERT_EQ(test_file, listener_->path());
321   ASSERT_EQ(this, listener_->params());
322 }
323
324 IN_PROC_BROWSER_TEST_F(SelectFileDialogExtensionBrowserTest,
325                        OpenSingletonTabAndCancel) {
326   AddMountPoint(downloads_dir_);
327
328   gfx::NativeWindow owning_window = browser()->window()->GetNativeWindow();
329
330   ASSERT_NO_FATAL_FAILURE(OpenDialog(ui::SelectFileDialog::SELECT_OPEN_FILE,
331                                      base::FilePath(), owning_window, ""));
332
333   // Open a singleton tab in background.
334   chrome::NavigateParams p(browser(), GURL("www.google.com"),
335                            content::PAGE_TRANSITION_LINK);
336   p.window_action = chrome::NavigateParams::SHOW_WINDOW;
337   p.disposition = SINGLETON_TAB;
338   chrome::Navigate(&p);
339
340   // Press cancel button.
341   CloseDialog(DIALOG_BTN_CANCEL, owning_window);
342
343   // Listener should have been informed of the cancellation.
344   ASSERT_FALSE(listener_->file_selected());
345   ASSERT_TRUE(listener_->canceled());
346   ASSERT_EQ(this, listener_->params());
347 }
348
349 IN_PROC_BROWSER_TEST_F(SelectFileDialogExtensionBrowserTest,
350                        OpenTwoDialogs) {
351   AddMountPoint(downloads_dir_);
352
353   gfx::NativeWindow owning_window = browser()->window()->GetNativeWindow();
354
355   ASSERT_NO_FATAL_FAILURE(OpenDialog(ui::SelectFileDialog::SELECT_OPEN_FILE,
356                                      base::FilePath(), owning_window, ""));
357
358   TryOpeningSecondDialog(owning_window);
359
360   // Second dialog should not be running.
361   ASSERT_FALSE(second_dialog_->IsRunning(owning_window));
362
363   // Click cancel button.
364   CloseDialog(DIALOG_BTN_CANCEL, owning_window);
365
366   // Listener should have been informed of the cancellation.
367   ASSERT_FALSE(listener_->file_selected());
368   ASSERT_TRUE(listener_->canceled());
369   ASSERT_EQ(this, listener_->params());
370 }