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.
5 #include "chrome/browser/safe_browsing/client_side_detection_host.h"
9 #include "base/logging.h"
10 #include "base/memory/ref_counted.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/metrics/histogram.h"
13 #include "base/prefs/pref_service.h"
14 #include "base/sequenced_task_runner_helpers.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "chrome/browser/browser_process.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/browser/safe_browsing/browser_feature_extractor.h"
19 #include "chrome/browser/safe_browsing/client_side_detection_service.h"
20 #include "chrome/browser/safe_browsing/database_manager.h"
21 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
22 #include "chrome/common/chrome_switches.h"
23 #include "chrome/common/chrome_version_info.h"
24 #include "chrome/common/pref_names.h"
25 #include "chrome/common/safe_browsing/csd.pb.h"
26 #include "chrome/common/safe_browsing/safebrowsing_messages.h"
27 #include "content/public/browser/browser_thread.h"
28 #include "content/public/browser/navigation_controller.h"
29 #include "content/public/browser/navigation_details.h"
30 #include "content/public/browser/navigation_entry.h"
31 #include "content/public/browser/notification_details.h"
32 #include "content/public/browser/notification_source.h"
33 #include "content/public/browser/notification_types.h"
34 #include "content/public/browser/render_process_host.h"
35 #include "content/public/browser/render_view_host.h"
36 #include "content/public/browser/resource_request_details.h"
37 #include "content/public/browser/web_contents.h"
38 #include "content/public/common/frame_navigate_params.h"
41 using content::BrowserThread
;
42 using content::NavigationEntry
;
43 using content::ResourceRequestDetails
;
44 using content::WebContents
;
46 namespace safe_browsing
{
48 const int ClientSideDetectionHost::kMaxUrlsPerIP
= 20;
49 const int ClientSideDetectionHost::kMaxIPsPerBrowse
= 200;
51 const char kSafeBrowsingMatchKey
[] = "safe_browsing_match";
53 // This class is instantiated each time a new toplevel URL loads, and
54 // asynchronously checks whether the phishing classifier should run for this
55 // URL. If so, it notifies the renderer with a StartPhishingDetection IPC.
56 // Objects of this class are ref-counted and will be destroyed once nobody
57 // uses it anymore. If |web_contents|, |csd_service| or |host| go away you need
58 // to call Cancel(). We keep the |database_manager| alive in a ref pointer for
59 // as long as it takes.
60 class ClientSideDetectionHost::ShouldClassifyUrlRequest
61 : public base::RefCountedThreadSafe
<
62 ClientSideDetectionHost::ShouldClassifyUrlRequest
> {
64 ShouldClassifyUrlRequest(const content::FrameNavigateParams
& params
,
65 WebContents
* web_contents
,
66 ClientSideDetectionService
* csd_service
,
67 SafeBrowsingDatabaseManager
* database_manager
,
68 ClientSideDetectionHost
* host
)
71 web_contents_(web_contents
),
72 csd_service_(csd_service
),
73 database_manager_(database_manager
),
75 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
76 DCHECK(web_contents_
);
78 DCHECK(database_manager_
.get());
83 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
85 // We start by doing some simple checks that can run on the UI thread.
86 UMA_HISTOGRAM_COUNTS("SBClientPhishing.ClassificationStart", 1);
88 // Only classify [X]HTML documents.
89 if (params_
.contents_mime_type
!= "text/html" &&
90 params_
.contents_mime_type
!= "application/xhtml+xml") {
91 VLOG(1) << "Skipping phishing classification for URL: " << params_
.url
92 << " because it has an unsupported MIME type: "
93 << params_
.contents_mime_type
;
94 UMA_HISTOGRAM_ENUMERATION("SBClientPhishing.PreClassificationCheckFail",
95 NO_CLASSIFY_UNSUPPORTED_MIME_TYPE
,
100 if (csd_service_
->IsPrivateIPAddress(params_
.socket_address
.host())) {
101 VLOG(1) << "Skipping phishing classification for URL: " << params_
.url
102 << " because of hosting on private IP: "
103 << params_
.socket_address
.host();
104 UMA_HISTOGRAM_ENUMERATION("SBClientPhishing.PreClassificationCheckFail",
105 NO_CLASSIFY_PRIVATE_IP
,
110 // Don't run the phishing classifier if the tab is incognito.
111 if (web_contents_
->GetBrowserContext()->IsOffTheRecord()) {
112 VLOG(1) << "Skipping phishing classification for URL: " << params_
.url
113 << " because we're browsing incognito.";
114 UMA_HISTOGRAM_ENUMERATION("SBClientPhishing.PreClassificationCheckFail",
115 NO_CLASSIFY_OFF_THE_RECORD
,
121 // We lookup the csd-whitelist before we lookup the cache because
122 // a URL may have recently been whitelisted. If the URL matches
123 // the csd-whitelist we won't start classification. The
124 // csd-whitelist check has to be done on the IO thread because it
125 // uses the SafeBrowsing service class.
126 BrowserThread::PostTask(
129 base::Bind(&ShouldClassifyUrlRequest::CheckCsdWhitelist
,
135 // Just to make sure we don't do anything stupid we reset all these
136 // pointers except for the safebrowsing service class which may be
137 // accessed by CheckCsdWhitelist().
138 web_contents_
= NULL
;
144 friend class base::RefCountedThreadSafe
<
145 ClientSideDetectionHost::ShouldClassifyUrlRequest
>;
147 // Enum used to keep stats about why the pre-classification check failed.
148 enum PreClassificationCheckFailures
{
149 OBSOLETE_NO_CLASSIFY_PROXY_FETCH
,
150 NO_CLASSIFY_PRIVATE_IP
,
151 NO_CLASSIFY_OFF_THE_RECORD
,
152 NO_CLASSIFY_MATCH_CSD_WHITELIST
,
153 NO_CLASSIFY_TOO_MANY_REPORTS
,
154 NO_CLASSIFY_UNSUPPORTED_MIME_TYPE
,
156 NO_CLASSIFY_MAX
// Always add new values before this one.
159 // The destructor can be called either from the UI or the IO thread.
160 virtual ~ShouldClassifyUrlRequest() { }
162 void CheckCsdWhitelist(const GURL
& url
) {
163 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
164 if (!database_manager_
.get() ||
165 database_manager_
->MatchCsdWhitelistUrl(url
)) {
166 // We're done. There is no point in going back to the UI thread.
167 VLOG(1) << "Skipping phishing classification for URL: " << url
168 << " because it matches the csd whitelist";
169 UMA_HISTOGRAM_ENUMERATION("SBClientPhishing.PreClassificationCheckFail",
170 NO_CLASSIFY_MATCH_CSD_WHITELIST
,
175 bool malware_killswitch_on
= database_manager_
->IsMalwareKillSwitchOn();
177 BrowserThread::PostTask(
180 base::Bind(&ShouldClassifyUrlRequest::CheckCache
, this,
181 malware_killswitch_on
));
184 void CheckCache(bool malware_killswitch_on
) {
185 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
190 host_
->SetMalwareKillSwitch(malware_killswitch_on
);
191 // If result is cached, we don't want to run classification again
193 if (csd_service_
->GetValidCachedResult(params_
.url
, &is_phishing
)) {
194 VLOG(1) << "Satisfying request for " << params_
.url
<< " from cache";
195 UMA_HISTOGRAM_COUNTS("SBClientPhishing.RequestSatisfiedFromCache", 1);
196 // Since we are already on the UI thread, this is safe.
197 host_
->MaybeShowPhishingWarning(params_
.url
, is_phishing
);
201 // We want to limit the number of requests, though we will ignore the
202 // limit for urls in the cache. We don't want to start classifying
203 // too many pages as phishing, but for those that we already think are
204 // phishing we want to give ourselves a chance to fix false positives.
205 if (csd_service_
->IsInCache(params_
.url
)) {
206 VLOG(1) << "Reporting limit skipped for " << params_
.url
207 << " as it was in the cache.";
208 UMA_HISTOGRAM_COUNTS("SBClientPhishing.ReportLimitSkipped", 1);
209 } else if (csd_service_
->OverPhishingReportLimit()) {
210 VLOG(1) << "Too many report phishing requests sent recently, "
211 << "not running classification for " << params_
.url
;
212 UMA_HISTOGRAM_ENUMERATION("SBClientPhishing.PreClassificationCheckFail",
213 NO_CLASSIFY_TOO_MANY_REPORTS
,
218 // Everything checks out, so start classification.
219 // |web_contents_| is safe to call as we will be destructed
221 VLOG(1) << "Instruct renderer to start phishing detection for URL: "
223 content::RenderViewHost
* rvh
= web_contents_
->GetRenderViewHost();
224 rvh
->Send(new SafeBrowsingMsg_StartPhishingDetection(
225 rvh
->GetRoutingID(), params_
.url
));
228 // No need to protect |canceled_| with a lock because it is only read and
229 // written by the UI thread.
231 content::FrameNavigateParams params_
;
232 WebContents
* web_contents_
;
233 ClientSideDetectionService
* csd_service_
;
234 // We keep a ref pointer here just to make sure the safe browsing
235 // database manager stays alive long enough.
236 scoped_refptr
<SafeBrowsingDatabaseManager
> database_manager_
;
237 ClientSideDetectionHost
* host_
;
239 DISALLOW_COPY_AND_ASSIGN(ShouldClassifyUrlRequest
);
243 ClientSideDetectionHost
* ClientSideDetectionHost::Create(
245 return new ClientSideDetectionHost(tab
);
248 ClientSideDetectionHost::ClientSideDetectionHost(WebContents
* tab
)
249 : content::WebContentsObserver(tab
),
252 unsafe_unique_page_id_(-1),
253 malware_killswitch_on_(false),
254 malware_report_enabled_(false) {
256 // Note: csd_service_ and sb_service will be NULL here in testing.
257 csd_service_
= g_browser_process
->safe_browsing_detection_service();
258 feature_extractor_
.reset(new BrowserFeatureExtractor(tab
, this));
259 registrar_
.Add(this, content::NOTIFICATION_RESOURCE_RESPONSE_STARTED
,
260 content::Source
<WebContents
>(tab
));
262 scoped_refptr
<SafeBrowsingService
> sb_service
=
263 g_browser_process
->safe_browsing_service();
264 if (sb_service
.get()) {
265 ui_manager_
= sb_service
->ui_manager();
266 database_manager_
= sb_service
->database_manager();
267 ui_manager_
->AddObserver(this);
270 // Only enable the malware bad IP matching and report feature for canary
272 chrome::VersionInfo::Channel channel
= chrome::VersionInfo::GetChannel();
273 malware_report_enabled_
= (
274 channel
== chrome::VersionInfo::CHANNEL_DEV
||
275 channel
== chrome::VersionInfo::CHANNEL_CANARY
);
278 ClientSideDetectionHost::~ClientSideDetectionHost() {
279 if (ui_manager_
.get())
280 ui_manager_
->RemoveObserver(this);
283 bool ClientSideDetectionHost::OnMessageReceived(const IPC::Message
& message
) {
285 IPC_BEGIN_MESSAGE_MAP(ClientSideDetectionHost
, message
)
286 IPC_MESSAGE_HANDLER(SafeBrowsingHostMsg_PhishingDetectionDone
,
287 OnPhishingDetectionDone
)
288 IPC_MESSAGE_UNHANDLED(handled
= false)
289 IPC_END_MESSAGE_MAP()
293 void ClientSideDetectionHost::DidNavigateMainFrame(
294 const content::LoadCommittedDetails
& details
,
295 const content::FrameNavigateParams
& params
) {
296 // TODO(noelutz): move this DCHECK to WebContents and fix all the unit tests
297 // that don't call this method on the UI thread.
298 // DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
299 if (details
.is_in_page
) {
300 // If the navigation is within the same page, the user isn't really
301 // navigating away. We don't need to cancel a pending callback or
302 // begin a new classification.
305 // If we navigate away and there currently is a pending phishing
306 // report request we have to cancel it to make sure we don't display
307 // an interstitial for the wrong page. Note that this won't cancel
308 // the server ping back but only cancel the showing of the
310 weak_factory_
.InvalidateWeakPtrs();
316 // Cancel any pending classification request.
317 if (classification_request_
.get()) {
318 classification_request_
->Cancel();
320 browse_info_
.reset(new BrowseInfo
);
322 // Store redirect chain information.
323 if (params
.url
.host() != cur_host_
) {
324 cur_host_
= params
.url
.host();
325 cur_host_redirects_
= params
.redirects
;
327 browse_info_
->host_redirects
= cur_host_redirects_
;
328 browse_info_
->url_redirects
= params
.redirects
;
329 browse_info_
->referrer
= params
.referrer
.url
;
330 browse_info_
->http_status_code
= details
.http_status_code
;
332 // Notify the renderer if it should classify this URL.
333 classification_request_
= new ShouldClassifyUrlRequest(
334 params
, web_contents(), csd_service_
, database_manager_
.get(), this);
335 classification_request_
->Start();
338 void ClientSideDetectionHost::OnSafeBrowsingHit(
339 const SafeBrowsingUIManager::UnsafeResource
& resource
) {
340 if (!web_contents() || !web_contents()->GetController().GetActiveEntry())
343 // Check that the hit is either malware or phishing.
344 if (resource
.threat_type
!= SB_THREAT_TYPE_URL_PHISHING
&&
345 resource
.threat_type
!= SB_THREAT_TYPE_URL_MALWARE
)
348 // Check that this notification is really for us.
349 content::RenderViewHost
* hit_rvh
= content::RenderViewHost::FromID(
350 resource
.render_process_host_id
, resource
.render_view_id
);
352 web_contents() != content::WebContents::FromRenderViewHost(hit_rvh
))
355 // Store the unique page ID for later.
356 unsafe_unique_page_id_
=
357 web_contents()->GetController().GetActiveEntry()->GetUniqueID();
359 // We also keep the resource around in order to be able to send the
360 // malicious URL to the server.
361 unsafe_resource_
.reset(new SafeBrowsingUIManager::UnsafeResource(resource
));
362 unsafe_resource_
->callback
.Reset(); // Don't do anything stupid.
365 void ClientSideDetectionHost::OnSafeBrowsingMatch(
366 const SafeBrowsingUIManager::UnsafeResource
& resource
) {
367 if (!web_contents() || !web_contents()->GetController().GetActiveEntry())
370 // Check that this notification is really for us.
371 content::RenderViewHost
* hit_rvh
= content::RenderViewHost::FromID(
372 resource
.render_process_host_id
, resource
.render_view_id
);
374 web_contents() != content::WebContents::FromRenderViewHost(hit_rvh
))
377 web_contents()->GetController().GetActiveEntry()->SetExtraData(
378 kSafeBrowsingMatchKey
, base::ASCIIToUTF16("1"));
381 scoped_refptr
<SafeBrowsingDatabaseManager
>
382 ClientSideDetectionHost::database_manager() {
383 return database_manager_
;
386 bool ClientSideDetectionHost::DidPageReceiveSafeBrowsingMatch() const {
387 if (!web_contents() || !web_contents()->GetController().GetVisibleEntry())
390 // If an interstitial page is showing, GetVisibleEntry will return the
391 // transient NavigationEntry for the interstitial. The transient entry
392 // will not have the flag set, so use the pending entry instead if there
394 NavigationEntry
* entry
= web_contents()->GetController().GetPendingEntry();
396 entry
= web_contents()->GetController().GetVisibleEntry();
397 if (entry
->GetPageType() == content::PAGE_TYPE_INTERSTITIAL
)
398 entry
= web_contents()->GetController().GetLastCommittedEntry();
403 base::string16 value
;
404 return entry
->GetExtraData(kSafeBrowsingMatchKey
, &value
);
407 void ClientSideDetectionHost::WebContentsDestroyed(WebContents
* tab
) {
409 // Tell any pending classification request that it is being canceled.
410 if (classification_request_
.get()) {
411 classification_request_
->Cancel();
413 // Cancel all pending feature extractions.
414 feature_extractor_
.reset();
417 void ClientSideDetectionHost::OnPhishingDetectionDone(
418 const std::string
& verdict_str
) {
419 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
420 // There is something seriously wrong if there is no service class but
421 // this method is called. The renderer should not start phishing detection
422 // if there isn't any service class in the browser.
423 DCHECK(csd_service_
);
424 // There shouldn't be any pending requests because we revoke them everytime
426 DCHECK(!weak_factory_
.HasWeakPtrs());
427 DCHECK(browse_info_
.get());
429 // We parse the protocol buffer here. If we're unable to parse it we won't
430 // send the verdict further.
431 scoped_ptr
<ClientPhishingRequest
> verdict(new ClientPhishingRequest
);
433 !weak_factory_
.HasWeakPtrs() &&
434 browse_info_
.get() &&
435 verdict
->ParseFromString(verdict_str
) &&
436 verdict
->IsInitialized()) {
437 // We do the malware IP matching and request sending if the feature
439 if (malware_report_enabled_
&& !MalwareKillSwitchIsOn()) {
440 scoped_ptr
<ClientMalwareRequest
> malware_verdict(
441 new ClientMalwareRequest
);
442 // Start browser-side malware feature extraction. Once we're done it will
443 // send the malware client verdict request.
444 malware_verdict
->set_url(verdict
->url());
445 const GURL
& referrer
= browse_info_
->referrer
;
446 if (referrer
.SchemeIs("http")) { // Only send http urls.
447 malware_verdict
->set_referrer_url(referrer
.spec());
449 // This function doesn't expect browse_info_ to stay around after this
451 feature_extractor_
->ExtractMalwareFeatures(
453 malware_verdict
.release(),
454 base::Bind(&ClientSideDetectionHost::MalwareFeatureExtractionDone
,
455 weak_factory_
.GetWeakPtr()));
458 // We only send phishing verdict to the server if the verdict is phishing or
459 // if a SafeBrowsing interstitial was already shown for this site. E.g., a
460 // malware or phishing interstitial was shown but the user clicked
462 if (verdict
->is_phishing() || DidShowSBInterstitial()) {
463 if (DidShowSBInterstitial()) {
464 browse_info_
->unsafe_resource
.reset(unsafe_resource_
.release());
466 // Start browser-side feature extraction. Once we're done it will send
467 // the client verdict request.
468 feature_extractor_
->ExtractFeatures(
471 base::Bind(&ClientSideDetectionHost::FeatureExtractionDone
,
472 weak_factory_
.GetWeakPtr()));
475 browse_info_
.reset();
478 void ClientSideDetectionHost::MaybeShowPhishingWarning(GURL phishing_url
,
480 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
481 VLOG(2) << "Received server phishing verdict for URL:" << phishing_url
482 << " is_phishing:" << is_phishing
;
484 DCHECK(web_contents());
485 if (ui_manager_
.get()) {
486 SafeBrowsingUIManager::UnsafeResource resource
;
487 resource
.url
= phishing_url
;
488 resource
.original_url
= phishing_url
;
489 resource
.is_subresource
= false;
490 resource
.threat_type
= SB_THREAT_TYPE_CLIENT_SIDE_PHISHING_URL
;
491 resource
.render_process_host_id
=
492 web_contents()->GetRenderProcessHost()->GetID();
493 resource
.render_view_id
=
494 web_contents()->GetRenderViewHost()->GetRoutingID();
495 if (!ui_manager_
->IsWhitelisted(resource
)) {
496 // We need to stop any pending navigations, otherwise the interstital
497 // might not get created properly.
498 web_contents()->GetController().DiscardNonCommittedEntries();
500 ui_manager_
->DisplayBlockingPage(resource
);
502 // If there is true phishing verdict, invalidate weakptr so that no longer
503 // consider the malware vedict.
504 weak_factory_
.InvalidateWeakPtrs();
508 void ClientSideDetectionHost::MaybeShowMalwareWarning(GURL original_url
,
511 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
512 VLOG(2) << "Received server malawre IP verdict for URL:" << malware_url
513 << " is_malware:" << is_malware
;
514 if (is_malware
&& malware_url
.is_valid() && original_url
.is_valid()) {
515 DCHECK(web_contents());
516 if (ui_manager_
.get()) {
517 SafeBrowsingUIManager::UnsafeResource resource
;
518 resource
.url
= malware_url
;
519 resource
.original_url
= original_url
;
520 resource
.is_subresource
= (malware_url
.host() != original_url
.host());
521 resource
.threat_type
= SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL
;
522 resource
.render_process_host_id
=
523 web_contents()->GetRenderProcessHost()->GetID();
524 resource
.render_view_id
=
525 web_contents()->GetRenderViewHost()->GetRoutingID();
526 if (!ui_manager_
->IsWhitelisted(resource
)) {
527 // We need to stop any pending navigations, otherwise the interstital
528 // might not get created properly.
529 web_contents()->GetController().DiscardNonCommittedEntries();
531 ui_manager_
->DisplayBlockingPage(resource
);
533 // If there is true malware verdict, invalidate weakptr so that no longer
534 // consider the phishing vedict.
535 weak_factory_
.InvalidateWeakPtrs();
539 void ClientSideDetectionHost::FeatureExtractionDone(
541 ClientPhishingRequest
* request
) {
543 VLOG(2) << "Feature extraction done (success:" << success
<< ") for URL: "
544 << request
->url() << ". Start sending client phishing request.";
545 ClientSideDetectionService::ClientReportPhishingRequestCallback callback
;
546 // If the client-side verdict isn't phishing we don't care about the server
547 // response because we aren't going to display a warning.
548 if (request
->is_phishing()) {
549 callback
= base::Bind(&ClientSideDetectionHost::MaybeShowPhishingWarning
,
550 weak_factory_
.GetWeakPtr());
552 // Send ping even if the browser feature extraction failed.
553 csd_service_
->SendClientReportPhishingRequest(
554 request
, // The service takes ownership of the request object.
558 void ClientSideDetectionHost::MalwareFeatureExtractionDone(
559 bool feature_extraction_success
,
560 scoped_ptr
<ClientMalwareRequest
> request
) {
561 DCHECK(request
.get());
562 VLOG(2) << "Malware Feature extraction done for URL: " << request
->url()
563 << ", with badip url count:" << request
->bad_ip_url_info_size();
565 // Send ping if there is matching features.
566 if (feature_extraction_success
&& request
->bad_ip_url_info_size() > 0) {
567 VLOG(1) << "Start sending client malware request.";
568 ClientSideDetectionService::ClientReportMalwareRequestCallback callback
;
569 callback
= base::Bind(&ClientSideDetectionHost::MaybeShowMalwareWarning
,
570 weak_factory_
.GetWeakPtr());
571 csd_service_
->SendClientReportMalwareRequest(request
.release(), callback
);
575 void ClientSideDetectionHost::UpdateIPUrlMap(
576 const std::string
& ip
,
577 const std::string
& url
,
578 const std::string
& method
,
579 const std::string
& referrer
,
580 const ResourceType::Type resource_type
) {
581 if (ip
.empty() || url
.empty())
584 IPUrlMap::iterator it
= browse_info_
->ips
.find(ip
);
585 if (it
== browse_info_
->ips
.end()) {
586 if (int(browse_info_
->ips
.size()) < kMaxIPsPerBrowse
) {
587 std::vector
<IPUrlInfo
> url_infos
;
588 url_infos
.push_back(IPUrlInfo(url
, method
, referrer
, resource_type
));
589 browse_info_
->ips
.insert(make_pair(ip
, url_infos
));
591 } else if (int(it
->second
.size()) < kMaxUrlsPerIP
) {
592 it
->second
.push_back(IPUrlInfo(url
, method
, referrer
, resource_type
));
596 void ClientSideDetectionHost::Observe(
598 const content::NotificationSource
& source
,
599 const content::NotificationDetails
& details
) {
600 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
601 DCHECK_EQ(type
, content::NOTIFICATION_RESOURCE_RESPONSE_STARTED
);
602 const ResourceRequestDetails
* req
= content::Details
<ResourceRequestDetails
>(
604 if (req
&& browse_info_
.get() && malware_report_enabled_
&&
605 !MalwareKillSwitchIsOn()) {
606 if (req
->url
.is_valid()) {
607 UpdateIPUrlMap(req
->socket_address
.host() /* ip */,
608 req
->url
.spec() /* url */,
616 bool ClientSideDetectionHost::DidShowSBInterstitial() const {
617 if (unsafe_unique_page_id_
<= 0 || !web_contents()) {
620 const NavigationEntry
* nav_entry
=
621 web_contents()->GetController().GetActiveEntry();
622 return (nav_entry
&& nav_entry
->GetUniqueID() == unsafe_unique_page_id_
);
625 void ClientSideDetectionHost::set_client_side_detection_service(
626 ClientSideDetectionService
* service
) {
627 csd_service_
= service
;
630 void ClientSideDetectionHost::set_safe_browsing_managers(
631 SafeBrowsingUIManager
* ui_manager
,
632 SafeBrowsingDatabaseManager
* database_manager
) {
633 if (ui_manager_
.get())
634 ui_manager_
->RemoveObserver(this);
636 ui_manager_
= ui_manager
;
638 ui_manager_
->AddObserver(this);
640 database_manager_
= database_manager
;
643 bool ClientSideDetectionHost::MalwareKillSwitchIsOn() {
644 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
645 return malware_killswitch_on_
;
648 void ClientSideDetectionHost::SetMalwareKillSwitch(bool killswitch_on
) {
649 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
650 malware_killswitch_on_
= killswitch_on
;
653 } // namespace safe_browsing