[Extensions] Make extension message bubble factory platform-abstract
[chromium-blink-merge.git] / chrome / browser / ui / views / select_file_dialog_extension_browsertest.cc
blob286863e41c5dbeb6dc92768a6fb2250a20ddd5e4
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.
5 #include "chrome/browser/ui/views/select_file_dialog_extension.h"
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/render_frame_host.h"
26 #include "content/public/browser/render_view_host.h"
27 #include "content/public/test/test_utils.h"
28 #include "extensions/test/extension_test_message_listener.h"
29 #include "ui/shell_dialogs/select_file_dialog.h"
30 #include "ui/shell_dialogs/selected_file_info.h"
32 using content::BrowserContext;
34 // Mock listener used by test below.
35 class MockSelectFileDialogListener : public ui::SelectFileDialog::Listener {
36 public:
37 MockSelectFileDialogListener()
38 : file_selected_(false),
39 canceled_(false),
40 params_(NULL) {
43 bool file_selected() const { return file_selected_; }
44 bool canceled() const { return canceled_; }
45 base::FilePath path() const { return path_; }
46 void* params() const { return params_; }
48 // ui::SelectFileDialog::Listener implementation.
49 void FileSelected(const base::FilePath& path,
50 int index,
51 void* params) override {
52 file_selected_ = true;
53 path_ = path;
54 params_ = params;
55 QuitMessageLoop();
57 void FileSelectedWithExtraInfo(const ui::SelectedFileInfo& selected_file_info,
58 int index,
59 void* params) override {
60 FileSelected(selected_file_info.local_path, index, params);
62 void MultiFilesSelected(const std::vector<base::FilePath>& files,
63 void* params) override {
64 QuitMessageLoop();
66 void FileSelectionCanceled(void* params) override {
67 canceled_ = true;
68 params_ = params;
69 QuitMessageLoop();
72 void WaitForCalled() {
73 message_loop_runner_ = new content::MessageLoopRunner();
74 message_loop_runner_->Run();
77 private:
78 void QuitMessageLoop() {
79 if (message_loop_runner_.get())
80 message_loop_runner_->Quit();
83 bool file_selected_;
84 bool canceled_;
85 base::FilePath path_;
86 void* params_;
87 scoped_refptr<content::MessageLoopRunner> message_loop_runner_;
89 DISALLOW_COPY_AND_ASSIGN(MockSelectFileDialogListener);
92 class SelectFileDialogExtensionBrowserTest : public ExtensionBrowserTest {
93 public:
94 enum DialogButtonType {
95 DIALOG_BTN_OK,
96 DIALOG_BTN_CANCEL
99 void SetUp() override {
100 extensions::ComponentLoader::EnableBackgroundExtensionsForTesting();
102 // Create the dialog wrapper object, but don't show it yet.
103 listener_.reset(new MockSelectFileDialogListener());
104 dialog_ = new SelectFileDialogExtension(listener_.get(), NULL);
106 // We have to provide at least one mount point.
107 // File manager looks for "Downloads" mount point, so use this name.
108 base::FilePath tmp_path;
109 PathService::Get(base::DIR_TEMP, &tmp_path);
110 ASSERT_TRUE(tmp_dir_.CreateUniqueTempDirUnderPath(tmp_path));
111 downloads_dir_ = tmp_dir_.path().Append("Downloads");
112 base::CreateDirectory(downloads_dir_);
114 // Must run after our setup because it actually runs the test.
115 ExtensionBrowserTest::SetUp();
118 void TearDown() override {
119 ExtensionBrowserTest::TearDown();
121 // Delete the dialog first, as it holds a pointer to the listener.
122 dialog_ = NULL;
123 listener_.reset();
125 second_dialog_ = NULL;
126 second_listener_.reset();
129 // Creates a file system mount point for a directory.
130 void AddMountPoint(const base::FilePath& path) {
131 EXPECT_TRUE(file_manager::VolumeManager::Get(
132 browser()->profile())->RegisterDownloadsDirectoryForTesting(path));
133 browser()->profile()->GetPrefs()->SetFilePath(
134 prefs::kDownloadDefaultDirectory, downloads_dir_);
137 void CheckJavascriptErrors() {
138 content::RenderFrameHost* host =
139 dialog_->GetRenderViewHost()->GetMainFrame();
140 scoped_ptr<base::Value> value =
141 content::ExecuteScriptAndGetValue(host, "window.JSErrorCount");
142 int js_error_count = 0;
143 ASSERT_TRUE(value->GetAsInteger(&js_error_count));
144 ASSERT_EQ(0, js_error_count);
147 void OpenDialog(ui::SelectFileDialog::Type dialog_type,
148 const base::FilePath& file_path,
149 const gfx::NativeWindow& owning_window,
150 const std::string& additional_message) {
151 // Spawn a dialog to open a file. The dialog will signal that it is ready
152 // via chrome.test.sendMessage() in the extension JavaScript.
153 ExtensionTestMessageListener init_listener("ready", false /* will_reply */);
155 scoped_ptr<ExtensionTestMessageListener> additional_listener;
156 if (!additional_message.empty()) {
157 additional_listener.reset(
158 new ExtensionTestMessageListener(additional_message, false));
161 dialog_->SelectFile(dialog_type,
162 base::string16() /* title */,
163 file_path,
164 NULL /* file_types */,
165 0 /* file_type_index */,
166 FILE_PATH_LITERAL("") /* default_extension */,
167 owning_window,
168 this /* params */);
170 LOG(INFO) << "Waiting for JavaScript ready message.";
171 ASSERT_TRUE(init_listener.WaitUntilSatisfied());
173 if (additional_listener.get()) {
174 LOG(INFO) << "Waiting for JavaScript " << additional_message
175 << " message.";
176 ASSERT_TRUE(additional_listener->WaitUntilSatisfied());
179 // Dialog should be running now.
180 ASSERT_TRUE(dialog_->IsRunning(owning_window));
182 ASSERT_NO_FATAL_FAILURE(CheckJavascriptErrors());
185 void TryOpeningSecondDialog(const gfx::NativeWindow& owning_window) {
186 second_listener_.reset(new MockSelectFileDialogListener());
187 second_dialog_ = new SelectFileDialogExtension(second_listener_.get(),
188 NULL);
190 // At the moment we don't really care about dialog type, but we have to put
191 // some dialog type.
192 second_dialog_->SelectFile(ui::SelectFileDialog::SELECT_OPEN_FILE,
193 base::string16() /* title */,
194 base::FilePath() /* default_path */,
195 NULL /* file_types */,
196 0 /* file_type_index */,
197 FILE_PATH_LITERAL("") /* default_extension */,
198 owning_window,
199 this /* params */);
202 void CloseDialog(DialogButtonType button_type,
203 const gfx::NativeWindow& owning_window) {
204 // Inject JavaScript to click the cancel button and wait for notification
205 // that the window has closed.
206 content::RenderViewHost* host = dialog_->GetRenderViewHost();
207 std::string button_class =
208 (button_type == DIALOG_BTN_OK) ? ".button-panel .ok" :
209 ".button-panel .cancel";
210 base::string16 script = base::ASCIIToUTF16(
211 "console.log(\'Test JavaScript injected.\');"
212 "document.querySelector(\'" + button_class + "\').click();");
213 // The file selection handler closes the dialog and does not return control
214 // to JavaScript, so do not wait for return values.
215 host->GetMainFrame()->ExecuteJavaScript(script);
216 LOG(INFO) << "Waiting for window close notification.";
217 listener_->WaitForCalled();
219 // Dialog no longer believes it is running.
220 ASSERT_FALSE(dialog_->IsRunning(owning_window));
223 scoped_ptr<MockSelectFileDialogListener> listener_;
224 scoped_refptr<SelectFileDialogExtension> dialog_;
226 scoped_ptr<MockSelectFileDialogListener> second_listener_;
227 scoped_refptr<SelectFileDialogExtension> second_dialog_;
229 base::ScopedTempDir tmp_dir_;
230 base::FilePath downloads_dir_;
233 IN_PROC_BROWSER_TEST_F(SelectFileDialogExtensionBrowserTest, CreateAndDestroy) {
234 // Browser window must be up for us to test dialog window parent.
235 gfx::NativeWindow native_window = browser()->window()->GetNativeWindow();
236 ASSERT_TRUE(native_window != NULL);
238 // Before we call SelectFile, dialog is not running/visible.
239 ASSERT_FALSE(dialog_->IsRunning(native_window));
242 IN_PROC_BROWSER_TEST_F(SelectFileDialogExtensionBrowserTest, DestroyListener) {
243 // Some users of SelectFileDialog destroy their listener before cleaning
244 // up the dialog. Make sure we don't crash.
245 dialog_->ListenerDestroyed();
246 listener_.reset();
249 // TODO(jamescook): Add a test for selecting a file for an <input type='file'/>
250 // page element, as that uses different memory management pathways.
251 // crbug.com/98791
253 IN_PROC_BROWSER_TEST_F(SelectFileDialogExtensionBrowserTest,
254 SelectFileAndCancel) {
255 AddMountPoint(downloads_dir_);
257 gfx::NativeWindow owning_window = browser()->window()->GetNativeWindow();
259 // base::FilePath() for default path.
260 ASSERT_NO_FATAL_FAILURE(OpenDialog(ui::SelectFileDialog::SELECT_OPEN_FILE,
261 base::FilePath(), owning_window, ""));
263 // Press cancel button.
264 CloseDialog(DIALOG_BTN_CANCEL, owning_window);
266 // Listener should have been informed of the cancellation.
267 ASSERT_FALSE(listener_->file_selected());
268 ASSERT_TRUE(listener_->canceled());
269 ASSERT_EQ(this, listener_->params());
272 IN_PROC_BROWSER_TEST_F(SelectFileDialogExtensionBrowserTest,
273 SelectFileAndOpen) {
274 AddMountPoint(downloads_dir_);
276 base::FilePath test_file =
277 downloads_dir_.AppendASCII("file_manager_test.html");
279 // Create an empty file to give us something to select.
280 FILE* fp = base::OpenFile(test_file, "w");
281 ASSERT_TRUE(fp != NULL);
282 ASSERT_TRUE(base::CloseFile(fp));
284 gfx::NativeWindow owning_window = browser()->window()->GetNativeWindow();
286 // Spawn a dialog to open a file. Provide the path to the file so the dialog
287 // will automatically select it. Ensure that the OK button is enabled by
288 // waiting for chrome.test.sendMessage('selection-change-complete').
289 ASSERT_NO_FATAL_FAILURE(OpenDialog(ui::SelectFileDialog::SELECT_OPEN_FILE,
290 test_file, owning_window,
291 "dialog-ready"));
293 // Click open button.
294 CloseDialog(DIALOG_BTN_OK, owning_window);
296 // Listener should have been informed that the file was opened.
297 ASSERT_TRUE(listener_->file_selected());
298 ASSERT_FALSE(listener_->canceled());
299 ASSERT_EQ(test_file, listener_->path());
300 ASSERT_EQ(this, listener_->params());
303 IN_PROC_BROWSER_TEST_F(SelectFileDialogExtensionBrowserTest,
304 SelectFileAndSave) {
305 AddMountPoint(downloads_dir_);
307 base::FilePath test_file =
308 downloads_dir_.AppendASCII("file_manager_test.html");
310 gfx::NativeWindow owning_window = browser()->window()->GetNativeWindow();
312 // Spawn a dialog to save a file, providing a suggested path.
313 // Ensure "Save" button is enabled by waiting for notification from
314 // chrome.test.sendMessage().
315 ASSERT_NO_FATAL_FAILURE(OpenDialog(ui::SelectFileDialog::SELECT_SAVEAS_FILE,
316 test_file, owning_window,
317 "dialog-ready"));
319 // Click save button.
320 CloseDialog(DIALOG_BTN_OK, owning_window);
322 // Listener should have been informed that the file was selected.
323 ASSERT_TRUE(listener_->file_selected());
324 ASSERT_FALSE(listener_->canceled());
325 ASSERT_EQ(test_file, listener_->path());
326 ASSERT_EQ(this, listener_->params());
329 IN_PROC_BROWSER_TEST_F(SelectFileDialogExtensionBrowserTest,
330 OpenSingletonTabAndCancel) {
331 AddMountPoint(downloads_dir_);
333 gfx::NativeWindow owning_window = browser()->window()->GetNativeWindow();
335 ASSERT_NO_FATAL_FAILURE(OpenDialog(ui::SelectFileDialog::SELECT_OPEN_FILE,
336 base::FilePath(), owning_window, ""));
338 // Open a singleton tab in background.
339 chrome::NavigateParams p(browser(), GURL("http://www.google.com"),
340 ui::PAGE_TRANSITION_LINK);
341 p.window_action = chrome::NavigateParams::SHOW_WINDOW;
342 p.disposition = SINGLETON_TAB;
343 chrome::Navigate(&p);
345 // Press cancel button.
346 CloseDialog(DIALOG_BTN_CANCEL, owning_window);
348 // Listener should have been informed of the cancellation.
349 ASSERT_FALSE(listener_->file_selected());
350 ASSERT_TRUE(listener_->canceled());
351 ASSERT_EQ(this, listener_->params());
354 IN_PROC_BROWSER_TEST_F(SelectFileDialogExtensionBrowserTest,
355 OpenTwoDialogs) {
356 AddMountPoint(downloads_dir_);
358 gfx::NativeWindow owning_window = browser()->window()->GetNativeWindow();
360 ASSERT_NO_FATAL_FAILURE(OpenDialog(ui::SelectFileDialog::SELECT_OPEN_FILE,
361 base::FilePath(), owning_window, ""));
363 TryOpeningSecondDialog(owning_window);
365 // Second dialog should not be running.
366 ASSERT_FALSE(second_dialog_->IsRunning(owning_window));
368 // Click cancel button.
369 CloseDialog(DIALOG_BTN_CANCEL, owning_window);
371 // Listener should have been informed of the cancellation.
372 ASSERT_FALSE(listener_->file_selected());
373 ASSERT_TRUE(listener_->canceled());
374 ASSERT_EQ(this, listener_->params());