Skip to content

Commit

Permalink
Introduce a helper for OPENFILENAME struct to make certain logic test…
Browse files Browse the repository at this point in the history
…able.

BUG=73098
R=sky@chromium.org

Review URL: https://codereview.chromium.org/426673003

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@287048 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
erikwright@chromium.org committed Aug 1, 2014
1 parent d9d01ea commit 41814c9
Show file tree
Hide file tree
Showing 7 changed files with 319 additions and 76 deletions.
2 changes: 2 additions & 0 deletions ui/base/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,8 @@ component("base") {
"win/internal_constants.h",
"win/lock_state.cc",
"win/lock_state.h",
"win/open_file_name_win.cc",
"win/open_file_name_win.h",
"win/message_box_win.cc",
"win/message_box_win.h",
"win/mouse_wheel_util.cc",
Expand Down
2 changes: 2 additions & 0 deletions ui/base/ui_base.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -352,6 +352,8 @@
'win/message_box_win.h',
'win/mouse_wheel_util.cc',
'win/mouse_wheel_util.h',
'win/open_file_name_win.cc',
'win/open_file_name_win.h',
'win/scoped_ole_initializer.cc',
'win/scoped_ole_initializer.h',
'win/shell.cc',
Expand Down
87 changes: 87 additions & 0 deletions ui/base/win/open_file_name_win.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
// Copyright (c) 2014 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/base/win/open_file_name_win.h"

#include "base/files/file_path.h"
#include "base/strings/string_util.h"

namespace ui {
namespace win {

OpenFileName::OpenFileName(HWND parent_window, DWORD flags) {
::ZeroMemory(&openfilename_, sizeof(openfilename_));
openfilename_.lStructSize = sizeof(openfilename_);

// According to http://support.microsoft.com/?scid=kb;en-us;222003&x=8&y=12,
// The lpstrFile Buffer MUST be NULL Terminated.
filename_buffer_[0] = 0;
openfilename_.lpstrFile = filename_buffer_;
openfilename_.nMaxFile = arraysize(filename_buffer_);

openfilename_.Flags = flags;
openfilename_.hwndOwner = parent_window;
}

OpenFileName::~OpenFileName() {
}

void OpenFileName::SetInitialSelection(const base::FilePath& initial_directory,
const base::FilePath& initial_filename) {
// First reset to the default case.
// According to http://support.microsoft.com/?scid=kb;en-us;222003&x=8&y=12,
// The lpstrFile Buffer MUST be NULL Terminated.
filename_buffer_[0] = 0;
openfilename_.lpstrFile = filename_buffer_;
openfilename_.nMaxFile = arraysize(filename_buffer_);
openfilename_.lpstrInitialDir = NULL;
initial_directory_buffer_.clear();

if (initial_directory.empty())
return;

initial_directory_buffer_ = initial_directory.value();
openfilename_.lpstrInitialDir = initial_directory_buffer_.c_str();

if (initial_filename.empty())
return;

// The filename is ignored if no initial directory is supplied.
base::wcslcpy(filename_buffer_,
initial_filename.value().c_str(),
arraysize(filename_buffer_));
}

base::FilePath OpenFileName::GetSingleResult() {
base::FilePath directory;
std::vector<base::FilePath> filenames;
GetResult(&directory, &filenames);
if (filenames.size() != 1)
return base::FilePath();
return directory.Append(filenames[0]);
}

void OpenFileName::GetResult(base::FilePath* directory,
std::vector<base::FilePath>* filenames) {
DCHECK(filenames->empty());
const wchar_t* selection = openfilename_.lpstrFile;
while (*selection) { // Empty string indicates end of list.
filenames->push_back(base::FilePath(selection));
// Skip over filename and null-terminator.
selection += filenames->back().value().length() + 1;
}
if (filenames->size() == 1) {
// When there is one file, it contains the path and filename.
*directory = (*filenames)[0].DirName();
(*filenames)[0] = (*filenames)[0].BaseName();
} else if (filenames->size() > 1) {
// Otherwise, the first string is the path, and the remainder are
// filenames.
*directory = (*filenames)[0];
filenames->erase(filenames->begin());
}
}

} // namespace win
} // namespace ui
61 changes: 61 additions & 0 deletions ui/base/win/open_file_name_win.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
// Copyright (c) 2014 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_BASE_WIN_OPEN_FILE_NAME_WIN_H_
#define UI_BASE_WIN_OPEN_FILE_NAME_WIN_H_

#include <Windows.h>
#include <Commdlg.h>

#include <vector>

#include "base/macros.h"
#include "base/strings/string16.h"
#include "base/tuple.h"
#include "ui/base/ui_base_export.h"

namespace base {
class FilePath;
} // namespace base

namespace ui {
namespace win {

// Encapsulates an OPENFILENAME struct and related buffers. Also provides static
// methods for interpreting the properties of an OPENFILENAME.
class UI_BASE_EXPORT OpenFileName {
public:
// Initializes the OPENFILENAME, which may be accessed using Get(). All fields
// will be NULL except for |lStructSize|, |lpstrFile|, and |nMaxFile|. The
// file buffer will initially contain a null-terminated empty string.
OpenFileName(HWND parent_window, DWORD flags);
~OpenFileName();

// Sets |lpstrInitialDir| and |lpstrFile|.
void SetInitialSelection(const base::FilePath& initial_directory,
const base::FilePath& initial_filename);

// Returns the single selected file, or an empty path if there are more or
// less than one results.
base::FilePath GetSingleResult();

// Returns the selected file or files.
void GetResult(base::FilePath* directory,
std::vector<base::FilePath>* filenames);

// Returns the OPENFILENAME structure.
OPENFILENAME* GetOPENFILENAME() { return &openfilename_; }

private:
OPENFILENAME openfilename_;
base::string16 initial_directory_buffer_;
wchar_t filename_buffer_[UNICODE_STRING_MAX_CHARS];

DISALLOW_COPY_AND_ASSIGN(OpenFileName);
};

} // namespace win
} // namespace ui

#endif // UI_BASE_WIN_OPEN_FILE_NAME_WIN_H_
137 changes: 137 additions & 0 deletions ui/base/win/open_file_name_win_unittest.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
// Copyright (c) 2014 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/base/win/open_file_name_win.h"

#include "base/files/file_path.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace {
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]));
}
}
}

} // namespace

TEST(OpenFileNameTest, Initialization) {
ui::win::OpenFileName ofn(kHwnd, kFlags);
EXPECT_EQ(kHwnd, ofn.GetOPENFILENAME()->hwndOwner);
EXPECT_EQ(kFlags, ofn.GetOPENFILENAME()->Flags);
EXPECT_EQ(sizeof(OPENFILENAME), ofn.GetOPENFILENAME()->lStructSize);
ASSERT_TRUE(ofn.GetOPENFILENAME()->lpstrFile);
ASSERT_GT(ofn.GetOPENFILENAME()->nMaxFile, 0u);
EXPECT_EQ(0, ofn.GetOPENFILENAME()->lpstrFile[0]);
}

TEST(OpenFileNameTest, SetInitialSelection) {
const base::FilePath kDirectory(L"C:\\directory\\child_directory");
const base::FilePath kFile(L"file_name.ext");
ui::win::OpenFileName ofn(kHwnd, kFlags);
ofn.SetInitialSelection(kDirectory, kFile);
EXPECT_EQ(kDirectory, base::FilePath(ofn.GetOPENFILENAME()->lpstrInitialDir));
EXPECT_EQ(kFile, base::FilePath(ofn.GetOPENFILENAME()->lpstrFile));

ofn.SetInitialSelection(kDirectory, base::FilePath());
EXPECT_EQ(kDirectory, base::FilePath(ofn.GetOPENFILENAME()->lpstrInitialDir));
// Filename buffer will still be a valid pointer, to receive a result.
ASSERT_TRUE(ofn.GetOPENFILENAME()->lpstrFile);
EXPECT_EQ(base::FilePath(), base::FilePath(ofn.GetOPENFILENAME()->lpstrFile));

ofn.SetInitialSelection(base::FilePath(), base::FilePath());
// No initial directory will lead to a NULL buffer.
ASSERT_FALSE(ofn.GetOPENFILENAME()->lpstrInitialDir);
ASSERT_TRUE(ofn.GetOPENFILENAME()->lpstrFile);
EXPECT_EQ(base::FilePath(), base::FilePath(ofn.GetOPENFILENAME()->lpstrFile));

// Make sure that both values are cleared when directory is missing.
ofn.SetInitialSelection(kDirectory, kFile);
EXPECT_EQ(kDirectory, base::FilePath(ofn.GetOPENFILENAME()->lpstrInitialDir));
EXPECT_EQ(kFile, base::FilePath(ofn.GetOPENFILENAME()->lpstrFile));
ofn.SetInitialSelection(base::FilePath(), base::FilePath());
ASSERT_FALSE(ofn.GetOPENFILENAME()->lpstrInitialDir);
ASSERT_TRUE(ofn.GetOPENFILENAME()->lpstrFile);
EXPECT_EQ(base::FilePath(), base::FilePath(ofn.GetOPENFILENAME()->lpstrFile));

// File is ignored in absence of a directory.
ofn.SetInitialSelection(base::FilePath(), kFile);
ASSERT_FALSE(ofn.GetOPENFILENAME()->lpstrInitialDir);
ASSERT_TRUE(ofn.GetOPENFILENAME()->lpstrFile);
EXPECT_EQ(base::FilePath(), base::FilePath(ofn.GetOPENFILENAME()->lpstrFile));
}

TEST(OpenFileNameTest, GetSingleResult) {
const base::string16 kNull(L"\0", 1);
ui::win::OpenFileName ofn(kHwnd, kFlags);
base::FilePath result;

SetResult(L"C:\\dir\\file" + kNull, &ofn);
result = ofn.GetSingleResult();
EXPECT_EQ(base::FilePath(L"C:\\dir\\file"), result);

SetResult(L"C:\\dir" + kNull + L"file" + kNull, &ofn);
result = ofn.GetSingleResult();
EXPECT_EQ(base::FilePath(L"C:\\dir\\file"), result);

SetResult(L"C:\\dir" + kNull + L"file" + kNull + L"otherfile" + kNull, &ofn);
result = ofn.GetSingleResult();
EXPECT_EQ(base::FilePath(), result);

SetResult(L"", &ofn);
result = ofn.GetSingleResult();
EXPECT_EQ(base::FilePath(), result);
}

TEST(OpenFileNameTest, GetResult) {
const base::string16 kNull(L"\0", 1);

ui::win::OpenFileName ofn(kHwnd, kFlags);
base::FilePath directory;
std::vector<base::FilePath> filenames;

SetResult(L"C:\\dir\\file" + kNull, &ofn);
ofn.GetResult(&directory, &filenames);
EXPECT_EQ(base::FilePath(L"C:\\dir"), directory);
ASSERT_EQ(1u, filenames.size());
EXPECT_EQ(base::FilePath(L"file"), filenames[0]);

directory.clear();
filenames.clear();

SetResult(L"C:\\dir" + kNull + L"file" + kNull, &ofn);
ofn.GetResult(&directory, &filenames);
EXPECT_EQ(base::FilePath(L"C:\\dir"), directory);
ASSERT_EQ(1u, filenames.size());
EXPECT_EQ(base::FilePath(L"file"), filenames[0]);

directory.clear();
filenames.clear();

SetResult(L"C:\\dir" + kNull + L"file" + kNull + L"otherfile" + kNull, &ofn);
ofn.GetResult(&directory, &filenames);
EXPECT_EQ(base::FilePath(L"C:\\dir"), directory);
ASSERT_EQ(2u, filenames.size());
EXPECT_EQ(base::FilePath(L"file"), filenames[0]);
EXPECT_EQ(base::FilePath(L"otherfile"), filenames[1]);

directory.clear();
filenames.clear();

SetResult(L"", &ofn);
ofn.GetResult(&directory, &filenames);
EXPECT_EQ(base::FilePath(), directory);
ASSERT_EQ(0u, filenames.size());
}
Loading

0 comments on commit 41814c9

Please sign in to comment.