diff options
author | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
---|---|---|
committer | initial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-07-26 23:55:29 +0000 |
commit | 09911bf300f1a419907a9412154760efd0b7abc3 (patch) | |
tree | f131325fb4e2ad12c6d3504ab75b16dd92facfed /chrome/browser/ssl_manager.cc | |
parent | 586acc5fe142f498261f52c66862fa417c3d52d2 (diff) | |
download | chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.zip chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.gz chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.bz2 |
Add chrome to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/ssl_manager.cc')
-rw-r--r-- | chrome/browser/ssl_manager.cc | 780 |
1 files changed, 780 insertions, 0 deletions
diff --git a/chrome/browser/ssl_manager.cc b/chrome/browser/ssl_manager.cc new file mode 100644 index 0000000..c15436d --- /dev/null +++ b/chrome/browser/ssl_manager.cc @@ -0,0 +1,780 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "chrome/browser/ssl_manager.h" + +#include "base/message_loop.h" +#include "base/string_util.h" +#include "chrome/app/theme/theme_resources.h" +#include "chrome/browser/browser_process.h" +#include "chrome/browser/provisional_load_details.h" +#include "chrome/browser/load_notification_details.h" +#include "chrome/browser/load_from_memory_cache_details.h" +#include "chrome/browser/navigation_controller.h" +#include "chrome/browser/navigation_entry.h" +#include "chrome/browser/render_view_host.h" +#include "chrome/browser/resource_request_details.h" +#include "chrome/browser/ssl_error_info.h" +#include "chrome/browser/ssl_policy.h" +#include "chrome/browser/standard_layout.h" +#include "chrome/browser/tab_contents.h" +#include "chrome/browser/tab_util.h" +#include "chrome/browser/web_contents.h" +#include "chrome/browser/views/info_bar_view.h" +#include "chrome/common/l10n_util.h" +#include "chrome/common/notification_service.h" +#include "chrome/common/pref_names.h" +#include "chrome/common/pref_service.h" +#include "chrome/common/resource_bundle.h" +#include "chrome/views/decision.h" +#include "chrome/views/link.h" +#include "net/base/cert_status_flags.h" +#include "net/base/net_errors.h" +#include "net/url_request/url_request.h" +#include "webkit/glue/resource_type.h" +#include "generated_resources.h" + +//////////////////////////////////////////////////////////////////////////////// +// SSLInfoBar +// +// An info bar with a message and an optional link that runs a task when +// clicked. + +class SSLInfoBar : public InfoBarItemView, + public ChromeViews::LinkController { + public: + SSLInfoBar::SSLInfoBar(SSLManager* manager, + const std::wstring& message, + const std::wstring& link_text, + Task* task); + + virtual SSLInfoBar::~SSLInfoBar(); + + const std::wstring GetMessageText() const; + + // ChromeViews::LinkController method. + virtual void LinkActivated(ChromeViews::Link* source, int event_flags); + + private: + ChromeViews::Label* label_; + ChromeViews::Link* link_; + SSLManager* manager_; + scoped_ptr<Task> task_; + + DISALLOW_EVIL_CONSTRUCTORS(SSLInfoBar); +}; + +SSLInfoBar::SSLInfoBar(SSLManager* manager, + const std::wstring& message, + const std::wstring& link_text, + Task* task) + : label_(NULL), + link_(NULL), + manager_(manager), + task_(task) { + ResourceBundle& rb = ResourceBundle::GetSharedInstance(); + if (!message.empty()) { + label_ = new ChromeViews::Label(message); + label_->SetFont(rb.GetFont(ResourceBundle::MediumFont)); + label_->SetHorizontalAlignment(ChromeViews::Label::ALIGN_LEFT); + AddChildViewLeading(label_); + } + + if (!link_text.empty()) { + DCHECK(task) << L"Link and no task"; // What do you want to do when users + // click the link?? + link_ = new ChromeViews::Link(link_text); + link_->SetFont(rb.GetFont(ResourceBundle::MediumFont)); + link_->SetController(this); + AddChildViewTrailing(link_); + } + + SetIcon(*rb.GetBitmapNamed(IDR_INFOBAR_SSL_WARNING)); + DCHECK(manager); +} + +SSLInfoBar::~SSLInfoBar() { + // Notify our manager that we no longer exist. + manager_->OnInfoBarClose(this); +} + +const std::wstring SSLInfoBar::GetMessageText() const { + if (!label_) + return std::wstring(); + + return label_->GetText(); +} + +void SSLInfoBar::LinkActivated(ChromeViews::Link* source, int event_flags) { + if (task_.get()) { + task_->Run(); + task_.reset(); // Ensures we won't run the task again. + } + Close(); +} + +//////////////////////////////////////////////////////////////////////////////// +// SSLManager + +// static +void SSLManager::RegisterUserPrefs(PrefService* prefs) { + prefs->RegisterIntegerPref(prefs::kMixedContentFiltering, + FilterPolicy::DONT_FILTER); +} + +SSLManager::SSLManager(NavigationController* controller, Delegate* delegate) + : controller_(controller), + delegate_(delegate) { + DCHECK(controller_); + + // If do delegate is supplied, use the default policy. + if (!delegate_) + delegate_ = SSLPolicy::GetDefaultPolicy(); + + // Subscribe to various notifications. + NotificationService* service = NotificationService::current(); + service->AddObserver(this, NOTIFY_FRAME_PROVISIONAL_LOAD_COMMITTED, + Source<NavigationController>(controller_)); + service->AddObserver(this, NOTIFY_FAIL_PROVISIONAL_LOAD_WITH_ERROR, + Source<NavigationController>(controller_)); + service->AddObserver(this, NOTIFY_RESOURCE_RESPONSE_STARTED, + Source<NavigationController>(controller_)); + service->AddObserver(this, NOTIFY_RESOURCE_RECEIVED_REDIRECT, + Source<NavigationController>(controller_)); + service->AddObserver(this, NOTIFY_LOAD_FROM_MEMORY_CACHE, + Source<NavigationController>(controller_)); +} + +SSLManager::~SSLManager() { + // Close any of our info bars that are still left. + FOR_EACH_OBSERVER(SSLInfoBar, visible_info_bars_, Close()); + + // Unsubscribe from various notifications. + NotificationService* service = NotificationService::current(); + service->RemoveObserver(this, NOTIFY_FRAME_PROVISIONAL_LOAD_COMMITTED, + Source<NavigationController>(controller_)); + service->RemoveObserver(this, NOTIFY_FAIL_PROVISIONAL_LOAD_WITH_ERROR, + Source<NavigationController>(controller_)); + service->RemoveObserver(this, NOTIFY_RESOURCE_RESPONSE_STARTED, + Source<NavigationController>(controller_)); + service->RemoveObserver(this, NOTIFY_RESOURCE_RECEIVED_REDIRECT, + Source<NavigationController>(controller_)); + service->RemoveObserver(this, NOTIFY_LOAD_FROM_MEMORY_CACHE, + Source<NavigationController>(controller_)); +} + +// Delegate API method. +void SSLManager::ShowMessage(const std::wstring& msg) { + ShowMessageWithLink(msg, std::wstring(), NULL); +} + +void SSLManager::ShowMessageWithLink(const std::wstring& msg, + const std::wstring& link_text, + Task* task) { + if (controller_->GetPendingEntry()) { + // The main frame is currently loading, wait until the load is committed so + // to show the error on the right page (once the location bar shows the + // correct url). + if (std::find(pending_messages_.begin(), pending_messages_.end(), msg) == + pending_messages_.end()) + pending_messages_.push_back(SSLMessageInfo(msg, link_text, task)); + + return; + } + + NavigationEntry* entry = controller_->GetActiveEntry(); + if (!entry) + return; + + // Don't show the message if the user doesn't expect an authenticated session. + if (entry->GetSecurityStyle() <= SECURITY_STYLE_UNAUTHENTICATED) + return; + + InfoBarView* info_bar_view = + controller_->active_contents()->GetInfoBarView(); + DCHECK(info_bar_view); + + if (!info_bar_view) + return; + + // Check if we are already displaying this message. + ObserverList<SSLInfoBar>::Iterator it(visible_info_bars_); + SSLInfoBar* info_bar; + while (info_bar = it.GetNext()) { + if (info_bar->GetMessageText() == msg) + return; + } + + // Show the message. + info_bar = new SSLInfoBar(this, msg, link_text, task); + visible_info_bars_.AddObserver(info_bar); + + // false indicates info_bar is not automatically removed. + info_bar_view->AppendInfoBarItem(info_bar, false); +} + +// Delegate API method. +void SSLManager::SetMaxSecurityStyle(SecurityStyle style) { + NavigationEntry* entry = controller_->GetActiveEntry(); + if (!entry) { + NOTREACHED(); + return; + } + + if (entry->GetSecurityStyle() > style) { + entry->SetSecurityStyle(style); + controller_->EntryUpdated(entry); + } +} + +// Delegate API method. +void SSLManager::AddMessageToConsole(const std::wstring& msg, + ConsoleMessageLevel level) { + WebContents* web_contents = + controller_->GetTabContents(TAB_CONTENTS_WEB)->AsWebContents(); + if (!web_contents) + return; + + web_contents->AddMessageToConsole(L"", msg, level); +} + + +// Delegate API method. +void SSLManager::DenyCertForHost(X509Certificate* cert, + const std::string& host) { + // Remember that we don't like this cert for this host. + // TODO(abarth): Do we want to persist this information in the user's profile? + cert_policy_for_host_[host].Deny(cert); +} + +// Delegate API method. +void SSLManager::AllowCertForHost(X509Certificate* cert, + const std::string& host) { + // Remember that we do like this cert for this host. + // TODO(abarth): Do we want to persist this information in the user's profile? + cert_policy_for_host_[host].Allow(cert); +} + +// Delegate API method. +X509Certificate::Policy::Judgment SSLManager::QueryPolicy( + X509Certificate* cert, const std::string& host) { + // TODO(abarth): Do we want to read this information from the user's profile? + return cert_policy_for_host_[host].Check(cert); +} + +bool SSLManager::CanShowInsecureContent(const GURL& url) { + // TODO(jcampan): Do we want to read this information from the user's profile? + return (can_show_insecure_content_for_host_.find(url.host()) != + can_show_insecure_content_for_host_.end()); +} + +void SSLManager::AllowShowInsecureContentForURL(const GURL& url) { + can_show_insecure_content_for_host_.insert(url.host()); +} + +void SSLManager::OnInfoBarClose(SSLInfoBar* info_bar) { + // |info_bar| is no longer visible. No need to track it any more. + visible_info_bars_.RemoveObserver(info_bar); +} + +bool SSLManager::ProcessedSSLErrorFromRequest() const { + NavigationEntry* entry = controller_->GetActiveEntry(); + if (!entry) { + NOTREACHED(); + return false; + } + + return net::IsCertStatusError(entry->GetSSLCertStatus()); +} + +//////////////////////////////////////////////////////////////////////////////// +// ErrorHandler + +SSLManager::ErrorHandler::ErrorHandler(ResourceDispatcherHost* rdh, + URLRequest* request, + MessageLoop* ui_loop) + : ui_loop_(ui_loop), + io_loop_(MessageLoop::current()), + manager_(NULL), + resource_dispatcher_host_(rdh), + request_has_been_notified_(false), + request_id_(0, 0), + request_url_(request->url()) { + DCHECK(MessageLoop::current() != ui_loop); + + ResourceDispatcherHost::ExtraRequestInfo* info = + ResourceDispatcherHost::ExtraInfoForRequest(request); + request_id_.render_process_host_id = info->render_process_host_id; + request_id_.request_id = info->request_id; + + if (!tab_util::GetTabContentsID(request, + &render_process_host_id_, + &tab_contents_id_)) + NOTREACHED(); + + // This makes sure we don't disappear on the IO thread until we've given an + // answer to the URLRequest. + // + // Release in CompleteCancelRequest, CompleteContinueRequest, + // CompleteStartRequest or CompleteTakeNoAction. + AddRef(); +} + +void SSLManager::ErrorHandler::Dispatch() { + DCHECK(MessageLoop::current() == ui_loop_); + + TabContents* tab_contents = + tab_util::GetTabContentsByID(render_process_host_id_, tab_contents_id_); + + if (!tab_contents) { + // We arrived on the UI thread, but the tab we're looking for is no longer + // here. + OnDispatchFailed(); + return; + } + + // Hand ourselves off to the SSLManager. + manager_ = tab_contents->controller()->ssl_manager(); + OnDispatched(); +} + +TabContents* SSLManager::ErrorHandler::GetTabContents() { + return tab_util::GetTabContentsByID(render_process_host_id_, + tab_contents_id_); +} + +void SSLManager::ErrorHandler::CancelRequest() { + DCHECK(MessageLoop::current() == ui_loop_); + + // We need to complete this task on the IO thread. + io_loop_->PostTask(FROM_HERE, NewRunnableMethod( + this, &SSLManager::ErrorHandler::CompleteCancelRequest, + net::ERR_ABORTED)); +} + +void SSLManager::ErrorHandler::DenyRequest() { + DCHECK(MessageLoop::current() == ui_loop_); + + // We need to complete this task on the IO thread. + io_loop_->PostTask(FROM_HERE, NewRunnableMethod( + this, &SSLManager::ErrorHandler::CompleteCancelRequest, + net::ERR_INSECURE_RESPONSE)); +} + +void SSLManager::ErrorHandler::ContinueRequest() { + DCHECK(MessageLoop::current() == ui_loop_); + + // We need to complete this task on the IO thread. + io_loop_->PostTask(FROM_HERE, NewRunnableMethod( + this, &SSLManager::ErrorHandler::CompleteContinueRequest)); +} + +void SSLManager::ErrorHandler::StartRequest(FilterPolicy::Type filter_policy) { + DCHECK(MessageLoop::current() == ui_loop_); + + // We need to complete this task on the IO thread. + io_loop_->PostTask(FROM_HERE, NewRunnableMethod( + this, &SSLManager::ErrorHandler::CompleteStartRequest, filter_policy)); +} + +void SSLManager::ErrorHandler::TakeNoAction() { + DCHECK(MessageLoop::current() == ui_loop_); + + // We need to complete this task on the IO thread. + io_loop_->PostTask(FROM_HERE, NewRunnableMethod( + this, &SSLManager::ErrorHandler::CompleteTakeNoAction)); +} + +void SSLManager::ErrorHandler::CompleteCancelRequest(int error) { + DCHECK(MessageLoop::current() == io_loop_); + + // It is important that we notify the URLRequest only once. If we try to + // notify the request twice, it may no longer exist and |this| might have + // already have been deleted. + DCHECK(!request_has_been_notified_); + + if (!request_has_been_notified_) { + URLRequest* request = resource_dispatcher_host_->GetURLRequest(request_id_); + if (request) { + // The request can be NULL if it was cancelled by the renderer (as the + // result of the user navigating to a new page from the location bar). + DLOG(INFO) << "CompleteCancelRequest() url: " << request->url().spec(); + request->CancelWithError(error); + } + request_has_been_notified_ = true; + + // We're done with this object on the IO thread. + Release(); + } +} + +void SSLManager::ErrorHandler::CompleteContinueRequest() { + DCHECK(MessageLoop::current() == io_loop_); + + // It is important that we notify the URLRequest only once. If we try to + // notify the request twice, it may no longer exist and |this| might have + // already have been deleted. + DCHECK(!request_has_been_notified_); + + if (!request_has_been_notified_) { + URLRequest* request = resource_dispatcher_host_->GetURLRequest(request_id_); + if (request) { + // The request can be NULL if it was cancelled by the renderer (as the + // result of the user navigating to a new page from the location bar). + DLOG(INFO) << "CompleteContinueRequest() url: " << request->url().spec(); + request->ContinueDespiteLastError(); + } + request_has_been_notified_ = true; + + // We're done with this object on the IO thread. + Release(); + } +} + +void SSLManager::ErrorHandler::CompleteStartRequest( + FilterPolicy::Type filter_policy) { + DCHECK(MessageLoop::current() == io_loop_); + + // It is important that we notify the URLRequest only once. If we try to + // notify the request twice, it may no longer exist and |this| might have + // already have been deleted. + DCHECK(!request_has_been_notified_); + + if (request_has_been_notified_) + return; + + URLRequest* request = resource_dispatcher_host_->GetURLRequest(request_id_); + if (request) { + // The request can be NULL if it was cancelled by the renderer (as the + // result of the user navigating to a new page from the location bar). + DLOG(INFO) << "CompleteStartRequest() url: " << request->url().spec(); + // The request should not have been started (SUCCESS is the initial state). + DCHECK(request->status().status() == URLRequestStatus::SUCCESS); + ResourceDispatcherHost::ExtraRequestInfo* info = + ResourceDispatcherHost::ExtraInfoForRequest(request); + info->filter_policy = filter_policy; + request->Start(); + } + request_has_been_notified_ = true; + + // We're done with this object on the IO thread. + Release(); +} + +void SSLManager::ErrorHandler::CompleteTakeNoAction() { + DCHECK(MessageLoop::current() == io_loop_); + + // It is important that we notify the URLRequest only once. If we try to + // notify the request twice, it may no longer exist and |this| might have + // already have been deleted. + DCHECK(!request_has_been_notified_); + + if (!request_has_been_notified_) { + request_has_been_notified_ = true; + + // We're done with this object on the IO thread. + Release(); + } +} + + +//////////////////////////////////////////////////////////////////////////////// +// CertError + +SSLManager::CertError::CertError( + ResourceDispatcherHost* rdh, + URLRequest* request, + ResourceType::Type resource_type, + int cert_error, + X509Certificate* cert, + MessageLoop* ui_loop) + : ErrorHandler(rdh, request, ui_loop), + cert_error_(cert_error), + resource_type_(resource_type) { + DCHECK(request == resource_dispatcher_host_->GetURLRequest(request_id_)); + + // We cannot use the request->ssl_info(), it's not been initialized yet, so + // we have to set the fields manually. + ssl_info_.cert = cert; + ssl_info_.SetCertError(cert_error); +} + +// static +void SSLManager::OnSSLCertificateError(ResourceDispatcherHost* rdh, + URLRequest* request, + int cert_error, + X509Certificate* cert, + MessageLoop* ui_loop) { + DLOG(INFO) << "OnSSLCertificateError() cert_error: " << cert_error << + " url: " << request->url().spec(); + + ResourceDispatcherHost::ExtraRequestInfo* info = + ResourceDispatcherHost::ExtraInfoForRequest(request); + DCHECK(info); + + // A certificate error occurred. Construct a CertError object and hand it + // over to the UI thread for processing. + ui_loop->PostTask(FROM_HERE, + NewRunnableMethod(new CertError(rdh, request, info->resource_type, + cert_error, cert, ui_loop), + &CertError::Dispatch)); +} + +// static +void SSLManager::OnMixedContentRequest(ResourceDispatcherHost* rdh, + URLRequest* request, + MessageLoop* ui_loop) { + ui_loop->PostTask(FROM_HERE, + NewRunnableMethod(new MixedContentHandler(rdh, request, ui_loop), + &MixedContentHandler::Dispatch)); +} + +void SSLManager::OnCertError(CertError* error) { + // Ask our delegate to deal with the error. + NavigationEntry* entry = controller_->GetActiveEntry(); + delegate()->OnCertError(entry->GetURL(), error); +} + +void SSLManager::OnMixedContent(MixedContentHandler* mixed_content) { + // Ask our delegate to deal with the mixed content. + NavigationEntry* entry = controller_->GetActiveEntry(); + delegate()->OnMixedContent(controller_, entry->GetURL(), mixed_content); +} + +void SSLManager::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + // We should only be getting notifications from our controller. + DCHECK(source == Source<NavigationController>(controller_)); + + // Dispatch by type. + switch (type) { + case NOTIFY_FRAME_PROVISIONAL_LOAD_COMMITTED: + DidCommitProvisionalLoad(Details<ProvisionalLoadDetails>(details).ptr()); + break; + case NOTIFY_FAIL_PROVISIONAL_LOAD_WITH_ERROR: + DidFailProvisionalLoadWithError( + Details<ProvisionalLoadDetails>(details).ptr()); + break; + case NOTIFY_RESOURCE_RESPONSE_STARTED: + DidStartResourceResponse(Details<ResourceRequestDetails>(details).ptr()); + break; + case NOTIFY_RESOURCE_RECEIVED_REDIRECT: + DidReceiveResourceRedirect( + Details<ResourceRedirectDetails>(details).ptr()); + break; + case NOTIFY_LOAD_FROM_MEMORY_CACHE: + DidLoadFromMemoryCache( + Details<LoadFromMemoryCacheDetails>(details).ptr()); + break; + default: + NOTREACHED() << "The SSLManager received an unexpected notification."; + } +} + +void SSLManager::InitializeEntryIfNeeded(NavigationEntry* entry) { + DCHECK(entry); + + // If the security style of the entry is SECURITY_STYLE_UNKNOWN, then it is a + // fresh entry and should get the default style. + if (entry->GetSecurityStyle() == SECURITY_STYLE_UNKNOWN) + entry->SetSecurityStyle(delegate()->GetDefaultStyle(entry->GetURL())); +} + +void SSLManager::NavigationStateChanged() { + NavigationEntry* active_entry = controller_->GetActiveEntry(); + if (!active_entry) + return; // Nothing showing yet. + + // This might be a new entry we've never seen before. + InitializeEntryIfNeeded(active_entry); +} + +void SSLManager::DidLoadFromMemoryCache(LoadFromMemoryCacheDetails* details) { + DCHECK(details); + + // Simulate loading this resource through the usual path. + // Note that we specify SUB_RESOURCE as the resource type as WebCore only + // caches sub-resources. + delegate()->OnRequestStarted(this, details->url(), + ResourceType::SUB_RESOURCE, + details->ssl_cert_id(), + details->ssl_cert_status()); +} + +void SSLManager::DidCommitProvisionalLoad(ProvisionalLoadDetails* details) { + // Ignore in-page navigations, they should not change the security style or + // the info-bars. + if (details->in_page_navigation()) + return; + + if (details->main_frame()) { + // We are navigating to a new page, it's time to close the info bars. They + // will automagically disappear from the visible_info_bars_ list (when + // OnInfoBarClose is invoked). + // TODO(jcampan): if we are navigating to an interstitial page, we close + // the info-bars. That is unfortunate as if you decide to go back to the + // original page, the info-bars are now gone. We would need a way to + // restore them in that case. + FOR_EACH_OBSERVER(SSLInfoBar, visible_info_bars_, Close()); + + // Update the SSL states of the pending entry. + NavigationEntry* entry = controller_->GetActiveEntry(); + if (entry) { + // We may not have an entry if this is a navigation to an initial blank + // page. + entry->ResetSSLStates(); // Clears mixed/unsafe content state. + entry->SetSSLCertID(details->ssl_cert_id()); + entry->SetSSLCertStatus(details->ssl_cert_status()); + entry->SetSSLSecurityBits(details->ssl_security_bits()); + controller_->EntryUpdated(entry); + } + + if (details->interstitial_page()) { + // We should not have any errors when loading an interstitial page, and as + // a consequence no messages. + DCHECK(pending_messages_.empty()); + } + ShowPendingMessages(); + } + + // An HTTPS response may not have a certificate for some reason. When that + // happens, use the unauthenticated (HTTP) rather than the authentication + // broken security style so that we can detect this error condition. + if (net::IsCertStatusError(details->ssl_cert_status())) + SetMaxSecurityStyle(SECURITY_STYLE_AUTHENTICATION_BROKEN); + else if (details->url().SchemeIsSecure() && !details->ssl_cert_id()) + SetMaxSecurityStyle(SECURITY_STYLE_UNAUTHENTICATED); +} + +void SSLManager::DidFailProvisionalLoadWithError( + ProvisionalLoadDetails* details) { + DCHECK(details); + // A transitional page is not expected to fail. + DCHECK(!details->interstitial_page()); + + // Ignore in-page navigations. + if (details->in_page_navigation()) + return; + + if (details->main_frame() && !details->interstitial_page()) + ClearPendingMessages(); +} + +void SSLManager::DidStartResourceResponse(ResourceRequestDetails* details) { + DCHECK(details); + + // Notify our delegate that we started a resource request. Ideally, the + // delegate should have the ability to cancel the request, but we can't do + // that yet. + delegate()->OnRequestStarted(this, details->url(), + details->resource_type(), + details->ssl_cert_id() , + details->ssl_cert_status()); +} + +void SSLManager::DidReceiveResourceRedirect(ResourceRedirectDetails* details) { + // TODO(jcampan): when we receive a redirect for a sub-resource, we may want + // to clear any mixed/unsafe content error that it may have triggered. +} + +void SSLManager::ShowPendingMessages() { + std::vector<SSLMessageInfo>::const_iterator iter; + for (iter = pending_messages_.begin(); + iter != pending_messages_.end(); ++iter) { + ShowMessageWithLink(iter->message, iter->link_text, iter->action); + } + ClearPendingMessages(); +} + +void SSLManager::ClearPendingMessages() { + pending_messages_.clear(); +} + +// static +std::string SSLManager::SerializeSecurityInfo(int cert_id, + int cert_status, + int security_bits) { + Pickle pickle; + pickle.WriteInt(cert_id); + pickle.WriteInt(cert_status); + pickle.WriteInt(security_bits); + return std::string(static_cast<const char*>(pickle.data()), pickle.size()); +} + +// static +bool SSLManager::DeserializeSecurityInfo(const std::string& state, + int* cert_id, + int* cert_status, + int* security_bits) { + DCHECK(cert_id && cert_status && security_bits); + if (state.empty()) { + // No SSL used. + *cert_id = 0; + *cert_status = 0; + *security_bits = -1; + return false; + } + + Pickle pickle(state.data(), static_cast<int>(state.size())); + void * iter = NULL; + pickle.ReadInt(&iter, cert_id); + pickle.ReadInt(&iter, cert_status); + pickle.ReadInt(&iter, security_bits); + return true; +} + +// static +bool SSLManager::GetEVCertNames(const X509Certificate& cert, + std::wstring* short_name, + std::wstring* ca_name) { + DCHECK(short_name || ca_name); + + // EV are required to have an organization name and country. + if (cert.subject().organization_names.empty() || + cert.subject().country_name.empty()) { + NOTREACHED(); + return false; + } + + if (short_name) { + *short_name = l10n_util::GetStringF( + IDS_SECURE_CONNECTION_EV, + UTF8ToWide(cert.subject().organization_names[0]), + UTF8ToWide(cert.subject().country_name)); + } + + if (ca_name) { + // TODO(wtc): should we show the root CA's name instead? + *ca_name = l10n_util::GetStringF( + IDS_SECURE_CONNECTION_EV_CA, + UTF8ToWide(cert.issuer().organization_names[0])); + } + return true; +} |