safe_browsing_blocking_page.cc revision 4a5e2dc747d50c653511c68ccb2cfbfb740bd5a7
1// Copyright (c) 2010 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 "app/l10n_util.h"
12#include "app/resource_bundle.h"
13#include "base/i18n/rtl.h"
14#include "base/string_number_conversions.h"
15#include "base/utf_string_conversions.h"
16#include "base/values.h"
17#include "chrome/browser/browser_thread.h"
18#include "chrome/browser/dom_operation_notification_details.h"
19#include "chrome/browser/dom_ui/new_tab_ui.h"
20#include "chrome/browser/google/google_util.h"
21#include "chrome/browser/metrics/user_metrics.h"
22#include "chrome/browser/safe_browsing/safe_browsing_service.h"
23#include "chrome/browser/tab_contents/navigation_controller.h"
24#include "chrome/browser/tab_contents/navigation_entry.h"
25#include "chrome/browser/tab_contents/tab_util.h"
26#include "chrome/browser/tab_contents/tab_contents.h"
27#include "chrome/common/jstemplate_builder.h"
28#include "chrome/common/url_constants.h"
29#include "grit/browser_resources.h"
30#include "grit/generated_resources.h"
31#include "grit/locale_settings.h"
32#include "net/base/escape.h"
33
34// For malware interstitial pages, we link the problematic URL to Google's
35// diagnostic page.
36#if defined(GOOGLE_CHROME_BUILD)
37static const char* const kSbDiagnosticUrl =
38    "http://safebrowsing.clients.google.com/safebrowsing/diagnostic?site=%s&client=googlechrome";
39#else
40static const char* const kSbDiagnosticUrl =
41    "http://safebrowsing.clients.google.com/safebrowsing/diagnostic?site=%s&client=chromium";
42#endif
43
44static const char* const kSbReportPhishingUrl =
45    "http://www.google.com/safebrowsing/report_error/";
46
47// URL for the "Learn more" link on the multi threat malware blocking page.
48static const char* const kLearnMoreMalwareUrl =
49    "http://www.google.com/support/bin/answer.py?answer=45449&topic=360"
50    "&sa=X&oi=malwarewarninglink&resnum=1&ct=help";
51
52// URL for the "Learn more" link on the phishing blocking page.
53static const char* const kLearnMorePhishingUrl =
54    "http://www.google.com/support/bin/answer.py?answer=106318";
55
56static const wchar_t* const kSbDiagnosticHtml =
57    L"<a href=\"\" onclick=\"sendCommand('showDiagnostic'); return false;\" "
58    L"onmousedown=\"return false;\">%ls</a>";
59
60static const wchar_t* const kPLinkHtml =
61    L"<a href=\"\" onclick=\"sendCommand('proceed'); return false;\" "
62    L"onmousedown=\"return false;\">%ls</a>";
63
64// The commands returned by the page when the user performs an action.
65static const char* const kShowDiagnosticCommand = "showDiagnostic";
66static const char* const kReportErrorCommand = "reportError";
67static const char* const kLearnMoreCommand = "learnMore";
68static const char* const kProceedCommand = "proceed";
69static const char* const kTakeMeBackCommand = "takeMeBack";
70
71// static
72SafeBrowsingBlockingPageFactory* SafeBrowsingBlockingPage::factory_ = NULL;
73
74// The default SafeBrowsingBlockingPageFactory.  Global, made a singleton so we
75// don't leak it.
76class SafeBrowsingBlockingPageFactoryImpl
77    : public SafeBrowsingBlockingPageFactory {
78 public:
79  SafeBrowsingBlockingPage* CreateSafeBrowsingPage(
80      SafeBrowsingService* service,
81      TabContents* tab_contents,
82      const SafeBrowsingBlockingPage::UnsafeResourceList& unsafe_resources) {
83    return new SafeBrowsingBlockingPage(service, tab_contents,
84                                        unsafe_resources);
85  }
86
87 private:
88  friend struct DefaultSingletonTraits<SafeBrowsingBlockingPageFactoryImpl>;
89
90  SafeBrowsingBlockingPageFactoryImpl() { }
91
92  DISALLOW_COPY_AND_ASSIGN(SafeBrowsingBlockingPageFactoryImpl);
93};
94
95SafeBrowsingBlockingPage::SafeBrowsingBlockingPage(
96    SafeBrowsingService* sb_service,
97    TabContents* tab_contents,
98    const UnsafeResourceList& unsafe_resources)
99    : InterstitialPage(tab_contents,
100                       IsMainPage(unsafe_resources),
101                       unsafe_resources[0].url),
102      sb_service_(sb_service),
103      is_main_frame_(IsMainPage(unsafe_resources)),
104      unsafe_resources_(unsafe_resources) {
105  RecordUserAction(SHOW);
106  if (!is_main_frame_) {
107    navigation_entry_index_to_remove_ =
108        tab()->controller().last_committed_entry_index();
109  } else {
110    navigation_entry_index_to_remove_ = -1;
111  }
112}
113
114SafeBrowsingBlockingPage::~SafeBrowsingBlockingPage() {
115}
116
117std::string SafeBrowsingBlockingPage::GetHTMLContents() {
118  // Load the HTML page and create the template components.
119  DictionaryValue strings;
120  ResourceBundle& rb = ResourceBundle::GetSharedInstance();
121  std::string html;
122
123  if (unsafe_resources_.empty()) {
124    NOTREACHED();
125    return std::string();
126  }
127
128  if (unsafe_resources_.size() > 1) {
129    PopulateMultipleThreatStringDictionary(&strings);
130    html = rb.GetRawDataResource(
131        IDR_SAFE_BROWSING_MULTIPLE_THREAT_BLOCK).as_string();
132  } else if (unsafe_resources_[0].threat_type ==
133             SafeBrowsingService::URL_MALWARE) {
134    PopulateMalwareStringDictionary(&strings);
135    html = rb.GetRawDataResource(IDR_SAFE_BROWSING_MALWARE_BLOCK).as_string();
136  } else {  // Phishing.
137    DCHECK(unsafe_resources_[0].threat_type ==
138           SafeBrowsingService::URL_PHISHING);
139    PopulatePhishingStringDictionary(&strings);
140    html = rb.GetRawDataResource(IDR_SAFE_BROWSING_PHISHING_BLOCK).as_string();
141  }
142
143  return jstemplate_builder::GetTemplatesHtml(html, &strings, "template_root");
144}
145
146void SafeBrowsingBlockingPage::PopulateStringDictionary(
147    DictionaryValue* strings,
148    const std::wstring& title,
149    const std::wstring& headline,
150    const std::wstring& description1,
151    const std::wstring& description2,
152    const std::wstring& description3) {
153  strings->SetString("title", WideToUTF16Hack(title));
154  strings->SetString("headLine", WideToUTF16Hack(headline));
155  strings->SetString("description1", WideToUTF16Hack(description1));
156  strings->SetString("description2", WideToUTF16Hack(description2));
157  strings->SetString("description3", WideToUTF16Hack(description3));
158}
159
160void SafeBrowsingBlockingPage::PopulateMultipleThreatStringDictionary(
161    DictionaryValue* strings) {
162  bool malware = false;
163  bool phishing = false;
164
165  string16 malware_label =
166      l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_MALWARE_LABEL);
167  string16 malware_link =
168      l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_MALWARE_DIAGNOSTIC_PAGE);
169  string16 phishing_label =
170      l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_PHISHING_LABEL);
171  string16 phishing_link =
172      l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_PHISHING_REPORT_ERROR);
173
174  ListValue* error_strings = new ListValue;
175  for (UnsafeResourceList::const_iterator iter = unsafe_resources_.begin();
176       iter != unsafe_resources_.end(); ++iter) {
177    const SafeBrowsingService::UnsafeResource& resource = *iter;
178    DictionaryValue* current_error_strings = new DictionaryValue;
179    if (resource.threat_type == SafeBrowsingService::URL_MALWARE) {
180      malware = true;
181      current_error_strings->SetString("type", "malware");
182      current_error_strings->SetString("typeLabel", malware_label);
183      current_error_strings->SetString("errorLink", malware_link);
184    } else {
185      DCHECK(resource.threat_type == SafeBrowsingService::URL_PHISHING);
186      phishing = true;
187      current_error_strings->SetString("type", "phishing");
188      current_error_strings->SetString("typeLabel", phishing_label);
189      current_error_strings->SetString("errorLink", phishing_link);
190    }
191    current_error_strings->SetString("url", resource.url.spec());
192    error_strings->Append(current_error_strings);
193  }
194  strings->Set("errors", error_strings);
195  DCHECK(phishing || malware);
196
197  if (malware && phishing) {
198    PopulateStringDictionary(
199        strings,
200        // Use the malware headline, it is the scariest one.
201        l10n_util::GetString(IDS_SAFE_BROWSING_MULTI_THREAT_TITLE),
202        l10n_util::GetString(IDS_SAFE_BROWSING_MALWARE_HEADLINE),
203        l10n_util::GetStringF(IDS_SAFE_BROWSING_MULTI_THREAT_DESCRIPTION1,
204                              UTF8ToWide(tab()->GetURL().host())),
205        l10n_util::GetString(IDS_SAFE_BROWSING_MULTI_THREAT_DESCRIPTION2),
206        L"");
207  } else if (malware) {
208    // Just malware.
209    PopulateStringDictionary(
210        strings,
211        l10n_util::GetString(IDS_SAFE_BROWSING_MALWARE_TITLE),
212        l10n_util::GetString(IDS_SAFE_BROWSING_MALWARE_HEADLINE),
213        l10n_util::GetStringF(IDS_SAFE_BROWSING_MULTI_MALWARE_DESCRIPTION1,
214                              UTF8ToWide(tab()->GetURL().host())),
215        l10n_util::GetString(IDS_SAFE_BROWSING_MULTI_MALWARE_DESCRIPTION2),
216        l10n_util::GetString(IDS_SAFE_BROWSING_MULTI_MALWARE_DESCRIPTION3));
217  } else {
218    // Just phishing.
219    PopulateStringDictionary(
220        strings,
221        l10n_util::GetString(IDS_SAFE_BROWSING_PHISHING_TITLE),
222        l10n_util::GetString(IDS_SAFE_BROWSING_PHISHING_HEADLINE),
223        l10n_util::GetStringF(IDS_SAFE_BROWSING_MULTI_PHISHING_DESCRIPTION1,
224                              UTF8ToWide(tab()->GetURL().host())),
225        L"", L"");
226  }
227
228  strings->SetString("confirm_text",
229                     l10n_util::GetStringUTF16(
230                         IDS_SAFE_BROWSING_MULTI_MALWARE_DESCRIPTION_AGREE));
231  strings->SetString("continue_button",
232                     l10n_util::GetStringUTF16(
233                         IDS_SAFE_BROWSING_MULTI_MALWARE_PROCEED_BUTTON));
234  strings->SetString("back_button",
235      l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_MALWARE_BACK_BUTTON));
236  strings->SetString("textdirection", base::i18n::IsRTL() ? "rtl" : "ltr");
237}
238
239void SafeBrowsingBlockingPage::PopulateMalwareStringDictionary(
240    DictionaryValue* strings) {
241  std::wstring diagnostic_link = StringPrintf(kSbDiagnosticHtml,
242      l10n_util::GetString(IDS_SAFE_BROWSING_MALWARE_DIAGNOSTIC_PAGE).c_str());
243
244  strings->SetString("badURL", url().host());
245  // Check to see if we're blocking the main page, or a sub-resource on the
246  // main page.
247  std::wstring description1, description3, description5;
248  if (is_main_frame_) {
249    description1 = l10n_util::GetStringF(IDS_SAFE_BROWSING_MALWARE_DESCRIPTION1,
250                                         UTF8ToWide(url().host()));
251  } else {
252    description1 = l10n_util::GetStringF(IDS_SAFE_BROWSING_MALWARE_DESCRIPTION4,
253                                         UTF8ToWide(tab()->GetURL().host()),
254                                         UTF8ToWide(url().host()));
255  }
256
257  std::wstring proceed_link = StringPrintf(kPLinkHtml,
258      l10n_util::GetString(IDS_SAFE_BROWSING_MALWARE_PROCEED_LINK).c_str());
259  description3 = l10n_util::GetStringF(IDS_SAFE_BROWSING_MALWARE_DESCRIPTION3,
260                                       proceed_link);
261
262  PopulateStringDictionary(
263      strings,
264      l10n_util::GetString(IDS_SAFE_BROWSING_MALWARE_TITLE),
265      l10n_util::GetString(IDS_SAFE_BROWSING_MALWARE_HEADLINE),
266      description1,
267      l10n_util::GetString(IDS_SAFE_BROWSING_MALWARE_DESCRIPTION2),
268      description3);
269
270  description5 = l10n_util::GetStringF(IDS_SAFE_BROWSING_MALWARE_DESCRIPTION5,
271                                       UTF8ToWide(url().host()),
272                                       UTF8ToWide(url().host()),
273                                       diagnostic_link);
274
275  strings->SetString("description5", WideToUTF16Hack(description5));
276
277  strings->SetString("back_button",
278      l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_MALWARE_BACK_BUTTON));
279  strings->SetString("more_info_button",
280      l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_MALWARE_MORE_INFO_BUTTON));
281  strings->SetString("less_info_button",
282      l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_MALWARE_LESS_INFO_BUTTON));
283  strings->SetString("proceed_link",
284      l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_MALWARE_PROCEED_LINK));
285  strings->SetString("textdirection", base::i18n::IsRTL() ? "rtl" : "ltr");
286}
287
288void SafeBrowsingBlockingPage::PopulatePhishingStringDictionary(
289    DictionaryValue* strings) {
290  PopulateStringDictionary(
291      strings,
292      l10n_util::GetString(IDS_SAFE_BROWSING_PHISHING_TITLE),
293      l10n_util::GetString(IDS_SAFE_BROWSING_PHISHING_HEADLINE),
294      l10n_util::GetStringF(IDS_SAFE_BROWSING_PHISHING_DESCRIPTION1,
295                            UTF8ToWide(url().host())),
296      l10n_util::GetStringF(IDS_SAFE_BROWSING_PHISHING_DESCRIPTION2,
297                            UTF8ToWide(url().host())),
298      L"");
299
300  strings->SetString("continue_button",
301      l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_PHISHING_PROCEED_BUTTON));
302  strings->SetString("back_button",
303      l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_PHISHING_BACK_BUTTON));
304  strings->SetString("report_error",
305      l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_PHISHING_REPORT_ERROR));
306  strings->SetString("textdirection", base::i18n::IsRTL() ? "rtl" : "ltr");
307}
308
309void SafeBrowsingBlockingPage::CommandReceived(const std::string& cmd) {
310  std::string command(cmd);  // Make a local copy so we can modify it.
311  // The Jasonified response has quotes, remove them.
312  if (command.length() > 1 && command[0] == '"') {
313    command = command.substr(1, command.length() - 2);
314  }
315
316  if (command == kLearnMoreCommand) {
317    // User pressed "Learn more".
318    GURL url;
319    if (unsafe_resources_[0].threat_type == SafeBrowsingService::URL_MALWARE) {
320      url = google_util::AppendGoogleLocaleParam(GURL(kLearnMoreMalwareUrl));
321    } else if (unsafe_resources_[0].threat_type ==
322               SafeBrowsingService::URL_PHISHING) {
323      url = google_util::AppendGoogleLocaleParam(GURL(kLearnMorePhishingUrl));
324    } else {
325      NOTREACHED();
326    }
327    tab()->OpenURL(url, GURL(), CURRENT_TAB, PageTransition::LINK);
328    return;
329  }
330
331  if (command == kProceedCommand) {
332    Proceed();
333    // We are deleted after this.
334    return;
335  }
336
337  if (command == kTakeMeBackCommand) {
338    DontProceed();
339    // We are deleted after this.
340    return;
341  }
342
343  // The "report error" and "show diagnostic" commands can have a number
344  // appended to them, which is the index of the element they apply to.
345  int element_index = 0;
346  size_t colon_index = command.find(':');
347  if (colon_index != std::string::npos) {
348    DCHECK(colon_index < command.size() - 1);
349    bool result = base::StringToInt(command.begin() + colon_index + 1,
350                                    command.end(),
351                                    &element_index);
352    command = command.substr(0, colon_index);
353    DCHECK(result);
354  }
355
356  if (element_index >= static_cast<int>(unsafe_resources_.size())) {
357    NOTREACHED();
358    return;
359  }
360
361  std::string bad_url_spec = unsafe_resources_[element_index].url.spec();
362  if (command == kReportErrorCommand) {
363    // User pressed "Report error" for a phishing site.
364    // Note that we cannot just put a link in the interstitial at this point.
365    // It is not OK to navigate in the context of an interstitial page.
366    DCHECK(unsafe_resources_[element_index].threat_type ==
367           SafeBrowsingService::URL_PHISHING);
368    GURL report_url =
369        safe_browsing_util::GeneratePhishingReportUrl(kSbReportPhishingUrl,
370                                                      bad_url_spec);
371    tab()->OpenURL(report_url, GURL(), CURRENT_TAB, PageTransition::LINK);
372    return;
373  }
374
375  if (command == kShowDiagnosticCommand) {
376    // We're going to take the user to Google's SafeBrowsing diagnostic page.
377    std::string diagnostic =
378        StringPrintf(kSbDiagnosticUrl,
379                     EscapeQueryParamValue(bad_url_spec, true).c_str());
380    GURL diagnostic_url(diagnostic);
381    diagnostic_url = google_util::AppendGoogleLocaleParam(diagnostic_url);
382    DCHECK(unsafe_resources_[element_index].threat_type ==
383           SafeBrowsingService::URL_MALWARE);
384    tab()->OpenURL(diagnostic_url, GURL(), CURRENT_TAB, PageTransition::LINK);
385    return;
386  }
387
388  NOTREACHED() << "Unexpected command: " << command;
389}
390
391void SafeBrowsingBlockingPage::Proceed() {
392  RecordUserAction(PROCEED);
393
394  NotifySafeBrowsingService(sb_service_, unsafe_resources_, true);
395
396  // Check to see if some new notifications of unsafe resources have been
397  // received while we were showing the interstitial.
398  UnsafeResourceMap* unsafe_resource_map = GetUnsafeResourcesMap();
399  UnsafeResourceMap::iterator iter = unsafe_resource_map->find(tab());
400  SafeBrowsingBlockingPage* blocking_page = NULL;
401  if (iter != unsafe_resource_map->end() && !iter->second.empty()) {
402    // Build an interstitial for all the unsafe resources notifications.
403    // Don't show it now as showing an interstitial while an interstitial is
404    // already showing would cause DontProceed() to be invoked.
405    blocking_page = factory_->CreateSafeBrowsingPage(sb_service_, tab(),
406                                                     iter->second);
407    unsafe_resource_map->erase(iter);
408  }
409
410  InterstitialPage::Proceed();
411  // We are now deleted.
412
413  // Now that this interstitial is gone, we can show the new one.
414  if (blocking_page)
415    blocking_page->Show();
416}
417
418void SafeBrowsingBlockingPage::DontProceed() {
419  DCHECK(action_taken() != DONT_PROCEED_ACTION);
420  // We could have already called Proceed(), in which case we must not notify
421  // the SafeBrowsingService again, as the client has been deleted.
422  if (action_taken() == PROCEED_ACTION) {
423    // We still want to hide the interstitial page.
424    InterstitialPage::DontProceed();
425    // We are now deleted.
426    return;
427  }
428
429  RecordUserAction(DONT_PROCEED);
430
431  NotifySafeBrowsingService(sb_service_, unsafe_resources_, false);
432
433  // The user does not want to proceed, clear the queued unsafe resources
434  // notifications we received while the interstitial was showing.
435  UnsafeResourceMap* unsafe_resource_map = GetUnsafeResourcesMap();
436  UnsafeResourceMap::iterator iter = unsafe_resource_map->find(tab());
437  if (iter != unsafe_resource_map->end() && !iter->second.empty()) {
438    NotifySafeBrowsingService(sb_service_, iter->second, false);
439    unsafe_resource_map->erase(iter);
440  }
441
442  // We don't remove the navigation entry if the tab is being destroyed as this
443  // would trigger a navigation that would cause trouble as the render view host
444  // for the tab has by then already been destroyed.
445  if (navigation_entry_index_to_remove_ != -1 && !tab()->is_being_destroyed()) {
446    tab()->controller().RemoveEntryAtIndex(navigation_entry_index_to_remove_,
447                                           GURL(chrome::kChromeUINewTabURL));
448    navigation_entry_index_to_remove_ = -1;
449  }
450  InterstitialPage::DontProceed();
451  // We are now deleted.
452}
453
454void SafeBrowsingBlockingPage::RecordUserAction(BlockingPageEvent event) {
455  // Determine the interstitial type from the blocked resources.
456  // This is the same logic that is used to actually construct the
457  // page contents; we can look at the title to see which type of
458  // interstitial is being displayed.
459  DictionaryValue strings;
460  PopulateMultipleThreatStringDictionary(&strings);
461
462  string16 title;
463  DCHECK(strings.GetString("title", &title));
464
465  std::string action = "SBInterstitial";
466  if (title ==
467          l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_MULTI_THREAT_TITLE)) {
468    action.append("Multiple");
469  } else if (title ==
470                 l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_MALWARE_TITLE)) {
471    action.append("Malware");
472  } else {
473    DCHECK_EQ(title,
474              l10n_util::GetStringUTF16(IDS_SAFE_BROWSING_PHISHING_TITLE));
475    action.append("Phishing");
476  }
477
478  switch (event) {
479    case SHOW:
480      action.append("Show");
481      break;
482    case PROCEED:
483      action.append("Proceed");
484      break;
485    case DONT_PROCEED:
486      action.append("DontProceed");
487      break;
488    default:
489      NOTREACHED() << "Unexpected event: " << event;
490  }
491
492  UserMetrics::RecordComputedAction(action);
493}
494
495// static
496void SafeBrowsingBlockingPage::NotifySafeBrowsingService(
497    SafeBrowsingService* sb_service,
498    const UnsafeResourceList& unsafe_resources,
499    bool proceed) {
500  BrowserThread::PostTask(
501      BrowserThread::IO, FROM_HERE,
502      NewRunnableMethod(
503          sb_service, &SafeBrowsingService::OnBlockingPageDone,
504          unsafe_resources, proceed));
505}
506
507// static
508SafeBrowsingBlockingPage::UnsafeResourceMap*
509    SafeBrowsingBlockingPage::GetUnsafeResourcesMap() {
510  return Singleton<UnsafeResourceMap>::get();
511}
512
513// static
514void SafeBrowsingBlockingPage::ShowBlockingPage(
515    SafeBrowsingService* sb_service,
516    const SafeBrowsingService::UnsafeResource& unsafe_resource) {
517  TabContents* tab_contents = tab_util::GetTabContentsByID(
518      unsafe_resource.render_process_host_id, unsafe_resource.render_view_id);
519
520  InterstitialPage* interstitial =
521      InterstitialPage::GetInterstitialPage(tab_contents);
522  if (interstitial &&
523      unsafe_resource.resource_type == ResourceType::MAIN_FRAME) {
524    // There is already an interstitial showing and we are about to display a
525    // new one for the main frame. Just hide the current one, it is now
526    // irrelevent
527    interstitial->DontProceed();
528    interstitial = NULL;
529  }
530
531  if (!interstitial) {
532    // There are no interstitial currently showing in that tab, go ahead and
533    // show this interstitial.
534    std::vector<SafeBrowsingService::UnsafeResource> resources;
535    resources.push_back(unsafe_resource);
536    // Set up the factory if this has not been done already (tests do that
537    // before this method is called).
538    if (!factory_)
539      factory_ = Singleton<SafeBrowsingBlockingPageFactoryImpl>::get();
540    SafeBrowsingBlockingPage* blocking_page =
541        factory_->CreateSafeBrowsingPage(sb_service, tab_contents, resources);
542    blocking_page->Show();
543    return;
544  }
545
546  // This is an interstitial for a page's resource, let's queue it.
547  UnsafeResourceMap* unsafe_resource_map = GetUnsafeResourcesMap();
548  (*unsafe_resource_map)[tab_contents].push_back(unsafe_resource);
549}
550
551// static
552bool SafeBrowsingBlockingPage::IsMainPage(
553    const UnsafeResourceList& unsafe_resources) {
554  return unsafe_resources.size() == 1 &&
555         unsafe_resources[0].resource_type == ResourceType::MAIN_FRAME;
556}
557