diff options
11 files changed, 287 insertions, 153 deletions
diff --git a/chrome/browser/extensions/extension_file_browser_private_api.cc b/chrome/browser/extensions/extension_file_browser_private_api.cc index b0ad304..b767eb0 100644 --- a/chrome/browser/extensions/extension_file_browser_private_api.cc +++ b/chrome/browser/extensions/extension_file_browser_private_api.cc @@ -25,6 +25,7 @@ #include "chrome/browser/prefs/pref_service.h" #include "chrome/browser/prefs/scoped_user_pref_update.h" #include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/views/file_manager_dialog.h" #include "chrome/browser/ui/webui/extension_icon_source.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/extensions/extension.h" @@ -870,37 +871,6 @@ FileDialogFunction::FileDialogFunction() { FileDialogFunction::~FileDialogFunction() { } -// static -FileDialogFunction::Callback -FileDialogFunction::Callback::null_(NULL, NULL); - -// static -FileDialogFunction::Callback::Map FileDialogFunction::Callback::map_; - -// static -void FileDialogFunction::Callback::Add(int32 tab_id, - SelectFileDialog::Listener* listener, - void* params) { - if (map_.find(tab_id) == map_.end()) { - map_.insert(std::make_pair(tab_id, Callback(listener, params))); - } else { - DLOG_ASSERT("FileDialogFunction::AddCallback tab_id already present"); - } -} - -// static -void FileDialogFunction::Callback::Remove(int32 tab_id) { - map_.erase(tab_id); -} - -// static -const FileDialogFunction::Callback& -FileDialogFunction::Callback::Find(int32 tab_id) { - Callback::Map::const_iterator it = map_.find(tab_id); - return (it == map_.end()) ? null_ : it->second; -} - - int32 FileDialogFunction::GetTabId() const { int32 tab_id = 0; if (!dispatcher()) { @@ -920,16 +890,6 @@ int32 FileDialogFunction::GetTabId() const { return tab_id; } -const FileDialogFunction::Callback& FileDialogFunction::GetCallback() const { - return Callback::Find(GetTabId()); -} - -void FileDialogFunction::RemoveCallback() { - // Listeners expect to be invoked exactly once, so we need to remove our - // callback objects afterwards. - Callback::Remove(GetTabId()); -} - // GetFileSystemRootPathOnFileThread can only be called from the file thread, // so here we are. This function takes a vector of virtual paths, converts // them to local paths and calls GetLocalPathsResponseOnUIThread with the @@ -1010,17 +970,8 @@ void SelectFileFunction::GetLocalPathsResponseOnUIThread( } int index; args_->GetInteger(1, &index); - const Callback& callback = GetCallback(); - DCHECK(!callback.IsNull()); - if (!callback.IsNull()) { - LOG(INFO) << "FileBrowser: FileSelected"; - callback.listener()->FileSelected(files[0], - index, - callback.params()); - RemoveCallback(); // Listeners expect to be invoked exactly once. - } else { - LOG(WARNING) << "Callback not found"; - } + int32 tab_id = GetTabId(); + FileManagerDialog::OnFileSelected(tab_id, files[0], index); SendResponse(true); } @@ -1109,29 +1060,14 @@ bool SelectFilesFunction::RunImpl() { void SelectFilesFunction::GetLocalPathsResponseOnUIThread( const FilePathList& files, const std::string& internal_task_id) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - - const Callback& callback = GetCallback(); - DCHECK(!callback.IsNull()); - if (!callback.IsNull()) { - LOG(INFO) << "FileBrowser: MultiFilesSelected"; - callback.listener()->MultiFilesSelected(files, callback.params()); - RemoveCallback(); // Listeners expect to be invoked exactly once. - } else { - LOG(WARNING) << "Callback not found"; - } + int32 tab_id = GetTabId(); + FileManagerDialog::OnMultiFilesSelected(tab_id, files); SendResponse(true); } bool CancelFileDialogFunction::RunImpl() { - const Callback& callback = GetCallback(); - DCHECK(!callback.IsNull()); - if (!callback.IsNull()) { - LOG(INFO) << "FileBrowser: Cancel"; - callback.listener()->FileSelectionCanceled(callback.params()); - RemoveCallback(); // Listeners expect to be invoked exactly once. - } else { - LOG(WARNING) << "Callback not found"; - } + int32 tab_id = GetTabId(); + FileManagerDialog::OnFileSelectionCanceled(tab_id); SendResponse(true); return true; } diff --git a/chrome/browser/extensions/extension_file_browser_private_api.h b/chrome/browser/extensions/extension_file_browser_private_api.h index d087895..f283014 100644 --- a/chrome/browser/extensions/extension_file_browser_private_api.h +++ b/chrome/browser/extensions/extension_file_browser_private_api.h @@ -123,37 +123,6 @@ class FileDialogFunction public: FileDialogFunction(); - // Register/unregister callbacks. - // When file selection events occur in a tab, we'll call back the - // appropriate listener with the right params. - class Callback { - public: - Callback(SelectFileDialog::Listener* listener, - void* params) - : listener_(listener), - params_(params) { - } - SelectFileDialog::Listener* listener() const { return listener_; } - void* params() const { return params_; } - bool IsNull() const { return listener_ == NULL; } - - static void Add(int32 tab_id, - SelectFileDialog::Listener* listener, - void* params); - static void Remove(int32 tab_id); - static const Callback& Find(int32 tab_id); - static const Callback& null() { return null_; } - - private: - SelectFileDialog::Listener* listener_; - void* params_; - - // statics. - typedef std::map<int32, Callback> Map; - static Map map_; - static Callback null_; - }; - protected: typedef std::vector<GURL> UrlList; typedef std::vector<FilePath> FilePathList; @@ -168,13 +137,6 @@ class FileDialogFunction virtual void GetLocalPathsResponseOnUIThread(const FilePathList& files, const std::string& internal_task_id) {} - // Get the callback for the hosting tab. - const Callback& GetCallback() const; - - // Remove the callback for the hosting tab. - void RemoveCallback(); - - private: // Figure out the tab_id of the hosting tab. int32 GetTabId() const; }; diff --git a/chrome/browser/resources/file_manager/js/file_manager.js b/chrome/browser/resources/file_manager/js/file_manager.js index 20f94b2..d45da99 100644 --- a/chrome/browser/resources/file_manager/js/file_manager.js +++ b/chrome/browser/resources/file_manager/js/file_manager.js @@ -1682,6 +1682,9 @@ FileManager.prototype = { * the actual selection change event. */ FileManager.prototype.onSelectionChangeComplete_ = function(event) { + // Inform tests it's OK to click buttons now. + chrome.test.sendMessage('selection-change-complete'); + if (!this.showCheckboxes_) return; @@ -2166,24 +2169,13 @@ FileManager.prototype = { }; /** - * Close the extension window, but first give the extension API time to - * process the last request. Disable the UI during the wait. - * TODO(jamescook): Remove this hack by listening for an "OK to close" - * event from the C++ extension API, then call window.close(). - */ - FileManager.prototype.closeWindow_ = function() { - this.dialogDom_.style.opacity = '0'; - setTimeout(function() { window.close(); }, 0); - }; - - /** * Handle a click of the cancel button. Closes the window. * * @param {Event} event The click event. */ FileManager.prototype.onCancel_ = function(event) { + // Closes the window and does not return. chrome.fileBrowserPrivate.cancelDialog(); - this.closeWindow_(); }; /** @@ -2209,10 +2201,10 @@ FileManager.prototype = { if (!this.validateFileName_(filename, false)) return; + // Closes the window and does not return. chrome.fileBrowserPrivate.selectFile( currentDirUrl + encodeURIComponent(filename), this.getSelectedFilterIndex_(filename)); - this.closeWindow_(); return; } @@ -2237,8 +2229,8 @@ FileManager.prototype = { // Multi-file selection has no other restrictions. if (this.dialogType_ == FileManager.DialogType.SELECT_OPEN_MULTI_FILE) { + // Closes the window and does not return. chrome.fileBrowserPrivate.selectFiles(ary); - this.closeWindow_(); return; } @@ -2261,9 +2253,9 @@ FileManager.prototype = { throw new Error('Selected entry is not a file!'); } + // Closes the window and does not return. chrome.fileBrowserPrivate.selectFile( ary[0], this.getSelectedFilterIndex_(ary[0])); - this.closeWindow_(); }; /** diff --git a/chrome/browser/ui/views/extensions/extension_dialog.cc b/chrome/browser/ui/views/extensions/extension_dialog.cc index 4455ec0..b6b5123 100644 --- a/chrome/browser/ui/views/extensions/extension_dialog.cc +++ b/chrome/browser/ui/views/extensions/extension_dialog.cc @@ -10,6 +10,7 @@ #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/views/bubble/bubble_border.h" +#include "chrome/browser/ui/views/extensions/extension_dialog_observer.h" #include "chrome/browser/ui/views/frame/browser_view.h" #include "chrome/common/extensions/extension.h" #include "content/browser/renderer_host/render_view_host.h" @@ -28,7 +29,7 @@ ExtensionDialog::ExtensionDialog(ExtensionHost* host, views::Widget* frame, const gfx::Rect& relative_to, int width, - int height, Observer* observer) + int height, ExtensionDialogObserver* observer) : BrowserBubble(host->view(), frame, relative_to, @@ -61,7 +62,7 @@ ExtensionDialog* ExtensionDialog::Show( Browser* browser, int width, int height, - Observer* observer) { + ExtensionDialogObserver* observer) { CHECK(browser); ExtensionProcessManager* manager = browser->profile()->GetExtensionProcessManager(); diff --git a/chrome/browser/ui/views/extensions/extension_dialog.h b/chrome/browser/ui/views/extensions/extension_dialog.h index a83dac4..9a601e2 100644 --- a/chrome/browser/ui/views/extensions/extension_dialog.h +++ b/chrome/browser/ui/views/extensions/extension_dialog.h @@ -7,12 +7,13 @@ #pragma once #include "base/memory/ref_counted.h" -#include "chrome/browser/extensions/extension_host.h" #include "chrome/browser/ui/views/browser_bubble.h" #include "chrome/browser/ui/views/extensions/extension_view.h" #include "content/common/notification_observer.h" +#include "content/common/notification_registrar.h" class Browser; +class ExtensionDialogObserver; class ExtensionHost; class GURL; class Profile; @@ -32,15 +33,6 @@ class ExtensionDialog : public BrowserBubble, public NotificationObserver, public base::RefCounted<ExtensionDialog> { public: - // Observer to ExtensionDialog events. - class Observer { - public: - // Called when the ExtensionDialog is closing. Note that it - // is ref-counted, and thus will be released shortly after - // making this delegate call. - virtual void ExtensionDialogIsClosing(ExtensionDialog* popup) = 0; - }; - virtual ~ExtensionDialog(); // Create and show a dialog with |url| centered over the browser window. @@ -49,7 +41,7 @@ class ExtensionDialog : public BrowserBubble, static ExtensionDialog* Show(const GURL& url, Browser* browser, int width, int height, - Observer* observer); + ExtensionDialogObserver* observer); // Notifies the dialog that the observer has been destroyed and should not // be sent notifications. @@ -83,7 +75,7 @@ class ExtensionDialog : public BrowserBubble, private: ExtensionDialog(ExtensionHost* host, views::Widget* frame, const gfx::Rect& relative_to, int width, int height, - Observer* observer); + ExtensionDialogObserver* observer); // The contained host for the view. scoped_ptr<ExtensionHost> extension_host_; @@ -98,7 +90,7 @@ class ExtensionDialog : public BrowserBubble, NotificationRegistrar registrar_; // The observer of this popup. - Observer* observer_; + ExtensionDialogObserver* observer_; DISALLOW_COPY_AND_ASSIGN(ExtensionDialog); }; diff --git a/chrome/browser/ui/views/extensions/extension_dialog_observer.cc b/chrome/browser/ui/views/extensions/extension_dialog_observer.cc new file mode 100644 index 0000000..c9a4b15 --- /dev/null +++ b/chrome/browser/ui/views/extensions/extension_dialog_observer.cc @@ -0,0 +1,9 @@ +// Copyright (c) 2011 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/extensions/extension_dialog_observer.h" + +ExtensionDialogObserver::ExtensionDialogObserver() {} + +ExtensionDialogObserver::~ExtensionDialogObserver() {} diff --git a/chrome/browser/ui/views/extensions/extension_dialog_observer.h b/chrome/browser/ui/views/extensions/extension_dialog_observer.h new file mode 100644 index 0000000..7883d59 --- /dev/null +++ b/chrome/browser/ui/views/extensions/extension_dialog_observer.h @@ -0,0 +1,23 @@ +// Copyright (c) 2011 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. + +#ifndef CHROME_BROWSER_UI_VIEWS_EXTENSIONS_EXTENSION_DIALOG_OBSERVER_H_ +#define CHROME_BROWSER_UI_VIEWS_EXTENSIONS_EXTENSION_DIALOG_OBSERVER_H_ +#pragma once + +class ExtensionDialog; + +// Observer to ExtensionDialog events. +class ExtensionDialogObserver { + public: + ExtensionDialogObserver(); + virtual ~ExtensionDialogObserver(); + + // Called when the ExtensionDialog is closing. Note that it + // is ref-counted, and thus will be released shortly after + // making this delegate call. + virtual void ExtensionDialogIsClosing(ExtensionDialog* popup) = 0; +}; + +#endif // CHROME_BROWSER_UI_VIEWS_EXTENSIONS_EXTENSION_DIALOG_OBSERVER_H_ diff --git a/chrome/browser/ui/views/file_manager_dialog.cc b/chrome/browser/ui/views/file_manager_dialog.cc index 8aed05e..27f923b4 100644 --- a/chrome/browser/ui/views/file_manager_dialog.cc +++ b/chrome/browser/ui/views/file_manager_dialog.cc @@ -7,6 +7,7 @@ #include "base/logging.h" #include "base/memory/ref_counted.h" #include "chrome/browser/extensions/extension_file_browser_private_api.h" +#include "chrome/browser/extensions/extension_host.h" #include "chrome/browser/extensions/file_manager_util.h" #include "chrome/browser/sessions/restore_tab_helper.h" #include "chrome/browser/ui/browser.h" @@ -21,6 +22,53 @@ namespace { const int kFileManagerWidth = 720; // pixels const int kFileManagerHeight = 580; // pixels + +// Object to hold references to file manager dialogs that have callbacks +// pending to their listeners. +class PendingDialog { + public: + static void Add(int32 tab_id, FileManagerDialog* dialog); + static void Remove(int32 tab_id); + static FileManagerDialog* Find(int32 tab_id); + + private: + explicit PendingDialog(FileManagerDialog* dialog) + : dialog_(dialog) { + } + + scoped_refptr<FileManagerDialog> dialog_; + + typedef std::map<int32, PendingDialog> Map; + static Map map_; +}; + +// static +PendingDialog::Map PendingDialog::map_; + +// static +void PendingDialog::Add(int32 tab_id, FileManagerDialog* dialog) { + DCHECK(dialog); + if (map_.find(tab_id) == map_.end()) + map_.insert(std::make_pair(tab_id, PendingDialog(dialog))); + else + LOG(WARNING) << "Duplicate pending dialog " << tab_id; +} + +// static +void PendingDialog::Remove(int32 tab_id) { + map_.erase(tab_id); +} + +// static +FileManagerDialog* PendingDialog::Find(int32 tab_id) { + Map::const_iterator it = map_.find(tab_id); + if (it == map_.end()) { + LOG(WARNING) << "Pending dialog not found " << tab_id; + return NULL; + } + return it->second.dialog_.get(); +} + } // namespace // Linking this implementation of SelectFileDialog::Create into the target @@ -35,6 +83,7 @@ SelectFileDialog* SelectFileDialog::Create(Listener* listener) { FileManagerDialog::FileManagerDialog(Listener* listener) : SelectFileDialog(listener), + params_(NULL), tab_id_(0), owner_window_(0) { } @@ -42,7 +91,7 @@ FileManagerDialog::FileManagerDialog(Listener* listener) FileManagerDialog::~FileManagerDialog() { if (extension_dialog_) extension_dialog_->ObserverDestroyed(); - FileDialogFunction::Callback::Remove(tab_id_); + PendingDialog::Remove(tab_id_); } bool FileManagerDialog::IsRunning(gfx::NativeWindow owner_window) const { @@ -51,15 +100,53 @@ bool FileManagerDialog::IsRunning(gfx::NativeWindow owner_window) const { void FileManagerDialog::ListenerDestroyed() { listener_ = NULL; - FileDialogFunction::Callback::Remove(tab_id_); + params_ = NULL; + PendingDialog::Remove(tab_id_); } void FileManagerDialog::ExtensionDialogIsClosing(ExtensionDialog* dialog) { - LOG(INFO) << "FileBrowser: ExtensionDialogIsClosing"; owner_window_ = NULL; // Release our reference to the dialog to allow it to close. extension_dialog_ = NULL; - FileDialogFunction::Callback::Remove(tab_id_); + PendingDialog::Remove(tab_id_); +} + +void FileManagerDialog::Close() { + if (extension_dialog_) + extension_dialog_->Close(); + PendingDialog::Remove(tab_id_); +} + +// static +void FileManagerDialog::OnFileSelected( + int32 tab_id, const FilePath& path, int index) { + FileManagerDialog* self = PendingDialog::Find(tab_id); + if (self) { + DCHECK(self->listener_); + self->listener_->FileSelected(path, index, self->params_); + self->Close(); + } +} + +// static +void FileManagerDialog::OnMultiFilesSelected( + int32 tab_id, const std::vector<FilePath>& files) { + FileManagerDialog* self = PendingDialog::Find(tab_id); + if (self) { + DCHECK(self->listener_); + self->listener_->MultiFilesSelected(files, self->params_); + self->Close(); + } +} + +// static +void FileManagerDialog::OnFileSelectionCanceled(int32 tab_id) { + FileManagerDialog* self = PendingDialog::Find(tab_id); + if (self) { + DCHECK(self->listener_); + self->listener_->FileSelectionCanceled(self->params_); + self->Close(); + } } RenderViewHost* FileManagerDialog::GetRenderViewHost() { @@ -99,7 +186,7 @@ void FileManagerDialog::SelectFileImpl( // Connect our listener to FileDialogFunction's per-tab callbacks. TabContentsWrapper* tab = owner_browser->GetSelectedTabContentsWrapper(); int32 tab_id = (tab ? tab->restore_tab_helper()->session_id().id() : 0); - FileDialogFunction::Callback::Add(tab_id, listener_, params); + PendingDialog::Add(tab_id, this); tab_id_ = tab_id; owner_window_ = owner_window; diff --git a/chrome/browser/ui/views/file_manager_dialog.h b/chrome/browser/ui/views/file_manager_dialog.h index 22e0677..8989f12 100644 --- a/chrome/browser/ui/views/file_manager_dialog.h +++ b/chrome/browser/ui/views/file_manager_dialog.h @@ -6,17 +6,21 @@ #define CHROME_BROWSER_UI_VIEWS_FILE_MANAGER_DIALOG_H_ #pragma once +#include <map> + #include "base/memory/ref_counted.h" #include "chrome/browser/ui/shell_dialogs.h" // SelectFileDialog -#include "chrome/browser/ui/views/extensions/extension_dialog.h" +#include "chrome/browser/ui/views/extensions/extension_dialog_observer.h" +#include "ui/gfx/native_widget_types.h" // gfx::NativeWindow +class ExtensionDialog; class RenderViewHost; // Shows a dialog box for selecting a file or a folder, using the // file manager extension implementation. class FileManagerDialog : public SelectFileDialog, - public ExtensionDialog::Observer { + public ExtensionDialogObserver { public: explicit FileManagerDialog(SelectFileDialog::Listener* listener); @@ -28,6 +32,13 @@ class FileManagerDialog // ExtensionDialog::Observer implementation. virtual void ExtensionDialogIsClosing(ExtensionDialog* dialog) OVERRIDE; + // Routes callback to appropriate SelectFileDialog::Listener based on + // the owning |tab_id|, then closes the dialog. + static void OnFileSelected(int32 tab_id, const FilePath& path, int index); + static void OnMultiFilesSelected(int32 tab_id, + const std::vector<FilePath>& files); + static void OnFileSelectionCanceled(int32 tab_id); + // For testing, so we can inject JavaScript into the contained view. RenderViewHost* GetRenderViewHost(); @@ -46,6 +57,14 @@ class FileManagerDialog // Object is ref-counted, see SelectFileDialog. virtual ~FileManagerDialog(); + // Closes the dialog and cleans up callbacks and listeners. + void Close(); + + // Posts a task to close the dialog. + void PostTaskToClose(); + + void* params_; + // Host for the extension that implements this dialog. scoped_refptr<ExtensionDialog> extension_dialog_; diff --git a/chrome/browser/ui/views/file_manager_dialog_browsertest.cc b/chrome/browser/ui/views/file_manager_dialog_browsertest.cc index cc189ae..2d76a93 100644 --- a/chrome/browser/ui/views/file_manager_dialog_browsertest.cc +++ b/chrome/browser/ui/views/file_manager_dialog_browsertest.cc @@ -4,31 +4,53 @@ #include "chrome/browser/ui/views/file_manager_dialog.h" +#include "base/file_util.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/path_service.h" +#include "base/scoped_temp_dir.h" #include "base/threading/platform_thread.h" +#include "base/utf_string_conversions.h" // ASCIIToUTF16 #include "build/build_config.h" #include "chrome/browser/extensions/extension_browsertest.h" #include "chrome/browser/extensions/extension_test_message_listener.h" +#include "chrome/browser/profiles/profile.h" #include "chrome/browser/ui/browser.h" #include "chrome/browser/ui/browser_window.h" #include "chrome/browser/ui/shell_dialogs.h" // SelectFileDialog +#include "chrome/common/chrome_paths.h" #include "chrome/test/ui_test_utils.h" +#include "content/browser/renderer_host/render_view_host.h" +#include "webkit/fileapi/file_system_context.h" +#include "webkit/fileapi/file_system_mount_point_provider.h" +#include "webkit/fileapi/file_system_path_manager.h" class FileManagerDialogTest : public ExtensionBrowserTest { + public: + // Creates a file system mount point for a directory. + void AddMountPoint(const FilePath& path) { + fileapi::FileSystemPathManager* path_manager = + browser()->profile()->GetFileSystemContext()->path_manager(); + fileapi::ExternalFileSystemMountPointProvider* provider = + path_manager->external_provider(); + provider->AddMountPoint(path); + } }; class MockSelectFileDialogListener : public SelectFileDialog::Listener { public: MockSelectFileDialogListener() - : canceled_(false) { + : file_selected_(false), + canceled_(false) { } + bool file_selected() const { return file_selected_; } bool canceled() const { return canceled_; } // SelectFileDialog::Listener implementation. - virtual void FileSelected(const FilePath& path, int index, void* params) {} + virtual void FileSelected(const FilePath& path, int index, void* params) { + file_selected_ = true; + } virtual void MultiFilesSelected( const std::vector<FilePath>& files, void* params) {} virtual void FileSelectionCanceled(void* params) { @@ -36,6 +58,7 @@ class MockSelectFileDialogListener : public SelectFileDialog::Listener { } private: + bool file_selected_; bool canceled_; DISALLOW_COPY_AND_ASSIGN(MockSelectFileDialogListener); @@ -107,11 +130,13 @@ IN_PROC_BROWSER_TEST_F(FileManagerDialogTest, SelectFileAndCancel) { NotificationType::RENDER_WIDGET_HOST_DESTROYED, NotificationService::AllSources()); RenderViewHost* host = dialog->GetRenderViewHost(); - std::wstring script = - L"console.log('Test JavaScript injected.');" - L"document.querySelector('.cancel').click();"; - ASSERT_TRUE(ui_test_utils::ExecuteJavaScript( - host, L"" /* frame_xpath */, script)); + string16 main_frame; + string16 script = ASCIIToUTF16( + "console.log(\'Test JavaScript injected.\');" + "document.querySelector(\'.cancel\').click();"); + // The file selection handler closes the dialog and does not return control + // to JavaScript, so do not wait for return values. + host->ExecuteJavascriptInWebFrame(main_frame, script); LOG(INFO) << "Waiting for window close notification."; host_destroyed.Wait(); @@ -119,9 +144,93 @@ IN_PROC_BROWSER_TEST_F(FileManagerDialogTest, SelectFileAndCancel) { ASSERT_FALSE(dialog->IsRunning(owning_window)); // Listener should have been informed of the cancellation. + ASSERT_FALSE(listener->file_selected()); ASSERT_TRUE(listener->canceled()); // Enforce deleting the dialog first. dialog.release(); listener.reset(); } + +IN_PROC_BROWSER_TEST_F(FileManagerDialogTest, SelectFileAndOpen) { + // Create the dialog wrapper object, but don't show it yet. + scoped_ptr<MockSelectFileDialogListener> listener( + new MockSelectFileDialogListener()); + scoped_refptr<FileManagerDialog> dialog = + new FileManagerDialog(listener.get()); + + // Allow the tmp directory to be mounted. We explicitly use /tmp because + // it it whitelisted for file system access on Chrome OS. + FilePath tmp_dir("/tmp"); + AddMountPoint(tmp_dir); + + // Create a directory with a single file in it. ScopedTempDir will delete + // itself and our temp file when it goes out of scope. + ScopedTempDir scoped_temp_dir; + ASSERT_TRUE(scoped_temp_dir.CreateUniqueTempDirUnderPath(tmp_dir)); + FilePath temp_dir = scoped_temp_dir.path(); + FilePath test_file = temp_dir.AppendASCII("file_manager_test.html"); + + // Put HTML with a known title into the file. + FILE* fp = file_util::OpenFile(test_file, "w"); + ASSERT_TRUE(fp != NULL); + fputs( + "<html>" + " <head>" + " <title>File Manager Test</title>" + " </head>" + " <body>" + " <p>Test file</p>" + " </body>" + "</html>", + fp); + ASSERT_TRUE(file_util::CloseFile(fp)); + + // Spawn a dialog to open a file. Provide the path to the file so the dialog + // will automatically select it. Ensure that the OK button is enabled by + // waiting for chrome.test.sendMessage('selection-change-complete'). + ExtensionTestMessageListener msg_listener("selection-change-complete", + false /* will_reply */); + gfx::NativeWindow owning_window = browser()->window()->GetNativeHandle(); + dialog->SelectFile(SelectFileDialog::SELECT_OPEN_FILE, + string16() /* title */, + test_file, + NULL /* file_types */, + 0 /* file_type_index */, + FILE_PATH_LITERAL("") /* default_extension */, + NULL /* source_contents */, + owning_window, + NULL /* params */); + LOG(INFO) << "Waiting for JavaScript selection-change-complete message."; + ASSERT_TRUE(msg_listener.WaitUntilSatisfied()); + + // Dialog should be running now. + ASSERT_TRUE(dialog->IsRunning(owning_window)); + + // Inject JavaScript to click the open button and wait for notification + // that the window has closed. + ui_test_utils::WindowedNotificationObserver host_destroyed( + NotificationType::RENDER_WIDGET_HOST_DESTROYED, + NotificationService::AllSources()); + RenderViewHost* host = dialog->GetRenderViewHost(); + string16 main_frame; + string16 script = ASCIIToUTF16( + "console.log(\'Test JavaScript injected.\');" + "document.querySelector('.ok').click();"); + // The file selection handler closes the dialog and does not return control + // to JavaScript, so do not wait for return values. + host->ExecuteJavascriptInWebFrame(main_frame, script); + LOG(INFO) << "Waiting for window close notification."; + host_destroyed.Wait(); + + // Dialog no longer believes it is running. + ASSERT_FALSE(dialog->IsRunning(owning_window)); + + // Listener should have been informed that the file was opened. + ASSERT_TRUE(listener->file_selected()); + ASSERT_FALSE(listener->canceled()); + + // Enforce deleting the dialog before the listener. + dialog.release(); + listener.reset(); +} diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 107d2c4..5068cab 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -3053,6 +3053,8 @@ 'browser/ui/views/extensions/browser_action_drag_data.h', 'browser/ui/views/extensions/browser_action_overflow_menu_controller.cc', 'browser/ui/views/extensions/browser_action_overflow_menu_controller.h', + 'browser/ui/views/extensions/extension_dialog_observer.cc', + 'browser/ui/views/extensions/extension_dialog_observer.h', 'browser/ui/views/extensions/extension_dialog.cc', 'browser/ui/views/extensions/extension_dialog.h', 'browser/ui/views/extensions/extension_install_dialog_view.cc', @@ -4164,6 +4166,8 @@ ['include', '^browser/ui/views/extensions/browser_action_drag_data.h'], ['include', '^browser/ui/views/extensions/browser_action_overflow_menu_controller.cc'], ['include', '^browser/ui/views/extensions/browser_action_overflow_menu_controller.h'], + ['include', '^browser/ui/views/extensions/extension_dialog_observer.cc'], + ['include', '^browser/ui/views/extensions/extension_dialog_observer.h'], ['include', '^browser/ui/views/extensions/extension_dialog.cc'], ['include', '^browser/ui/views/extensions/extension_dialog.h'], ['include', '^browser/ui/views/extensions/extension_install_dialog_view.cc'], |