summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--chrome/browser/extensions/extension_file_browser_private_api.cc78
-rw-r--r--chrome/browser/extensions/extension_file_browser_private_api.h38
-rw-r--r--chrome/browser/resources/file_manager/js/file_manager.js22
-rw-r--r--chrome/browser/ui/views/extensions/extension_dialog.cc5
-rw-r--r--chrome/browser/ui/views/extensions/extension_dialog.h18
-rw-r--r--chrome/browser/ui/views/extensions/extension_dialog_observer.cc9
-rw-r--r--chrome/browser/ui/views/extensions/extension_dialog_observer.h23
-rw-r--r--chrome/browser/ui/views/file_manager_dialog.cc97
-rw-r--r--chrome/browser/ui/views/file_manager_dialog.h23
-rw-r--r--chrome/browser/ui/views/file_manager_dialog_browsertest.cc123
-rw-r--r--chrome/chrome_browser.gypi4
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'],