Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(604)

Side by Side Diff: chrome/browser/ui/views/select_file_dialog_extension_views_browsertest.cc

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

Powered by Google App Engine
This is Rietveld 408576698