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