| // Copyright (c) 2006-2008 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_blocking_page.h" |
| |
| #include "base/string_piece.h" |
| #include "chrome/browser/browser.h" |
| #include "chrome/browser/browser_resources.h" |
| #include "chrome/browser/cert_store.h" |
| #include "chrome/browser/dom_operation_notification_details.h" |
| #include "chrome/browser/navigation_controller.h" |
| #include "chrome/browser/navigation_entry.h" |
| #include "chrome/browser/profile.h" |
| #include "chrome/browser/render_view_host.h" |
| #include "chrome/browser/ssl_error_info.h" |
| #include "chrome/browser/tab_contents.h" |
| #include "chrome/browser/web_contents.h" |
| #include "chrome/common/jstemplate_builder.h" |
| #include "chrome/common/l10n_util.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/common/pref_service.h" |
| #include "chrome/common/resource_bundle.h" |
| |
| #include "generated_resources.h" |
| |
| // static |
| SSLBlockingPage::SSLBlockingPageMap* |
| SSLBlockingPage::tab_to_blocking_page_ = NULL; |
| |
| SSLBlockingPage::SSLBlockingPage(SSLManager::CertError* error, |
| Delegate* delegate) |
| : error_(error), |
| delegate_(delegate), |
| delegate_has_been_notified_(false), |
| remove_last_entry_(true), |
| created_nav_entry_(false) { |
| InitSSLBlockingPageMap(); |
| // Remember the tab, because we might not be able to get to it later |
| // via the error. |
| tab_ = error->GetTabContents(); |
| DCHECK(tab_); |
| |
| // If there's already an interstitial in this tab, then we're about to |
| // replace it. We should be ok with just deleting the previous |
| // SSLBlockingPage (not hiding it first), since we're about to be shown. |
| SSLBlockingPageMap::const_iterator iter = tab_to_blocking_page_->find(tab_); |
| if (iter != tab_to_blocking_page_->end()) { |
| // Deleting the SSLBlockingPage will also remove it from the map. |
| delete iter->second; |
| |
| // Since WebContents::InterstitialPageGone won't be called, we need |
| // to clear the last NavigationEntry manually. |
| tab_->controller()->RemoveLastEntryForInterstitial(); |
| } |
| (*tab_to_blocking_page_)[tab_] = this; |
| |
| // Register notifications so we can delete ourself if the tab is closed. |
| NotificationService::current()->AddObserver(this, |
| NOTIFY_TAB_CLOSING, |
| Source<NavigationController>(tab_->controller())); |
| |
| NotificationService::current()->AddObserver(this, |
| NOTIFY_INTERSTITIAL_PAGE_CLOSED, |
| Source<NavigationController>(tab_->controller())); |
| |
| // Register for DOM operations, this is how the blocking page notifies us of |
| // what the user chooses. |
| NotificationService::current()->AddObserver(this, |
| NOTIFY_DOM_OPERATION_RESPONSE, |
| Source<TabContents>(tab_)); |
| } |
| |
| SSLBlockingPage::~SSLBlockingPage() { |
| NotificationService::current()->RemoveObserver(this, |
| NOTIFY_TAB_CLOSING, |
| Source<NavigationController>(tab_->controller())); |
| |
| NotificationService::current()->RemoveObserver(this, |
| NOTIFY_INTERSTITIAL_PAGE_CLOSED, |
| Source<NavigationController>(tab_->controller())); |
| |
| NotificationService::current()->RemoveObserver(this, |
| NOTIFY_DOM_OPERATION_RESPONSE, |
| Source<TabContents>(tab_)); |
| |
| SSLBlockingPageMap::iterator iter = |
| tab_to_blocking_page_->find(tab_); |
| DCHECK(iter != tab_to_blocking_page_->end()); |
| tab_to_blocking_page_->erase(iter); |
| |
| if (!delegate_has_been_notified_) { |
| // The page is closed without the user having chosen what to do, default to |
| // deny. |
| NotifyDenyCertificate(); |
| } |
| } |
| |
| void SSLBlockingPage::Show() { |
| // Let's build the html error page. |
| DictionaryValue strings; |
| SSLErrorInfo error_info = delegate_->GetSSLErrorInfo(error_); |
| strings.SetString(L"title", |
| l10n_util::GetString(IDS_SSL_BLOCKING_PAGE_TITLE)); |
| strings.SetString(L"headLine", error_info.title()); |
| strings.SetString(L"description", error_info.details()); |
| |
| strings.SetString(L"moreInfoTitle", |
| l10n_util::GetString(IDS_CERT_ERROR_EXTRA_INFO_TITLE)); |
| SetExtraInfo(&strings, error_info.extra_information()); |
| |
| strings.SetString(L"proceed", |
| l10n_util::GetString(IDS_SSL_BLOCKING_PAGE_PROCEED)); |
| strings.SetString(L"exit", |
| l10n_util::GetString(IDS_SSL_BLOCKING_PAGE_EXIT)); |
| |
| strings.SetString(L"textdirection", |
| (l10n_util::GetTextDirection() == l10n_util::RIGHT_TO_LEFT) ? |
| L"rtl" : L"ltr"); |
| |
| static const StringPiece html( |
| ResourceBundle::GetSharedInstance().GetRawDataResource( |
| IDR_SSL_ROAD_BLOCK_HTML)); |
| |
| std::string html_text(jstemplate_builder::GetTemplateHtml(html, |
| &strings, |
| "template_root")); |
| |
| DCHECK(tab_->type() == TAB_CONTENTS_WEB); |
| WebContents* tab = tab_->AsWebContents(); |
| const net::SSLInfo& ssl_info = error_->ssl_info(); |
| int cert_id = CertStore::GetSharedInstance()->StoreCert( |
| ssl_info.cert, tab->render_view_host()->process()->host_id()); |
| |
| if (tab_->controller()->GetPendingEntryIndex() == -1) { |
| // For new navigations, we just create a new navigation entry. |
| NavigationEntry new_entry(TAB_CONTENTS_WEB); |
| new_entry.set_url(error_->request_url()); |
| tab_->controller()->AddDummyEntryForInterstitial(new_entry); |
| created_nav_entry_ = true; |
| } else { |
| // When there is a pending entry index, that means we're doing a |
| // back/forward navigation. Clone that entry instead. |
| tab_->controller()->AddDummyEntryForInterstitial( |
| *tab_->controller()->GetPendingEntry()); |
| } |
| |
| NavigationEntry* entry = tab_->controller()->GetActiveEntry(); |
| entry->set_page_type(NavigationEntry::INTERSTITIAL_PAGE); |
| |
| entry->ssl().set_security_style(SECURITY_STYLE_AUTHENTICATION_BROKEN); |
| entry->ssl().set_cert_id(cert_id); |
| entry->ssl().set_cert_status(ssl_info.cert_status); |
| entry->ssl().set_security_bits(ssl_info.security_bits); |
| NotificationService::current()->Notify( |
| NOTIFY_SSL_STATE_CHANGED, |
| Source<NavigationController>(tab_->controller()), |
| NotificationService::NoDetails()); |
| |
| tab->ShowInterstitialPage(html_text, NULL); |
| } |
| |
| void SSLBlockingPage::Observe(NotificationType type, |
| const NotificationSource& source, |
| const NotificationDetails& details) { |
| switch (type) { |
| case NOTIFY_TAB_CLOSING: |
| case NOTIFY_INTERSTITIAL_PAGE_CLOSED: { |
| // We created a navigation entry for the interstitial, remove it. |
| // Note that we don't remove the entry if we are closing all tabs so that |
| // the last entry is kept for the restoring on next start-up. |
| Browser* browser = Browser::GetBrowserForController(tab_->controller(), |
| NULL); |
| // We may not have a browser (this is the case for constrained popups), in |
| // which case it does not matter if we do not remove the temporary entry |
| // as their navigation history is not saved. |
| if (remove_last_entry_ && browser && |
| !browser->tabstrip_model()->closing_all()) { |
| tab_->controller()->RemoveLastEntryForInterstitial(); |
| } |
| delete this; |
| break; |
| } |
| case NOTIFY_DOM_OPERATION_RESPONSE: { |
| std::string json = |
| Details<DomOperationNotificationDetails>(details)->json(); |
| if (json == "1") { |
| Proceed(); |
| } else { |
| DontProceed(); |
| } |
| break; |
| } |
| default: |
| NOTREACHED(); |
| } |
| } |
| |
| void SSLBlockingPage::Proceed() { |
| // We hide the interstitial page first as allowing the certificate will |
| // resume the request and we want the WebContents back to showing the |
| // non interstitial page (otherwise the request completion messages may |
| // confuse the WebContents if it is still showing the interstitial |
| // page). |
| DCHECK(tab_->type() == TAB_CONTENTS_WEB); |
| tab_->AsWebContents()->HideInterstitialPage(true, true); |
| |
| // Accepting the certificate resumes the loading of the page. |
| NotifyAllowCertificate(); |
| |
| // Do not remove the navigation entry if we have not created it explicitly |
| // as in such cases (session restore) the controller would not create a new |
| // entry on navigation since the page id is less than max page id. |
| if (!created_nav_entry_) |
| remove_last_entry_ = false; |
| } |
| |
| void SSLBlockingPage::DontProceed() { |
| NotifyDenyCertificate(); |
| |
| // We are navigating, remove the current entry before we mess with it. |
| remove_last_entry_ = false; |
| tab_->controller()->RemoveLastEntryForInterstitial(); |
| |
| NavigationEntry* entry = tab_->controller()->GetActiveEntry(); |
| if (!entry) { |
| // Nothing to go to, default to about:blank. Navigating will cause the |
| // interstitial to hide which will trigger "this" to be deleted. |
| tab_->controller()->LoadURL(GURL("about:blank"), |
| PageTransition::AUTO_BOOKMARK); |
| } else if (entry->tab_type() != TAB_CONTENTS_WEB) { |
| // Not a WebContent, reload it so to recreate the TabContents for it. |
| tab_->controller()->Reload(); |
| } else { |
| DCHECK(tab_->type() == TAB_CONTENTS_WEB); |
| if (entry->restored()) { |
| // If this page was restored, it is not available, we have to navigate to |
| // it. |
| tab_->controller()->GoToOffset(0); |
| } else { |
| tab_->AsWebContents()->HideInterstitialPage(false, false); |
| } |
| } |
| // WARNING: we are now deleted! |
| } |
| |
| // static |
| void SSLBlockingPage::InitSSLBlockingPageMap() { |
| if (!tab_to_blocking_page_) |
| tab_to_blocking_page_ = new SSLBlockingPageMap; |
| } |
| |
| void SSLBlockingPage::NotifyDenyCertificate() { |
| DCHECK(!delegate_has_been_notified_); |
| |
| delegate_->OnDenyCertificate(error_); |
| delegate_has_been_notified_ = true; |
| } |
| |
| void SSLBlockingPage::NotifyAllowCertificate() { |
| DCHECK(!delegate_has_been_notified_); |
| |
| delegate_->OnAllowCertificate(error_); |
| delegate_has_been_notified_ = true; |
| } |
| |
| // static |
| SSLBlockingPage* SSLBlockingPage::GetSSLBlockingPage( |
| TabContents* tab_contents) { |
| InitSSLBlockingPageMap(); |
| SSLBlockingPageMap::const_iterator iter = |
| tab_to_blocking_page_->find(tab_contents); |
| if (iter == tab_to_blocking_page_->end()) |
| return NULL; |
| |
| return iter->second; |
| } |
| |
| // static |
| void SSLBlockingPage::SetExtraInfo( |
| DictionaryValue* strings, |
| const std::vector<std::wstring>& extra_info) { |
| DCHECK(extra_info.size() < 5); // We allow 5 paragraphs max. |
| const std::wstring keys[5] = { |
| L"moreInfo1", L"moreInfo2", L"moreInfo3", L"moreInfo4", L"moreInfo5" |
| }; |
| int i; |
| for (i = 0; i < static_cast<int>(extra_info.size()); i++) { |
| strings->SetString(keys[i], extra_info[i]); |
| } |
| for (;i < 5; i++) { |
| strings->SetString(keys[i], L""); |
| } |
| } |
| |