diff options
author | mattm@chromium.org <mattm@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-21 02:56:58 +0000 |
---|---|---|
committer | mattm@chromium.org <mattm@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-07-21 02:56:58 +0000 |
commit | f245542e9805530611a6ce4f594e9a1d71b195f8 (patch) | |
tree | 45bef210fefc64d205858bfdca47a478839cb268 | |
parent | 67d2d80c1505fe6ba82124b2cc1d37d0764e0048 (diff) | |
download | chromium_src-f245542e9805530611a6ce4f594e9a1d71b195f8.zip chromium_src-f245542e9805530611a6ce4f594e9a1d71b195f8.tar.gz chromium_src-f245542e9805530611a6ce4f594e9a1d71b195f8.tar.bz2 |
Win: Replace native modal cert selector dialog with Views tab constrained implementation.
Handle the "multiple tabs requesting certs for same site" issue.
BUG=73223,50710
TEST=Try some sites that use SSL client auth. See bug 73223 for multiple-tabs issue.
Review URL: http://codereview.chromium.org/7362008
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@93323 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/app/generated_resources.grd | 6 | ||||
-rw-r--r-- | chrome/browser/ssl_client_certificate_selector.h | 5 | ||||
-rw-r--r-- | chrome/browser/ui/cocoa/ssl_client_certificate_selector.mm | 1 | ||||
-rw-r--r-- | chrome/browser/ui/views/ssl_client_certificate_selector.cc | 293 | ||||
-rw-r--r-- | chrome/browser/ui/views/ssl_client_certificate_selector.h | 92 | ||||
-rw-r--r-- | chrome/browser/ui/views/ssl_client_certificate_selector_browsertest.cc | 205 | ||||
-rw-r--r-- | chrome/browser/ui/views/ssl_client_certificate_selector_win.cc | 72 | ||||
-rw-r--r-- | chrome/chrome_browser.gypi | 3 | ||||
-rw-r--r-- | chrome/chrome_tests.gypi | 11 | ||||
-rw-r--r-- | content/browser/ssl/ssl_client_auth_handler.h | 7 | ||||
-rw-r--r-- | content/browser/ssl/ssl_client_auth_handler_mock.h | 23 |
11 files changed, 635 insertions, 83 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 8fb2918..3db0806 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd @@ -2710,6 +2710,12 @@ are declared in build/common.gypi. </message> + <!-- Win certificate selector dialog strings. --> + <message name="IDS_CERT_SELECTOR_TABLE_CERT_FORMAT" desc="Format for a certificate row in the certificate selector table."> + <ph name="SUBJECT">$1<ex>FOAF ME Cert http://foaf.me/test</ex></ph> (<ph name="ISSUER">$2<ex>FOAF.ME</ex></ph>) + </message> + + <!-- Certificate selector dialog strings. These are only used on platforms that don't have a native certificate selection dialog, such as Linux. --> <message name="IDS_CERT_SELECTOR_SITE_DESCRIPTION_LABEL" desc="Text in the certificate selection dialog before the site"> This site has requested that you identify yourself with a certificate: diff --git a/chrome/browser/ssl_client_certificate_selector.h b/chrome/browser/ssl_client_certificate_selector.h index 1886766..eece21d 100644 --- a/chrome/browser/ssl_client_certificate_selector.h +++ b/chrome/browser/ssl_client_certificate_selector.h @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// 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. @@ -20,9 +20,6 @@ namespace browser { // selection, the dialog will report back to |delegate|. |delegate| is notified // when the dialog closes in call cases; if the user cancels the dialog, we call // with a NULL certificate. -// -// Note: constrained dialog currently only implemented on Linux and OS X. On -// Windows, a window-modal dialog will be used. void ShowSSLClientCertificateSelector( TabContents* parent, net::SSLCertRequestInfo* cert_request_info, diff --git a/chrome/browser/ui/cocoa/ssl_client_certificate_selector.mm b/chrome/browser/ui/cocoa/ssl_client_certificate_selector.mm index a0d1d59..3e78bb4 100644 --- a/chrome/browser/ui/cocoa/ssl_client_certificate_selector.mm +++ b/chrome/browser/ui/cocoa/ssl_client_certificate_selector.mm @@ -97,7 +97,6 @@ void ShowSSLClientCertificateSelector( TabContents* parent, net::SSLCertRequestInfo* cert_request_info, SSLClientAuthHandler* delegate) { - // TODO(davidben): Implement a tab-modal dialog. DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); SSLClientCertificateSelectorCocoa* selector = [[[SSLClientCertificateSelectorCocoa alloc] diff --git a/chrome/browser/ui/views/ssl_client_certificate_selector.cc b/chrome/browser/ui/views/ssl_client_certificate_selector.cc new file mode 100644 index 0000000..734ff26 --- /dev/null +++ b/chrome/browser/ui/views/ssl_client_certificate_selector.cc @@ -0,0 +1,293 @@ +// 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/ssl_client_certificate_selector.h" + +#include "base/compiler_specific.h" +#include "base/i18n/time_formatting.h" +#include "base/logging.h" +#include "base/utf_string_conversions.h" +#include "chrome/browser/certificate_viewer.h" +#include "content/browser/browser_thread.h" +#include "content/browser/tab_contents/tab_contents.h" +#include "grit/generated_resources.h" +#include "net/base/x509_certificate.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/models/table_model.h" +#include "ui/base/models/table_model_observer.h" +#include "ui/gfx/native_widget_types.h" +#include "views/controls/button/text_button.h" +#include "views/controls/label.h" +#include "views/controls/table/table_view.h" +#include "views/layout/grid_layout.h" +#include "views/layout/layout_constants.h" + +namespace { + +// The dimensions of the certificate selector table view, in pixels. +static const int kTableViewWidth = 400; +static const int kTableViewHeight = 100; + +} // namespace + +/////////////////////////////////////////////////////////////////////////////// +// CertificateSelectorTableModel: + +class CertificateSelectorTableModel : public ui::TableModel { + public: + explicit CertificateSelectorTableModel( + net::SSLCertRequestInfo* cert_request_info); + + // ui::TableModel implementation: + virtual int RowCount() OVERRIDE; + virtual string16 GetText(int index, int column_id) OVERRIDE; + virtual void SetObserver(ui::TableModelObserver* observer) OVERRIDE; + + private: + std::vector<string16> items_; + + DISALLOW_COPY_AND_ASSIGN(CertificateSelectorTableModel); +}; + +CertificateSelectorTableModel::CertificateSelectorTableModel( + net::SSLCertRequestInfo* cert_request_info) { + for (size_t i = 0; i < cert_request_info->client_certs.size(); ++i) { + net::X509Certificate* cert = cert_request_info->client_certs[i]; + string16 text = l10n_util::GetStringFUTF16( + IDS_CERT_SELECTOR_TABLE_CERT_FORMAT, + UTF8ToUTF16(cert->subject().GetDisplayName()), + UTF8ToUTF16(cert->issuer().GetDisplayName())); + items_.push_back(text); + } +} + +int CertificateSelectorTableModel::RowCount() { + return items_.size(); +} + +string16 CertificateSelectorTableModel::GetText(int index, int column_id) { + DCHECK_EQ(column_id, 0); + DCHECK_GE(index, 0); + DCHECK_LT(index, static_cast<int>(items_.size())); + + return items_[index]; +} + +void CertificateSelectorTableModel::SetObserver(TableModelObserver* observer) { +} + +/////////////////////////////////////////////////////////////////////////////// +// SSLClientCertificateSelector: + +SSLClientCertificateSelector::SSLClientCertificateSelector( + TabContents* parent, + net::SSLCertRequestInfo* cert_request_info, + SSLClientAuthHandler* delegate) + : SSLClientAuthObserver(cert_request_info, delegate), + cert_request_info_(cert_request_info), + delegate_(delegate), + model_(new CertificateSelectorTableModel(cert_request_info)), + tab_contents_(parent), + window_(NULL) { + DVLOG(1) << __FUNCTION__; +} + +SSLClientCertificateSelector::~SSLClientCertificateSelector() { + table_->SetModel(NULL); +} + +void SSLClientCertificateSelector::Init() { + views::GridLayout* layout = views::GridLayout::CreatePanel(this); + SetLayoutManager(layout); + + const int column_set_id = 0; + views::ColumnSet* column_set = layout->AddColumnSet(column_set_id); + column_set->AddColumn( + views::GridLayout::FILL, views::GridLayout::FILL, + 1, views::GridLayout::USE_PREF, 0, 0); + + layout->StartRow(0, column_set_id); + std::wstring text = UTF16ToWide(l10n_util::GetStringFUTF16( + IDS_CLIENT_CERT_DIALOG_TEXT, + ASCIIToUTF16(cert_request_info_->host_and_port))); + views::Label* label = new views::Label(text); + label->SetMultiLine(true); + label->SetHorizontalAlignment(views::Label::ALIGN_LEFT); + label->SetAllowCharacterBreak(true); + layout->AddView(label); + + layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); + + CreateCertTable(); + layout->StartRow(1, column_set_id); + layout->AddView(table_); + + layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); + + CreateViewCertButton(); + + StartObserving(); + + window_ = tab_contents_->CreateConstrainedDialog(this); + + // Select the first row automatically. This must be done after the dialog has + // been created. + table_->Select(0); +} + +net::X509Certificate* SSLClientCertificateSelector::GetSelectedCert() const { + int selected = table_->FirstSelectedRow(); + if (selected >= 0 && + selected < static_cast<int>( + cert_request_info_->client_certs.size())) + return cert_request_info_->client_certs[selected]; + return NULL; +} + +/////////////////////////////////////////////////////////////////////////////// +// SSLClientAuthObserver implementation: + +void SSLClientCertificateSelector::OnCertSelectedByNotification() { + DVLOG(1) << __FUNCTION__; + delegate_ = NULL; + DCHECK(window_); + window_->CloseConstrainedWindow(); +} + +/////////////////////////////////////////////////////////////////////////////// +// DialogDelegateView implementation: + +bool SSLClientCertificateSelector::CanResize() const { + return true; +} + +std::wstring SSLClientCertificateSelector::GetWindowTitle() const { + return UTF16ToWide(l10n_util::GetStringUTF16(IDS_CLIENT_CERT_DIALOG_TITLE)); +} + +void SSLClientCertificateSelector::DeleteDelegate() { + DVLOG(1) << __FUNCTION__; + delete this; +} + +bool SSLClientCertificateSelector::IsDialogButtonEnabled( + ui::MessageBoxFlags::DialogButton button) const { + if (button == MessageBoxFlags::DIALOGBUTTON_OK) + return !!GetSelectedCert(); + return true; +} + +bool SSLClientCertificateSelector::Cancel() { + DVLOG(1) << __FUNCTION__; + StopObserving(); + if (delegate_) { + delegate_->CertificateSelected(NULL); + delegate_ = NULL; + } + + return true; +} + +bool SSLClientCertificateSelector::Accept() { + DVLOG(1) << __FUNCTION__; + net::X509Certificate* cert = GetSelectedCert(); + if (cert) { + StopObserving(); + delegate_->CertificateSelected(GetSelectedCert()); + delegate_ = NULL; + + return true; + } + + return false; +} + +views::View* SSLClientCertificateSelector::GetInitiallyFocusedView() { + return table_; +} + +views::View* SSLClientCertificateSelector::GetContentsView() { + return this; +} + +views::View* SSLClientCertificateSelector::GetExtraView() { + return view_cert_button_container_; +} + +/////////////////////////////////////////////////////////////////////////////// +// views::ButtonListener implementation: + +void SSLClientCertificateSelector::ButtonPressed( + views::Button* sender, const views::Event& event) { + if (sender == view_cert_button_) { + net::X509Certificate* cert = GetSelectedCert(); + if (cert) + ShowCertificateViewer(tab_contents_->GetDialogRootWindow(), cert); + } +} + +/////////////////////////////////////////////////////////////////////////////// +// views::TableViewObserver implementation: +void SSLClientCertificateSelector::OnSelectionChanged() { + GetDialogClientView()->ok_button()->SetEnabled(!!GetSelectedCert()); +} + +void SSLClientCertificateSelector::OnDoubleClick() { + if (Accept()) + window_->CloseConstrainedWindow(); +} + +/////////////////////////////////////////////////////////////////////////////// +// SSLClientCertificateSelector private methods: + +void SSLClientCertificateSelector::CreateCertTable() { + std::vector<TableColumn> columns; + columns.push_back(TableColumn()); + table_ = new views::TableView( + model_.get(), + columns, + views::TEXT_ONLY, + true, // single_selection + true, // resizable_columns + true); // autosize_columns + table_->SetPreferredSize( + gfx::Size(kTableViewWidth, kTableViewHeight)); + table_->SetObserver(this); +} + +void SSLClientCertificateSelector::CreateViewCertButton() { + view_cert_button_ = new views::NativeTextButton(this, UTF16ToWide( + l10n_util::GetStringUTF16(IDS_PAGEINFO_CERT_INFO_BUTTON))); + + // Wrap the view cert button in a grid layout in order to left-align it. + view_cert_button_container_ = new views::View(); + views::GridLayout* layout = new views::GridLayout( + view_cert_button_container_); + view_cert_button_container_->SetLayoutManager(layout); + + int column_set_id = 0; + views::ColumnSet* column_set = layout->AddColumnSet(column_set_id); + column_set->AddColumn(views::GridLayout::LEADING, views::GridLayout::LEADING, + 0, views::GridLayout::USE_PREF, 0, 0); + layout->StartRow(0, column_set_id); + layout->AddView(view_cert_button_); +} + +/////////////////////////////////////////////////////////////////////////////// +// SSLClientCertificateSelector public interface: + +namespace browser { + +void ShowSSLClientCertificateSelector( + TabContents* parent, + net::SSLCertRequestInfo* cert_request_info, + SSLClientAuthHandler* delegate) { + DVLOG(1) << __FUNCTION__ << " " << parent; + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + (new SSLClientCertificateSelector(parent, + cert_request_info, + delegate))->Init(); +} + +} // namespace browser diff --git a/chrome/browser/ui/views/ssl_client_certificate_selector.h b/chrome/browser/ui/views/ssl_client_certificate_selector.h new file mode 100644 index 0000000..9514144 --- /dev/null +++ b/chrome/browser/ui/views/ssl_client_certificate_selector.h @@ -0,0 +1,92 @@ +// 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_SSL_CLIENT_CERTIFICATE_SELECTOR_H_ +#define CHROME_BROWSER_UI_VIEWS_SSL_CLIENT_CERTIFICATE_SELECTOR_H_ +#pragma once + +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/string16.h" +#include "base/memory/ref_counted.h" +#include "content/browser/ssl/ssl_client_auth_handler.h" +#include "content/browser/tab_contents/constrained_window.h" +#include "ui/base/message_box_flags.h" +#include "views/controls/button/button.h" +#include "views/controls/table/table_view_observer.h" +#include "views/view.h" +#include "views/window/dialog_delegate.h" + +// This header file exists only for testing. Chrome should access the +// certificate selector only through the cross-platform interface +// chrome/browser/ssl_client_certificate_selector.h. + +namespace views { +class TableView; +class TextButton; +} + +class CertificateSelectorTableModel; + +class SSLClientCertificateSelector : public SSLClientAuthObserver, + public views::DialogDelegateView, + public views::ButtonListener, + public views::TableViewObserver { + public: + SSLClientCertificateSelector( + TabContents* parent, + net::SSLCertRequestInfo* cert_request_info, + SSLClientAuthHandler* delegate); + virtual ~SSLClientCertificateSelector(); + + void Init(); + + net::X509Certificate* GetSelectedCert() const; + + // SSLClientAuthObserver implementation: + virtual void OnCertSelectedByNotification() OVERRIDE; + + // DialogDelegateView: + virtual bool CanResize() const OVERRIDE; + virtual std::wstring GetWindowTitle() const OVERRIDE; + virtual void DeleteDelegate() OVERRIDE; + virtual bool IsDialogButtonEnabled( + ui::MessageBoxFlags::DialogButton button) const OVERRIDE; + virtual bool Cancel() OVERRIDE; + virtual bool Accept() OVERRIDE; + virtual views::View* GetInitiallyFocusedView() OVERRIDE; + virtual views::View* GetContentsView() OVERRIDE; + virtual views::View* GetExtraView() OVERRIDE; + + // views::ButtonListener: + virtual void ButtonPressed(views::Button* sender, + const views::Event& event) OVERRIDE; + + // views::TableViewObserver: + virtual void OnSelectionChanged() OVERRIDE; + virtual void OnDoubleClick() OVERRIDE; + + private: + void CreateCertTable(); + void CreateViewCertButton(); + + scoped_refptr<net::SSLCertRequestInfo> cert_request_info_; + + scoped_refptr<SSLClientAuthHandler> delegate_; + + scoped_ptr<CertificateSelectorTableModel> model_; + + TabContents* tab_contents_; + + ConstrainedWindow* window_; + views::TableView* table_; + views::TextButton* view_cert_button_; + views::View* view_cert_button_container_; + + DISALLOW_COPY_AND_ASSIGN(SSLClientCertificateSelector); +}; + +#endif // CHROME_BROWSER_UI_VIEWS_SSL_CLIENT_CERTIFICATE_SELECTOR_H_ diff --git a/chrome/browser/ui/views/ssl_client_certificate_selector_browsertest.cc b/chrome/browser/ui/views/ssl_client_certificate_selector_browsertest.cc new file mode 100644 index 0000000..9a55e0d --- /dev/null +++ b/chrome/browser/ui/views/ssl_client_certificate_selector_browsertest.cc @@ -0,0 +1,205 @@ +// 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 "base/file_path.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/browser/ui/views/ssl_client_certificate_selector.h" +#include "chrome/test/in_process_browser_test.h" +#include "chrome/test/ui_test_utils.h" +#include "content/browser/ssl/ssl_client_auth_handler_mock.h" +#include "net/base/cert_test_util.h" +#include "net/base/x509_certificate.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using ::testing::Mock; +using ::testing::StrictMock; + +// We don't have a way to do end-to-end SSL client auth testing, so this test +// creates a certificate selector_ manually with a mocked +// SSLClientAuthHandler. + +class SSLClientCertificateSelectorTest : public InProcessBrowserTest { + public: + virtual void SetUpInProcessBrowserTestFixture() { + FilePath certs_dir = net::GetTestCertsDirectory(); + + mit_davidben_cert_ = net::ImportCertFromFile(certs_dir, "mit.davidben.der"); + ASSERT_NE(static_cast<net::X509Certificate*>(NULL), mit_davidben_cert_); + + foaf_me_chromium_test_cert_ = net::ImportCertFromFile( + certs_dir, "foaf.me.chromium-test-cert.der"); + ASSERT_NE(static_cast<net::X509Certificate*>(NULL), + foaf_me_chromium_test_cert_); + + cert_request_info_ = new net::SSLCertRequestInfo; + cert_request_info_->host_and_port = "foo:123"; + cert_request_info_->client_certs.push_back(mit_davidben_cert_); + cert_request_info_->client_certs.push_back(foaf_me_chromium_test_cert_); + } + + virtual void SetUpOnMainThread() { + auth_handler_ = new StrictMock<SSLClientAuthHandlerMock>( + static_cast<net::URLRequest*>(NULL), cert_request_info_); + + ui_test_utils::WaitForLoadStop(browser()->GetSelectedTabContents()); + selector_ = new SSLClientCertificateSelector( + browser()->GetSelectedTabContents(), + cert_request_info_, + auth_handler_); + selector_->Init(); + + EXPECT_EQ(mit_davidben_cert_.get(), selector_->GetSelectedCert()); + } + + // Have to release our reference to the auth handler during the test to allow + // it to be destroyed while the Browser and its IO thread still exist. + virtual void CleanUpOnMainThread() { + auth_handler_ = NULL; + } + + protected: + scoped_refptr<net::X509Certificate> mit_davidben_cert_; + scoped_refptr<net::X509Certificate> foaf_me_chromium_test_cert_; + scoped_refptr<net::SSLCertRequestInfo> cert_request_info_; + scoped_refptr<StrictMock<SSLClientAuthHandlerMock> > auth_handler_; + // The selector will be deleted when a cert is selected or the tab is closed. + SSLClientCertificateSelector* selector_; +}; + +class SSLClientCertificateSelectorMultiTabTest + : public SSLClientCertificateSelectorTest { + public: + virtual void SetUpInProcessBrowserTestFixture() { + SSLClientCertificateSelectorTest::SetUpInProcessBrowserTestFixture(); + + cert_request_info_1_ = new net::SSLCertRequestInfo; + cert_request_info_1_->host_and_port = "bar:123"; + cert_request_info_1_->client_certs.push_back(mit_davidben_cert_); + cert_request_info_1_->client_certs.push_back(foaf_me_chromium_test_cert_); + + cert_request_info_2_ = new net::SSLCertRequestInfo; + cert_request_info_2_->host_and_port = "bar:123"; + cert_request_info_2_->client_certs.push_back(mit_davidben_cert_); + cert_request_info_2_->client_certs.push_back(foaf_me_chromium_test_cert_); + } + + virtual void SetUpOnMainThread() { + SSLClientCertificateSelectorTest::SetUpOnMainThread(); + + auth_handler_1_ = new StrictMock<SSLClientAuthHandlerMock>( + static_cast<net::URLRequest*>(NULL), cert_request_info_1_); + auth_handler_2_ = new StrictMock<SSLClientAuthHandlerMock>( + static_cast<net::URLRequest*>(NULL), cert_request_info_2_); + + AddTabAtIndex(1, GURL("about:blank"), PageTransition::LINK); + AddTabAtIndex(2, GURL("about:blank"), PageTransition::LINK); + ASSERT_TRUE(NULL != browser()->GetTabContentsAt(0)); + ASSERT_TRUE(NULL != browser()->GetTabContentsAt(1)); + ASSERT_TRUE(NULL != browser()->GetTabContentsAt(2)); + ui_test_utils::WaitForLoadStop(browser()->GetTabContentsAt(1)); + ui_test_utils::WaitForLoadStop(browser()->GetTabContentsAt(2)); + + selector_1_ = new SSLClientCertificateSelector( + browser()->GetTabContentsAt(1), + cert_request_info_1_, + auth_handler_1_); + selector_1_->Init(); + selector_2_ = new SSLClientCertificateSelector( + browser()->GetTabContentsAt(2), + cert_request_info_2_, + auth_handler_2_); + selector_2_->Init(); + + EXPECT_EQ(2, browser()->active_index()); + EXPECT_EQ(mit_davidben_cert_.get(), selector_1_->GetSelectedCert()); + EXPECT_EQ(mit_davidben_cert_.get(), selector_2_->GetSelectedCert()); + } + + virtual void CleanUpOnMainThread() { + auth_handler_2_ = NULL; + auth_handler_1_ = NULL; + SSLClientCertificateSelectorTest::CleanUpOnMainThread(); + } + + protected: + scoped_refptr<net::SSLCertRequestInfo> cert_request_info_1_; + scoped_refptr<net::SSLCertRequestInfo> cert_request_info_2_; + scoped_refptr<StrictMock<SSLClientAuthHandlerMock> > auth_handler_1_; + scoped_refptr<StrictMock<SSLClientAuthHandlerMock> > auth_handler_2_; + SSLClientCertificateSelector* selector_1_; + SSLClientCertificateSelector* selector_2_; +}; + +IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorTest, SelectNone) { + EXPECT_CALL(*auth_handler_, CertificateSelectedNoNotify(NULL)); + + // Let the mock get checked on destruction. +} + +IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorTest, Escape) { + EXPECT_CALL(*auth_handler_, CertificateSelectedNoNotify(NULL)); + + EXPECT_TRUE(ui_test_utils::SendKeyPressSync( + browser(), ui::VKEY_ESCAPE, false, false, false, false)); + + Mock::VerifyAndClear(auth_handler_); +} + +IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorTest, SelectDefault) { + EXPECT_CALL(*auth_handler_, + CertificateSelectedNoNotify(mit_davidben_cert_.get())); + + EXPECT_TRUE(ui_test_utils::SendKeyPressSync( + browser(), ui::VKEY_RETURN, false, false, false, false)); + + Mock::VerifyAndClear(auth_handler_); +} + +IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorMultiTabTest, Escape) { + // auth_handler_1_ should get selected automatically by the + // SSLClientAuthObserver when selector_2_ is accepted, since both 1 & 2 have + // the same host:port. + EXPECT_CALL(*auth_handler_1_, CertificateSelectedNoNotify(NULL)); + EXPECT_CALL(*auth_handler_2_, CertificateSelectedNoNotify(NULL)); + + EXPECT_TRUE(ui_test_utils::SendKeyPressSync( + browser(), ui::VKEY_ESCAPE, false, false, false, false)); + + Mock::VerifyAndClear(auth_handler_); + Mock::VerifyAndClear(auth_handler_1_); + Mock::VerifyAndClear(auth_handler_2_); + + // Now let the default selection for auth_handler_ mock get checked on + // destruction. + EXPECT_CALL(*auth_handler_, CertificateSelectedNoNotify(NULL)); +} + +IN_PROC_BROWSER_TEST_F(SSLClientCertificateSelectorMultiTabTest, SelectSecond) { + // auth_handler_1_ should get selected automatically by the + // SSLClientAuthObserver when selector_2_ is accepted, since both 1 & 2 have + // the same host:port. + EXPECT_CALL(*auth_handler_1_, + CertificateSelectedNoNotify(foaf_me_chromium_test_cert_.get())); + EXPECT_CALL(*auth_handler_2_, + CertificateSelectedNoNotify(foaf_me_chromium_test_cert_.get())); + + EXPECT_TRUE(ui_test_utils::SendKeyPressSync( + browser(), ui::VKEY_DOWN, false, false, false, false)); + + EXPECT_EQ(mit_davidben_cert_.get(), selector_->GetSelectedCert()); + EXPECT_EQ(mit_davidben_cert_.get(), selector_1_->GetSelectedCert()); + EXPECT_EQ(foaf_me_chromium_test_cert_.get(), selector_2_->GetSelectedCert()); + + EXPECT_TRUE(ui_test_utils::SendKeyPressSync( + browser(), ui::VKEY_RETURN, false, false, false, false)); + + Mock::VerifyAndClear(auth_handler_); + Mock::VerifyAndClear(auth_handler_1_); + Mock::VerifyAndClear(auth_handler_2_); + + // Now let the default selection for auth_handler_ mock get checked on + // destruction. + EXPECT_CALL(*auth_handler_, CertificateSelectedNoNotify(NULL)); +} diff --git a/chrome/browser/ui/views/ssl_client_certificate_selector_win.cc b/chrome/browser/ui/views/ssl_client_certificate_selector_win.cc deleted file mode 100644 index 51bfa20..0000000 --- a/chrome/browser/ui/views/ssl_client_certificate_selector_win.cc +++ /dev/null @@ -1,72 +0,0 @@ -// 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/ssl_client_certificate_selector.h" - -#include <windows.h> -#include <cryptuiapi.h> -#pragma comment(lib, "cryptui.lib") - -#include "base/string_util.h" -#include "base/utf_string_conversions.h" -#include "chrome/browser/ui/browser_list.h" -#include "chrome/browser/ui/browser_window.h" -#include "content/browser/ssl/ssl_client_auth_handler.h" -#include "content/browser/tab_contents/tab_contents.h" -#include "grit/generated_resources.h" -#include "net/url_request/url_request.h" -#include "ui/base/l10n/l10n_util.h" - -namespace browser { - -void ShowSSLClientCertificateSelector( - TabContents* parent, - net::SSLCertRequestInfo* cert_request_info, - SSLClientAuthHandler* delegate) { - // TODO(jcampan): replace this with our own cert selection dialog. - // CryptUIDlgSelectCertificateFromStore is blocking (but still processes - // Windows messages), which is scary. - // - // TODO(davidben): Make this dialog tab-modal to the - // TabContents. This depends on the above TODO. - HCERTSTORE client_certs = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, NULL, - 0, NULL); - BOOL ok; - for (size_t i = 0; i < cert_request_info->client_certs.size(); ++i) { - PCCERT_CONTEXT cc = cert_request_info->client_certs[i]->os_cert_handle(); - ok = CertAddCertificateContextToStore(client_certs, cc, - CERT_STORE_ADD_ALWAYS, NULL); - DCHECK(ok); - } - - std::wstring title = - UTF16ToWide(l10n_util::GetStringUTF16(IDS_CLIENT_CERT_DIALOG_TITLE)); - std::wstring text = UTF16ToWide(l10n_util::GetStringFUTF16( - IDS_CLIENT_CERT_DIALOG_TEXT, - ASCIIToUTF16(cert_request_info->host_and_port))); - PCCERT_CONTEXT cert_context = CryptUIDlgSelectCertificateFromStore( - client_certs, parent->GetDialogRootWindow(), - title.c_str(), text.c_str(), 0, 0, NULL); - - net::X509Certificate* cert = NULL; - if (cert_context) { - for (size_t i = 0; i < cert_request_info->client_certs.size(); ++i) { - net::X509Certificate* client_cert = cert_request_info->client_certs[i]; - if (net::X509Certificate::IsSameOSCert(cert_context, - client_cert->os_cert_handle())) { - cert = client_cert; - break; - } - } - DCHECK(cert != NULL); - net::X509Certificate::FreeOSCertHandle(cert_context); - } - - ok = CertCloseStore(client_certs, CERT_CLOSE_STORE_CHECK_FLAG); - DCHECK(ok); - - delegate->CertificateSelected(cert); -} - -} // namespace browser diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 2df153e..9a9c605 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -3246,7 +3246,8 @@ 'browser/ui/views/sad_tab_view.h', 'browser/ui/views/select_file_dialog.cc', 'browser/ui/views/shell_dialogs_win.cc', - 'browser/ui/views/ssl_client_certificate_selector_win.cc', + 'browser/ui/views/ssl_client_certificate_selector.cc', + 'browser/ui/views/ssl_client_certificate_selector.h', 'browser/ui/views/status_bubble_views.cc', 'browser/ui/views/status_bubble_views.h', 'browser/ui/views/status_icons/status_icon_win.cc', diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index e75d5de..114a2f7 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -201,6 +201,7 @@ '../content/browser/renderer_host/test_backing_store.h', '../content/browser/renderer_host/test_render_view_host.cc', '../content/browser/renderer_host/test_render_view_host.h', + '../content/browser/ssl/ssl_client_auth_handler_mock.h', '../content/browser/tab_contents/test_tab_contents.cc', '../content/browser/tab_contents/test_tab_contents.h', '../content/common/notification_observer_mock.cc', @@ -467,6 +468,7 @@ '../third_party/icu/icu.gyp:icui18n', '../third_party/libpng/libpng.gyp:libpng', '../third_party/zlib/zlib.gyp:zlib', + '../testing/gmock.gyp:gmock', '../testing/gtest.gyp:gtest', '../third_party/npapi/npapi.gyp:npapi', # run time dependency @@ -490,8 +492,9 @@ 'browser/ui/views/bookmarks/bookmark_bar_view_test.cc', 'browser/ui/views/button_dropdown_test.cc', 'browser/ui/views/find_bar_host_interactive_uitest.cc', - 'browser/ui/views/tabs/tab_dragging_test.cc', 'browser/ui/views/menu_item_view_test.cc', + 'browser/ui/views/ssl_client_certificate_selector_browsertest.cc', + 'browser/ui/views/tabs/tab_dragging_test.cc', 'browser/ui/webui/workers_ui_browsertest.cc', 'test/interactive_ui/fast_shutdown_interactive_uitest.cc', 'test/interactive_ui/infobars_uitest.cc', @@ -622,7 +625,11 @@ }, }, }, # configurations - }], # OS=="win" + }, { # else: OS != "win" + 'sources!': [ + 'browser/ui/views/ssl_client_certificate_selector_browsertest.cc', + ], + }], # OS != "win" ], # conditions }, { diff --git a/content/browser/ssl/ssl_client_auth_handler.h b/content/browser/ssl/ssl_client_auth_handler.h index 404b8d2..4dbcaa7 100644 --- a/content/browser/ssl/ssl_client_auth_handler.h +++ b/content/browser/ssl/ssl_client_auth_handler.h @@ -46,17 +46,18 @@ class SSLClientAuthHandler // Like CertificateSelected, but does not send SSL_CLIENT_AUTH_CERT_SELECTED // notification. Used to avoid notification re-spamming when other // certificate selectors act on a notification matching the same host. - void CertificateSelectedNoNotify(net::X509Certificate* cert); + virtual void CertificateSelectedNoNotify(net::X509Certificate* cert); // Returns the SSLCertRequestInfo for this handler. net::SSLCertRequestInfo* cert_request_info() { return cert_request_info_; } + protected: + virtual ~SSLClientAuthHandler(); + private: friend class BrowserThread; friend class DeleteTask<SSLClientAuthHandler>; - virtual ~SSLClientAuthHandler(); - // Notifies that the user has selected a cert. // Called on the IO thread. void DoCertificateSelected(net::X509Certificate* cert); diff --git a/content/browser/ssl/ssl_client_auth_handler_mock.h b/content/browser/ssl/ssl_client_auth_handler_mock.h new file mode 100644 index 0000000..4ebb6f0 --- /dev/null +++ b/content/browser/ssl/ssl_client_auth_handler_mock.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 CONTENT_BROWSER_SSL_SSL_CLIENT_AUTH_HANDLER_MOCK_H_ +#define CONTENT_BROWSER_SSL_SSL_CLIENT_AUTH_HANDLER_MOCK_H_ +#pragma once + +#include "content/browser/ssl/ssl_client_auth_handler.h" +#include "testing/gmock/include/gmock/gmock.h" + +class SSLClientAuthHandlerMock : public SSLClientAuthHandler { + public: + SSLClientAuthHandlerMock( + net::URLRequest* request, + net::SSLCertRequestInfo* cert_request_info) + : SSLClientAuthHandler(request, cert_request_info) { + } + MOCK_METHOD1(CertificateSelectedNoNotify, void(net::X509Certificate* cert)); +}; + + +#endif // CONTENT_BROWSER_SSL_SSL_CLIENT_AUTH_HANDLER_MOCK_H_ |