| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ui/views/select_file_dialog_extension.h" |
| |
| #include <memory> |
| |
| #include "ash/public/interfaces/constants.mojom.h" |
| #include "ash/public/interfaces/shell_test_api.test-mojom-test-utils.h" |
| #include "ash/public/interfaces/shell_test_api.test-mojom.h" |
| #include "base/bind_helpers.h" |
| #include "base/files/file_util.h" |
| #include "base/files/scoped_temp_dir.h" |
| #include "base/logging.h" |
| #include "base/macros.h" |
| #include "base/path_service.h" |
| #include "base/run_loop.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/threading/platform_thread.h" |
| #include "base/threading/thread_restrictions.h" |
| #include "build/build_config.h" |
| #include "chrome/browser/chromeos/file_manager/file_manager_test_util.h" |
| #include "chrome/browser/chromeos/file_manager/volume_manager.h" |
| #include "chrome/browser/extensions/extension_browsertest.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/ui/ash/keyboard/chrome_keyboard_controller_client.h" |
| #include "chrome/browser/ui/ash/tablet_mode_client_test_util.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/browser_navigator.h" |
| #include "chrome/browser/ui/browser_navigator_params.h" |
| #include "chrome/browser/ui/browser_window.h" |
| #include "chrome/common/chrome_paths.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/test/base/ui_test_utils.h" |
| #include "chromeos/constants/chromeos_features.h" |
| #include "components/prefs/pref_service.h" |
| #include "content/public/browser/render_frame_host.h" |
| #include "content/public/browser/render_view_host.h" |
| #include "content/public/common/service_manager_connection.h" |
| #include "content/public/test/browser_test_utils.h" |
| #include "content/public/test/test_utils.h" |
| #include "extensions/browser/extension_registry.h" |
| #include "extensions/browser/extension_registry_factory.h" |
| #include "extensions/browser/process_manager.h" |
| #include "extensions/test/background_page_watcher.h" |
| #include "extensions/test/extension_test_message_listener.h" |
| #include "net/test/embedded_test_server/embedded_test_server.h" |
| #include "services/service_manager/public/cpp/connector.h" |
| #include "ui/keyboard/public/keyboard_switches.h" |
| #include "ui/shell_dialogs/select_file_dialog.h" |
| #include "ui/shell_dialogs/select_file_policy.h" |
| #include "ui/shell_dialogs/selected_file_info.h" |
| |
| namespace { |
| |
| class KeyboardVisibleWaiter : public ChromeKeyboardControllerClient::Observer { |
| public: |
| KeyboardVisibleWaiter() { |
| ChromeKeyboardControllerClient::Get()->AddObserver(this); |
| } |
| ~KeyboardVisibleWaiter() override { |
| ChromeKeyboardControllerClient::Get()->RemoveObserver(this); |
| } |
| |
| void Wait() { run_loop_.Run(); } |
| |
| // ChromeKeyboardControllerClient::Observer |
| void OnKeyboardVisibilityChanged(bool visible) override { |
| if (visible) |
| run_loop_.QuitWhenIdle(); |
| } |
| |
| private: |
| base::RunLoop run_loop_; |
| }; |
| |
| } // namespace |
| |
| class MockSelectFileDialogListener : public ui::SelectFileDialog::Listener { |
| public: |
| MockSelectFileDialogListener() |
| : file_selected_(false), |
| canceled_(false), |
| params_(NULL) { |
| } |
| |
| bool file_selected() const { return file_selected_; } |
| bool canceled() const { return canceled_; } |
| base::FilePath path() const { return path_; } |
| void* params() const { return params_; } |
| |
| // ui::SelectFileDialog::Listener: |
| void FileSelected(const base::FilePath& path, |
| int index, |
| void* params) override { |
| file_selected_ = true; |
| path_ = path; |
| params_ = params; |
| QuitMessageLoop(); |
| } |
| void FileSelectedWithExtraInfo(const ui::SelectedFileInfo& selected_file_info, |
| int index, |
| void* params) override { |
| FileSelected(selected_file_info.local_path, index, params); |
| } |
| void MultiFilesSelected(const std::vector<base::FilePath>& files, |
| void* params) override { |
| QuitMessageLoop(); |
| } |
| void FileSelectionCanceled(void* params) override { |
| canceled_ = true; |
| params_ = params; |
| QuitMessageLoop(); |
| } |
| |
| void WaitForCalled() { |
| message_loop_runner_ = new content::MessageLoopRunner(); |
| message_loop_runner_->Run(); |
| } |
| |
| private: |
| void QuitMessageLoop() { |
| if (message_loop_runner_.get()) |
| message_loop_runner_->Quit(); |
| } |
| |
| bool file_selected_; |
| bool canceled_; |
| base::FilePath path_; |
| void* params_; |
| scoped_refptr<content::MessageLoopRunner> message_loop_runner_; |
| |
| DISALLOW_COPY_AND_ASSIGN(MockSelectFileDialogListener); |
| }; |
| |
| class SelectFileDialogExtensionBrowserTest |
| : public extensions::ExtensionBrowserTest, |
| public testing::WithParamInterface<bool> { |
| public: |
| enum DialogButtonType { |
| DIALOG_BTN_OK, |
| DIALOG_BTN_CANCEL |
| }; |
| |
| void SetUp() override { |
| feature_list_.InitAndEnableFeature(chromeos::features::kMyFilesVolume); |
| // Create the dialog wrapper and listener objects. |
| listener_.reset(new MockSelectFileDialogListener()); |
| dialog_ = new SelectFileDialogExtension(listener_.get(), NULL); |
| |
| // One mount point will be needed. Files app looks for the "Downloads" |
| // volume mount point by default, so use that. |
| base::FilePath tmp_path; |
| base::PathService::Get(base::DIR_TEMP, &tmp_path); |
| ASSERT_TRUE(tmp_dir_.CreateUniqueTempDirUnderPath(tmp_path)); |
| downloads_dir_ = |
| tmp_dir_.GetPath().AppendASCII("My Files").AppendASCII("Downloads"); |
| base::CreateDirectory(downloads_dir_); |
| |
| // Must run after our setup because it actually runs the test. |
| extensions::ExtensionBrowserTest::SetUp(); |
| } |
| |
| void SetUpCommandLine(base::CommandLine* command_line) override { |
| // Ash tablet mode does not automatically enable the virtual keyboard, so |
| // force the virtual keyboard via the command line for tablet mode tests. |
| const char* test_name = |
| ::testing::UnitTest::GetInstance()->current_test_info()->name(); |
| if (base::StringPiece(test_name).find("_TabletMode") != std::string::npos) |
| command_line->AppendSwitch(keyboard::switches::kEnableVirtualKeyboard); |
| |
| extensions::ExtensionBrowserTest::SetUpCommandLine(command_line); |
| } |
| |
| void SetUpOnMainThread() override { |
| extensions::ExtensionBrowserTest::SetUpOnMainThread(); |
| CHECK(profile()); |
| |
| // Create a file system mount point for the "Downloads" directory. |
| EXPECT_TRUE( |
| file_manager::VolumeManager::Get(profile()) |
| ->RegisterDownloadsDirectoryForTesting(downloads_dir_.DirName())); |
| profile()->GetPrefs()->SetFilePath(prefs::kDownloadDefaultDirectory, |
| downloads_dir_); |
| |
| // The test resources are setup: enable and add default ChromeOS component |
| // extensions now and not before: crbug.com/831074, crbug.com/804413. |
| file_manager::test::AddDefaultComponentExtensionsOnMainThread(profile()); |
| |
| // Ensure the Files app background page has shut down. These tests should |
| // ensure launching without the background page functions correctly. |
| extensions::ProcessManager::SetEventPageIdleTimeForTesting(1); |
| extensions::ProcessManager::SetEventPageSuspendingTimeForTesting(1); |
| const auto* extension = |
| extensions::ExtensionRegistryFactory::GetForBrowserContext(profile()) |
| ->GetExtensionById(extension_misc::kFilesManagerAppId, |
| extensions::ExtensionRegistry::ENABLED); |
| extensions::BackgroundPageWatcher background_page_watcher( |
| extensions::ProcessManager::Get(profile()), extension); |
| background_page_watcher.WaitForClose(); |
| } |
| |
| void TearDown() override { |
| extensions::ExtensionBrowserTest::TearDown(); |
| |
| // Delete the dialogs first since they hold a pointer to their listener. |
| dialog_ = NULL; |
| listener_.reset(); |
| second_dialog_ = NULL; |
| second_listener_.reset(); |
| } |
| |
| void CheckJavascriptErrors() { |
| content::RenderFrameHost* host = |
| dialog_->GetRenderViewHost()->GetMainFrame(); |
| base::Value value = |
| content::ExecuteScriptAndGetValue(host, "window.JSErrorCount"); |
| int js_error_count = value.GetInt(); |
| ASSERT_EQ(0, js_error_count); |
| } |
| |
| void ClickElement(const std::string& selector) { |
| content::RenderFrameHost* frame_host = |
| dialog_->GetRenderViewHost()->GetMainFrame(); |
| |
| auto* web_contents = content::WebContents::FromRenderFrameHost(frame_host); |
| CHECK(web_contents); |
| |
| int x; |
| ASSERT_TRUE(content::ExecuteScriptAndExtractInt( |
| web_contents, |
| "var bounds = document.querySelector('" + selector + |
| "').getBoundingClientRect();" |
| "domAutomationController.send(" |
| " Math.floor(bounds.left + bounds.width / 2));", |
| &x)); |
| |
| int y; |
| ASSERT_TRUE(content::ExecuteScriptAndExtractInt( |
| web_contents, |
| "var bounds = document.querySelector('" + selector + |
| "').getBoundingClientRect();" |
| "domAutomationController.send(" |
| " Math.floor(bounds.top + bounds.height / 2));", |
| &y)); |
| |
| LOG(INFO) << "ClickElement " << selector << " (" << x << "," << y << ")"; |
| constexpr auto kButton = blink::WebMouseEvent::Button::kLeft; |
| content::SimulateMouseClickAt(web_contents, 0, kButton, gfx::Point(x, y)); |
| } |
| |
| void OpenDialog(ui::SelectFileDialog::Type dialog_type, |
| const base::FilePath& file_path, |
| const gfx::NativeWindow& owning_window, |
| const std::string& additional_message, |
| const bool check_js_errors = false) { |
| // Open the file dialog: Files app will signal that it is loaded via the |
| // "ready" chrome.test.sendMessage(). |
| const bool will_reply = false; |
| ExtensionTestMessageListener init_listener("ready", will_reply); |
| |
| std::unique_ptr<ExtensionTestMessageListener> additional_listener; |
| if (!additional_message.empty()) { |
| additional_listener.reset( |
| new ExtensionTestMessageListener(additional_message, will_reply)); |
| } |
| |
| // Include a file type filter. This triggers additional functionality within |
| // the Files app. |
| ui::SelectFileDialog::FileTypeInfo file_types; |
| file_types.extensions = {{"html"}}; |
| dialog_->SelectFile(dialog_type, base::string16() /* title */, file_path, |
| GetParam() ? &file_types : nullptr, |
| 0 /* file_type_index */, |
| FILE_PATH_LITERAL("") /* default_extension */, |
| owning_window, this /* params */); |
| |
| LOG(INFO) << "Waiting for JavaScript ready message."; |
| ASSERT_TRUE(init_listener.WaitUntilSatisfied()); |
| |
| if (additional_listener.get()) { |
| LOG(INFO) << "Waiting for JavaScript " << additional_message |
| << " message."; |
| ASSERT_TRUE(additional_listener->WaitUntilSatisfied()); |
| } |
| |
| // Dialog should be running now. |
| ASSERT_TRUE(dialog_->IsRunning(owning_window)); |
| |
| if (check_js_errors) { |
| // TODO(895703): Files app currently has errors during this call. Work |
| // out why and either fix or remove this code. |
| ASSERT_NO_FATAL_FAILURE(CheckJavascriptErrors()); |
| } |
| } |
| |
| bool OpenDialogIsResizable() const { |
| return dialog_->IsResizeable(); // See crrev.com/600185. |
| } |
| |
| void TryOpeningSecondDialog(const gfx::NativeWindow& owning_window) { |
| second_listener_.reset(new MockSelectFileDialogListener()); |
| second_dialog_ = new SelectFileDialogExtension(second_listener_.get(), |
| NULL); |
| |
| // The dialog type is not relevant for this test but is required: use the |
| // open file dialog type. |
| second_dialog_->SelectFile(ui::SelectFileDialog::SELECT_OPEN_FILE, |
| base::string16() /* title */, |
| base::FilePath() /* default_path */, |
| NULL /* file_types */, |
| 0 /* file_type_index */, |
| FILE_PATH_LITERAL("") /* default_extension */, |
| owning_window, |
| this /* params */); |
| } |
| |
| void CloseDialog(DialogButtonType button_type, |
| const gfx::NativeWindow& owning_window) { |
| // Inject JavaScript into the dialog to click the dialog |button_type|. |
| content::RenderViewHost* host = dialog_->GetRenderViewHost(); |
| std::string button_class = |
| (button_type == DIALOG_BTN_OK) ? ".button-panel .ok" : |
| ".button-panel .cancel"; |
| base::string16 script = base::ASCIIToUTF16( |
| "console.log(\'Test JavaScript injected.\');" |
| "document.querySelector(\'" + button_class + "\').click();"); |
| // The file selection handler code closes the dialog but does not return |
| // control to JavaScript, so do not wait for the script return value. |
| host->GetMainFrame()->ExecuteJavaScriptForTests(script, |
| base::NullCallback()); |
| |
| // Instead, wait for Listener notification that the window has closed. |
| LOG(INFO) << "Waiting for window close notification."; |
| listener_->WaitForCalled(); |
| |
| // Dialog no longer believes it is running. |
| if (owning_window) |
| ASSERT_FALSE(dialog_->IsRunning(owning_window)); |
| } |
| |
| base::test::ScopedFeatureList feature_list_; |
| base::ScopedTempDir tmp_dir_; |
| base::FilePath downloads_dir_; |
| |
| std::unique_ptr<MockSelectFileDialogListener> listener_; |
| scoped_refptr<SelectFileDialogExtension> dialog_; |
| |
| std::unique_ptr<MockSelectFileDialogListener> second_listener_; |
| scoped_refptr<SelectFileDialogExtension> second_dialog_; |
| }; |
| |
| IN_PROC_BROWSER_TEST_P(SelectFileDialogExtensionBrowserTest, CreateAndDestroy) { |
| // The browser window must exist for us to test dialog's parent window. |
| gfx::NativeWindow owning_window = browser()->window()->GetNativeWindow(); |
| ASSERT_NE(nullptr, owning_window); |
| |
| // Before we call SelectFile, the dialog should not be running/visible. |
| ASSERT_FALSE(dialog_->IsRunning(owning_window)); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(SelectFileDialogExtensionBrowserTest, DestroyListener) { |
| // Some users of SelectFileDialog destroy their listener before cleaning |
| // up the dialog. Make sure we don't crash. |
| dialog_->ListenerDestroyed(); |
| listener_.reset(); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(SelectFileDialogExtensionBrowserTest, CanResize) { |
| gfx::NativeWindow owning_window = browser()->window()->GetNativeWindow(); |
| ASSERT_NE(nullptr, owning_window); |
| |
| // Open the file dialog on the default path. |
| ASSERT_NO_FATAL_FAILURE(OpenDialog(ui::SelectFileDialog::SELECT_OPEN_FILE, |
| base::FilePath(), owning_window, "")); |
| |
| // The dialog should be resizable. |
| ASSERT_TRUE(OpenDialogIsResizable()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(SelectFileDialogExtensionBrowserTest, |
| CanResize_TabletMode) { |
| gfx::NativeWindow owning_window = browser()->window()->GetNativeWindow(); |
| ASSERT_NE(nullptr, owning_window); |
| |
| // Setup tablet mode. |
| test::SetAndWaitForTabletMode(true); |
| |
| // Open the file dialog on the default path. |
| ASSERT_NO_FATAL_FAILURE(OpenDialog(ui::SelectFileDialog::SELECT_OPEN_FILE, |
| base::FilePath(), owning_window, "")); |
| |
| // The dialog should not be resizable in tablet mode. |
| ASSERT_FALSE(OpenDialogIsResizable()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(SelectFileDialogExtensionBrowserTest, |
| SelectFileAndCancel) { |
| gfx::NativeWindow owning_window = browser()->window()->GetNativeWindow(); |
| ASSERT_NE(nullptr, owning_window); |
| |
| // Open the file dialog on the default path. |
| ASSERT_NO_FATAL_FAILURE(OpenDialog(ui::SelectFileDialog::SELECT_OPEN_FILE, |
| base::FilePath(), owning_window, "")); |
| // Click the "Cancel" button. |
| CloseDialog(DIALOG_BTN_CANCEL, owning_window); |
| |
| // Listener should have been informed of the cancellation. |
| ASSERT_FALSE(listener_->file_selected()); |
| ASSERT_TRUE(listener_->canceled()); |
| ASSERT_EQ(this, listener_->params()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(SelectFileDialogExtensionBrowserTest, |
| SelectFileAndOpen) { |
| gfx::NativeWindow owning_window = browser()->window()->GetNativeWindow(); |
| ASSERT_NE(nullptr, owning_window); |
| |
| // Create an empty file to provide the file to open. |
| const base::FilePath test_file = |
| downloads_dir_.AppendASCII("file_manager_test.html"); |
| { |
| base::ScopedAllowBlockingForTesting allow_io; |
| FILE* fp = base::OpenFile(test_file, "w"); |
| ASSERT_TRUE(fp != NULL); |
| ASSERT_TRUE(base::CloseFile(fp)); |
| } |
| |
| // Open the file dialog, providing a path to the file to open so the dialog |
| // will automatically select it. Ensure the "Open" button is enabled by |
| // waiting for notification from chrome.test.sendMessage(). |
| ASSERT_NO_FATAL_FAILURE(OpenDialog(ui::SelectFileDialog::SELECT_OPEN_FILE, |
| test_file, owning_window, |
| "dialog-ready")); |
| // Click the "Open" button. |
| CloseDialog(DIALOG_BTN_OK, owning_window); |
| |
| // Listener should have been informed that the file was opened. |
| ASSERT_TRUE(listener_->file_selected()); |
| ASSERT_FALSE(listener_->canceled()); |
| ASSERT_EQ(test_file, listener_->path()); |
| ASSERT_EQ(this, listener_->params()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(SelectFileDialogExtensionBrowserTest, |
| SelectFileAndSave) { |
| gfx::NativeWindow owning_window = browser()->window()->GetNativeWindow(); |
| ASSERT_NE(nullptr, owning_window); |
| |
| // Open the file dialog to save a file, providing a suggested file path. |
| // Ensure the "Save" button is enabled by waiting for notification from |
| // chrome.test.sendMessage(). |
| const base::FilePath test_file = |
| downloads_dir_.AppendASCII("file_manager_save.html"); |
| ASSERT_NO_FATAL_FAILURE(OpenDialog(ui::SelectFileDialog::SELECT_SAVEAS_FILE, |
| test_file, owning_window, |
| "dialog-ready")); |
| // Click the "Save" button. |
| CloseDialog(DIALOG_BTN_OK, owning_window); |
| |
| // Listener should have been informed that the file was saved. |
| ASSERT_TRUE(listener_->file_selected()); |
| ASSERT_FALSE(listener_->canceled()); |
| ASSERT_EQ(test_file, listener_->path()); |
| ASSERT_EQ(this, listener_->params()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(SelectFileDialogExtensionBrowserTest, |
| SelectFileVirtualKeyboard_TabletMode) { |
| gfx::NativeWindow owning_window = browser()->window()->GetNativeWindow(); |
| ASSERT_NE(nullptr, owning_window); |
| |
| // Setup tablet mode. |
| test::SetAndWaitForTabletMode(true); |
| |
| // Enable the virtual keyboard. |
| ash::mojom::ShellTestApiPtr shell_test_api; |
| content::ServiceManagerConnection::GetForProcess() |
| ->GetConnector() |
| ->BindInterface(ash::mojom::kServiceName, &shell_test_api); |
| ash::mojom::ShellTestApiAsyncWaiter waiter(shell_test_api.get()); |
| waiter.EnableVirtualKeyboard(); |
| |
| auto* client = ChromeKeyboardControllerClient::Get(); |
| EXPECT_FALSE(client->is_keyboard_visible()); |
| |
| // Open the file dialog to save a file, providing a suggested file path. |
| // Ensure the "Save" button is enabled by waiting for notification from |
| // chrome.test.sendMessage(). |
| const base::FilePath test_file = |
| downloads_dir_.AppendASCII("file_manager_save.html"); |
| ASSERT_NO_FATAL_FAILURE(OpenDialog(ui::SelectFileDialog::SELECT_SAVEAS_FILE, |
| test_file, owning_window, "dialog-ready")); |
| |
| // Click the dialog's filename input element. |
| ASSERT_NO_FATAL_FAILURE(ClickElement("#filename-input-textbox")); |
| |
| // The virtual keyboard should be shown. |
| KeyboardVisibleWaiter().Wait(); |
| EXPECT_TRUE(client->is_keyboard_visible()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(SelectFileDialogExtensionBrowserTest, |
| OpenSingletonTabAndCancel) { |
| gfx::NativeWindow owning_window = browser()->window()->GetNativeWindow(); |
| ASSERT_NE(nullptr, owning_window); |
| |
| // Open the file dialog on the default path. |
| ASSERT_NO_FATAL_FAILURE(OpenDialog(ui::SelectFileDialog::SELECT_OPEN_FILE, |
| base::FilePath(), owning_window, "")); |
| |
| // Open a singleton tab in background. |
| NavigateParams p(browser(), GURL("http://www.google.com"), |
| ui::PAGE_TRANSITION_LINK); |
| p.window_action = NavigateParams::SHOW_WINDOW; |
| p.disposition = WindowOpenDisposition::SINGLETON_TAB; |
| Navigate(&p); |
| |
| // Click the "Cancel" button. |
| CloseDialog(DIALOG_BTN_CANCEL, owning_window); |
| |
| // Listener should have been informed of the cancellation. |
| ASSERT_FALSE(listener_->file_selected()); |
| ASSERT_TRUE(listener_->canceled()); |
| ASSERT_EQ(this, listener_->params()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(SelectFileDialogExtensionBrowserTest, OpenTwoDialogs) { |
| gfx::NativeWindow owning_window = browser()->window()->GetNativeWindow(); |
| ASSERT_NE(nullptr, owning_window); |
| |
| // Open the file dialog on the default path. |
| ASSERT_NO_FATAL_FAILURE(OpenDialog(ui::SelectFileDialog::SELECT_OPEN_FILE, |
| base::FilePath(), owning_window, "")); |
| |
| // Requests to open a second file dialog should fail. |
| TryOpeningSecondDialog(owning_window); |
| ASSERT_FALSE(second_dialog_->IsRunning(owning_window)); |
| |
| // Click the "Cancel" button. |
| CloseDialog(DIALOG_BTN_CANCEL, owning_window); |
| |
| // Listener should have been informed of the cancellation. |
| ASSERT_FALSE(listener_->file_selected()); |
| ASSERT_TRUE(listener_->canceled()); |
| ASSERT_EQ(this, listener_->params()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(SelectFileDialogExtensionBrowserTest, FileInputElement) { |
| gfx::NativeWindow owning_window = browser()->window()->GetNativeWindow(); |
| ASSERT_NE(nullptr, owning_window); |
| |
| // Start the embedded test server. |
| base::FilePath source_dir; |
| ASSERT_TRUE(base::PathService::Get(base::DIR_SOURCE_ROOT, &source_dir)); |
| auto test_data_dir = source_dir.AppendASCII("chrome") |
| .AppendASCII("test") |
| .AppendASCII("data") |
| .AppendASCII("chromeos") |
| .AppendASCII("file_manager"); |
| embedded_test_server()->ServeFilesFromDirectory(test_data_dir); |
| ASSERT_TRUE(embedded_test_server()->Start()); |
| |
| // Navigate the browser to the file input element test page. |
| const GURL url = embedded_test_server()->GetURL("/file_input/element.html"); |
| ui_test_utils::NavigateToURL(browser(), url); |
| content::WebContents* web_contents = |
| browser()->tab_strip_model()->GetActiveWebContents(); |
| ASSERT_EQ(url, web_contents->GetLastCommittedURL()); |
| |
| // Create a listener for the file dialog's "ready" message. |
| ExtensionTestMessageListener listener("ready", false); |
| |
| // Click the file <input> element to open the file dialog. |
| constexpr auto kButton = blink::WebMouseEvent::Button::kLeft; |
| content::SimulateMouseClickAt(web_contents, 0, kButton, gfx::Point(0, 0)); |
| |
| // Wait for file dialog's "ready" message. |
| EXPECT_TRUE(listener.WaitUntilSatisfied()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(SelectFileDialogExtensionBrowserTest, |
| OpenDialogWithoutOwningWindow) { |
| gfx::NativeWindow owning_window = nullptr; |
| |
| // Open the file dialog with no |owning_window|. |
| ASSERT_NO_FATAL_FAILURE(OpenDialog(ui::SelectFileDialog::SELECT_OPEN_FILE, |
| base::FilePath(), owning_window, "")); |
| |
| // Click the "Cancel" button. |
| CloseDialog(DIALOG_BTN_CANCEL, owning_window); |
| |
| // Listener should have been informed of the cancellation. |
| ASSERT_TRUE(listener_->canceled()); |
| ASSERT_EQ(this, listener_->params()); |
| } |
| |
| IN_PROC_BROWSER_TEST_P(SelectFileDialogExtensionBrowserTest, MultipleOpenFile) { |
| // No use-after-free when Browser::OpenFile is called multiple times. |
| browser()->OpenFile(); |
| browser()->OpenFile(); |
| } |
| |
| INSTANTIATE_TEST_SUITE_P(SelectFileDialogExtensionBrowserTest, |
| SelectFileDialogExtensionBrowserTest, |
| testing::Bool()); |