Upstream version 11.40.277.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/files/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/profiles/profile.h"
20 #include "chrome/browser/ui/browser.h"
21 #include "chrome/browser/ui/browser_navigator.h"
22 #include "chrome/browser/ui/browser_window.h"
23 #include "chrome/common/chrome_paths.h"
24 #include "chrome/common/pref_names.h"
25 #include "content/public/browser/notification_service.h"
26 #include "content/public/browser/notification_types.h"
27 #include "content/public/browser/render_frame_host.h"
28 #include "content/public/browser/render_view_host.h"
29 #include "content/public/test/test_utils.h"
30 #include "extensions/test/extension_test_message_listener.h"
31 #include "ui/shell_dialogs/select_file_dialog.h"
32 #include "ui/shell_dialogs/selected_file_info.h"
33
34 using content::BrowserContext;
35
36 // Mock listener used by test below.
37 class MockSelectFileDialogListener : public ui::SelectFileDialog::Listener {
38  public:
39   MockSelectFileDialogListener()
40     : file_selected_(false),
41       canceled_(false),
42       params_(NULL) {
43   }
44
45   bool file_selected() const { return file_selected_; }
46   bool canceled() const { return canceled_; }
47   base::FilePath path() const { return path_; }
48   void* params() const { return params_; }
49
50   // ui::SelectFileDialog::Listener implementation.
51   virtual void FileSelected(const base::FilePath& path,
52                             int index,
53                             void* params) override {
54     file_selected_ = true;
55     path_ = path;
56     params_ = params;
57   }
58   virtual void FileSelectedWithExtraInfo(
59       const ui::SelectedFileInfo& selected_file_info,
60       int index,
61       void* params) override {
62     FileSelected(selected_file_info.local_path, index, params);
63   }
64   virtual void MultiFilesSelected(
65       const std::vector<base::FilePath>& files, void* params) override {}
66   virtual void FileSelectionCanceled(void* params) override {
67     canceled_ = true;
68     params_ = params;
69   }
70
71  private:
72   bool file_selected_;
73   bool canceled_;
74   base::FilePath path_;
75   void* params_;
76
77   DISALLOW_COPY_AND_ASSIGN(MockSelectFileDialogListener);
78 };
79
80 class SelectFileDialogExtensionBrowserTest : public ExtensionBrowserTest {
81  public:
82   enum DialogButtonType {
83     DIALOG_BTN_OK,
84     DIALOG_BTN_CANCEL
85   };
86
87   virtual void SetUp() override {
88     extensions::ComponentLoader::EnableBackgroundExtensionsForTesting();
89
90     // Create the dialog wrapper object, but don't show it yet.
91     listener_.reset(new MockSelectFileDialogListener());
92     dialog_ = new SelectFileDialogExtension(listener_.get(), NULL);
93
94     // We have to provide at least one mount point.
95     // File manager looks for "Downloads" mount point, so use this name.
96     base::FilePath tmp_path;
97     PathService::Get(base::DIR_TEMP, &tmp_path);
98     ASSERT_TRUE(tmp_dir_.CreateUniqueTempDirUnderPath(tmp_path));
99     downloads_dir_ = tmp_dir_.path().Append("Downloads");
100     base::CreateDirectory(downloads_dir_);
101
102     // Must run after our setup because it actually runs the test.
103     ExtensionBrowserTest::SetUp();
104   }
105
106   virtual void TearDown() override {
107     ExtensionBrowserTest::TearDown();
108
109     // Delete the dialog first, as it holds a pointer to the listener.
110     dialog_ = NULL;
111     listener_.reset();
112
113     second_dialog_ = NULL;
114     second_listener_.reset();
115   }
116
117   // Creates a file system mount point for a directory.
118   void AddMountPoint(const base::FilePath& path) {
119     EXPECT_TRUE(file_manager::VolumeManager::Get(
120         browser()->profile())->RegisterDownloadsDirectoryForTesting(path));
121     browser()->profile()->GetPrefs()->SetFilePath(
122         prefs::kDownloadDefaultDirectory, downloads_dir_);
123   }
124
125   void CheckJavascriptErrors() {
126     content::RenderFrameHost* host =
127         dialog_->GetRenderViewHost()->GetMainFrame();
128     scoped_ptr<base::Value> value =
129         content::ExecuteScriptAndGetValue(host, "window.JSErrorCount");
130     int js_error_count = 0;
131     ASSERT_TRUE(value->GetAsInteger(&js_error_count));
132     ASSERT_EQ(0, js_error_count);
133   }
134
135   void OpenDialog(ui::SelectFileDialog::Type dialog_type,
136                   const base::FilePath& file_path,
137                   const gfx::NativeWindow& owning_window,
138                   const std::string& additional_message) {
139     // Spawn a dialog to open a file.  The dialog will signal that it is ready
140     // via chrome.test.sendMessage() in the extension JavaScript.
141     ExtensionTestMessageListener init_listener("ready", false /* will_reply */);
142
143     scoped_ptr<ExtensionTestMessageListener> additional_listener;
144     if (!additional_message.empty()) {
145       additional_listener.reset(
146           new ExtensionTestMessageListener(additional_message, false));
147     }
148
149     dialog_->SelectFile(dialog_type,
150                         base::string16() /* title */,
151                         file_path,
152                         NULL /* file_types */,
153                          0 /* file_type_index */,
154                         FILE_PATH_LITERAL("") /* default_extension */,
155                         owning_window,
156                         this /* params */);
157
158     LOG(INFO) << "Waiting for JavaScript ready message.";
159     ASSERT_TRUE(init_listener.WaitUntilSatisfied());
160
161     if (additional_listener.get()) {
162       LOG(INFO) << "Waiting for JavaScript " << additional_message
163                 << " message.";
164       ASSERT_TRUE(additional_listener->WaitUntilSatisfied());
165     }
166
167     // Dialog should be running now.
168     ASSERT_TRUE(dialog_->IsRunning(owning_window));
169
170     ASSERT_NO_FATAL_FAILURE(CheckJavascriptErrors());
171   }
172
173   void TryOpeningSecondDialog(const gfx::NativeWindow& owning_window) {
174     second_listener_.reset(new MockSelectFileDialogListener());
175     second_dialog_ = new SelectFileDialogExtension(second_listener_.get(),
176                                                    NULL);
177
178     // At the moment we don't really care about dialog type, but we have to put
179     // some dialog type.
180     second_dialog_->SelectFile(ui::SelectFileDialog::SELECT_OPEN_FILE,
181                                base::string16() /* title */,
182                                base::FilePath() /* default_path */,
183                                NULL /* file_types */,
184                                0 /* file_type_index */,
185                                FILE_PATH_LITERAL("") /* default_extension */,
186                                owning_window,
187                                this /* params */);
188   }
189
190   void CloseDialog(DialogButtonType button_type,
191                    const gfx::NativeWindow& owning_window) {
192     // Inject JavaScript to click the cancel button and wait for notification
193     // that the window has closed.
194     content::WindowedNotificationObserver host_destroyed(
195         content::NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED,
196         content::NotificationService::AllSources());
197     content::RenderViewHost* host = dialog_->GetRenderViewHost();
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->GetMainFrame()->ExecuteJavaScript(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   ASSERT_NO_FATAL_FAILURE(OpenDialog(ui::SelectFileDialog::SELECT_OPEN_FILE,
281                                      test_file, owning_window,
282                                      "selection-change-complete"));
283
284   // Click open button.
285   CloseDialog(DIALOG_BTN_OK, owning_window);
286
287   // Listener should have been informed that the file was opened.
288   ASSERT_TRUE(listener_->file_selected());
289   ASSERT_FALSE(listener_->canceled());
290   ASSERT_EQ(test_file, listener_->path());
291   ASSERT_EQ(this, listener_->params());
292 }
293
294 IN_PROC_BROWSER_TEST_F(SelectFileDialogExtensionBrowserTest,
295                        SelectFileAndSave) {
296   AddMountPoint(downloads_dir_);
297
298   base::FilePath test_file =
299       downloads_dir_.AppendASCII("file_manager_test.html");
300
301   gfx::NativeWindow owning_window = browser()->window()->GetNativeWindow();
302
303   // Spawn a dialog to save a file, providing a suggested path.
304   // Ensure "Save" button is enabled by waiting for notification from
305   // chrome.test.sendMessage().
306   ASSERT_NO_FATAL_FAILURE(OpenDialog(ui::SelectFileDialog::SELECT_SAVEAS_FILE,
307                                      test_file, owning_window,
308                                      "directory-change-complete"));
309
310   // Click save button.
311   CloseDialog(DIALOG_BTN_OK, owning_window);
312
313   // Listener should have been informed that the file was selected.
314   ASSERT_TRUE(listener_->file_selected());
315   ASSERT_FALSE(listener_->canceled());
316   ASSERT_EQ(test_file, listener_->path());
317   ASSERT_EQ(this, listener_->params());
318 }
319
320 IN_PROC_BROWSER_TEST_F(SelectFileDialogExtensionBrowserTest,
321                        OpenSingletonTabAndCancel) {
322   AddMountPoint(downloads_dir_);
323
324   gfx::NativeWindow owning_window = browser()->window()->GetNativeWindow();
325
326   ASSERT_NO_FATAL_FAILURE(OpenDialog(ui::SelectFileDialog::SELECT_OPEN_FILE,
327                                      base::FilePath(), owning_window, ""));
328
329   // Open a singleton tab in background.
330   chrome::NavigateParams p(browser(), GURL("http://www.google.com"),
331                            ui::PAGE_TRANSITION_LINK);
332   p.window_action = chrome::NavigateParams::SHOW_WINDOW;
333   p.disposition = SINGLETON_TAB;
334   chrome::Navigate(&p);
335
336   // Press cancel button.
337   CloseDialog(DIALOG_BTN_CANCEL, owning_window);
338
339   // Listener should have been informed of the cancellation.
340   ASSERT_FALSE(listener_->file_selected());
341   ASSERT_TRUE(listener_->canceled());
342   ASSERT_EQ(this, listener_->params());
343 }
344
345 IN_PROC_BROWSER_TEST_F(SelectFileDialogExtensionBrowserTest,
346                        OpenTwoDialogs) {
347   AddMountPoint(downloads_dir_);
348
349   gfx::NativeWindow owning_window = browser()->window()->GetNativeWindow();
350
351   ASSERT_NO_FATAL_FAILURE(OpenDialog(ui::SelectFileDialog::SELECT_OPEN_FILE,
352                                      base::FilePath(), owning_window, ""));
353
354   TryOpeningSecondDialog(owning_window);
355
356   // Second dialog should not be running.
357   ASSERT_FALSE(second_dialog_->IsRunning(owning_window));
358
359   // Click cancel button.
360   CloseDialog(DIALOG_BTN_CANCEL, owning_window);
361
362   // Listener should have been informed of the cancellation.
363   ASSERT_FALSE(listener_->file_selected());
364   ASSERT_TRUE(listener_->canceled());
365   ASSERT_EQ(this, listener_->params());
366 }