// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "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/strings/utf_string_conversions.h" #include "chrome/browser/certificate_viewer.h" #include "chrome/browser/ui/views/constrained_window_views.h" #include "components/web_modal/web_contents_modal_dialog_host.h" #include "components/web_modal/web_contents_modal_dialog_manager.h" #include "components/web_modal/web_contents_modal_dialog_manager_delegate.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/web_contents.h" #include "content/public/browser/web_contents_view.h" #include "grit/generated_resources.h" #include "net/cert/x509_certificate.h" #include "net/ssl/ssl_cert_request_info.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 "ui/views/controls/button/label_button.h" #include "ui/views/controls/label.h" #include "ui/views/controls/table/table_view.h" #include "ui/views/layout/grid_layout.h" #include "ui/views/layout/layout_constants.h" #include "ui/views/widget/widget.h" #include "ui/views/window/dialog_client_view.h" #if defined(USE_NSS) #include "chrome/browser/ui/crypto_module_password_dialog_nss.h" #endif using content::BrowserThread; using content::WebContents; using web_modal::WebContentsModalDialogManager; using web_modal::WebContentsModalDialogManagerDelegate; 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 base::string16 GetText(int index, int column_id) OVERRIDE; virtual void SetObserver(ui::TableModelObserver* observer) OVERRIDE; private: std::vector 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].get(); base::string16 text = l10n_util::GetStringFUTF16( IDS_CERT_SELECTOR_TABLE_CERT_FORMAT, base::UTF8ToUTF16(cert->subject().GetDisplayName()), base::UTF8ToUTF16(cert->issuer().GetDisplayName())); items_.push_back(text); } } int CertificateSelectorTableModel::RowCount() { return items_.size(); } base::string16 CertificateSelectorTableModel::GetText(int index, int column_id) { DCHECK_EQ(column_id, 0); DCHECK_GE(index, 0); DCHECK_LT(index, static_cast(items_.size())); return items_[index]; } void CertificateSelectorTableModel::SetObserver( ui::TableModelObserver* observer) { } /////////////////////////////////////////////////////////////////////////////// // SSLClientCertificateSelector: SSLClientCertificateSelector::SSLClientCertificateSelector( WebContents* web_contents, const net::HttpNetworkSession* network_session, net::SSLCertRequestInfo* cert_request_info, const chrome::SelectCertificateCallback& callback) : SSLClientAuthObserver(network_session, cert_request_info, callback), model_(new CertificateSelectorTableModel(cert_request_info)), web_contents_(web_contents), window_(NULL), table_(NULL), view_cert_button_(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); base::string16 text = l10n_util::GetStringFUTF16( IDS_CLIENT_CERT_DIALOG_TEXT, base::ASCIIToUTF16(cert_request_info()->host_and_port.ToString())); views::Label* label = new views::Label(text); label->SetMultiLine(true); label->SetHorizontalAlignment(gfx::ALIGN_LEFT); label->SetAllowCharacterBreak(true); layout->AddView(label); layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); CreateCertTable(); layout->StartRow(1, column_set_id); layout->AddView(table_->CreateParentIfNecessary(), 1, 1, views::GridLayout::FILL, views::GridLayout::FILL, kTableViewWidth, kTableViewHeight); layout->AddPaddingRow(0, views::kRelatedControlVerticalSpacing); StartObserving(); WebContentsModalDialogManager* web_contents_modal_dialog_manager = WebContentsModalDialogManager::FromWebContents(web_contents_); WebContentsModalDialogManagerDelegate* modal_delegate = web_contents_modal_dialog_manager->delegate(); DCHECK(modal_delegate); window_ = views::Widget::CreateWindowAsFramelessChild( this, modal_delegate->GetWebContentsModalDialogHost()->GetHostView()); web_contents_modal_dialog_manager->ShowDialog(window_->GetNativeView()); // 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( cert_request_info()->client_certs.size())) return cert_request_info()->client_certs[selected].get(); return NULL; } /////////////////////////////////////////////////////////////////////////////// // SSLClientAuthObserver implementation: void SSLClientCertificateSelector::OnCertSelectedByNotification() { DVLOG(1) << __FUNCTION__; DCHECK(window_); window_->Close(); } /////////////////////////////////////////////////////////////////////////////// // DialogDelegateView implementation: bool SSLClientCertificateSelector::CanResize() const { return true; } base::string16 SSLClientCertificateSelector::GetWindowTitle() const { return l10n_util::GetStringUTF16(IDS_CLIENT_CERT_DIALOG_TITLE); } void SSLClientCertificateSelector::DeleteDelegate() { DVLOG(1) << __FUNCTION__; delete this; } bool SSLClientCertificateSelector::IsDialogButtonEnabled( ui::DialogButton button) const { if (button == ui::DIALOG_BUTTON_OK) return !!GetSelectedCert(); return true; } bool SSLClientCertificateSelector::Cancel() { DVLOG(1) << __FUNCTION__; StopObserving(); CertificateSelected(NULL); return true; } bool SSLClientCertificateSelector::Accept() { DVLOG(1) << __FUNCTION__; scoped_refptr cert = GetSelectedCert(); if (cert) { // Remove the observer before we try unlocking, otherwise we might act on a // notification while waiting for the unlock dialog, causing us to delete // ourself before the Unlocked callback gets called. StopObserving(); #if defined(USE_NSS) chrome::UnlockCertSlotIfNecessary( cert, chrome::kCryptoModulePasswordClientAuth, cert_request_info()->host_and_port, window_->GetNativeView(), base::Bind(&SSLClientCertificateSelector::Unlocked, base::Unretained(this), cert)); #else Unlocked(cert); #endif return false; // Unlocked() will close the dialog. } return false; } // TODO(wittman): Remove this override once we move to the new style frame view // on all dialogs. views::NonClientFrameView* SSLClientCertificateSelector::CreateNonClientFrameView( views::Widget* widget) { return CreateConstrainedStyleNonClientFrameView( widget, web_contents_->GetBrowserContext()); } views::View* SSLClientCertificateSelector::GetInitiallyFocusedView() { return table_; } views::View* SSLClientCertificateSelector::CreateExtraView() { DCHECK(!view_cert_button_); view_cert_button_ = new views::LabelButton(this, l10n_util::GetStringUTF16(IDS_PAGEINFO_CERT_INFO_BUTTON)); view_cert_button_->SetStyle(views::Button::STYLE_BUTTON); return view_cert_button_; } ui::ModalType SSLClientCertificateSelector::GetModalType() const { #if defined(USE_ASH) return ui::MODAL_TYPE_CHILD; #else return views::WidgetDelegate::GetModalType(); #endif } /////////////////////////////////////////////////////////////////////////////// // views::ButtonListener implementation: void SSLClientCertificateSelector::ButtonPressed( views::Button* sender, const ui::Event& event) { if (sender == view_cert_button_) { net::X509Certificate* cert = GetSelectedCert(); if (cert) ShowCertificateViewer(web_contents_, web_contents_->GetView()->GetTopLevelNativeWindow(), cert); } } /////////////////////////////////////////////////////////////////////////////// // views::TableViewObserver implementation: void SSLClientCertificateSelector::OnSelectionChanged() { GetDialogClientView()->ok_button()->SetEnabled(!!GetSelectedCert()); } void SSLClientCertificateSelector::OnDoubleClick() { if (Accept()) window_->Close(); } /////////////////////////////////////////////////////////////////////////////// // SSLClientCertificateSelector private methods: void SSLClientCertificateSelector::CreateCertTable() { std::vector columns; columns.push_back(ui::TableColumn()); table_ = new views::TableView(model_.get(), columns, views::TEXT_ONLY, true /* single_selection */); table_->SetObserver(this); } void SSLClientCertificateSelector::Unlocked(net::X509Certificate* cert) { DVLOG(1) << __FUNCTION__; CertificateSelected(cert); window_->Close(); } namespace chrome { void ShowSSLClientCertificateSelector( content::WebContents* contents, const net::HttpNetworkSession* network_session, net::SSLCertRequestInfo* cert_request_info, const chrome::SelectCertificateCallback& callback) { DVLOG(1) << __FUNCTION__ << " " << contents; DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); (new SSLClientCertificateSelector( contents, network_session, cert_request_info, callback))->Init(); } } // namespace chrome