summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormattm@chromium.org <mattm@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-07-21 02:56:58 +0000
committermattm@chromium.org <mattm@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-07-21 02:56:58 +0000
commitf245542e9805530611a6ce4f594e9a1d71b195f8 (patch)
tree45bef210fefc64d205858bfdca47a478839cb268
parent67d2d80c1505fe6ba82124b2cc1d37d0764e0048 (diff)
downloadchromium_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.grd6
-rw-r--r--chrome/browser/ssl_client_certificate_selector.h5
-rw-r--r--chrome/browser/ui/cocoa/ssl_client_certificate_selector.mm1
-rw-r--r--chrome/browser/ui/views/ssl_client_certificate_selector.cc293
-rw-r--r--chrome/browser/ui/views/ssl_client_certificate_selector.h92
-rw-r--r--chrome/browser/ui/views/ssl_client_certificate_selector_browsertest.cc205
-rw-r--r--chrome/browser/ui/views/ssl_client_certificate_selector_win.cc72
-rw-r--r--chrome/chrome_browser.gypi3
-rw-r--r--chrome/chrome_tests.gypi11
-rw-r--r--content/browser/ssl/ssl_client_auth_handler.h7
-rw-r--r--content/browser/ssl/ssl_client_auth_handler_mock.h23
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_