[Metrics] Make MetricsStateManager take a callback param to check if UMA is enabled.
[chromium-blink-merge.git] / chrome / browser / ui / views / select_file_dialog_extension_browsertest.cc
blob48ffb93bafb56fa88718a6cef42403c1163d7a9a
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/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_frame_host.h"
29 #include "content/public/browser/render_view_host.h"
30 #include "content/public/test/test_utils.h"
31 #include "ui/shell_dialogs/select_file_dialog.h"
32 #include "ui/shell_dialogs/selected_file_info.h"
34 using content::BrowserContext;
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) {
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_; }
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;
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);
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;
71 private:
72 bool file_selected_;
73 bool canceled_;
74 base::FilePath path_;
75 void* params_;
77 DISALLOW_COPY_AND_ASSIGN(MockSelectFileDialogListener);
80 class SelectFileDialogExtensionBrowserTest : public ExtensionBrowserTest {
81 public:
82 enum DialogButtonType {
83 DIALOG_BTN_OK,
84 DIALOG_BTN_CANCEL
87 virtual void SetUp() OVERRIDE {
88 extensions::ComponentLoader::EnableBackgroundExtensionsForTesting();
90 // Create the dialog wrapper object, but don't show it yet.
91 listener_.reset(new MockSelectFileDialogListener());
92 dialog_ = new SelectFileDialogExtension(listener_.get(), NULL);
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_);
102 // Must run after our setup because it actually runs the test.
103 ExtensionBrowserTest::SetUp();
106 virtual void TearDown() OVERRIDE {
107 ExtensionBrowserTest::TearDown();
109 // Delete the dialog first, as it holds a pointer to the listener.
110 dialog_ = NULL;
111 listener_.reset();
113 second_dialog_ = NULL;
114 second_listener_.reset();
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_);
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);
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("worker-initialized",
142 false /* will_reply */);
144 scoped_ptr<ExtensionTestMessageListener> additional_listener;
145 if (!additional_message.empty()) {
146 additional_listener.reset(
147 new ExtensionTestMessageListener(additional_message, false));
150 dialog_->SelectFile(dialog_type,
151 base::string16() /* title */,
152 file_path,
153 NULL /* file_types */,
154 0 /* file_type_index */,
155 FILE_PATH_LITERAL("") /* default_extension */,
156 owning_window,
157 this /* params */);
159 LOG(INFO) << "Waiting for JavaScript ready message.";
160 ASSERT_TRUE(init_listener.WaitUntilSatisfied());
162 if (additional_listener.get()) {
163 LOG(INFO) << "Waiting for JavaScript " << additional_message
164 << " message.";
165 ASSERT_TRUE(additional_listener->WaitUntilSatisfied());
168 // Dialog should be running now.
169 ASSERT_TRUE(dialog_->IsRunning(owning_window));
171 ASSERT_NO_FATAL_FAILURE(CheckJavascriptErrors());
174 void TryOpeningSecondDialog(const gfx::NativeWindow& owning_window) {
175 second_listener_.reset(new MockSelectFileDialogListener());
176 second_dialog_ = new SelectFileDialogExtension(second_listener_.get(),
177 NULL);
179 // At the moment we don't really care about dialog type, but we have to put
180 // some dialog type.
181 second_dialog_->SelectFile(ui::SelectFileDialog::SELECT_OPEN_FILE,
182 base::string16() /* title */,
183 base::FilePath() /* default_path */,
184 NULL /* file_types */,
185 0 /* file_type_index */,
186 FILE_PATH_LITERAL("") /* default_extension */,
187 owning_window,
188 this /* params */);
191 void CloseDialog(DialogButtonType button_type,
192 const gfx::NativeWindow& owning_window) {
193 // Inject JavaScript to click the cancel button and wait for notification
194 // that the window has closed.
195 content::WindowedNotificationObserver host_destroyed(
196 content::NOTIFICATION_RENDER_WIDGET_HOST_DESTROYED,
197 content::NotificationService::AllSources());
198 content::RenderViewHost* host = dialog_->GetRenderViewHost();
199 std::string button_class =
200 (button_type == DIALOG_BTN_OK) ? ".button-panel .ok" :
201 ".button-panel .cancel";
202 base::string16 script = base::ASCIIToUTF16(
203 "console.log(\'Test JavaScript injected.\');"
204 "document.querySelector(\'" + button_class + "\').click();");
205 // The file selection handler closes the dialog and does not return control
206 // to JavaScript, so do not wait for return values.
207 host->GetMainFrame()->ExecuteJavaScript(script);
208 LOG(INFO) << "Waiting for window close notification.";
209 host_destroyed.Wait();
211 // Dialog no longer believes it is running.
212 ASSERT_FALSE(dialog_->IsRunning(owning_window));
215 scoped_ptr<MockSelectFileDialogListener> listener_;
216 scoped_refptr<SelectFileDialogExtension> dialog_;
218 scoped_ptr<MockSelectFileDialogListener> second_listener_;
219 scoped_refptr<SelectFileDialogExtension> second_dialog_;
221 base::ScopedTempDir tmp_dir_;
222 base::FilePath downloads_dir_;
225 IN_PROC_BROWSER_TEST_F(SelectFileDialogExtensionBrowserTest, CreateAndDestroy) {
226 // Browser window must be up for us to test dialog window parent.
227 gfx::NativeWindow native_window = browser()->window()->GetNativeWindow();
228 ASSERT_TRUE(native_window != NULL);
230 // Before we call SelectFile, dialog is not running/visible.
231 ASSERT_FALSE(dialog_->IsRunning(native_window));
234 IN_PROC_BROWSER_TEST_F(SelectFileDialogExtensionBrowserTest, DestroyListener) {
235 // Some users of SelectFileDialog destroy their listener before cleaning
236 // up the dialog. Make sure we don't crash.
237 dialog_->ListenerDestroyed();
238 listener_.reset();
241 // TODO(jamescook): Add a test for selecting a file for an <input type='file'/>
242 // page element, as that uses different memory management pathways.
243 // crbug.com/98791
245 IN_PROC_BROWSER_TEST_F(SelectFileDialogExtensionBrowserTest,
246 SelectFileAndCancel) {
247 AddMountPoint(downloads_dir_);
249 gfx::NativeWindow owning_window = browser()->window()->GetNativeWindow();
251 // base::FilePath() for default path.
252 ASSERT_NO_FATAL_FAILURE(OpenDialog(ui::SelectFileDialog::SELECT_OPEN_FILE,
253 base::FilePath(), owning_window, ""));
255 // Press cancel button.
256 CloseDialog(DIALOG_BTN_CANCEL, owning_window);
258 // Listener should have been informed of the cancellation.
259 ASSERT_FALSE(listener_->file_selected());
260 ASSERT_TRUE(listener_->canceled());
261 ASSERT_EQ(this, listener_->params());
264 IN_PROC_BROWSER_TEST_F(SelectFileDialogExtensionBrowserTest,
265 SelectFileAndOpen) {
266 AddMountPoint(downloads_dir_);
268 base::FilePath test_file =
269 downloads_dir_.AppendASCII("file_manager_test.html");
271 // Create an empty file to give us something to select.
272 FILE* fp = base::OpenFile(test_file, "w");
273 ASSERT_TRUE(fp != NULL);
274 ASSERT_TRUE(base::CloseFile(fp));
276 gfx::NativeWindow owning_window = browser()->window()->GetNativeWindow();
278 // Spawn a dialog to open a file. Provide the path to the file so the dialog
279 // will automatically select it. Ensure that the OK button is enabled by
280 // waiting for chrome.test.sendMessage('selection-change-complete').
281 // The extension starts a Web Worker to read file metadata, so it may send
282 // 'selection-change-complete' before 'worker-initialized'. This is OK.
283 ASSERT_NO_FATAL_FAILURE(OpenDialog(ui::SelectFileDialog::SELECT_OPEN_FILE,
284 test_file, owning_window,
285 "selection-change-complete"));
287 // Click open button.
288 CloseDialog(DIALOG_BTN_OK, owning_window);
290 // Listener should have been informed that the file was opened.
291 ASSERT_TRUE(listener_->file_selected());
292 ASSERT_FALSE(listener_->canceled());
293 ASSERT_EQ(test_file, listener_->path());
294 ASSERT_EQ(this, listener_->params());
297 IN_PROC_BROWSER_TEST_F(SelectFileDialogExtensionBrowserTest,
298 SelectFileAndSave) {
299 AddMountPoint(downloads_dir_);
301 base::FilePath test_file =
302 downloads_dir_.AppendASCII("file_manager_test.html");
304 gfx::NativeWindow owning_window = browser()->window()->GetNativeWindow();
306 // Spawn a dialog to save a file, providing a suggested path.
307 // Ensure "Save" button is enabled by waiting for notification from
308 // chrome.test.sendMessage().
309 // The extension starts a Web Worker to read file metadata, so it may send
310 // 'directory-change-complete' before 'worker-initialized'. This is OK.
311 ASSERT_NO_FATAL_FAILURE(OpenDialog(ui::SelectFileDialog::SELECT_SAVEAS_FILE,
312 test_file, owning_window,
313 "directory-change-complete"));
315 // Click save button.
316 CloseDialog(DIALOG_BTN_OK, owning_window);
318 // Listener should have been informed that the file was selected.
319 ASSERT_TRUE(listener_->file_selected());
320 ASSERT_FALSE(listener_->canceled());
321 ASSERT_EQ(test_file, listener_->path());
322 ASSERT_EQ(this, listener_->params());
325 IN_PROC_BROWSER_TEST_F(SelectFileDialogExtensionBrowserTest,
326 OpenSingletonTabAndCancel) {
327 AddMountPoint(downloads_dir_);
329 gfx::NativeWindow owning_window = browser()->window()->GetNativeWindow();
331 ASSERT_NO_FATAL_FAILURE(OpenDialog(ui::SelectFileDialog::SELECT_OPEN_FILE,
332 base::FilePath(), owning_window, ""));
334 // Open a singleton tab in background.
335 chrome::NavigateParams p(browser(), GURL("www.google.com"),
336 content::PAGE_TRANSITION_LINK);
337 p.window_action = chrome::NavigateParams::SHOW_WINDOW;
338 p.disposition = SINGLETON_TAB;
339 chrome::Navigate(&p);
341 // Press cancel button.
342 CloseDialog(DIALOG_BTN_CANCEL, owning_window);
344 // Listener should have been informed of the cancellation.
345 ASSERT_FALSE(listener_->file_selected());
346 ASSERT_TRUE(listener_->canceled());
347 ASSERT_EQ(this, listener_->params());
350 IN_PROC_BROWSER_TEST_F(SelectFileDialogExtensionBrowserTest,
351 OpenTwoDialogs) {
352 AddMountPoint(downloads_dir_);
354 gfx::NativeWindow owning_window = browser()->window()->GetNativeWindow();
356 ASSERT_NO_FATAL_FAILURE(OpenDialog(ui::SelectFileDialog::SELECT_OPEN_FILE,
357 base::FilePath(), owning_window, ""));
359 TryOpeningSecondDialog(owning_window);
361 // Second dialog should not be running.
362 ASSERT_FALSE(second_dialog_->IsRunning(owning_window));
364 // Click cancel button.
365 CloseDialog(DIALOG_BTN_CANCEL, owning_window);
367 // Listener should have been informed of the cancellation.
368 ASSERT_FALSE(listener_->file_selected());
369 ASSERT_TRUE(listener_->canceled());
370 ASSERT_EQ(this, listener_->params());