1// Copyright (c) 2012 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/bind.h"
12#include "base/command_line.h"
13#include "base/i18n/rtl.h"
14#include "base/lazy_instance.h"
15#include "base/metrics/field_trial.h"
16#include "base/metrics/histogram.h"
17#include "base/prefs/pref_service.h"
18#include "base/strings/string_number_conversions.h"
19#include "base/strings/string_piece.h"
20#include "base/strings/stringprintf.h"
21#include "base/strings/utf_string_conversions.h"
22#include "base/time/time.h"
23#include "base/values.h"
24#include "chrome/browser/browser_process.h"
25#include "chrome/browser/history/history_service_factory.h"
26#include "chrome/browser/profiles/profile.h"
27#include "chrome/browser/renderer_preferences_util.h"
28#include "chrome/browser/safe_browsing/malware_details.h"
29#include "chrome/browser/safe_browsing/ui_manager.h"
30#include "chrome/browser/tab_contents/tab_util.h"
31#include "chrome/common/chrome_switches.h"
32#include "chrome/common/pref_names.h"
33#include "chrome/common/url_constants.h"
34#include "chrome/grit/generated_resources.h"
35#include "chrome/grit/locale_settings.h"
36#include "components/google/core/browser/google_util.h"
37#include "content/public/browser/browser_thread.h"
38#include "content/public/browser/interstitial_page.h"
39#include "content/public/browser/navigation_controller.h"
40#include "content/public/browser/user_metrics.h"
41#include "content/public/browser/web_contents.h"
42#include "grit/browser_resources.h"
43#include "net/base/escape.h"
44#include "ui/base/l10n/l10n_util.h"
45#include "ui/base/resource/resource_bundle.h"
46#include "ui/base/webui/jstemplate_builder.h"
47#include "ui/base/webui/web_ui_util.h"
48
49#if defined(ENABLE_EXTENSIONS)
50#include "chrome/browser/extensions/api/experience_sampling_private/experience_sampling.h"
51#endif
52
53using base::UserMetricsAction;
54using content::BrowserThread;
55using content::InterstitialPage;
56using content::OpenURLParams;
57using content::Referrer;
58using content::WebContents;
59
60#if defined(ENABLE_EXTENSIONS)
61using extensions::ExperienceSamplingEvent;
62#endif
63
64namespace {
65
66// For malware interstitial pages, we link the problematic URL to Google's
67// diagnostic page.
68#if defined(GOOGLE_CHROME_BUILD)
69const char* const kSbDiagnosticUrl =
70    "http://safebrowsing.clients.google.com/safebrowsing/diagnostic?site=%s&client=googlechrome";
71#else
72const char* const kSbDiagnosticUrl =
73    "http://safebrowsing.clients.google.com/safebrowsing/diagnostic?site=%s&client=chromium";
74#endif
75
76// URL for malware and phishing, V2.
77const char kLearnMoreMalwareUrlV2[] =
78    "https://www.google.com/transparencyreport/safebrowsing/";
79const char kLearnMorePhishingUrlV2[] =
80    "https://www.google.com/transparencyreport/safebrowsing/";
81
82const char kPrivacyLinkHtml[] =
83    "<a id=\"privacy-link\" href=\"\" onclick=\"sendCommand('showPrivacy'); "
84    "return false;\" onmousedown=\"return false;\">%s</a>";
85
86// After a malware interstitial where the user opted-in to the report
87// but clicked "proceed anyway", we delay the call to
88// MalwareDetails::FinishCollection() by this much time (in
89// milliseconds).
90const int64 kMalwareDetailsProceedDelayMilliSeconds = 3000;
91
92// The commands returned by the page when the user performs an action.
93const char kDoReportCommand[] = "doReport";
94const char kDontReportCommand[] = "dontReport";
95const char kExpandedSeeMoreCommand[] = "expandedSeeMore";
96const char kLearnMoreCommand[] = "learnMore2";
97const char kProceedCommand[] = "proceed";
98const char kShowDiagnosticCommand[] = "showDiagnostic";
99const char kShowPrivacyCommand[] = "showPrivacy";
100const char kTakeMeBackCommand[] = "takeMeBack";
101
102// Other constants used to communicate with the JavaScript.
103const char kBoxChecked[] = "boxchecked";
104const char kDisplayCheckBox[] = "displaycheckbox";
105
106// Constants for the Experience Sampling instrumentation.
107#if defined(ENABLE_EXTENSIONS)
108const char kEventNameMalware[] = "safebrowsing_interstitial_";
109const char kEventNameHarmful[] = "harmful_interstitial_";
110const char kEventNamePhishing[] = "phishing_interstitial_";
111const char kEventNameOther[] = "safebrowsing_other_interstitial_";
112#endif
113
114base::LazyInstance<SafeBrowsingBlockingPage::UnsafeResourceMap>
115    g_unsafe_resource_map = LAZY_INSTANCE_INITIALIZER;
116
117}  // namespace
118
119// static
120SafeBrowsingBlockingPageFactory* SafeBrowsingBlockingPage::factory_ = NULL;
121
122// The default SafeBrowsingBlockingPageFactory.  Global, made a singleton so we
123// don't leak it.
124class SafeBrowsingBlockingPageFactoryImpl
125    : public SafeBrowsingBlockingPageFactory {
126 public:
127  virtual SafeBrowsingBlockingPage* CreateSafeBrowsingPage(
128      SafeBrowsingUIManager* ui_manager,
129      WebContents* web_contents,
130      const SafeBrowsingBlockingPage::UnsafeResourceList& unsafe_resources)
131      OVERRIDE {
132    return new SafeBrowsingBlockingPage(ui_manager, web_contents,
133        unsafe_resources);
134  }
135
136 private:
137  friend struct base::DefaultLazyInstanceTraits<
138      SafeBrowsingBlockingPageFactoryImpl>;
139
140  SafeBrowsingBlockingPageFactoryImpl() { }
141
142  DISALLOW_COPY_AND_ASSIGN(SafeBrowsingBlockingPageFactoryImpl);
143};
144
145static base::LazyInstance<SafeBrowsingBlockingPageFactoryImpl>
146    g_safe_browsing_blocking_page_factory_impl = LAZY_INSTANCE_INITIALIZER;
147
148SafeBrowsingBlockingPage::SafeBrowsingBlockingPage(
149    SafeBrowsingUIManager* ui_manager,
150    WebContents* web_contents,
151    const UnsafeResourceList& unsafe_resources)
152    : malware_details_proceed_delay_ms_(
153          kMalwareDetailsProceedDelayMilliSeconds),
154      ui_manager_(ui_manager),
155      report_loop_(NULL),
156      is_main_frame_load_blocked_(IsMainPageLoadBlocked(unsafe_resources)),
157      unsafe_resources_(unsafe_resources),
158      proceeded_(false),
159      web_contents_(web_contents),
160      url_(unsafe_resources[0].url),
161      interstitial_page_(NULL),
162      create_view_(true),
163      num_visits_(-1) {
164  bool malware = false;
165  bool harmful = false;
166  bool phishing = false;
167  for (UnsafeResourceList::const_iterator iter = unsafe_resources_.begin();
168       iter != unsafe_resources_.end(); ++iter) {
169    const UnsafeResource& resource = *iter;
170    SBThreatType threat_type = resource.threat_type;
171    if (threat_type == SB_THREAT_TYPE_URL_MALWARE ||
172        threat_type == SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL) {
173      malware = true;
174    } else if (threat_type == SB_THREAT_TYPE_URL_HARMFUL) {
175      harmful = true;
176    } else {
177      DCHECK(threat_type == SB_THREAT_TYPE_URL_PHISHING ||
178             threat_type == SB_THREAT_TYPE_CLIENT_SIDE_PHISHING_URL);
179      phishing = true;
180    }
181  }
182  DCHECK(phishing || malware || harmful);
183  if (malware)
184    interstitial_type_ = TYPE_MALWARE;
185  else if (harmful)
186    interstitial_type_ = TYPE_HARMFUL;
187  else
188    interstitial_type_ = TYPE_PHISHING;
189
190  RecordUserDecision(SHOW);
191  RecordUserInteraction(TOTAL_VISITS);
192  if (IsPrefEnabled(prefs::kSafeBrowsingProceedAnywayDisabled))
193    RecordUserDecision(PROCEEDING_DISABLED);
194
195  HistoryService* history_service = HistoryServiceFactory::GetForProfile(
196          Profile::FromBrowserContext(web_contents->GetBrowserContext()),
197          Profile::EXPLICIT_ACCESS);
198  if (history_service) {
199    history_service->GetVisibleVisitCountToHost(
200        url_,
201        base::Bind(&SafeBrowsingBlockingPage::OnGotHistoryCount,
202                   base::Unretained(this)),
203        &request_tracker_);
204  }
205
206  if (!is_main_frame_load_blocked_) {
207    navigation_entry_index_to_remove_ =
208        web_contents->GetController().GetLastCommittedEntryIndex();
209  } else {
210    navigation_entry_index_to_remove_ = -1;
211  }
212
213  // Start computing malware details. They will be sent only
214  // if the user opts-in on the blocking page later.
215  // If there's more than one malicious resources, it means the user
216  // clicked through the first warning, so we don't prepare additional
217  // reports.
218  if (unsafe_resources.size() == 1 &&
219      unsafe_resources[0].threat_type == SB_THREAT_TYPE_URL_MALWARE &&
220      malware_details_.get() == NULL && CanShowMalwareDetailsOption()) {
221    malware_details_ = MalwareDetails::NewMalwareDetails(
222        ui_manager_, web_contents, unsafe_resources[0]);
223  }
224
225#if defined(ENABLE_EXTENSIONS)
226  // ExperienceSampling: Set up new sampling event for this interstitial.
227  // This needs to handle all types of warnings this interstitial can show.
228  std::string event_name;
229  switch (interstitial_type_) {
230    case TYPE_MALWARE:
231      event_name = kEventNameMalware;
232      break;
233    case TYPE_HARMFUL:
234      event_name = kEventNameHarmful;
235      break;
236    case TYPE_PHISHING:
237      event_name = kEventNamePhishing;
238      break;
239    default:
240      event_name = kEventNameOther;
241      break;
242  }
243  sampling_event_.reset(new ExperienceSamplingEvent(
244      event_name,
245      url_,
246      web_contents_->GetLastCommittedURL(),
247      web_contents_->GetBrowserContext()));
248#endif
249
250  // Creating interstitial_page_ without showing it leaks memory, so don't
251  // create it here.
252}
253
254bool SafeBrowsingBlockingPage::CanShowMalwareDetailsOption() {
255  return (!web_contents_->GetBrowserContext()->IsOffTheRecord() &&
256          web_contents_->GetURL().SchemeIs(url::kHttpScheme));
257}
258
259SafeBrowsingBlockingPage::~SafeBrowsingBlockingPage() {
260}
261
262void SafeBrowsingBlockingPage::CommandReceived(const std::string& cmd) {
263  std::string command(cmd);  // Make a local copy so we can modify it.
264  // The Jasonified response has quotes, remove them.
265  if (command.length() > 1 && command[0] == '"') {
266    command = command.substr(1, command.length() - 2);
267  }
268  if (command == kDoReportCommand) {
269    SetReportingPreference(true);
270    return;
271  }
272
273  if (command == kDontReportCommand) {
274    SetReportingPreference(false);
275    return;
276  }
277
278  if (command == kLearnMoreCommand) {
279    // User pressed "Learn more".
280    RecordUserInteraction(SHOW_LEARN_MORE);
281    GURL learn_more_url(interstitial_type_ == TYPE_PHISHING ?
282                        kLearnMorePhishingUrlV2 : kLearnMoreMalwareUrlV2);
283    learn_more_url = google_util::AppendGoogleLocaleParam(
284        learn_more_url, g_browser_process->GetApplicationLocale());
285    OpenURLParams params(learn_more_url,
286                         Referrer(),
287                         CURRENT_TAB,
288                         ui::PAGE_TRANSITION_LINK,
289                         false);
290    web_contents_->OpenURL(params);
291    return;
292  }
293
294  if (command == kShowPrivacyCommand) {
295    // User pressed "Safe Browsing privacy policy".
296    RecordUserInteraction(SHOW_PRIVACY_POLICY);
297    GURL privacy_url(
298        l10n_util::GetStringUTF8(IDS_SAFE_BROWSING_PRIVACY_POLICY_URL));
299    privacy_url = google_util::AppendGoogleLocaleParam(
300        privacy_url, g_browser_process->GetApplicationLocale());
301    OpenURLParams params(privacy_url,
302                         Referrer(),
303                         CURRENT_TAB,
304                         ui::PAGE_TRANSITION_LINK,
305                         false);
306    web_contents_->OpenURL(params);
307    return;
308  }
309
310  bool proceed_blocked = false;
311  if (command == kProceedCommand) {
312    if (IsPrefEnabled(prefs::kSafeBrowsingProceedAnywayDisabled)) {
313      proceed_blocked = true;
314    } else {
315      RecordUserDecision(PROCEED);
316      interstitial_page_->Proceed();
317      // |this| has been deleted after Proceed() returns.
318      return;
319    }
320  }
321
322  if (command == kTakeMeBackCommand || proceed_blocked) {
323    // Don't record the user action here because there are other ways of
324    // triggering DontProceed, like clicking the back button.
325    if (is_main_frame_load_blocked_) {
326      // If the load is blocked, we want to close the interstitial and discard
327      // the pending entry.
328      interstitial_page_->DontProceed();
329      // |this| has been deleted after DontProceed() returns.
330      return;
331    }
332
333    // Otherwise the offending entry has committed, and we need to go back or
334    // to a safe page.  We will close the interstitial when that page commits.
335    if (web_contents_->GetController().CanGoBack()) {
336      web_contents_->GetController().GoBack();
337    } else {
338      web_contents_->GetController().LoadURL(
339          GURL(chrome::kChromeUINewTabURL),
340          content::Referrer(),
341          ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
342          std::string());
343    }
344    return;
345  }
346
347  // The "report error" and "show diagnostic" commands can have a number
348  // appended to them, which is the index of the element they apply to.
349  size_t element_index = 0;
350  size_t colon_index = command.find(':');
351  if (colon_index != std::string::npos) {
352    DCHECK(colon_index < command.size() - 1);
353    int result_int = 0;
354    bool result = base::StringToInt(base::StringPiece(command.begin() +
355                                                      colon_index + 1,
356                                                      command.end()),
357                                    &result_int);
358    command = command.substr(0, colon_index);
359    if (result)
360      element_index = static_cast<size_t>(result_int);
361  }
362
363  if (element_index >= unsafe_resources_.size()) {
364    NOTREACHED();
365    return;
366  }
367
368  std::string bad_url_spec = unsafe_resources_[element_index].url.spec();
369  if (command == kShowDiagnosticCommand) {
370    // We're going to take the user to Google's SafeBrowsing diagnostic page.
371    RecordUserInteraction(SHOW_DIAGNOSTIC);
372    std::string diagnostic =
373        base::StringPrintf(kSbDiagnosticUrl,
374            net::EscapeQueryParamValue(bad_url_spec, true).c_str());
375    GURL diagnostic_url(diagnostic);
376    diagnostic_url = google_util::AppendGoogleLocaleParam(
377        diagnostic_url, g_browser_process->GetApplicationLocale());
378    DCHECK(unsafe_resources_[element_index].threat_type ==
379           SB_THREAT_TYPE_URL_MALWARE ||
380           unsafe_resources_[element_index].threat_type ==
381           SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL);
382    OpenURLParams params(
383        diagnostic_url, Referrer(), CURRENT_TAB, ui::PAGE_TRANSITION_LINK,
384        false);
385    web_contents_->OpenURL(params);
386    return;
387  }
388
389  if (command == kExpandedSeeMoreCommand) {
390    RecordUserInteraction(SHOW_ADVANCED);
391    return;
392  }
393
394  NOTREACHED() << "Unexpected command: " << command;
395}
396
397void SafeBrowsingBlockingPage::OverrideRendererPrefs(
398      content::RendererPreferences* prefs) {
399  Profile* profile = Profile::FromBrowserContext(
400      web_contents_->GetBrowserContext());
401  renderer_preferences_util::UpdateFromSystemSettings(prefs, profile);
402 }
403
404void SafeBrowsingBlockingPage::SetReportingPreference(bool report) {
405  Profile* profile = Profile::FromBrowserContext(
406      web_contents_->GetBrowserContext());
407  PrefService* pref = profile->GetPrefs();
408  pref->SetBoolean(prefs::kSafeBrowsingExtendedReportingEnabled, report);
409  UMA_HISTOGRAM_BOOLEAN("SB2.SetExtendedReportingEnabled", report);
410}
411
412void SafeBrowsingBlockingPage::OnProceed() {
413  proceeded_ = true;
414  // Send the malware details, if we opted to.
415  FinishMalwareDetails(malware_details_proceed_delay_ms_);
416
417  NotifySafeBrowsingUIManager(ui_manager_, unsafe_resources_, true);
418
419  // Check to see if some new notifications of unsafe resources have been
420  // received while we were showing the interstitial.
421  UnsafeResourceMap* unsafe_resource_map = GetUnsafeResourcesMap();
422  UnsafeResourceMap::iterator iter = unsafe_resource_map->find(web_contents_);
423  SafeBrowsingBlockingPage* blocking_page = NULL;
424  if (iter != unsafe_resource_map->end() && !iter->second.empty()) {
425    // Build an interstitial for all the unsafe resources notifications.
426    // Don't show it now as showing an interstitial while an interstitial is
427    // already showing would cause DontProceed() to be invoked.
428    blocking_page = factory_->CreateSafeBrowsingPage(ui_manager_, web_contents_,
429                                                     iter->second);
430    unsafe_resource_map->erase(iter);
431  }
432
433  // Now that this interstitial is gone, we can show the new one.
434  if (blocking_page)
435    blocking_page->Show();
436}
437
438void SafeBrowsingBlockingPage::DontCreateViewForTesting() {
439  create_view_ = false;
440}
441
442void SafeBrowsingBlockingPage::Show() {
443  DCHECK(!interstitial_page_);
444  interstitial_page_ = InterstitialPage::Create(
445      web_contents_, is_main_frame_load_blocked_, url_, this);
446  if (!create_view_)
447    interstitial_page_->DontCreateViewForTesting();
448  interstitial_page_->Show();
449}
450
451void SafeBrowsingBlockingPage::OnDontProceed() {
452  // We could have already called Proceed(), in which case we must not notify
453  // the SafeBrowsingUIManager again, as the client has been deleted.
454  if (proceeded_)
455    return;
456
457  if (!IsPrefEnabled(prefs::kSafeBrowsingProceedAnywayDisabled))
458    RecordUserDecision(DONT_PROCEED);
459
460  // Send the malware details, if we opted to.
461  FinishMalwareDetails(0);  // No delay
462
463  NotifySafeBrowsingUIManager(ui_manager_, unsafe_resources_, false);
464
465  // The user does not want to proceed, clear the queued unsafe resources
466  // notifications we received while the interstitial was showing.
467  UnsafeResourceMap* unsafe_resource_map = GetUnsafeResourcesMap();
468  UnsafeResourceMap::iterator iter = unsafe_resource_map->find(web_contents_);
469  if (iter != unsafe_resource_map->end() && !iter->second.empty()) {
470    NotifySafeBrowsingUIManager(ui_manager_, iter->second, false);
471    unsafe_resource_map->erase(iter);
472  }
473
474  // We don't remove the navigation entry if the tab is being destroyed as this
475  // would trigger a navigation that would cause trouble as the render view host
476  // for the tab has by then already been destroyed.  We also don't delete the
477  // current entry if it has been committed again, which is possible on a page
478  // that had a subresource warning.
479  int last_committed_index =
480      web_contents_->GetController().GetLastCommittedEntryIndex();
481  if (navigation_entry_index_to_remove_ != -1 &&
482      navigation_entry_index_to_remove_ != last_committed_index &&
483      !web_contents_->IsBeingDestroyed()) {
484    CHECK(web_contents_->GetController().RemoveEntryAtIndex(
485        navigation_entry_index_to_remove_));
486    navigation_entry_index_to_remove_ = -1;
487  }
488}
489
490void SafeBrowsingBlockingPage::OnGotHistoryCount(bool success,
491                                                 int num_visits,
492                                                 base::Time first_visit) {
493  if (success)
494    num_visits_ = num_visits;
495}
496
497void SafeBrowsingBlockingPage::RecordUserDecision(Decision decision) {
498  switch (interstitial_type_) {
499    case TYPE_MALWARE:
500      UMA_HISTOGRAM_ENUMERATION("interstitial.malware.decision",
501                                decision,
502                                MAX_DECISION);
503      break;
504    case TYPE_HARMFUL:
505      UMA_HISTOGRAM_ENUMERATION("interstitial.harmful.decision",
506                                decision,
507                                MAX_DECISION);
508      break;
509    case TYPE_PHISHING:
510      UMA_HISTOGRAM_ENUMERATION("interstitial.phishing.decision",
511                                decision,
512                                MAX_DECISION);
513      break;
514  }
515
516#if defined(ENABLE_EXTENSIONS)
517  if (sampling_event_.get()) {
518    switch (decision) {
519      case PROCEED:
520        sampling_event_->CreateUserDecisionEvent(
521            ExperienceSamplingEvent::kProceed);
522        break;
523      case DONT_PROCEED:
524        sampling_event_->CreateUserDecisionEvent(
525            ExperienceSamplingEvent::kDeny);
526        break;
527      case SHOW:
528      case PROCEEDING_DISABLED:
529      case MAX_DECISION:
530        break;
531    }
532  }
533#endif
534
535  // Record additional information about malware sites that users have
536  // visited before.
537  if (num_visits_ < 1 || interstitial_type_ != TYPE_MALWARE)
538    return;
539  if (decision == PROCEED || decision == DONT_PROCEED) {
540    UMA_HISTOGRAM_ENUMERATION("interstitial.malware.decision.repeat_visit",
541                              SHOW,
542                              MAX_DECISION);
543    UMA_HISTOGRAM_ENUMERATION("interstitial.malware.decision.repeat_visit",
544                              decision,
545                              MAX_DECISION);
546  }
547}
548
549void SafeBrowsingBlockingPage::RecordUserInteraction(Interaction interaction) {
550  switch (interstitial_type_) {
551    case TYPE_MALWARE:
552      UMA_HISTOGRAM_ENUMERATION("interstitial.malware.interaction",
553                                interaction,
554                                MAX_INTERACTION);
555      break;
556    case TYPE_HARMFUL:
557      UMA_HISTOGRAM_ENUMERATION("interstitial.harmful.interaction",
558                                interaction,
559                                MAX_INTERACTION);
560      break;
561    case TYPE_PHISHING:
562      UMA_HISTOGRAM_ENUMERATION("interstitial.phishing.interaction",
563                                interaction,
564                                MAX_INTERACTION);
565      break;
566  }
567
568#if defined(ENABLE_EXTENSIONS)
569  if (!sampling_event_.get())
570    return;
571  switch (interaction) {
572    case SHOW_LEARN_MORE:
573      sampling_event_->set_has_viewed_learn_more(true);
574      break;
575    case SHOW_ADVANCED:
576      sampling_event_->set_has_viewed_details(true);
577      break;
578    case SHOW_PRIVACY_POLICY:
579    case SHOW_DIAGNOSTIC:
580    case TOTAL_VISITS:
581    case MAX_INTERACTION:
582      break;
583  }
584#endif
585}
586
587void SafeBrowsingBlockingPage::FinishMalwareDetails(int64 delay_ms) {
588  if (malware_details_.get() == NULL)
589    return;  // Not all interstitials have malware details (eg phishing).
590
591  const bool enabled =
592      IsPrefEnabled(prefs::kSafeBrowsingExtendedReportingEnabled);
593  UMA_HISTOGRAM_BOOLEAN("SB2.ExtendedReportingIsEnabled", enabled);
594  if (enabled) {
595    // Finish the malware details collection, send it over.
596    BrowserThread::PostDelayedTask(
597        BrowserThread::IO, FROM_HERE,
598        base::Bind(&MalwareDetails::FinishCollection, malware_details_.get()),
599        base::TimeDelta::FromMilliseconds(delay_ms));
600  }
601}
602
603bool SafeBrowsingBlockingPage::IsPrefEnabled(const char* pref) {
604  Profile* profile =
605      Profile::FromBrowserContext(web_contents_->GetBrowserContext());
606  return profile->GetPrefs()->GetBoolean(pref);
607}
608
609// static
610void SafeBrowsingBlockingPage::NotifySafeBrowsingUIManager(
611    SafeBrowsingUIManager* ui_manager,
612    const UnsafeResourceList& unsafe_resources,
613    bool proceed) {
614  BrowserThread::PostTask(
615      BrowserThread::IO, FROM_HERE,
616      base::Bind(&SafeBrowsingUIManager::OnBlockingPageDone,
617                 ui_manager, unsafe_resources, proceed));
618}
619
620// static
621SafeBrowsingBlockingPage::UnsafeResourceMap*
622    SafeBrowsingBlockingPage::GetUnsafeResourcesMap() {
623  return g_unsafe_resource_map.Pointer();
624}
625
626// static
627SafeBrowsingBlockingPage* SafeBrowsingBlockingPage::CreateBlockingPage(
628    SafeBrowsingUIManager* ui_manager,
629    WebContents* web_contents,
630    const UnsafeResource& unsafe_resource) {
631  std::vector<UnsafeResource> resources;
632  resources.push_back(unsafe_resource);
633  // Set up the factory if this has not been done already (tests do that
634  // before this method is called).
635  if (!factory_)
636    factory_ = g_safe_browsing_blocking_page_factory_impl.Pointer();
637  return factory_->CreateSafeBrowsingPage(ui_manager, web_contents, resources);
638}
639
640// static
641void SafeBrowsingBlockingPage::ShowBlockingPage(
642    SafeBrowsingUIManager* ui_manager,
643    const UnsafeResource& unsafe_resource) {
644  DVLOG(1) << __FUNCTION__ << " " << unsafe_resource.url.spec();
645  WebContents* web_contents = tab_util::GetWebContentsByID(
646      unsafe_resource.render_process_host_id, unsafe_resource.render_view_id);
647
648  InterstitialPage* interstitial =
649      InterstitialPage::GetInterstitialPage(web_contents);
650  if (interstitial && !unsafe_resource.is_subresource) {
651    // There is already an interstitial showing and we are about to display a
652    // new one for the main frame. Just hide the current one, it is now
653    // irrelevent
654    interstitial->DontProceed();
655    interstitial = NULL;
656  }
657
658  if (!interstitial) {
659    // There are no interstitial currently showing in that tab, go ahead and
660    // show this interstitial.
661    SafeBrowsingBlockingPage* blocking_page =
662        CreateBlockingPage(ui_manager, web_contents, unsafe_resource);
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)[web_contents].push_back(unsafe_resource);
670}
671
672// static
673bool SafeBrowsingBlockingPage::IsMainPageLoadBlocked(
674    const UnsafeResourceList& unsafe_resources) {
675  // Client-side phishing detection interstitials never block the main frame
676  // load, since they happen after the page is finished loading.
677  if (unsafe_resources[0].threat_type ==
678      SB_THREAT_TYPE_CLIENT_SIDE_PHISHING_URL) {
679    return false;
680  }
681
682  // Otherwise, check the threat type.
683  return unsafe_resources.size() == 1 && !unsafe_resources[0].is_subresource;
684}
685
686std::string SafeBrowsingBlockingPage::GetHTMLContents() {
687  DCHECK(!unsafe_resources_.empty());
688
689  // Fill in the shared values.
690  base::DictionaryValue load_time_data;
691  webui::SetFontAndTextDirection(&load_time_data);
692  load_time_data.SetBoolean("ssl", false);
693  load_time_data.SetString(
694      "tabTitle", l10n_util::GetStringUTF16(IDS_SAFEBROWSING_V3_TITLE));
695  load_time_data.SetString(
696      "openDetails",
697      l10n_util::GetStringUTF16(IDS_SAFEBROWSING_V3_OPEN_DETAILS_BUTTON));
698  load_time_data.SetString(
699      "closeDetails",
700      l10n_util::GetStringUTF16(IDS_SAFEBROWSING_V3_CLOSE_DETAILS_BUTTON));
701  load_time_data.SetString(
702      "primaryButtonText",
703      l10n_util::GetStringUTF16(IDS_SAFEBROWSING_OVERRIDABLE_SAFETY_BUTTON));
704  load_time_data.SetBoolean(
705      "overridable",
706      !IsPrefEnabled(prefs::kSafeBrowsingProceedAnywayDisabled));
707
708  switch (interstitial_type_) {
709    case TYPE_MALWARE:
710      PopulateMalwareLoadTimeData(&load_time_data);
711      break;
712    case TYPE_HARMFUL:
713      PopulateHarmfulLoadTimeData(&load_time_data);
714      break;
715    case TYPE_PHISHING:
716      PopulatePhishingLoadTimeData(&load_time_data);
717      break;
718  }
719
720  base::StringPiece html(
721      ResourceBundle::GetSharedInstance().GetRawDataResource(
722          IRD_SECURITY_INTERSTITIAL_HTML));
723  webui::UseVersion2 version;
724  return webui::GetI18nTemplateHtml(html, &load_time_data);
725}
726
727void SafeBrowsingBlockingPage::PopulateMalwareLoadTimeData(
728    base::DictionaryValue* load_time_data) {
729  load_time_data->SetBoolean("phishing", false);
730  load_time_data->SetString(
731      "heading", l10n_util::GetStringUTF16(IDS_MALWARE_V3_HEADING));
732  load_time_data->SetString(
733      "primaryParagraph",
734      l10n_util::GetStringFUTF16(
735          IDS_MALWARE_V3_PRIMARY_PARAGRAPH,
736          base::UTF8ToUTF16(url_.host())));
737  load_time_data->SetString(
738      "explanationParagraph",
739      is_main_frame_load_blocked_ ?
740          l10n_util::GetStringFUTF16(
741              IDS_MALWARE_V3_EXPLANATION_PARAGRAPH,
742              base::UTF8ToUTF16(url_.host())) :
743          l10n_util::GetStringFUTF16(
744              IDS_MALWARE_V3_EXPLANATION_PARAGRAPH_SUBRESOURCE,
745              base::UTF8ToUTF16(web_contents_->GetURL().host()),
746              base::UTF8ToUTF16(url_.host())));
747  load_time_data->SetString(
748      "finalParagraph",
749      l10n_util::GetStringUTF16(IDS_MALWARE_V3_PROCEED_PARAGRAPH));
750
751  load_time_data->SetBoolean(kDisplayCheckBox, CanShowMalwareDetailsOption());
752  if (CanShowMalwareDetailsOption()) {
753    std::string privacy_link = base::StringPrintf(
754        kPrivacyLinkHtml,
755        l10n_util::GetStringUTF8(
756            IDS_SAFE_BROWSING_PRIVACY_POLICY_PAGE).c_str());
757    load_time_data->SetString(
758        "optInLink",
759        l10n_util::GetStringFUTF16(IDS_SAFE_BROWSING_MALWARE_REPORTING_AGREE,
760                                   base::UTF8ToUTF16(privacy_link)));
761    load_time_data->SetBoolean(
762        kBoxChecked,
763        IsPrefEnabled(prefs::kSafeBrowsingExtendedReportingEnabled));
764  }
765}
766
767void SafeBrowsingBlockingPage::PopulateHarmfulLoadTimeData(
768    base::DictionaryValue* load_time_data) {
769  load_time_data->SetBoolean("phishing", false);
770  load_time_data->SetString(
771      "heading", l10n_util::GetStringUTF16(IDS_HARMFUL_V3_HEADING));
772  load_time_data->SetString(
773      "primaryParagraph",
774      l10n_util::GetStringFUTF16(
775          IDS_HARMFUL_V3_PRIMARY_PARAGRAPH,
776          base::UTF8ToUTF16(url_.host())));
777  load_time_data->SetString(
778      "explanationParagraph",
779      l10n_util::GetStringFUTF16(
780          IDS_HARMFUL_V3_EXPLANATION_PARAGRAPH,
781          base::UTF8ToUTF16(url_.host())));
782  load_time_data->SetString(
783      "finalParagraph",
784      l10n_util::GetStringUTF16(IDS_HARMFUL_V3_PROCEED_PARAGRAPH));
785
786  load_time_data->SetBoolean(kDisplayCheckBox, CanShowMalwareDetailsOption());
787  if (CanShowMalwareDetailsOption()) {
788    std::string privacy_link = base::StringPrintf(
789        kPrivacyLinkHtml,
790        l10n_util::GetStringUTF8(
791            IDS_SAFE_BROWSING_PRIVACY_POLICY_PAGE).c_str());
792    load_time_data->SetString(
793        "optInLink",
794        l10n_util::GetStringFUTF16(IDS_SAFE_BROWSING_MALWARE_REPORTING_AGREE,
795                                   base::UTF8ToUTF16(privacy_link)));
796    load_time_data->SetBoolean(
797        kBoxChecked,
798        IsPrefEnabled(prefs::kSafeBrowsingExtendedReportingEnabled));
799  }
800}
801
802void SafeBrowsingBlockingPage::PopulatePhishingLoadTimeData(
803    base::DictionaryValue* load_time_data) {
804  load_time_data->SetBoolean("phishing", true);
805  load_time_data->SetString(
806      "heading",
807      l10n_util::GetStringUTF16(IDS_PHISHING_V3_HEADING));
808  load_time_data->SetString(
809      "primaryParagraph",
810      l10n_util::GetStringFUTF16(
811          IDS_PHISHING_V3_PRIMARY_PARAGRAPH,
812          base::UTF8ToUTF16(url_.host())));
813  load_time_data->SetString(
814      "explanationParagraph",
815      l10n_util::GetStringFUTF16(IDS_PHISHING_V3_EXPLANATION_PARAGRAPH,
816                                 base::UTF8ToUTF16(url_.host())));
817  load_time_data->SetString(
818      "finalParagraph",
819      l10n_util::GetStringUTF16(IDS_PHISHING_V3_PROCEED_PARAGRAPH));
820}
821