Commit e5752ae0 authored by Patrick Monette's avatar Patrick Monette Committed by Commit Bot

Refactor the SelectFileDialog implementation on Windows

This is meant to make the code easier to read by making ownership more
obvious and standardize a little bit the different functions that
implement the various dialog types.

Also will facilitate a possible transition to the IFileDialog interface

This CL should not change the behavior of the dialogs.

Bug: 73098,884075
Change-Id: I0d2f5cc02e1d08ab3f54385fb166bc34ae1f5b53
Reviewed-on: https://chromium-review.googlesource.com/c/1249911Reviewed-by: default avatarScott Violet <sky@chromium.org>
Reviewed-by: default avatarRobert Liao <robliao@chromium.org>
Commit-Queue: Patrick Monette <pmonette@chromium.org>
Cr-Commit-Position: refs/heads/master@{#599827}
parent 8a961977
...@@ -47,13 +47,16 @@ class NetErrorDiagnosticsDialog : public ui::BaseShellDialogImpl { ...@@ -47,13 +47,16 @@ class NetErrorDiagnosticsDialog : public ui::BaseShellDialogImpl {
if (IsRunningDialogForOwner(parent)) if (IsRunningDialogForOwner(parent))
return; return;
RunState run_state = BeginRun(parent); std::unique_ptr<RunState> run_state = BeginRun(parent);
run_state.dialog_task_runner->PostTaskAndReply(
scoped_refptr<base::SingleThreadTaskRunner> task_runner =
run_state->dialog_task_runner;
task_runner->PostTaskAndReply(
FROM_HERE, FROM_HERE,
base::Bind(&NetErrorDiagnosticsDialog::ShowDialogOnPrivateThread, base::BindOnce(&NetErrorDiagnosticsDialog::ShowDialogOnPrivateThread,
base::Unretained(this), parent, failed_url), base::Unretained(this), parent, failed_url),
base::Bind(&NetErrorDiagnosticsDialog::DiagnosticsDone, base::BindOnce(&NetErrorDiagnosticsDialog::DiagnosticsDone,
base::Unretained(this), run_state, callback)); base::Unretained(this), std::move(run_state), callback));
} }
private: private:
...@@ -68,8 +71,9 @@ class NetErrorDiagnosticsDialog : public ui::BaseShellDialogImpl { ...@@ -68,8 +71,9 @@ class NetErrorDiagnosticsDialog : public ui::BaseShellDialogImpl {
NdfCloseIncident(incident_handle); NdfCloseIncident(incident_handle);
} }
void DiagnosticsDone(RunState run_state, const base::Closure& callback) { void DiagnosticsDone(std::unique_ptr<RunState> run_state,
EndRun(run_state); const base::Closure& callback) {
EndRun(std::move(run_state));
callback.Run(); callback.Run();
} }
......
...@@ -40,18 +40,21 @@ class CertificateViewerDialog : public ui::BaseShellDialogImpl { ...@@ -40,18 +40,21 @@ class CertificateViewerDialog : public ui::BaseShellDialogImpl {
return; return;
} }
RunState run_state = BeginRun(parent); std::unique_ptr<RunState> run_state = BeginRun(parent);
run_state.dialog_task_runner->PostTaskAndReply(
scoped_refptr<base::SingleThreadTaskRunner> task_runner =
run_state->dialog_task_runner;
task_runner->PostTaskAndReply(
FROM_HERE, FROM_HERE,
base::Bind(&CertificateViewerDialog::ShowOnDialogThread, base::BindOnce(&CertificateViewerDialog::ShowOnDialogThread,
base::Unretained(this), run_state, base::Unretained(this), parent,
base::WrapRefCounted(cert)), base::WrapRefCounted(cert)),
base::Bind(&CertificateViewerDialog::OnDialogClosed, base::BindOnce(&CertificateViewerDialog::OnDialogClosed,
base::Unretained(this), run_state, callback)); base::Unretained(this), std::move(run_state), callback));
} }
private: private:
void ShowOnDialogThread(const RunState& run_state, void ShowOnDialogThread(HWND owner,
const scoped_refptr<net::X509Certificate>& cert) { const scoped_refptr<net::X509Certificate>& cert) {
// Create a new cert context and store containing just the certificate // Create a new cert context and store containing just the certificate
// and its intermediate certificates. // and its intermediate certificates.
...@@ -65,7 +68,7 @@ class CertificateViewerDialog : public ui::BaseShellDialogImpl { ...@@ -65,7 +68,7 @@ class CertificateViewerDialog : public ui::BaseShellDialogImpl {
CRYPTUI_VIEWCERTIFICATE_STRUCT view_info = {0}; CRYPTUI_VIEWCERTIFICATE_STRUCT view_info = {0};
view_info.dwSize = sizeof(view_info); view_info.dwSize = sizeof(view_info);
view_info.hwndParent = run_state.owner; view_info.hwndParent = owner;
view_info.dwFlags = view_info.dwFlags =
CRYPTUI_DISABLE_EDITPROPERTIES | CRYPTUI_DISABLE_ADDTOSTORE; CRYPTUI_DISABLE_EDITPROPERTIES | CRYPTUI_DISABLE_ADDTOSTORE;
view_info.pCertContext = cert_list.get(); view_info.pCertContext = cert_list.get();
...@@ -77,9 +80,9 @@ class CertificateViewerDialog : public ui::BaseShellDialogImpl { ...@@ -77,9 +80,9 @@ class CertificateViewerDialog : public ui::BaseShellDialogImpl {
::CryptUIDlgViewCertificate(&view_info, &properties_changed); ::CryptUIDlgViewCertificate(&view_info, &properties_changed);
} }
void OnDialogClosed(const RunState& run_state, void OnDialogClosed(std::unique_ptr<RunState> run_state,
const base::Closure& callback) { const base::Closure& callback) {
EndRun(run_state); EndRun(std::move(run_state));
// May delete |this|. // May delete |this|.
callback.Run(); callback.Run();
} }
......
...@@ -6,6 +6,8 @@ ...@@ -6,6 +6,8 @@
#include <commdlg.h> #include <commdlg.h>
#include <utility>
#include "base/bind.h" #include "base/bind.h"
#include "base/macros.h" #include "base/macros.h"
#include "base/single_thread_task_runner.h" #include "base/single_thread_task_runner.h"
...@@ -22,14 +24,6 @@ using content::BrowserThread; ...@@ -22,14 +24,6 @@ using content::BrowserThread;
// static // static
COLORREF ColorChooserDialog::g_custom_colors[16]; COLORREF ColorChooserDialog::g_custom_colors[16];
ColorChooserDialog::ExecuteOpenParams::ExecuteOpenParams(SkColor color,
RunState run_state,
HWND owner)
: color(color),
run_state(run_state),
owner(owner) {
}
ColorChooserDialog::ColorChooserDialog(views::ColorChooserListener* listener, ColorChooserDialog::ColorChooserDialog(views::ColorChooserListener* listener,
SkColor initial_color, SkColor initial_color,
gfx::NativeWindow owning_window) gfx::NativeWindow owning_window)
...@@ -37,11 +31,14 @@ ColorChooserDialog::ColorChooserDialog(views::ColorChooserListener* listener, ...@@ -37,11 +31,14 @@ ColorChooserDialog::ColorChooserDialog(views::ColorChooserListener* listener,
DCHECK(listener_); DCHECK(listener_);
CopyCustomColors(g_custom_colors, custom_colors_); CopyCustomColors(g_custom_colors, custom_colors_);
HWND owning_hwnd = views::HWNDForNativeWindow(owning_window); HWND owning_hwnd = views::HWNDForNativeWindow(owning_window);
ExecuteOpenParams execute_params(initial_color, BeginRun(owning_hwnd),
owning_hwnd); std::unique_ptr<RunState> run_state = BeginRun(owning_hwnd);
execute_params.run_state.dialog_task_runner->PostTask(
FROM_HERE, scoped_refptr<base::SingleThreadTaskRunner> task_runner =
base::Bind(&ColorChooserDialog::ExecuteOpen, this, execute_params)); run_state->dialog_task_runner;
task_runner->PostTask(FROM_HERE,
base::BindOnce(&ColorChooserDialog::ExecuteOpen, this,
initial_color, std::move(run_state)));
} }
bool ColorChooserDialog::IsRunning(gfx::NativeWindow owning_window) const { bool ColorChooserDialog::IsRunning(gfx::NativeWindow owning_window) const {
...@@ -58,25 +55,27 @@ void ColorChooserDialog::ListenerDestroyed() { ...@@ -58,25 +55,27 @@ void ColorChooserDialog::ListenerDestroyed() {
ColorChooserDialog::~ColorChooserDialog() { ColorChooserDialog::~ColorChooserDialog() {
} }
void ColorChooserDialog::ExecuteOpen(const ExecuteOpenParams& params) { void ColorChooserDialog::ExecuteOpen(SkColor color,
std::unique_ptr<RunState> run_state) {
CHOOSECOLOR cc; CHOOSECOLOR cc;
cc.lStructSize = sizeof(CHOOSECOLOR); cc.lStructSize = sizeof(CHOOSECOLOR);
cc.hwndOwner = params.owner; cc.hwndOwner = run_state->owner;
cc.rgbResult = skia::SkColorToCOLORREF(params.color); cc.rgbResult = skia::SkColorToCOLORREF(color);
cc.lpCustColors = custom_colors_; cc.lpCustColors = custom_colors_;
cc.Flags = CC_ANYCOLOR | CC_FULLOPEN | CC_RGBINIT; cc.Flags = CC_ANYCOLOR | CC_FULLOPEN | CC_RGBINIT;
bool success = !!ChooseColor(&cc); bool success = !!ChooseColor(&cc);
DisableOwner(cc.hwndOwner); DisableOwner(cc.hwndOwner);
base::PostTaskWithTraits( base::PostTaskWithTraits(
FROM_HERE, {BrowserThread::UI}, FROM_HERE, {BrowserThread::UI},
base::Bind(&ColorChooserDialog::DidCloseDialog, this, success, base::BindOnce(&ColorChooserDialog::DidCloseDialog, this, success,
skia::COLORREFToSkColor(cc.rgbResult), params.run_state)); skia::COLORREFToSkColor(cc.rgbResult),
std::move(run_state)));
} }
void ColorChooserDialog::DidCloseDialog(bool chose_color, void ColorChooserDialog::DidCloseDialog(bool chose_color,
SkColor color, SkColor color,
RunState run_state) { std::unique_ptr<RunState> run_state) {
EndRun(run_state); EndRun(std::move(run_state));
CopyCustomColors(custom_colors_, g_custom_colors); CopyCustomColors(custom_colors_, g_custom_colors);
if (listener_) { if (listener_) {
if (chose_color) if (chose_color)
......
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
#ifndef CHROME_BROWSER_UI_VIEWS_COLOR_CHOOSER_DIALOG_H_ #ifndef CHROME_BROWSER_UI_VIEWS_COLOR_CHOOSER_DIALOG_H_
#define CHROME_BROWSER_UI_VIEWS_COLOR_CHOOSER_DIALOG_H_ #define CHROME_BROWSER_UI_VIEWS_COLOR_CHOOSER_DIALOG_H_
#include <memory>
#include "base/macros.h" #include "base/macros.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "chrome/browser/ui/views/color_chooser_dialog.h" #include "chrome/browser/ui/views/color_chooser_dialog.h"
...@@ -33,24 +35,19 @@ class ColorChooserDialog ...@@ -33,24 +35,19 @@ class ColorChooserDialog
friend class base::RefCountedThreadSafe<ColorChooserDialog>; friend class base::RefCountedThreadSafe<ColorChooserDialog>;
~ColorChooserDialog() override; ~ColorChooserDialog() override;
struct ExecuteOpenParams {
ExecuteOpenParams(SkColor color, RunState run_state, HWND owner);
SkColor color;
RunState run_state;
HWND owner;
};
// Called on the dialog thread to show the actual color chooser. This is // Called on the dialog thread to show the actual color chooser. This is
// shown modal to |params.owner|. Once it's closed, calls back to // shown modal to |run_state->owner|. Once it's closed, calls back to
// DidCloseDialog() on the UI thread. // DidCloseDialog() on the UI thread.
void ExecuteOpen(const ExecuteOpenParams& params); void ExecuteOpen(SkColor color, std::unique_ptr<RunState> run_state);
// Called on the UI thread when a color chooser is closed. |chose_color| is // Called on the UI thread when a color chooser is closed. |chose_color| is
// true if the user actually chose a color, in which case |color| is the // true if the user actually chose a color, in which case |color| is the
// chosen color. Calls back to the |listener_| (if applicable) to notify it // chosen color. Calls back to the |listener_| (if applicable) to notify it
// of the results, and copies the modified array of |custom_colors_| back to // of the results, and copies the modified array of |custom_colors_| back to
// |g_custom_colors| so future dialogs will see the changes. // |g_custom_colors| so future dialogs will see the changes.
void DidCloseDialog(bool chose_color, SkColor color, RunState run_state); void DidCloseDialog(bool chose_color,
SkColor color,
std::unique_ptr<RunState> run_state);
// Copies the array of colors in |src| to |dst|. // Copies the array of colors in |src| to |dst|.
void CopyCustomColors(COLORREF*, COLORREF*); void CopyCustomColors(COLORREF*, COLORREF*);
......
...@@ -22,9 +22,17 @@ using content::WebContents; ...@@ -22,9 +22,17 @@ using content::WebContents;
namespace { namespace {
class FakePdfPrinterHandler; std::pair<std::vector<base::FilePath>, int> ExecuteCancelledSelectFileDialog(
bool GetOpenFileNameImpl(OPENFILENAME* ofn); ui::SelectFileDialog::Type type,
bool GetSaveFileNameImpl(FakePdfPrinterHandler* handler, OPENFILENAME* ofn); const base::string16& title,
const base::FilePath& default_path,
const base::string16& filter,
int file_type_index,
const base::string16& default_extension,
HWND owner) {
// Return an empty result to simulate a cancelled dialog.
return {{}, 0};
}
class FakePdfPrinterHandler : public PdfPrinterHandler { class FakePdfPrinterHandler : public PdfPrinterHandler {
public: public:
...@@ -32,7 +40,6 @@ class FakePdfPrinterHandler : public PdfPrinterHandler { ...@@ -32,7 +40,6 @@ class FakePdfPrinterHandler : public PdfPrinterHandler {
content::WebContents* contents, content::WebContents* contents,
printing::StickySettings* sticky_settings) printing::StickySettings* sticky_settings)
: PdfPrinterHandler(profile, contents, sticky_settings), : PdfPrinterHandler(profile, contents, sticky_settings),
init_called_(false),
save_failed_(false) {} save_failed_(false) {}
void FileSelected(const base::FilePath& path, void FileSelected(const base::FilePath& path,
...@@ -55,10 +62,6 @@ class FakePdfPrinterHandler : public PdfPrinterHandler { ...@@ -55,10 +62,6 @@ class FakePdfPrinterHandler : public PdfPrinterHandler {
bool save_failed() const { return save_failed_; } bool save_failed() const { return save_failed_; }
bool init_called() const { return init_called_; }
void set_init_called() { init_called_ = true; }
private: private:
// Simplified version of select file to avoid checking preferences and sticky // Simplified version of select file to avoid checking preferences and sticky
// settings in the test // settings in the test
...@@ -70,7 +73,7 @@ class FakePdfPrinterHandler : public PdfPrinterHandler { ...@@ -70,7 +73,7 @@ class FakePdfPrinterHandler : public PdfPrinterHandler {
file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("pdf")); file_type_info.extensions[0].push_back(FILE_PATH_LITERAL("pdf"));
select_file_dialog_ = ui::CreateWinSelectFileDialog( select_file_dialog_ = ui::CreateWinSelectFileDialog(
this, nullptr /*policy already checked*/, this, nullptr /*policy already checked*/,
base::Bind(GetOpenFileNameImpl), base::Bind(GetSaveFileNameImpl, this)); base::BindRepeating(&ExecuteCancelledSelectFileDialog));
select_file_dialog_->SelectFile( select_file_dialog_->SelectFile(
ui::SelectFileDialog::SELECT_SAVEAS_FILE, base::string16(), ui::SelectFileDialog::SELECT_SAVEAS_FILE, base::string16(),
default_filename, &file_type_info, 0, base::FilePath::StringType(), default_filename, &file_type_info, 0, base::FilePath::StringType(),
...@@ -78,38 +81,10 @@ class FakePdfPrinterHandler : public PdfPrinterHandler { ...@@ -78,38 +81,10 @@ class FakePdfPrinterHandler : public PdfPrinterHandler {
nullptr); nullptr);
} }
bool init_called_;
bool save_failed_; bool save_failed_;
base::RunLoop run_loop_; base::RunLoop run_loop_;
}; };
// Hook function to cancel the dialog when it is successfully initialized.
UINT_PTR CALLBACK PdfPrinterHandlerTestHookFunction(HWND hdlg,
UINT message,
WPARAM wparam,
LPARAM lparam) {
if (message != WM_INITDIALOG)
return 0;
OPENFILENAME* ofn = reinterpret_cast<OPENFILENAME*>(lparam);
FakePdfPrinterHandler* handler =
reinterpret_cast<FakePdfPrinterHandler*>(ofn->lCustData);
handler->set_init_called();
PostMessage(GetParent(hdlg), WM_COMMAND, MAKEWPARAM(IDCANCEL, 0), 0);
return 1;
}
bool GetOpenFileNameImpl(OPENFILENAME* ofn) {
return ::GetOpenFileName(ofn);
}
bool GetSaveFileNameImpl(FakePdfPrinterHandler* handler, OPENFILENAME* ofn) {
// Modify ofn so that the hook function will be called.
ofn->Flags |= OFN_ENABLEHOOK;
ofn->lpfnHook = PdfPrinterHandlerTestHookFunction;
ofn->lCustData = reinterpret_cast<LPARAM>(handler);
return ::GetSaveFileName(ofn);
}
} // namespace } // namespace
class PdfPrinterHandlerWinTest : public BrowserWithTestWindowTest { class PdfPrinterHandlerWinTest : public BrowserWithTestWindowTest {
...@@ -138,7 +113,6 @@ class PdfPrinterHandlerWinTest : public BrowserWithTestWindowTest { ...@@ -138,7 +113,6 @@ class PdfPrinterHandlerWinTest : public BrowserWithTestWindowTest {
TEST_F(PdfPrinterHandlerWinTest, TestSaveAsPdf) { TEST_F(PdfPrinterHandlerWinTest, TestSaveAsPdf) {
pdf_printer_->StartPrintToPdf(L"111111111111111111111.html"); pdf_printer_->StartPrintToPdf(L"111111111111111111111.html");
EXPECT_TRUE(pdf_printer_->init_called());
EXPECT_TRUE(pdf_printer_->save_failed()); EXPECT_TRUE(pdf_printer_->save_failed());
} }
...@@ -149,6 +123,5 @@ TEST_F(PdfPrinterHandlerWinTest, TestSaveAsPdfLongFileName) { ...@@ -149,6 +123,5 @@ TEST_F(PdfPrinterHandlerWinTest, TestSaveAsPdfLongFileName) {
L"11111111111111111111111111111111111111111111111111111111111111111111111" L"11111111111111111111111111111111111111111111111111111111111111111111111"
L"11111111111111111111111111111111111111111111111111111111111111111111111" L"11111111111111111111111111111111111111111111111111111111111111111111111"
L"1111111111111111111111111111111111111111111111111.html"); L"1111111111111111111111111111111111111111111111111.html");
EXPECT_TRUE(pdf_printer_->init_called());
EXPECT_TRUE(pdf_printer_->save_failed()); EXPECT_TRUE(pdf_printer_->save_failed());
} }
...@@ -41,26 +41,29 @@ class ManageCertificatesDialog : public ui::BaseShellDialogImpl { ...@@ -41,26 +41,29 @@ class ManageCertificatesDialog : public ui::BaseShellDialogImpl {
return; return;
} }
RunState run_state = BeginRun(parent); std::unique_ptr<RunState> run_state = BeginRun(parent);
run_state.dialog_task_runner->PostTaskAndReply(
base::SingleThreadTaskRunner* task_runner =
run_state->dialog_task_runner.get();
task_runner->PostTaskAndReply(
FROM_HERE, FROM_HERE,
base::Bind(&ManageCertificatesDialog::ShowOnDialogThread, base::BindOnce(&ManageCertificatesDialog::ShowOnDialogThread,
base::Unretained(this), run_state), base::Unretained(this), parent),
base::Bind(&ManageCertificatesDialog::OnDialogClosed, base::BindOnce(&ManageCertificatesDialog::OnDialogClosed,
base::Unretained(this), run_state, callback)); base::Unretained(this), std::move(run_state), callback));
} }
private: private:
void ShowOnDialogThread(const RunState& run_state) { void ShowOnDialogThread(HWND owner) {
CRYPTUI_CERT_MGR_STRUCT cert_mgr = {0}; CRYPTUI_CERT_MGR_STRUCT cert_mgr = {0};
cert_mgr.dwSize = sizeof(CRYPTUI_CERT_MGR_STRUCT); cert_mgr.dwSize = sizeof(CRYPTUI_CERT_MGR_STRUCT);
cert_mgr.hwndParent = run_state.owner; cert_mgr.hwndParent = owner;
::CryptUIDlgCertMgr(&cert_mgr); ::CryptUIDlgCertMgr(&cert_mgr);
} }
void OnDialogClosed(const RunState& run_state, void OnDialogClosed(std::unique_ptr<RunState> run_state,
const base::Closure& callback) { const base::Closure& callback) {
EndRun(run_state); EndRun(std::move(run_state));
// May delete |this|. // May delete |this|.
callback.Run(); callback.Run();
} }
......
...@@ -5,37 +5,13 @@ ...@@ -5,37 +5,13 @@
#include "chrome/browser/win/chrome_select_file_dialog_factory.h" #include "chrome/browser/win/chrome_select_file_dialog_factory.h"
#include <utility> #include <utility>
#include <vector>
#include "base/bind.h" #include "base/bind.h"
#include "base/bind_helpers.h" #include "base/callback.h"
#include "base/feature_list.h" #include "ui/shell_dialogs/execute_select_file_win.h"
#include "base/strings/string16.h"
#include "base/strings/string_util.h"
#include "base/win/win_util.h"
#include "chrome/services/util_win/public/mojom/constants.mojom.h"
#include "chrome/services/util_win/public/mojom/shell_util_win.mojom.h"
#include "content/public/common/service_manager_connection.h"
#include "services/service_manager/public/cpp/connector.h"
#include "ui/base/win/open_file_name_win.h"
#include "ui/shell_dialogs/select_file_dialog_win.h" #include "ui/shell_dialogs/select_file_dialog_win.h"
#include "ui/shell_dialogs/select_file_policy.h" #include "ui/shell_dialogs/select_file_policy.h"
namespace {
constexpr base::Feature kIsolateShellOperations{
"IsolateShellOperations", base::FEATURE_DISABLED_BY_DEFAULT};
chrome::mojom::ShellUtilWinPtr BindShellUtilWin() {
chrome::mojom::ShellUtilWinPtr shell_util_win_ptr;
content::ServiceManagerConnection::GetForProcess()
->GetConnector()
->BindInterface(chrome::mojom::kUtilWinServiceName, &shell_util_win_ptr);
return shell_util_win_ptr;
}
} // namespace
ChromeSelectFileDialogFactory::ChromeSelectFileDialogFactory() = default; ChromeSelectFileDialogFactory::ChromeSelectFileDialogFactory() = default;
ChromeSelectFileDialogFactory::~ChromeSelectFileDialogFactory() = default; ChromeSelectFileDialogFactory::~ChromeSelectFileDialogFactory() = default;
...@@ -43,66 +19,8 @@ ChromeSelectFileDialogFactory::~ChromeSelectFileDialogFactory() = default; ...@@ -43,66 +19,8 @@ ChromeSelectFileDialogFactory::~ChromeSelectFileDialogFactory() = default;
ui::SelectFileDialog* ChromeSelectFileDialogFactory::Create( ui::SelectFileDialog* ChromeSelectFileDialogFactory::Create(
ui::SelectFileDialog::Listener* listener, ui::SelectFileDialog::Listener* listener,
std::unique_ptr<ui::SelectFilePolicy> policy) { std::unique_ptr<ui::SelectFilePolicy> policy) {
// TODO(pmonette): Re-implement the OOP file dialogs using the ShellUtilWin
// interface.
return ui::CreateWinSelectFileDialog( return ui::CreateWinSelectFileDialog(
listener, std::move(policy), listener, std::move(policy), base::BindRepeating(&ui::ExecuteSelectFile));
base::Bind(&ChromeSelectFileDialogFactory::BlockingGetOpenFileName),
base::Bind(&ChromeSelectFileDialogFactory::BlockingGetSaveFileName));
}
// static
bool ChromeSelectFileDialogFactory::BlockingGetOpenFileName(OPENFILENAME* ofn) {
if (!base::FeatureList::IsEnabled(kIsolateShellOperations))
return ::GetOpenFileName(ofn) == TRUE;
mojo::SyncCallRestrictions::ScopedAllowSyncCall allow_sync_call;
std::vector<base::FilePath> files;
base::FilePath directory;
// Sync operation, it's OK for the shell_util_win_ptr to go out of scope right
// after the call.
chrome::mojom::ShellUtilWinPtr shell_util_win_ptr = BindShellUtilWin();
shell_util_win_ptr->CallGetOpenFileName(
base::win::HandleToUint32(ofn->hwndOwner),
static_cast<uint32_t>(ofn->Flags & ~OFN_ENABLEHOOK),
ui::win::OpenFileName::GetFilters(ofn),
ofn->lpstrInitialDir ? base::FilePath(ofn->lpstrInitialDir)
: base::FilePath(),
base::FilePath(ofn->lpstrFile), &directory, &files);
if (files.empty())
return false;
ui::win::OpenFileName::SetResult(directory, files, ofn);
return true;
}
// static
bool ChromeSelectFileDialogFactory::BlockingGetSaveFileName(OPENFILENAME* ofn) {
if (!base::FeatureList::IsEnabled(kIsolateShellOperations))
return ::GetSaveFileName(ofn) == TRUE;
mojo::SyncCallRestrictions::ScopedAllowSyncCall allow_sync_call;
uint32_t filter_index = 0;
base::FilePath path;
// Sync operation, it's OK for the shell_util_win_ptr to go out of scope right
// after the call.
chrome::mojom::ShellUtilWinPtr shell_util_win_ptr = BindShellUtilWin();
shell_util_win_ptr->CallGetSaveFileName(
base::win::HandleToUint32(ofn->hwndOwner),
static_cast<uint32_t>(ofn->Flags & ~OFN_ENABLEHOOK),
ui::win::OpenFileName::GetFilters(ofn), ofn->nFilterIndex,
ofn->lpstrInitialDir ? base::FilePath(ofn->lpstrInitialDir)
: base::FilePath(),
base::FilePath(ofn->lpstrFile),
ofn->lpstrDefExt ? base::string16(ofn->lpstrDefExt) : base::string16(),
&path, &filter_index);
if (path.empty())
return false;
base::wcslcpy(ofn->lpstrFile, path.value().c_str(), ofn->nMaxFile);
ofn->nFilterIndex = static_cast<DWORD>(filter_index);
return true;
} }
...@@ -5,16 +5,14 @@ ...@@ -5,16 +5,14 @@
#ifndef CHROME_BROWSER_WIN_CHROME_SELECT_FILE_DIALOG_FACTORY_H_ #ifndef CHROME_BROWSER_WIN_CHROME_SELECT_FILE_DIALOG_FACTORY_H_
#define CHROME_BROWSER_WIN_CHROME_SELECT_FILE_DIALOG_FACTORY_H_ #define CHROME_BROWSER_WIN_CHROME_SELECT_FILE_DIALOG_FACTORY_H_
#include <Windows.h> #include <memory>
#include <commdlg.h>
#include "base/macros.h" #include "base/macros.h"
#include "ui/shell_dialogs/select_file_dialog_factory.h" #include "ui/shell_dialogs/select_file_dialog_factory.h"
// Implements a file Open / Save dialog in a utility process. The utility // Implements a file Open / Save dialog in a utility process. The utility
// process is used to isolate the Chrome browser process from potential // process is used to isolate the Chrome browser process from potential
// instability caused by Shell extension modules loaded by ::GetOpenFileName // instability caused by Shell extension modules loaded by the file dialogs.
// and ::GetSaveFileName.
class ChromeSelectFileDialogFactory : public ui::SelectFileDialogFactory { class ChromeSelectFileDialogFactory : public ui::SelectFileDialogFactory {
public: public:
ChromeSelectFileDialogFactory(); ChromeSelectFileDialogFactory();
...@@ -26,9 +24,6 @@ class ChromeSelectFileDialogFactory : public ui::SelectFileDialogFactory { ...@@ -26,9 +24,6 @@ class ChromeSelectFileDialogFactory : public ui::SelectFileDialogFactory {
std::unique_ptr<ui::SelectFilePolicy> policy) override; std::unique_ptr<ui::SelectFilePolicy> policy) override;
private: private:
static bool BlockingGetOpenFileName(OPENFILENAME* ofn);
static bool BlockingGetSaveFileName(OPENFILENAME* ofn);
DISALLOW_COPY_AND_ASSIGN(ChromeSelectFileDialogFactory); DISALLOW_COPY_AND_ASSIGN(ChromeSelectFileDialogFactory);
}; };
......
...@@ -18,6 +18,8 @@ jumbo_component("shell_dialogs") { ...@@ -18,6 +18,8 @@ jumbo_component("shell_dialogs") {
"base_shell_dialog.h", "base_shell_dialog.h",
"base_shell_dialog_win.cc", "base_shell_dialog_win.cc",
"base_shell_dialog_win.h", "base_shell_dialog_win.h",
"execute_select_file_win.cc",
"execute_select_file_win.h",
"select_file_dialog.cc", "select_file_dialog.cc",
"select_file_dialog.h", "select_file_dialog.h",
"select_file_dialog_factory.cc", "select_file_dialog_factory.cc",
...@@ -103,6 +105,7 @@ if (is_mac) { ...@@ -103,6 +105,7 @@ if (is_mac) {
test("shell_dialogs_unittests") { test("shell_dialogs_unittests") {
testonly = true testonly = true
sources = [ sources = [
"execute_select_file_win_unittest.cc",
"run_all_unittests.cc", "run_all_unittests.cc",
"select_file_dialog_mac_unittest.mm", "select_file_dialog_mac_unittest.mm",
"select_file_dialog_unittest.cc", "select_file_dialog_unittest.cc",
...@@ -115,6 +118,7 @@ test("shell_dialogs_unittests") { ...@@ -115,6 +118,7 @@ test("shell_dialogs_unittests") {
"//base/test:test_support", "//base/test:test_support",
"//testing/gtest", "//testing/gtest",
"//ui/base:base", "//ui/base:base",
"//ui/strings:ui_strings_grit",
] ]
if (is_mac) { if (is_mac) {
......
...@@ -25,13 +25,17 @@ scoped_refptr<base::SingleThreadTaskRunner> CreateDialogTaskRunner() { ...@@ -25,13 +25,17 @@ scoped_refptr<base::SingleThreadTaskRunner> CreateDialogTaskRunner() {
base::SingleThreadTaskRunnerThreadMode::DEDICATED); base::SingleThreadTaskRunnerThreadMode::DEDICATED);
} }
// Enables the window |owner|. Can only be run from the UI thread.
void SetOwnerEnabled(HWND owner, bool enabled) {
if (IsWindow(owner))
EnableWindow(owner, enabled);
}
} // namespace } // namespace
BaseShellDialogImpl::RunState::RunState() = default; BaseShellDialogImpl::RunState::RunState() = default;
BaseShellDialogImpl::RunState::~RunState() = default; BaseShellDialogImpl::RunState::~RunState() = default;
BaseShellDialogImpl::RunState::RunState(const RunState& run_state) = default;
// static // static
BaseShellDialogImpl::Owners BaseShellDialogImpl::owners_; BaseShellDialogImpl::Owners BaseShellDialogImpl::owners_;
int BaseShellDialogImpl::instance_count_ = 0; int BaseShellDialogImpl::instance_count_ = 0;
...@@ -46,15 +50,21 @@ BaseShellDialogImpl::~BaseShellDialogImpl() { ...@@ -46,15 +50,21 @@ BaseShellDialogImpl::~BaseShellDialogImpl() {
DCHECK(owners_.empty()); DCHECK(owners_.empty());
} }
BaseShellDialogImpl::RunState BaseShellDialogImpl::BeginRun(HWND owner) { // static
void BaseShellDialogImpl::DisableOwner(HWND owner) {
SetOwnerEnabled(owner, false);
}
std::unique_ptr<BaseShellDialogImpl::RunState> BaseShellDialogImpl::BeginRun(
HWND owner) {
// Cannot run a modal shell dialog if one is already running for this owner. // Cannot run a modal shell dialog if one is already running for this owner.
DCHECK(!IsRunningDialogForOwner(owner)); DCHECK(!IsRunningDialogForOwner(owner));
// The owner must be a top level window, otherwise we could end up with two // The owner must be a top level window, otherwise we could end up with two
// entries in our map for the same top level window. // entries in our map for the same top level window.
DCHECK(!owner || owner == GetAncestor(owner, GA_ROOT)); DCHECK(!owner || owner == GetAncestor(owner, GA_ROOT));
RunState run_state; auto run_state = std::make_unique<RunState>();
run_state.dialog_task_runner = CreateDialogTaskRunner(); run_state->dialog_task_runner = CreateDialogTaskRunner();
run_state.owner = owner; run_state->owner = owner;
if (owner) { if (owner) {
owners_.insert(owner); owners_.insert(owner);
DisableOwner(owner); DisableOwner(owner);
...@@ -62,12 +72,12 @@ BaseShellDialogImpl::RunState BaseShellDialogImpl::BeginRun(HWND owner) { ...@@ -62,12 +72,12 @@ BaseShellDialogImpl::RunState BaseShellDialogImpl::BeginRun(HWND owner) {
return run_state; return run_state;
} }
void BaseShellDialogImpl::EndRun(RunState run_state) { void BaseShellDialogImpl::EndRun(std::unique_ptr<RunState> run_state) {
if (run_state.owner) { if (run_state->owner) {
DCHECK(IsRunningDialogForOwner(run_state.owner)); DCHECK(IsRunningDialogForOwner(run_state->owner));
EnableOwner(run_state.owner); SetOwnerEnabled(run_state->owner, true);
DCHECK(owners_.find(run_state.owner) != owners_.end()); DCHECK(owners_.find(run_state->owner) != owners_.end());
owners_.erase(run_state.owner); owners_.erase(run_state->owner);
} }
} }
...@@ -75,14 +85,4 @@ bool BaseShellDialogImpl::IsRunningDialogForOwner(HWND owner) const { ...@@ -75,14 +85,4 @@ bool BaseShellDialogImpl::IsRunningDialogForOwner(HWND owner) const {
return (owner && owners_.find(owner) != owners_.end()); return (owner && owners_.find(owner) != owners_.end());
} }
void BaseShellDialogImpl::DisableOwner(HWND owner) {
if (IsWindow(owner))
EnableWindow(owner, FALSE);
}
void BaseShellDialogImpl::EnableOwner(HWND owner) {
if (IsWindow(owner))
EnableWindow(owner, TRUE);
}
} // namespace ui } // namespace ui
...@@ -6,6 +6,8 @@ ...@@ -6,6 +6,8 @@
#define UI_SHELL_DIALOGS_BASE_SHELL_DIALOG_WIN_H_ #define UI_SHELL_DIALOGS_BASE_SHELL_DIALOG_WIN_H_
#include <shlobj.h> #include <shlobj.h>
#include <memory>
#include <set> #include <set>
#include "base/macros.h" #include "base/macros.h"
...@@ -27,53 +29,50 @@ class SHELL_DIALOGS_EXPORT BaseShellDialogImpl { ...@@ -27,53 +29,50 @@ class SHELL_DIALOGS_EXPORT BaseShellDialogImpl {
BaseShellDialogImpl(); BaseShellDialogImpl();
virtual ~BaseShellDialogImpl(); virtual ~BaseShellDialogImpl();
// Disables the window |owner|. Can be run from either the ui or the dialog
// thread. This function is called on the dialog thread after the modal
// Windows Common dialog functions return because Windows automatically
// re-enables the owning window when those functions return, but we don't
// actually want them to be re-enabled until the response of the dialog
// propagates back to the UI thread, so we disable the owner manually after
// the Common dialog function returns.
static void DisableOwner(HWND owner);
protected: protected:
// Represents a run of a dialog. // Represents a run of a dialog.
struct SHELL_DIALOGS_EXPORT RunState { class SHELL_DIALOGS_EXPORT RunState {
public:
RunState(); RunState();
~RunState(); ~RunState();
RunState(const RunState& run_state);
// Owning HWND, may be null. // Owning HWND, may be null.
HWND owner; HWND owner;
// Dedicated sequence on which the dialog runs. // Dedicated sequence on which the dialog runs.
scoped_refptr<base::SingleThreadTaskRunner> dialog_task_runner; scoped_refptr<base::SingleThreadTaskRunner> dialog_task_runner;
private:
DISALLOW_COPY_AND_ASSIGN(RunState);
}; };
// Called at the beginning of a modal dialog run. Disables the owner window // Called at the beginning of a modal dialog run. Disables the owner window
// and tracks it. Returns the dedicated single-threaded sequence that the // and tracks it. Returns the dedicated single-threaded sequence that the
// dialog will be run on. // dialog will be run on.
RunState BeginRun(HWND owner); std::unique_ptr<RunState> BeginRun(HWND owner);
// Cleans up after a dialog run. If the run_state has a valid HWND this makes // Cleans up after a dialog run. If the run_state has a valid HWND this makes
// sure that the window is enabled. This is essential because BeginRun // sure that the window is enabled. This is essential because BeginRun
// aggressively guards against multiple modal dialogs per HWND. Must be called // aggressively guards against multiple modal dialogs per HWND. Must be called
// on the UI thread after the result of the dialog has been determined. // on the UI thread after the result of the dialog has been determined.
// void EndRun(std::unique_ptr<RunState> run_state);
// In addition this deletes the Thread in RunState.
void EndRun(RunState run_state);
// Returns true if a modal shell dialog is currently active for the specified // Returns true if a modal shell dialog is currently active for the specified
// owner. Must be called on the UI thread. // owner. Must be called on the UI thread.
bool IsRunningDialogForOwner(HWND owner) const; bool IsRunningDialogForOwner(HWND owner) const;
// Disables the window |owner|. Can be run from either the ui or the dialog
// thread. This function is called on the dialog thread after the modal
// Windows Common dialog functions return because Windows automatically
// re-enables the owning window when those functions return, but we don't
// actually want them to be re-enabled until the response of the dialog
// propagates back to the UI thread, so we disable the owner manually after
// the Common dialog function returns.
void DisableOwner(HWND owner);
private: private:
typedef std::set<HWND> Owners; typedef std::set<HWND> Owners;
// Enables the window |owner_|. Can only be run from the ui thread.
void EnableOwner(HWND owner);
// A list of windows that currently own active shell dialogs for this // A list of windows that currently own active shell dialogs for this
// instance. For example, if the DownloadManager owns an instance of this // instance. For example, if the DownloadManager owns an instance of this
// object and there are two browser windows open both with Save As dialog // object and there are two browser windows open both with Save As dialog
......
This diff is collapsed.
// Copyright 2018 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 UI_SHELL_DIALOGS_EXECUTE_SELECT_FILE_WIN_H_
#define UI_SHELL_DIALOGS_EXECUTE_SELECT_FILE_WIN_H_
#include <utility>
#include <vector>
#include "base/strings/string16.h"
#include "base/win/windows_types.h"
#include "ui/shell_dialogs/select_file_dialog.h"
#include "ui/shell_dialogs/shell_dialogs_export.h"
namespace base {
class FilePath;
}
namespace ui {
// Implementation detail exported for unit tests.
SHELL_DIALOGS_EXPORT std::wstring AppendExtensionIfNeeded(
const std::wstring& filename,
const std::wstring& filter_selected,
const std::wstring& suggested_ext);
// Shows the file selection dialog modal to |owner| returns the selected
// file(s) and file type index via the return value. The file path vector will
// be empty on failure.
SHELL_DIALOGS_EXPORT
std::pair<std::vector<base::FilePath>, int> ExecuteSelectFile(
SelectFileDialog::Type type,
const base::string16& title,
const base::FilePath& default_path,
const base::string16& filter,
int file_type_index,
const base::string16& default_extension,
HWND owner);
} // namespace ui
#endif // UI_SHELL_DIALOGS_EXECUTE_SELECT_FILE_WIN_H_
// 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 "ui/shell_dialogs/execute_select_file_win.h"
#include <stddef.h>
#include "base/macros.h"
#include "base/stl_util.h"
#include "base/strings/stringprintf.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/shell_dialogs/select_file_dialog.h"
TEST(ShellDialogsWin, AppendExtensionIfNeeded) {
struct AppendExtensionTestCase {
const wchar_t* filename;
const wchar_t* filter_selected;
const wchar_t* suggested_ext;
const wchar_t* expected_filename;
} test_cases[] = {
// Known extensions, with or without associated MIME types, should not get
// an extension appended.
{L"sample.html", L"*.txt", L"txt", L"sample.html"},
{L"sample.reg", L"*.txt", L"txt", L"sample.reg"},
// An unknown extension, or no extension, should get the default extension
// appended.
{L"sample.unknown", L"*.txt", L"txt", L"sample.unknown.txt"},
{L"sample", L"*.txt", L"txt", L"sample.txt"},
// ...unless the unknown and default extensions match.
{L"sample.unknown", L"*.unknown", L"unknown", L"sample.unknown"},
// The extension alone should be treated like a filename with no
// extension.
{L"txt", L"*.txt", L"txt", L"txt.txt"},
// Trailing dots should cause us to append an extension.
{L"sample.txt.", L"*.txt", L"txt", L"sample.txt.txt"},
{L"...", L"*.txt", L"txt", L"...txt"},
// If the filter is changed to "All files", we allow any filename.
{L"sample.unknown", L"*.*", L"", L"sample.unknown"},
};
for (size_t i = 0; i < base::size(test_cases); ++i) {
SCOPED_TRACE(base::StringPrintf("i=%zu", i));
EXPECT_EQ(std::wstring(test_cases[i].expected_filename),
ui::AppendExtensionIfNeeded(test_cases[i].filename,
test_cases[i].filter_selected,
test_cases[i].suggested_ext));
}
}
This diff is collapsed.
...@@ -5,29 +5,38 @@ ...@@ -5,29 +5,38 @@
#ifndef UI_SHELL_DIALOGS_SELECT_FILE_DIALOG_WIN_H_ #ifndef UI_SHELL_DIALOGS_SELECT_FILE_DIALOG_WIN_H_
#define UI_SHELL_DIALOGS_SELECT_FILE_DIALOG_WIN_H_ #define UI_SHELL_DIALOGS_SELECT_FILE_DIALOG_WIN_H_
#include <Windows.h> #include <memory>
#include <commdlg.h> #include <utility>
#include <vector>
#include "base/callback_forward.h" #include "base/callback_forward.h"
#include "base/strings/string16.h"
#include "ui/gfx/native_widget_types.h" #include "ui/gfx/native_widget_types.h"
#include "ui/shell_dialogs/select_file_dialog.h" #include "ui/shell_dialogs/select_file_dialog.h"
#include "ui/shell_dialogs/shell_dialogs_export.h" #include "ui/shell_dialogs/shell_dialogs_export.h"
namespace base {
class FilePath;
}
namespace ui { namespace ui {
class SelectFilePolicy; class SelectFilePolicy;
// Implementation detail exported for unit tests. using ExecuteSelectFileCallback =
SHELL_DIALOGS_EXPORT std::wstring AppendExtensionIfNeeded( base::RepeatingCallback<std::pair<std::vector<base::FilePath>, int>(
const std::wstring& filename, SelectFileDialog::Type type,
const std::wstring& filter_selected, const base::string16& title,
const std::wstring& suggested_ext); const base::FilePath& default_path,
const base::string16& filter,
int file_type_index,
const base::string16& default_extension,
HWND owner)>;
SHELL_DIALOGS_EXPORT SelectFileDialog* CreateWinSelectFileDialog( SHELL_DIALOGS_EXPORT SelectFileDialog* CreateWinSelectFileDialog(
SelectFileDialog::Listener* listener, SelectFileDialog::Listener* listener,
std::unique_ptr<SelectFilePolicy> policy, std::unique_ptr<SelectFilePolicy> policy,
const base::Callback<bool(OPENFILENAME* ofn)>& get_open_file_name_impl, const ExecuteSelectFileCallback& execute_select_file_callback);
const base::Callback<bool(OPENFILENAME* ofn)>& get_save_file_name_impl);
} // namespace ui } // namespace ui
......
...@@ -26,44 +26,6 @@ ...@@ -26,44 +26,6 @@
#include "ui/shell_dialogs/select_file_policy.h" #include "ui/shell_dialogs/select_file_policy.h"
#include "ui/strings/grit/ui_strings.h" #include "ui/strings/grit/ui_strings.h"
TEST(ShellDialogsWin, AppendExtensionIfNeeded) {
struct AppendExtensionTestCase {
const wchar_t* filename;
const wchar_t* filter_selected;
const wchar_t* suggested_ext;
const wchar_t* expected_filename;
} test_cases[] = {
// Known extensions, with or without associated MIME types, should not get
// an extension appended.
{ L"sample.html", L"*.txt", L"txt", L"sample.html" },
{ L"sample.reg", L"*.txt", L"txt", L"sample.reg" },
// An unknown extension, or no extension, should get the default extension
// appended.
{ L"sample.unknown", L"*.txt", L"txt", L"sample.unknown.txt" },
{ L"sample", L"*.txt", L"txt", L"sample.txt" },
// ...unless the unknown and default extensions match.
{ L"sample.unknown", L"*.unknown", L"unknown", L"sample.unknown" },
// The extension alone should be treated like a filename with no extension.
{ L"txt", L"*.txt", L"txt", L"txt.txt" },
// Trailing dots should cause us to append an extension.
{ L"sample.txt.", L"*.txt", L"txt", L"sample.txt.txt" },
{ L"...", L"*.txt", L"txt", L"...txt" },
// If the filter is changed to "All files", we allow any filename.
{ L"sample.unknown", L"*.*", L"", L"sample.unknown" },
};
for (size_t i = 0; i < arraysize(test_cases); ++i) {
EXPECT_EQ(std::wstring(test_cases[i].expected_filename),
ui::AppendExtensionIfNeeded(test_cases[i].filename,
test_cases[i].filter_selected,
test_cases[i].suggested_ext));
}
}
namespace { namespace {
// The default title for the various dialogs. // The default title for the various dialogs.
...@@ -169,9 +131,7 @@ void SendCommand(HWND window, int id) { ...@@ -169,9 +131,7 @@ void SendCommand(HWND window, int id) {
class SelectFileDialogWinTest : public ::testing::Test, class SelectFileDialogWinTest : public ::testing::Test,
public ui::SelectFileDialog::Listener { public ui::SelectFileDialog::Listener {
public: public:
SelectFileDialogWinTest() SelectFileDialogWinTest() = default;
: scoped_task_environment_(
base::test::ScopedTaskEnvironment::MainThreadType::UI) {}
~SelectFileDialogWinTest() override = default; ~SelectFileDialogWinTest() override = default;
// ui::SelectFileDialog::Listener: // ui::SelectFileDialog::Listener:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment