diff options
author | erikwright@chromium.org <erikwright@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-08-14 17:14:50 +0000 |
---|---|---|
committer | erikwright@chromium.org <erikwright@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-08-14 17:14:50 +0000 |
commit | b041ed5038d77ca7a8daddafc9f6600c0f3e05fb (patch) | |
tree | 3e1f5c5a916af11a7fcb017d1e1912e9c5b9d636 /ui | |
parent | 1e113d96b0f5b674f63e313c412dc1ee40bb8eba (diff) | |
download | chromium_src-b041ed5038d77ca7a8daddafc9f6600c0f3e05fb.zip chromium_src-b041ed5038d77ca7a8daddafc9f6600c0f3e05fb.tar.gz chromium_src-b041ed5038d77ca7a8daddafc9f6600c0f3e05fb.tar.bz2 |
Experimentally isolate GetOpenFileName in a utility process.
GetOpenFileName can cause 3rd-party shell extensions to be loaded into the caller's process. By putting it in a separate process we protect the browser process from potential instability.
BUG=73098
Review URL: https://codereview.chromium.org/419523006
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@289625 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ui')
-rw-r--r-- | ui/base/win/open_file_name_win.cc | 72 | ||||
-rw-r--r-- | ui/base/win/open_file_name_win.h | 19 | ||||
-rw-r--r-- | ui/base/win/open_file_name_win_unittest.cc | 119 | ||||
-rw-r--r-- | ui/shell_dialogs/select_file_dialog.cc | 2 | ||||
-rw-r--r-- | ui/shell_dialogs/select_file_dialog_win.cc | 57 | ||||
-rw-r--r-- | ui/shell_dialogs/select_file_dialog_win.h | 13 |
6 files changed, 239 insertions, 43 deletions
diff --git a/ui/base/win/open_file_name_win.cc b/ui/base/win/open_file_name_win.cc index 2d67b1f..b63eb47 100644 --- a/ui/base/win/open_file_name_win.cc +++ b/ui/base/win/open_file_name_win.cc @@ -27,6 +27,25 @@ OpenFileName::OpenFileName(HWND parent_window, DWORD flags) { OpenFileName::~OpenFileName() { } +void OpenFileName::SetFilters( + const std::vector<Tuple2<base::string16, base::string16> >& filters) { + openfilename_.lpstrFilter = NULL; + filter_buffer_.clear(); + if (filters.empty()) + return; + for (std::vector<Tuple2<base::string16, base::string16> >::const_iterator + it = filters.begin(); + it != filters.end(); + ++it) { + filter_buffer_.append(it->a); + filter_buffer_.push_back(0); + filter_buffer_.append(it->b); + filter_buffer_.push_back(0); + } + filter_buffer_.push_back(0); + openfilename_.lpstrFilter = filter_buffer_.c_str(); +} + void OpenFileName::SetInitialSelection(const base::FilePath& initial_directory, const base::FilePath& initial_filename) { // First reset to the default case. @@ -83,5 +102,58 @@ void OpenFileName::GetResult(base::FilePath* directory, } } +// static +void OpenFileName::SetResult(const base::FilePath& directory, + const std::vector<base::FilePath>& filenames, + OPENFILENAME* openfilename) { + base::string16 filename_value; + if (filenames.size() == 1) { + filename_value = directory.Append(filenames[0]).value(); + } else { + filename_value = directory.value(); + filename_value.push_back(0); + for (std::vector<base::FilePath>::const_iterator it = filenames.begin(); + it != filenames.end(); + ++it) { + filename_value.append(it->value()); + filename_value.push_back(0); + } + } + if (filename_value.size() + 1 < openfilename->nMaxFile) { + // Because the result has embedded nulls, we must memcpy. + memcpy(openfilename->lpstrFile, + filename_value.c_str(), + (filename_value.size() + 1) * sizeof(filename_value[0])); + } else if (openfilename->nMaxFile) { + openfilename->lpstrFile[0] = 0; + } +} + +// static +std::vector<Tuple2<base::string16, base::string16> > OpenFileName::GetFilters( + const OPENFILENAME* openfilename) { + std::vector<Tuple2<base::string16, base::string16> > filters; + + const base::char16* display_string = openfilename->lpstrFilter; + if (!display_string) + return filters; + + while (*display_string) { + const base::char16* display_string_end = display_string; + while (*display_string_end) + ++display_string_end; + const base::char16* pattern = display_string_end + 1; + const base::char16* pattern_end = pattern; + while (*pattern_end) + ++pattern_end; + filters.push_back( + MakeTuple(base::string16(display_string, display_string_end), + base::string16(pattern, pattern_end))); + display_string = pattern_end + 1; + } + + return filters; +} + } // namespace win } // namespace ui diff --git a/ui/base/win/open_file_name_win.h b/ui/base/win/open_file_name_win.h index 4787c83..79a7185 100644 --- a/ui/base/win/open_file_name_win.h +++ b/ui/base/win/open_file_name_win.h @@ -32,6 +32,10 @@ class UI_BASE_EXPORT OpenFileName { OpenFileName(HWND parent_window, DWORD flags); ~OpenFileName(); + // Initializes |lpstrFilter| from the label/pattern pairs in |filters|. + void SetFilters( + const std::vector<Tuple2<base::string16, base::string16> >& filters); + // Sets |lpstrInitialDir| and |lpstrFile|. void SetInitialSelection(const base::FilePath& initial_directory, const base::FilePath& initial_filename); @@ -47,10 +51,25 @@ class UI_BASE_EXPORT OpenFileName { // Returns the OPENFILENAME structure. OPENFILENAME* GetOPENFILENAME() { return &openfilename_; } + // Returns the OPENFILENAME structure. + const OPENFILENAME* GetOPENFILENAME() const { return &openfilename_; } + + // Stores directory and filenames in the buffer pointed to by + // |openfilename->lpstrFile| and sized |openfilename->nMaxFile|. + static void SetResult(const base::FilePath& directory, + const std::vector<base::FilePath>& filenames, + OPENFILENAME* openfilename); + + // Returns a vector of label/pattern pairs built from + // |openfilename->lpstrFilter|. + static std::vector<Tuple2<base::string16, base::string16> > GetFilters( + const OPENFILENAME* openfilename); + private: OPENFILENAME openfilename_; base::string16 initial_directory_buffer_; wchar_t filename_buffer_[UNICODE_STRING_MAX_CHARS]; + base::string16 filter_buffer_; DISALLOW_COPY_AND_ASSIGN(OpenFileName); }; diff --git a/ui/base/win/open_file_name_win_unittest.cc b/ui/base/win/open_file_name_win_unittest.cc index a38e4d6..f69ec97 100644 --- a/ui/base/win/open_file_name_win_unittest.cc +++ b/ui/base/win/open_file_name_win_unittest.cc @@ -12,16 +12,64 @@ const HWND kHwnd = reinterpret_cast<HWND>(0xDEADBEEF); const DWORD kFlags = OFN_OVERWRITEPROMPT | OFN_EXPLORER | OFN_ENABLESIZING; void SetResult(const base::string16& result, ui::win::OpenFileName* ofn) { - EXPECT_GT(ofn->GetOPENFILENAME()->nMaxFile, result.size()); - if (ofn->GetOPENFILENAME()->nMaxFile > result.size()) { - if (!result.size()) { - ofn->GetOPENFILENAME()->lpstrFile[0] = 0; - } else { - // Because the result has embedded nulls, we must memcpy. - memcpy(ofn->GetOPENFILENAME()->lpstrFile, - result.c_str(), - (result.size() + 1) * sizeof(result[0])); - } + if (ofn->GetOPENFILENAME()->nMaxFile <= result.size()) { + ADD_FAILURE() << "filename buffer insufficient."; + return; + } + if (!result.size()) { + ofn->GetOPENFILENAME()->lpstrFile[0] = 0; + } else { + // Because the result has embedded nulls, we must memcpy. + memcpy(ofn->GetOPENFILENAME()->lpstrFile, + result.c_str(), + (result.size() + 1) * sizeof(result[0])); + } +} + +void CheckFilters( + const std::vector<Tuple2<base::string16, base::string16> >& expected, + const std::vector<Tuple2<base::string16, base::string16> >& actual) { + if (expected.size() != actual.size()) { + ADD_FAILURE() << "filter count mismatch. Got " << actual.size() + << " expected " << expected.size() << "."; + return; + } + + for (size_t i = 0; i < expected.size(); ++i) { + EXPECT_EQ(expected[i].a, actual[i].a) << "Mismatch at index " << i; + EXPECT_EQ(expected[i].b, actual[i].b) << "Mismatch at index " << i; + } +} + +void CheckFilterString(const base::string16& expected, + const ui::win::OpenFileName& ofn) { + if (!ofn.GetOPENFILENAME()->lpstrFilter) { + ADD_FAILURE() << "Filter string is NULL."; + return; + } + if (expected.size() == 0) { + EXPECT_EQ(0, ofn.GetOPENFILENAME()->lpstrFilter[0]); + } else { + EXPECT_EQ(0, + memcmp(expected.c_str(), + ofn.GetOPENFILENAME()->lpstrFilter, + expected.size() + 1 * sizeof(expected[0]))); + } +} + +void CheckResult(const base::string16& expected, + const ui::win::OpenFileName& ofn) { + if (!ofn.GetOPENFILENAME()->lpstrFile) { + ADD_FAILURE() << "File string is NULL."; + return; + } + if (expected.size() == 0) { + EXPECT_EQ(0, ofn.GetOPENFILENAME()->lpstrFile[0]); + } else { + EXPECT_EQ(0, + memcmp(expected.c_str(), + ofn.GetOPENFILENAME()->lpstrFile, + expected.size() + 1 * sizeof(expected[0]))); } } @@ -135,3 +183,54 @@ TEST(OpenFileNameTest, GetResult) { EXPECT_EQ(base::FilePath(), directory); ASSERT_EQ(0u, filenames.size()); } + +TEST(OpenFileNameTest, SetAndGetFilters) { + const base::string16 kNull(L"\0", 1); + + ui::win::OpenFileName ofn(kHwnd, kFlags); + std::vector<Tuple2<base::string16, base::string16> > filters; + ofn.SetFilters(filters); + EXPECT_FALSE(ofn.GetOPENFILENAME()->lpstrFilter); + CheckFilters(filters, + ui::win::OpenFileName::GetFilters(ofn.GetOPENFILENAME())); + + filters.push_back(MakeTuple(base::string16(L"a"), base::string16(L"b"))); + ofn.SetFilters(filters); + CheckFilterString(L"a" + kNull + L"b" + kNull, ofn); + CheckFilters(filters, + ui::win::OpenFileName::GetFilters(ofn.GetOPENFILENAME())); + + filters.push_back(MakeTuple(base::string16(L"X"), base::string16(L"Y"))); + ofn.SetFilters(filters); + CheckFilterString(L"a" + kNull + L"b" + kNull + L"X" + kNull + L"Y" + kNull, + ofn); + CheckFilters(filters, + ui::win::OpenFileName::GetFilters(ofn.GetOPENFILENAME())); +} + +TEST(OpenFileNameTest, SetResult) { + const base::string16 kNull(L"\0", 1); + + ui::win::OpenFileName ofn(kHwnd, kFlags); + base::FilePath directory; + std::vector<base::FilePath> filenames; + + ui::win::OpenFileName::SetResult(directory, filenames, ofn.GetOPENFILENAME()); + CheckResult(L"", ofn); + + directory = base::FilePath(L"C:\\dir"); + filenames.push_back(base::FilePath(L"file")); + ui::win::OpenFileName::SetResult(directory, filenames, ofn.GetOPENFILENAME()); + CheckResult(L"C:\\dir\\file" + kNull, ofn); + + filenames.push_back(base::FilePath(L"otherfile")); + ui::win::OpenFileName::SetResult(directory, filenames, ofn.GetOPENFILENAME()); + CheckResult(L"C:\\dir" + kNull + L"file" + kNull + L"otherfile" + kNull, ofn); + + base::char16 short_buffer[10] = L""; + + ofn.GetOPENFILENAME()->lpstrFile = short_buffer; + ofn.GetOPENFILENAME()->nMaxFile = arraysize(short_buffer); + ui::win::OpenFileName::SetResult(directory, filenames, ofn.GetOPENFILENAME()); + CheckResult(L"", ofn); +} diff --git a/ui/shell_dialogs/select_file_dialog.cc b/ui/shell_dialogs/select_file_dialog.cc index 13d3ba9..e5abd07 100644 --- a/ui/shell_dialogs/select_file_dialog.cc +++ b/ui/shell_dialogs/select_file_dialog.cc @@ -84,7 +84,7 @@ scoped_refptr<SelectFileDialog> SelectFileDialog::Create( #if defined(OS_WIN) // TODO(ananta) // Fix this for Chrome ASH on Windows. - return CreateWinSelectFileDialog(listener, policy); + return CreateDefaultWinSelectFileDialog(listener, policy); #elif defined(OS_MACOSX) && !defined(USE_AURA) return CreateMacSelectFileDialog(listener, policy); #elif defined(OS_ANDROID) diff --git a/ui/shell_dialogs/select_file_dialog_win.cc b/ui/shell_dialogs/select_file_dialog_win.cc index 73aacf6..0fccc78 100644 --- a/ui/shell_dialogs/select_file_dialog_win.cc +++ b/ui/shell_dialogs/select_file_dialog_win.cc @@ -4,8 +4,6 @@ #include "ui/shell_dialogs/select_file_dialog_win.h" -#include <windows.h> -#include <commdlg.h> #include <shlobj.h> #include <algorithm> @@ -38,6 +36,10 @@ namespace { +bool CallBuiltinGetOpenFileName(OPENFILENAME* ofn) { + return ::GetOpenFileName(ofn) == TRUE; +} + // Given |extension|, if it's not empty, then remove the leading dot. std::wstring GetExtensionWithoutLeadingDot(const std::wstring& extension) { DCHECK(extension.empty() || extension[0] == L'.'); @@ -45,25 +47,6 @@ std::wstring GetExtensionWithoutLeadingDot(const std::wstring& extension) { } // Diverts to a metro-specific implementation as appropriate. -bool CallGetOpenFileName(OPENFILENAME* ofn) { - HMODULE metro_module = base::win::GetMetroModule(); - if (metro_module != NULL) { - typedef BOOL (*MetroGetOpenFileName)(OPENFILENAME*); - MetroGetOpenFileName metro_get_open_file_name = - reinterpret_cast<MetroGetOpenFileName>( - ::GetProcAddress(metro_module, "MetroGetOpenFileName")); - if (metro_get_open_file_name == NULL) { - NOTREACHED(); - return false; - } - - return metro_get_open_file_name(ofn) == TRUE; - } else { - return GetOpenFileName(ofn) == TRUE; - } -} - -// Diverts to a metro-specific implementation as appropriate. bool CallGetSaveFileName(OPENFILENAME* ofn) { HMODULE metro_module = base::win::GetMetroModule(); if (metro_module != NULL) { @@ -410,8 +393,10 @@ bool SaveFileAs(HWND owner, class SelectFileDialogImpl : public ui::SelectFileDialog, public ui::BaseShellDialogImpl { public: - explicit SelectFileDialogImpl(Listener* listener, - ui::SelectFilePolicy* policy); + SelectFileDialogImpl( + Listener* listener, + ui::SelectFilePolicy* policy, + const base::Callback<bool(OPENFILENAME*)>& get_open_file_name_impl); // BaseShellDialog implementation: virtual bool IsRunning(gfx::NativeWindow owning_window) const OVERRIDE; @@ -520,15 +505,19 @@ class SelectFileDialogImpl : public ui::SelectFileDialog, base::string16 GetFilterForFileTypes(const FileTypeInfo* file_types); bool has_multiple_file_type_choices_; + base::Callback<bool(OPENFILENAME*)> get_open_file_name_impl_; DISALLOW_COPY_AND_ASSIGN(SelectFileDialogImpl); }; -SelectFileDialogImpl::SelectFileDialogImpl(Listener* listener, - ui::SelectFilePolicy* policy) +SelectFileDialogImpl::SelectFileDialogImpl( + Listener* listener, + ui::SelectFilePolicy* policy, + const base::Callback<bool(OPENFILENAME*)>& get_open_file_name_impl) : SelectFileDialog(listener, policy), BaseShellDialogImpl(), - has_multiple_file_type_choices_(false) { + has_multiple_file_type_choices_(false), + get_open_file_name_impl_(get_open_file_name_impl) { } SelectFileDialogImpl::~SelectFileDialogImpl() { @@ -786,7 +775,8 @@ bool SelectFileDialogImpl::RunOpenFileDialog( if (!filter.empty()) ofn.GetOPENFILENAME()->lpstrFilter = filter.c_str(); - bool success = CallGetOpenFileName(ofn.GetOPENFILENAME()); + + bool success = get_open_file_name_impl_.Run(ofn.GetOPENFILENAME()); DisableOwner(owner); if (success) *path = ofn.GetSingleResult(); @@ -811,8 +801,9 @@ bool SelectFileDialogImpl::RunOpenMultiFileDialog( base::FilePath directory; std::vector<base::FilePath> filenames; - if (CallGetOpenFileName(ofn.GetOPENFILENAME())) + if (get_open_file_name_impl_.Run(ofn.GetOPENFILENAME())) ofn.GetResult(&directory, &filenames); + DisableOwner(owner); for (std::vector<base::FilePath>::iterator it = filenames.begin(); @@ -894,8 +885,16 @@ std::wstring AppendExtensionIfNeeded( SelectFileDialog* CreateWinSelectFileDialog( SelectFileDialog::Listener* listener, + SelectFilePolicy* policy, + const base::Callback<bool(OPENFILENAME* ofn)>& get_open_file_name_impl) { + return new SelectFileDialogImpl(listener, policy, get_open_file_name_impl); +} + +SelectFileDialog* CreateDefaultWinSelectFileDialog( + SelectFileDialog::Listener* listener, SelectFilePolicy* policy) { - return new SelectFileDialogImpl(listener, policy); + return CreateWinSelectFileDialog( + listener, policy, base::Bind(&CallBuiltinGetOpenFileName)); } } // namespace ui diff --git a/ui/shell_dialogs/select_file_dialog_win.h b/ui/shell_dialogs/select_file_dialog_win.h index 04025c9..d8035c5 100644 --- a/ui/shell_dialogs/select_file_dialog_win.h +++ b/ui/shell_dialogs/select_file_dialog_win.h @@ -5,6 +5,10 @@ #ifndef UI_SHELL_DIALOGS_SELECT_FILE_DIALOG_WIN_H_ #define UI_SHELL_DIALOGS_SELECT_FILE_DIALOG_WIN_H_ +#include <Windows.h> +#include <commdlg.h> + +#include "base/callback_forward.h" #include "ui/gfx/native_widget_types.h" #include "ui/shell_dialogs/select_file_dialog.h" #include "ui/shell_dialogs/shell_dialogs_export.h" @@ -18,9 +22,12 @@ SHELL_DIALOGS_EXPORT std::wstring AppendExtensionIfNeeded( const std::wstring& filter_selected, const std::wstring& suggested_ext); -// Intentionally not exported. Implementation detail of SelectFileDialog. Use -// SelectFileDialog::Create() instead. -SelectFileDialog* CreateWinSelectFileDialog( +SHELL_DIALOGS_EXPORT SelectFileDialog* CreateWinSelectFileDialog( + SelectFileDialog::Listener* listener, + SelectFilePolicy* policy, + const base::Callback<bool(OPENFILENAME* ofn)>& get_open_file_name_impl); + +SelectFileDialog* CreateDefaultWinSelectFileDialog( SelectFileDialog::Listener* listener, SelectFilePolicy* policy); |