Home | History | Annotate | Download | only in safe_browsing
      1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 //
      5 // Implementation of the SafeBrowsingBlockingPage class.
      6 
      7 #include "chrome/browser/safe_browsing/safe_browsing_blocking_page.h"
      8 
      9 #include <string>
     10 
     11 #include "base/i18n/rtl.h"
     12 #include "base/lazy_instance.h"
     13 #include "base/string_number_conversions.h"
     14 #include "base/utf_string_conversions.h"
     15 #include "base/values.h"
     16 #include "chrome/browser/browser_process.h"
     17 #include "chrome/browser/dom_operation_notification_details.h"
     18 #include "chrome/browser/google/google_util.h"
     19 #include "chrome/browser/metrics/user_metrics.h"
     20 #include "chrome/browser/prefs/pref_service.h"
     21 #include "chrome/browser/profiles/profile.h"
     22 #include "chrome/browser/safe_browsing/malware_details.h"
     23 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
     24 #include "chrome/browser/tab_contents/tab_util.h"
     25 #include "chrome/browser/ui/webui/new_tab_ui.h"
     26 #include "chrome/common/jstemplate_builder.h"
     27 #include "chrome/common/pref_names.h"
     28 #include "chrome/common/url_constants.h"
     29 #include "content/browser/browser_thread.h"
     30 #include "content/browser/tab_contents/navigation_controller.h"
     31 #include "content/browser/tab_contents/navigation_entry.h"
     32 #include "content/browser/tab_contents/tab_contents.h"
     33 #include "grit/browser_resources.h"
     34 #include "grit/generated_resources.h"
     35 #include "grit/locale_settings.h"
     36 #include "net/base/escape.h"
     37 #include "ui/base/l10n/l10n_util.h"
     38 #include "ui/base/resource/resource_bundle.h"
     39 
     40 // For malware interstitial pages, we link the problematic URL to Google's
     41 // diagnostic page.
     42 #if defined(GOOGLE_CHROME_BUILD)
     43 static const char* const kSbDiagnosticUrl =
     44     "http://safebrowsing.clients.google.com/safebrowsing/diagnostic?site=%s&client=googlechrome";
     45 #else
     46 static const char* const kSbDiagnosticUrl =
     47     "http://safebrowsing.clients.google.com/safebrowsing/diagnostic?site=%s&client=chromium";
     48 #endif
     49 
     50 static const char* const kSbReportPhishingUrl =
     51     "http://www.google.com/safebrowsing/report_error/";
     52 
     53 // URL for the "Learn more" link on the multi threat malware blocking page.
     54 static const char* const kLearnMoreMalwareUrl =
     55     "https://www.google.com/support/bin/answer.py?answer=45449&topic=360"
     56     "&sa=X&oi=malwarewarninglink&resnum=1&ct=help";
     57 
     58 // URL for the "Learn more" link on the phishing blocking page.
     59 static const char* const kLearnMorePhishingUrl =
     60     "https://www.google.com/support/bin/answer.py?answer=106318";
     61 
     62 // URL for the "Safe Browsing Privacy Policies" link on the blocking page.
     63 // Note: this page is not yet localized.
     64 static const char* const kSbPrivacyPolicyUrl =
     65     "http://www.google.com/intl/en_us/privacy/browsing.html";
     66 
     67 static const char* const kSbDiagnosticHtml =
     68     "<a href=\"\" onclick=\"sendCommand('showDiagnostic'); return false;\" "
     69     "onmousedown=\"return false;\">%s</a>";
     70 
     71 static const char* const kPLinkHtml =
     72     "<a href=\"\" onclick=\"sendCommand('proceed'); return false;\" "
     73     "onmousedown=\"return false;\">%s</a>";
     74 
     75 static const char* const kPrivacyLinkHtml =
     76     "<a href=\"\" onclick=\"sendCommand('showPrivacy'); return false;\" "
     77     "onmousedown=\"return false;\">%s</a>";
     78 
     79 // The commands returned by the page when the user performs an action.
     80 static const char* const kShowDiagnosticCommand = "showDiagnostic";
     81 static const char* const kReportErrorCommand = "reportError";
     82 static const char* const kLearnMoreCommand = "learnMore";
     83 static const char* const kShowPrivacyCommand = "showPrivacy";
     84 static const char* const kProceedCommand = "proceed";
     85 static const char* const kTakeMeBackCommand = "takeMeBack";
     86 static const char* const kDoReportCommand = "doReport";
     87 static const char* const kDontReportCommand = "dontReport";
     88 static const char* const kDisplayCheckBox = "displaycheckbox";
     89 static const char* const kBoxChecked = "boxchecked";
     90 
     91 // static
     92 SafeBrowsingBlockingPageFactory* SafeBrowsingBlockingPage::factory_ = NULL;
     93 
     94 static base::LazyInstance<SafeBrowsingBlockingPage::UnsafeResourceMap>
     95     g_unsafe_resource_map(base::LINKER_INITIALIZED);
     96 
     97 // The default SafeBrowsingBlockingPageFactory.  Global, made a singleton so we
     98 // don't leak it.
     99 class SafeBrowsingBlockingPageFactoryImpl
    100     : public SafeBrowsingBlockingPageFactory {
    101  public:
    102   SafeBrowsingBlockingPage* CreateSafeBrowsingPage(
    103       SafeBrowsingService* service,
    104       TabContents* tab_contents,
    105       const SafeBrowsingBlockingPage::UnsafeResourceList& unsafe_resources) {
    106     return new SafeBrowsingBlockingPage(service, tab_contents,
    107                                         unsafe_resources);
    108   }
    109 
    110  private:
    111   friend struct base::DefaultLazyInstanceTraits<
    112       SafeBrowsingBlockingPageFactoryImpl>;
    113 
    114   SafeBrowsingBlockingPageFactoryImpl() { }
    115 
    116   DISALLOW_COPY_AND_ASSIGN(SafeBrowsingBlockingPageFactoryImpl);
    117 };
    118 
    119 static base::LazyInstance<SafeBrowsingBlockingPageFactoryImpl>
    120     g_safe_browsing_blocking_page_factory_impl(base::LINKER_INITIALIZED);
    121 
    122 SafeBrowsingBlockingPage::SafeBrowsingBlockingPage(
    123     SafeBrowsingService* sb_service,
    124     TabContents* tab_contents,
    125     const UnsafeResourceList& unsafe_resources)
    126     : InterstitialPage(tab_contents,
    127                        IsMainPage(unsafe_resources),
    128                        unsafe_resources[0].url),
    129       sb_service_(sb_service),
    130       is_main_frame_(IsMainPage(unsafe_resources)),
    131       unsafe_resources_(unsafe_resources) {
    132   RecordUserAction(SHOW);
    133   if (!is_main_frame_) {
    134     navigation_entry_index_to_remove_ =
    135         tab()->controller().last_committed_entry_index();
    136   } else {
    137     navigation_entry_index_to_remove_ = -1;
    138   }
    139 
    140   // Start computing malware details. They will be sent only
    141   // if the user opts-in on the blocking page later.
    142   // If there's more than one malicious resources, it means the user
    143   // clicked through the first warning, so we don't prepare additional
    144   // reports.
    145   if (unsafe_resources.size() == 1 &&
    146       unsafe_resources[0].threat_type == SafeBrowsingService::URL_MALWARE &&
    147       malware_details_ == NULL &&
    148       CanShowMalwareDetailsOption()) {
    149     malware_details_ = MalwareDetails::NewMalwareDetails(
    150         sb_service_, tab(), unsafe_resources[0]);
    151   }
    152 }
    153 
    154 bool SafeBrowsingBlockingPage::CanShowMalwareDetailsOption() {
    155   return (!tab()->profile()->IsOffTheRecord() &&
    156           tab()->GetURL().SchemeIs(chrome::kHttpScheme));
    157 }
    158 
    159 SafeBrowsingBlockingPage::~SafeBrowsingBlockingPage() {
    160 }
    161 
    162 std::string SafeBrowsingBlockingPage::GetHTMLContents() {
    163   // Load the HTML page and create the template components.
    164   DictionaryValue strings;
    165   ResourceBundle& rb = ResourceBundle::GetSharedInstance();
    166   std::string html;
    167 
    168   if (unsafe_resources_.empty()) {
    169     NOTREACHED();
    170     return std::string();
    171   }
    172 
    173   if (unsafe_resources_.size() > 1) {
    174     PopulateMultipleThreatStringDictionary(&strings);
    175     html = rb.GetRawDataResource(
    176         IDR_SAFE_BROWSING_MULTIPLE_THREAT_BLOCK).as_string();
    177   } else if (unsafe_resources_[0].threat_type ==
    178              SafeBrowsingService::URL_MALWARE) {
    179     PopulateMalwareStringDictionary(&strings);
    180     html = rb.GetRawDataResource(IDR_SAFE_BROWSING_MALWARE_BLOCK).as_string();
    181   } else {  // Phishing.
    182     DCHECK(unsafe_resources_[0].threat_type ==
    183            SafeBrowsingService::URL_PHISHING);
    184     PopulatePhishingStringDictionary(&strings);
    185     html = rb.GetRawDataResource(IDR_SAFE_BROWSING_PHISHING_BLOCK).as_string();
    186   }
    187 
    188   return jstemplate_builder::GetTemplatesHtml(html, &strings, "template_root");
    189 }
    190 
    191 void SafeBrowsingBlockingPage::PopulateStringDictionary(
    192     DictionaryValue* strings,
    193     const string16& title,
    194     const string16& headline,
    195     const string16& description1,
    196     const string16& description2,
    197     const string16& description3) {
    198   strings->SetString("title", title);
    199   strings->SetString("headLine", headline);
    200   strings->SetString("description1", description1);
    201   strings->SetString("description2", description2);
    202   strings->SetString("description3", description3);
    203 }
    204 
    205 void SafeBrowsingBlockingPage::PopulateMultipleThreatStringDictionary(
    206     DictionaryValue* strings) {
    207   bool malware = false;
    208   bool phishing = false;
    209 
    210   string16 malware_label =
    211       l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_MALWARE_LABEL);
    212   string16 malware_link =
    213       l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_MALWARE_DIAGNOSTIC_PAGE);
    214   string16 phishing_label =
    215       l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_PHISHING_LABEL);
    216   string16 phishing_link =
    217       l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_PHISHING_REPORT_ERROR);
    218 
    219   ListValue* error_strings = new ListValue;
    220   for (UnsafeResourceList::const_iterator iter = unsafe_resources_.begin();
    221        iter != unsafe_resources_.end(); ++iter) {
    222     const SafeBrowsingService::UnsafeResource& resource = *iter;
    223     DictionaryValue* current_error_strings = new DictionaryValue;
    224     if (resource.threat_type == SafeBrowsingService::URL_MALWARE) {
    225       malware = true;
    226       current_error_strings->SetString("type", "malware");
    227       current_error_strings->SetString("typeLabel", malware_label);
    228       current_error_strings->SetString("errorLink", malware_link);
    229     } else {
    230       DCHECK(resource.threat_type == SafeBrowsingService::URL_PHISHING);
    231       phishing = true;
    232       current_error_strings->SetString("type", "phishing");
    233       current_error_strings->SetString("typeLabel", phishing_label);
    234       current_error_strings->SetString("errorLink", phishing_link);
    235     }
    236     current_error_strings->SetString("url", resource.url.spec());
    237     error_strings->Append(current_error_strings);
    238   }
    239   strings->Set("errors", error_strings);
    240   DCHECK(phishing || malware);
    241 
    242   if (malware && phishing) {
    243     PopulateStringDictionary(
    244         strings,
    245         // Use the malware headline, it is the scariest one.
    246         l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_MULTI_THREAT_TITLE),
    247         l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_MALWARE_HEADLINE),
    248         l10n_util::GetStringFUTF16(IDS_SAFE_BROWSING_MULTI_THREAT_DESCRIPTION1,
    249                                    UTF8ToUTF16(tab()->GetURL().host())),
    250         l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_MULTI_THREAT_DESCRIPTION2),
    251         string16());
    252   } else if (malware) {
    253     // Just malware.
    254     PopulateStringDictionary(
    255         strings,
    256         l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_MALWARE_TITLE),
    257         l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_MALWARE_HEADLINE),
    258         l10n_util::GetStringFUTF16(IDS_SAFE_BROWSING_MULTI_MALWARE_DESCRIPTION1,
    259                                    UTF8ToUTF16(tab()->GetURL().host())),
    260         l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_MULTI_MALWARE_DESCRIPTION2),
    261         l10n_util::GetStringUTF16(
    262             IDS_SAFE_BROWSING_MULTI_MALWARE_DESCRIPTION3));
    263   } else {
    264     // Just phishing.
    265     PopulateStringDictionary(
    266         strings,
    267         l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_PHISHING_TITLE),
    268         l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_PHISHING_HEADLINE),
    269         l10n_util::GetStringFUTF16(
    270             IDS_SAFE_BROWSING_MULTI_PHISHING_DESCRIPTION1,
    271             UTF8ToUTF16(tab()->GetURL().host())),
    272         string16(),
    273         string16());
    274   }
    275 
    276   strings->SetString("confirm_text",
    277                      l10n_util::GetStringUTF16(
    278                          IDS_SAFE_BROWSING_MULTI_MALWARE_DESCRIPTION_AGREE));
    279   strings->SetString("continue_button",
    280                      l10n_util::GetStringUTF16(
    281                          IDS_SAFE_BROWSING_MULTI_MALWARE_PROCEED_BUTTON));
    282   strings->SetString("back_button",
    283       l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_MALWARE_BACK_BUTTON));
    284   strings->SetString("textdirection", base::i18n::IsRTL() ? "rtl" : "ltr");
    285 }
    286 
    287 void SafeBrowsingBlockingPage::PopulateMalwareStringDictionary(
    288     DictionaryValue* strings) {
    289   std::string diagnostic_link = StringPrintf(kSbDiagnosticHtml,
    290       l10n_util::GetStringUTF8(
    291         IDS_SAFE_BROWSING_MALWARE_DIAGNOSTIC_PAGE).c_str());
    292 
    293   strings->SetString("badURL", url().host());
    294   // Check to see if we're blocking the main page, or a sub-resource on the
    295   // main page.
    296   string16 description1, description3, description5;
    297   if (is_main_frame_) {
    298     description1 = l10n_util::GetStringFUTF16(
    299         IDS_SAFE_BROWSING_MALWARE_DESCRIPTION1, UTF8ToUTF16(url().host()));
    300   } else {
    301     description1 = l10n_util::GetStringFUTF16(
    302         IDS_SAFE_BROWSING_MALWARE_DESCRIPTION4,
    303         UTF8ToUTF16(tab()->GetURL().host()),
    304         UTF8ToUTF16(url().host()));
    305   }
    306 
    307   std::string proceed_link = StringPrintf(kPLinkHtml,
    308       l10n_util::GetStringUTF8(IDS_SAFE_BROWSING_MALWARE_PROCEED_LINK).c_str());
    309   description3 =
    310       l10n_util::GetStringFUTF16(IDS_SAFE_BROWSING_MALWARE_DESCRIPTION3,
    311                                  UTF8ToUTF16(proceed_link));
    312 
    313   PopulateStringDictionary(
    314       strings,
    315       l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_MALWARE_TITLE),
    316       l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_MALWARE_HEADLINE),
    317       description1,
    318       l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_MALWARE_DESCRIPTION2),
    319       description3);
    320 
    321   description5 =
    322       l10n_util::GetStringFUTF16(IDS_SAFE_BROWSING_MALWARE_DESCRIPTION5,
    323                                  UTF8ToUTF16(url().host()),
    324                                  UTF8ToUTF16(url().host()),
    325                                  UTF8ToUTF16(diagnostic_link));
    326 
    327   strings->SetString("description5", description5);
    328 
    329   strings->SetString("back_button",
    330       l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_MALWARE_BACK_BUTTON));
    331   strings->SetString("proceed_link",
    332       l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_MALWARE_PROCEED_LINK));
    333   strings->SetString("textdirection", base::i18n::IsRTL() ? "rtl" : "ltr");
    334 
    335   if (!CanShowMalwareDetailsOption()) {
    336     strings->SetBoolean(kDisplayCheckBox, false);
    337   } else {
    338     // Show the checkbox for sending malware details.
    339     strings->SetBoolean(kDisplayCheckBox, true);
    340 
    341     std::string privacy_link = StringPrintf(
    342         kPrivacyLinkHtml,
    343         l10n_util::GetStringUTF8(
    344             IDS_SAFE_BROWSING_PRIVACY_POLICY_PAGE).c_str());
    345 
    346     strings->SetString("confirm_text",
    347                        l10n_util::GetStringFUTF16(
    348                            IDS_SAFE_BROWSING_MALWARE_REPORTING_AGREE,
    349                            UTF8ToUTF16(privacy_link)));
    350 
    351     const PrefService::Preference* pref =
    352         tab()->profile()->GetPrefs()->FindPreference(
    353             prefs::kSafeBrowsingReportingEnabled);
    354 
    355     bool value;
    356     if (pref && pref->GetValue()->GetAsBoolean(&value) && value) {
    357       strings->SetString(kBoxChecked, "yes");
    358     } else {
    359       strings->SetString(kBoxChecked, "");
    360     }
    361   }
    362 }
    363 
    364 void SafeBrowsingBlockingPage::PopulatePhishingStringDictionary(
    365     DictionaryValue* strings) {
    366   std::string proceed_link = StringPrintf(kPLinkHtml, l10n_util::GetStringUTF8(
    367       IDS_SAFE_BROWSING_PHISHING_PROCEED_LINK).c_str());
    368   string16 description3 = l10n_util::GetStringFUTF16(
    369       IDS_SAFE_BROWSING_PHISHING_DESCRIPTION3,
    370       UTF8ToUTF16(proceed_link));
    371 
    372   PopulateStringDictionary(
    373       strings,
    374       l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_PHISHING_TITLE),
    375       l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_PHISHING_HEADLINE),
    376       l10n_util::GetStringFUTF16(IDS_SAFE_BROWSING_PHISHING_DESCRIPTION1,
    377                                  UTF8ToUTF16(url().host())),
    378       l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_PHISHING_DESCRIPTION2),
    379       description3);
    380 
    381   strings->SetString("back_button",
    382       l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_PHISHING_BACK_BUTTON));
    383   strings->SetString("report_error",
    384       l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_PHISHING_REPORT_ERROR));
    385   strings->SetString("textdirection", base::i18n::IsRTL() ? "rtl" : "ltr");
    386 }
    387 
    388 void SafeBrowsingBlockingPage::CommandReceived(const std::string& cmd) {
    389   std::string command(cmd);  // Make a local copy so we can modify it.
    390   // The Jasonified response has quotes, remove them.
    391   if (command.length() > 1 && command[0] == '"') {
    392     command = command.substr(1, command.length() - 2);
    393   }
    394 
    395   if (command == kDoReportCommand) {
    396     SetReportingPreference(true);
    397     return;
    398   }
    399 
    400   if (command == kDontReportCommand) {
    401     SetReportingPreference(false);
    402     return;
    403   }
    404 
    405   if (command == kLearnMoreCommand) {
    406     // User pressed "Learn more".
    407     GURL url;
    408     if (unsafe_resources_[0].threat_type == SafeBrowsingService::URL_MALWARE) {
    409       url = google_util::AppendGoogleLocaleParam(GURL(kLearnMoreMalwareUrl));
    410     } else if (unsafe_resources_[0].threat_type ==
    411                SafeBrowsingService::URL_PHISHING) {
    412       url = google_util::AppendGoogleLocaleParam(GURL(kLearnMorePhishingUrl));
    413     } else {
    414       NOTREACHED();
    415     }
    416     tab()->OpenURL(url, GURL(), CURRENT_TAB, PageTransition::LINK);
    417     return;
    418   }
    419 
    420   if (command == kShowPrivacyCommand) {
    421     // User pressed "Safe Browsing privacy policy".
    422     GURL url(kSbPrivacyPolicyUrl);
    423     tab()->OpenURL(url, GURL(), CURRENT_TAB, PageTransition::LINK);
    424     return;
    425   }
    426 
    427   if (command == kProceedCommand) {
    428     Proceed();
    429     // We are deleted after this.
    430     return;
    431   }
    432 
    433   if (command == kTakeMeBackCommand) {
    434     DontProceed();
    435     // We are deleted after this.
    436     return;
    437   }
    438 
    439   // The "report error" and "show diagnostic" commands can have a number
    440   // appended to them, which is the index of the element they apply to.
    441   int element_index = 0;
    442   size_t colon_index = command.find(':');
    443   if (colon_index != std::string::npos) {
    444     DCHECK(colon_index < command.size() - 1);
    445     bool result = base::StringToInt(command.begin() + colon_index + 1,
    446                                     command.end(),
    447                                     &element_index);
    448     command = command.substr(0, colon_index);
    449     DCHECK(result);
    450   }
    451 
    452   if (element_index >= static_cast<int>(unsafe_resources_.size())) {
    453     NOTREACHED();
    454     return;
    455   }
    456 
    457   std::string bad_url_spec = unsafe_resources_[element_index].url.spec();
    458   if (command == kReportErrorCommand) {
    459     // User pressed "Report error" for a phishing site.
    460     // Note that we cannot just put a link in the interstitial at this point.
    461     // It is not OK to navigate in the context of an interstitial page.
    462     DCHECK(unsafe_resources_[element_index].threat_type ==
    463            SafeBrowsingService::URL_PHISHING);
    464     GURL report_url =
    465         safe_browsing_util::GeneratePhishingReportUrl(kSbReportPhishingUrl,
    466                                                       bad_url_spec);
    467     tab()->OpenURL(report_url, GURL(), CURRENT_TAB, PageTransition::LINK);
    468     return;
    469   }
    470 
    471   if (command == kShowDiagnosticCommand) {
    472     // We're going to take the user to Google's SafeBrowsing diagnostic page.
    473     std::string diagnostic =
    474         StringPrintf(kSbDiagnosticUrl,
    475                      EscapeQueryParamValue(bad_url_spec, true).c_str());
    476     GURL diagnostic_url(diagnostic);
    477     diagnostic_url = google_util::AppendGoogleLocaleParam(diagnostic_url);
    478     DCHECK(unsafe_resources_[element_index].threat_type ==
    479            SafeBrowsingService::URL_MALWARE);
    480     tab()->OpenURL(diagnostic_url, GURL(), CURRENT_TAB, PageTransition::LINK);
    481     return;
    482   }
    483 
    484   NOTREACHED() << "Unexpected command: " << command;
    485 }
    486 
    487 void SafeBrowsingBlockingPage::SetReportingPreference(bool report) {
    488   PrefService* pref = tab()->profile()->GetPrefs();
    489   pref->SetBoolean(prefs::kSafeBrowsingReportingEnabled, report);
    490 }
    491 
    492 void SafeBrowsingBlockingPage::Proceed() {
    493   RecordUserAction(PROCEED);
    494   FinishMalwareDetails();  // Send the malware details, if we opted to.
    495 
    496   NotifySafeBrowsingService(sb_service_, unsafe_resources_, true);
    497 
    498   // Check to see if some new notifications of unsafe resources have been
    499   // received while we were showing the interstitial.
    500   UnsafeResourceMap* unsafe_resource_map = GetUnsafeResourcesMap();
    501   UnsafeResourceMap::iterator iter = unsafe_resource_map->find(tab());
    502   SafeBrowsingBlockingPage* blocking_page = NULL;
    503   if (iter != unsafe_resource_map->end() && !iter->second.empty()) {
    504     // Build an interstitial for all the unsafe resources notifications.
    505     // Don't show it now as showing an interstitial while an interstitial is
    506     // already showing would cause DontProceed() to be invoked.
    507     blocking_page = factory_->CreateSafeBrowsingPage(sb_service_, tab(),
    508                                                      iter->second);
    509     unsafe_resource_map->erase(iter);
    510   }
    511 
    512   InterstitialPage::Proceed();
    513   // We are now deleted.
    514 
    515   // Now that this interstitial is gone, we can show the new one.
    516   if (blocking_page)
    517     blocking_page->Show();
    518 }
    519 
    520 void SafeBrowsingBlockingPage::DontProceed() {
    521   DCHECK(action_taken() != DONT_PROCEED_ACTION);
    522   // We could have already called Proceed(), in which case we must not notify
    523   // the SafeBrowsingService again, as the client has been deleted.
    524   if (action_taken() == PROCEED_ACTION) {
    525     // We still want to hide the interstitial page.
    526     InterstitialPage::DontProceed();
    527     // We are now deleted.
    528     return;
    529   }
    530 
    531   RecordUserAction(DONT_PROCEED);
    532   FinishMalwareDetails();  // Send the malware details, if we opted to.
    533 
    534   NotifySafeBrowsingService(sb_service_, unsafe_resources_, false);
    535 
    536   // The user does not want to proceed, clear the queued unsafe resources
    537   // notifications we received while the interstitial was showing.
    538   UnsafeResourceMap* unsafe_resource_map = GetUnsafeResourcesMap();
    539   UnsafeResourceMap::iterator iter = unsafe_resource_map->find(tab());
    540   if (iter != unsafe_resource_map->end() && !iter->second.empty()) {
    541     NotifySafeBrowsingService(sb_service_, iter->second, false);
    542     unsafe_resource_map->erase(iter);
    543   }
    544 
    545   // We don't remove the navigation entry if the tab is being destroyed as this
    546   // would trigger a navigation that would cause trouble as the render view host
    547   // for the tab has by then already been destroyed.
    548   if (navigation_entry_index_to_remove_ != -1 && !tab()->is_being_destroyed()) {
    549     tab()->controller().RemoveEntryAtIndex(navigation_entry_index_to_remove_,
    550                                            GURL(chrome::kChromeUINewTabURL));
    551     navigation_entry_index_to_remove_ = -1;
    552   }
    553   InterstitialPage::DontProceed();
    554   // We are now deleted.
    555 }
    556 
    557 void SafeBrowsingBlockingPage::RecordUserAction(BlockingPageEvent event) {
    558   // Determine the interstitial type from the blocked resources.
    559   // This is the same logic that is used to actually construct the
    560   // page contents; we can look at the title to see which type of
    561   // interstitial is being displayed.
    562   DictionaryValue strings;
    563   PopulateMultipleThreatStringDictionary(&strings);
    564 
    565   string16 title;
    566   DCHECK(strings.GetString("title", &title));
    567 
    568   std::string action = "SBInterstitial";
    569   if (title ==
    570           l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_MULTI_THREAT_TITLE)) {
    571     action.append("Multiple");
    572   } else if (title ==
    573                  l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_MALWARE_TITLE)) {
    574     action.append("Malware");
    575   } else {
    576     DCHECK_EQ(title,
    577               l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_PHISHING_TITLE));
    578     action.append("Phishing");
    579   }
    580 
    581   switch (event) {
    582     case SHOW:
    583       action.append("Show");
    584       break;
    585     case PROCEED:
    586       action.append("Proceed");
    587       break;
    588     case DONT_PROCEED:
    589       action.append("DontProceed");
    590       break;
    591     default:
    592       NOTREACHED() << "Unexpected event: " << event;
    593   }
    594 
    595   UserMetrics::RecordComputedAction(action);
    596 }
    597 
    598 void SafeBrowsingBlockingPage::FinishMalwareDetails() {
    599   if (malware_details_ == NULL)
    600     return;  // Not all interstitials have malware details (eg phishing).
    601 
    602   const PrefService::Preference* pref =
    603       tab()->profile()->GetPrefs()->FindPreference(
    604           prefs::kSafeBrowsingReportingEnabled);
    605 
    606   bool value;
    607   if (pref && pref->GetValue()->GetAsBoolean(&value) && value) {
    608     // Finish the malware details collection, send it over.
    609     BrowserThread::PostTask(
    610         BrowserThread::IO, FROM_HERE,
    611         NewRunnableMethod(
    612             malware_details_.get(), &MalwareDetails::FinishCollection));
    613   }
    614 }
    615 
    616 // static
    617 void SafeBrowsingBlockingPage::NotifySafeBrowsingService(
    618     SafeBrowsingService* sb_service,
    619     const UnsafeResourceList& unsafe_resources,
    620     bool proceed) {
    621   BrowserThread::PostTask(
    622       BrowserThread::IO, FROM_HERE,
    623       NewRunnableMethod(
    624           sb_service, &SafeBrowsingService::OnBlockingPageDone,
    625           unsafe_resources, proceed));
    626 }
    627 
    628 // static
    629 SafeBrowsingBlockingPage::UnsafeResourceMap*
    630     SafeBrowsingBlockingPage::GetUnsafeResourcesMap() {
    631   return g_unsafe_resource_map.Pointer();
    632 }
    633 
    634 // static
    635 void SafeBrowsingBlockingPage::ShowBlockingPage(
    636     SafeBrowsingService* sb_service,
    637     const SafeBrowsingService::UnsafeResource& unsafe_resource) {
    638   TabContents* tab_contents = tab_util::GetTabContentsByID(
    639       unsafe_resource.render_process_host_id, unsafe_resource.render_view_id);
    640 
    641   InterstitialPage* interstitial =
    642       InterstitialPage::GetInterstitialPage(tab_contents);
    643   if (interstitial &&
    644       unsafe_resource.resource_type == ResourceType::MAIN_FRAME) {
    645     // There is already an interstitial showing and we are about to display a
    646     // new one for the main frame. Just hide the current one, it is now
    647     // irrelevent
    648     interstitial->DontProceed();
    649     interstitial = NULL;
    650   }
    651 
    652   if (!interstitial) {
    653     // There are no interstitial currently showing in that tab, go ahead and
    654     // show this interstitial.
    655     std::vector<SafeBrowsingService::UnsafeResource> resources;
    656     resources.push_back(unsafe_resource);
    657     // Set up the factory if this has not been done already (tests do that
    658     // before this method is called).
    659     if (!factory_)
    660       factory_ = g_safe_browsing_blocking_page_factory_impl.Pointer();
    661     SafeBrowsingBlockingPage* blocking_page =
    662         factory_->CreateSafeBrowsingPage(sb_service, tab_contents, resources);
    663     blocking_page->Show();
    664     return;
    665   }
    666 
    667   // This is an interstitial for a page's resource, let's queue it.
    668   UnsafeResourceMap* unsafe_resource_map = GetUnsafeResourcesMap();
    669   (*unsafe_resource_map)[tab_contents].push_back(unsafe_resource);
    670 }
    671 
    672 // static
    673 bool SafeBrowsingBlockingPage::IsMainPage(
    674     const UnsafeResourceList& unsafe_resources) {
    675   return unsafe_resources.size() == 1 &&
    676          unsafe_resources[0].resource_type == ResourceType::MAIN_FRAME;
    677 }
    678