Fix ClientSideDetectionHost test of if a safebrowsing hit matches its WebContents.
[chromium-blink-merge.git] / chrome / browser / safe_browsing / client_side_detection_host.cc
blob5e20945e8d72e1e6dc9c7ea3e71671c15aa035bd
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"
7 #include <vector>
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 "chrome/browser/browser_process.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/browser/safe_browsing/browser_feature_extractor.h"
18 #include "chrome/browser/safe_browsing/client_side_detection_service.h"
19 #include "chrome/browser/safe_browsing/database_manager.h"
20 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
21 #include "chrome/common/chrome_switches.h"
22 #include "chrome/common/chrome_version_info.h"
23 #include "chrome/common/pref_names.h"
24 #include "chrome/common/safe_browsing/csd.pb.h"
25 #include "chrome/common/safe_browsing/safebrowsing_messages.h"
26 #include "content/public/browser/browser_thread.h"
27 #include "content/public/browser/navigation_controller.h"
28 #include "content/public/browser/navigation_details.h"
29 #include "content/public/browser/navigation_entry.h"
30 #include "content/public/browser/notification_details.h"
31 #include "content/public/browser/notification_source.h"
32 #include "content/public/browser/notification_types.h"
33 #include "content/public/browser/render_process_host.h"
34 #include "content/public/browser/render_view_host.h"
35 #include "content/public/browser/resource_request_details.h"
36 #include "content/public/browser/web_contents.h"
37 #include "content/public/common/frame_navigate_params.h"
38 #include "url/gurl.h"
40 using content::BrowserThread;
41 using content::NavigationEntry;
42 using content::ResourceRequestDetails;
43 using content::WebContents;
45 namespace safe_browsing {
47 const int ClientSideDetectionHost::kMaxUrlsPerIP = 20;
48 const int ClientSideDetectionHost::kMaxIPsPerBrowse = 200;
50 namespace {
52 void EmptyUrlCheckCallback(bool processed) {
53 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
56 } // namespace
58 // This class is instantiated each time a new toplevel URL loads, and
59 // asynchronously checks whether the phishing classifier should run for this
60 // URL. If so, it notifies the renderer with a StartPhishingDetection IPC.
61 // Objects of this class are ref-counted and will be destroyed once nobody
62 // uses it anymore. If |web_contents|, |csd_service| or |host| go away you need
63 // to call Cancel(). We keep the |database_manager| alive in a ref pointer for
64 // as long as it takes.
65 class ClientSideDetectionHost::ShouldClassifyUrlRequest
66 : public base::RefCountedThreadSafe<
67 ClientSideDetectionHost::ShouldClassifyUrlRequest> {
68 public:
69 ShouldClassifyUrlRequest(const content::FrameNavigateParams& params,
70 WebContents* web_contents,
71 ClientSideDetectionService* csd_service,
72 SafeBrowsingDatabaseManager* database_manager,
73 ClientSideDetectionHost* host)
74 : canceled_(false),
75 params_(params),
76 web_contents_(web_contents),
77 csd_service_(csd_service),
78 database_manager_(database_manager),
79 host_(host) {
80 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
81 DCHECK(web_contents_);
82 DCHECK(csd_service_);
83 DCHECK(database_manager_.get());
84 DCHECK(host_);
87 void Start() {
88 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
90 // We start by doing some simple checks that can run on the UI thread.
91 UMA_HISTOGRAM_COUNTS("SBClientPhishing.ClassificationStart", 1);
93 // Only classify [X]HTML documents.
94 if (params_.contents_mime_type != "text/html" &&
95 params_.contents_mime_type != "application/xhtml+xml") {
96 VLOG(1) << "Skipping phishing classification for URL: " << params_.url
97 << " because it has an unsupported MIME type: "
98 << params_.contents_mime_type;
99 UMA_HISTOGRAM_ENUMERATION("SBClientPhishing.PreClassificationCheckFail",
100 NO_CLASSIFY_UNSUPPORTED_MIME_TYPE,
101 NO_CLASSIFY_MAX);
102 return;
105 if (csd_service_->IsPrivateIPAddress(params_.socket_address.host())) {
106 VLOG(1) << "Skipping phishing classification for URL: " << params_.url
107 << " because of hosting on private IP: "
108 << params_.socket_address.host();
109 UMA_HISTOGRAM_ENUMERATION("SBClientPhishing.PreClassificationCheckFail",
110 NO_CLASSIFY_PRIVATE_IP,
111 NO_CLASSIFY_MAX);
112 return;
115 // Don't run the phishing classifier if the tab is incognito.
116 if (web_contents_->GetBrowserContext()->IsOffTheRecord()) {
117 VLOG(1) << "Skipping phishing classification for URL: " << params_.url
118 << " because we're browsing incognito.";
119 UMA_HISTOGRAM_ENUMERATION("SBClientPhishing.PreClassificationCheckFail",
120 NO_CLASSIFY_OFF_THE_RECORD,
121 NO_CLASSIFY_MAX);
123 return;
126 // We lookup the csd-whitelist before we lookup the cache because
127 // a URL may have recently been whitelisted. If the URL matches
128 // the csd-whitelist we won't start classification. The
129 // csd-whitelist check has to be done on the IO thread because it
130 // uses the SafeBrowsing service class.
131 BrowserThread::PostTask(
132 BrowserThread::IO,
133 FROM_HERE,
134 base::Bind(&ShouldClassifyUrlRequest::CheckCsdWhitelist,
135 this, params_.url));
138 void Cancel() {
139 canceled_ = true;
140 // Just to make sure we don't do anything stupid we reset all these
141 // pointers except for the safebrowsing service class which may be
142 // accessed by CheckCsdWhitelist().
143 web_contents_ = NULL;
144 csd_service_ = NULL;
145 host_ = NULL;
148 private:
149 friend class base::RefCountedThreadSafe<
150 ClientSideDetectionHost::ShouldClassifyUrlRequest>;
152 // Enum used to keep stats about why the pre-classification check failed.
153 enum PreClassificationCheckFailures {
154 OBSOLETE_NO_CLASSIFY_PROXY_FETCH,
155 NO_CLASSIFY_PRIVATE_IP,
156 NO_CLASSIFY_OFF_THE_RECORD,
157 NO_CLASSIFY_MATCH_CSD_WHITELIST,
158 NO_CLASSIFY_TOO_MANY_REPORTS,
159 NO_CLASSIFY_UNSUPPORTED_MIME_TYPE,
161 NO_CLASSIFY_MAX // Always add new values before this one.
164 // The destructor can be called either from the UI or the IO thread.
165 virtual ~ShouldClassifyUrlRequest() { }
167 void CheckCsdWhitelist(const GURL& url) {
168 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
169 if (!database_manager_.get() ||
170 database_manager_->MatchCsdWhitelistUrl(url)) {
171 // We're done. There is no point in going back to the UI thread.
172 VLOG(1) << "Skipping phishing classification for URL: " << url
173 << " because it matches the csd whitelist";
174 UMA_HISTOGRAM_ENUMERATION("SBClientPhishing.PreClassificationCheckFail",
175 NO_CLASSIFY_MATCH_CSD_WHITELIST,
176 NO_CLASSIFY_MAX);
177 return;
180 bool malware_killswitch_on = database_manager_->IsMalwareKillSwitchOn();
182 BrowserThread::PostTask(
183 BrowserThread::UI,
184 FROM_HERE,
185 base::Bind(&ShouldClassifyUrlRequest::CheckCache, this,
186 malware_killswitch_on));
189 void CheckCache(bool malware_killswitch_on) {
190 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
191 if (canceled_) {
192 return;
195 host_->SetMalwareKillSwitch(malware_killswitch_on);
196 // If result is cached, we don't want to run classification again
197 bool is_phishing;
198 if (csd_service_->GetValidCachedResult(params_.url, &is_phishing)) {
199 VLOG(1) << "Satisfying request for " << params_.url << " from cache";
200 UMA_HISTOGRAM_COUNTS("SBClientPhishing.RequestSatisfiedFromCache", 1);
201 // Since we are already on the UI thread, this is safe.
202 host_->MaybeShowPhishingWarning(params_.url, is_phishing);
203 return;
206 // We want to limit the number of requests, though we will ignore the
207 // limit for urls in the cache. We don't want to start classifying
208 // too many pages as phishing, but for those that we already think are
209 // phishing we want to give ourselves a chance to fix false positives.
210 if (csd_service_->IsInCache(params_.url)) {
211 VLOG(1) << "Reporting limit skipped for " << params_.url
212 << " as it was in the cache.";
213 UMA_HISTOGRAM_COUNTS("SBClientPhishing.ReportLimitSkipped", 1);
214 } else if (csd_service_->OverPhishingReportLimit()) {
215 VLOG(1) << "Too many report phishing requests sent recently, "
216 << "not running classification for " << params_.url;
217 UMA_HISTOGRAM_ENUMERATION("SBClientPhishing.PreClassificationCheckFail",
218 NO_CLASSIFY_TOO_MANY_REPORTS,
219 NO_CLASSIFY_MAX);
220 return;
223 // Everything checks out, so start classification.
224 // |web_contents_| is safe to call as we will be destructed
225 // before it is.
226 VLOG(1) << "Instruct renderer to start phishing detection for URL: "
227 << params_.url;
228 content::RenderViewHost* rvh = web_contents_->GetRenderViewHost();
229 rvh->Send(new SafeBrowsingMsg_StartPhishingDetection(
230 rvh->GetRoutingID(), params_.url));
233 // No need to protect |canceled_| with a lock because it is only read and
234 // written by the UI thread.
235 bool canceled_;
236 content::FrameNavigateParams params_;
237 WebContents* web_contents_;
238 ClientSideDetectionService* csd_service_;
239 // We keep a ref pointer here just to make sure the safe browsing
240 // database manager stays alive long enough.
241 scoped_refptr<SafeBrowsingDatabaseManager> database_manager_;
242 ClientSideDetectionHost* host_;
244 DISALLOW_COPY_AND_ASSIGN(ShouldClassifyUrlRequest);
247 // static
248 ClientSideDetectionHost* ClientSideDetectionHost::Create(
249 WebContents* tab) {
250 return new ClientSideDetectionHost(tab);
253 ClientSideDetectionHost::ClientSideDetectionHost(WebContents* tab)
254 : content::WebContentsObserver(tab),
255 csd_service_(NULL),
256 weak_factory_(this),
257 unsafe_unique_page_id_(-1),
258 malware_killswitch_on_(false),
259 malware_report_enabled_(false) {
260 DCHECK(tab);
261 // Note: csd_service_ and sb_service will be NULL here in testing.
262 csd_service_ = g_browser_process->safe_browsing_detection_service();
263 feature_extractor_.reset(new BrowserFeatureExtractor(tab, this));
264 registrar_.Add(this, content::NOTIFICATION_RESOURCE_RESPONSE_STARTED,
265 content::Source<WebContents>(tab));
267 scoped_refptr<SafeBrowsingService> sb_service =
268 g_browser_process->safe_browsing_service();
269 if (sb_service.get()) {
270 ui_manager_ = sb_service->ui_manager();
271 database_manager_ = sb_service->database_manager();
272 ui_manager_->AddObserver(this);
275 // Only enable the malware bad IP matching and report feature for canary
276 // and dev channel.
277 chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel();
278 malware_report_enabled_ = (
279 channel == chrome::VersionInfo::CHANNEL_DEV ||
280 channel == chrome::VersionInfo::CHANNEL_CANARY);
283 ClientSideDetectionHost::~ClientSideDetectionHost() {
284 if (ui_manager_.get())
285 ui_manager_->RemoveObserver(this);
288 bool ClientSideDetectionHost::OnMessageReceived(const IPC::Message& message) {
289 bool handled = true;
290 IPC_BEGIN_MESSAGE_MAP(ClientSideDetectionHost, message)
291 IPC_MESSAGE_HANDLER(SafeBrowsingHostMsg_PhishingDetectionDone,
292 OnPhishingDetectionDone)
293 IPC_MESSAGE_UNHANDLED(handled = false)
294 IPC_END_MESSAGE_MAP()
295 return handled;
298 void ClientSideDetectionHost::DidNavigateMainFrame(
299 const content::LoadCommittedDetails& details,
300 const content::FrameNavigateParams& params) {
301 // TODO(noelutz): move this DCHECK to WebContents and fix all the unit tests
302 // that don't call this method on the UI thread.
303 // DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
304 if (details.is_in_page) {
305 // If the navigation is within the same page, the user isn't really
306 // navigating away. We don't need to cancel a pending callback or
307 // begin a new classification.
308 return;
310 // If we navigate away and there currently is a pending phishing
311 // report request we have to cancel it to make sure we don't display
312 // an interstitial for the wrong page. Note that this won't cancel
313 // the server ping back but only cancel the showing of the
314 // interstial.
315 weak_factory_.InvalidateWeakPtrs();
317 if (!csd_service_) {
318 return;
321 // Cancel any pending classification request.
322 if (classification_request_.get()) {
323 classification_request_->Cancel();
325 browse_info_.reset(new BrowseInfo);
327 // Store redirect chain information.
328 if (params.url.host() != cur_host_) {
329 cur_host_ = params.url.host();
330 cur_host_redirects_ = params.redirects;
332 browse_info_->host_redirects = cur_host_redirects_;
333 browse_info_->url_redirects = params.redirects;
334 browse_info_->referrer = params.referrer.url;
335 browse_info_->http_status_code = details.http_status_code;
337 // Notify the renderer if it should classify this URL.
338 classification_request_ = new ShouldClassifyUrlRequest(
339 params, web_contents(), csd_service_, database_manager_.get(), this);
340 classification_request_->Start();
343 void ClientSideDetectionHost::OnSafeBrowsingHit(
344 const SafeBrowsingUIManager::UnsafeResource& resource) {
345 if (!web_contents() || !web_contents()->GetController().GetActiveEntry())
346 return;
348 // Check that the hit is either malware or phishing.
349 if (resource.threat_type != SB_THREAT_TYPE_URL_PHISHING &&
350 resource.threat_type != SB_THREAT_TYPE_URL_MALWARE)
351 return;
353 // Check that this notification is really for us.
354 content::RenderViewHost* hit_rvh = content::RenderViewHost::FromID(
355 resource.render_process_host_id, resource.render_view_id);
356 if (!hit_rvh ||
357 web_contents() != content::WebContents::FromRenderViewHost(hit_rvh))
358 return;
360 // Store the unique page ID for later.
361 unsafe_unique_page_id_ =
362 web_contents()->GetController().GetActiveEntry()->GetUniqueID();
363 // We also keep the resource around in order to be able to send the
364 // malicious URL to the server.
365 unsafe_resource_.reset(new SafeBrowsingUIManager::UnsafeResource(resource));
366 unsafe_resource_->callback.Reset(); // Don't do anything stupid.
369 scoped_refptr<SafeBrowsingDatabaseManager>
370 ClientSideDetectionHost::database_manager() {
371 return database_manager_;
374 void ClientSideDetectionHost::WebContentsDestroyed(WebContents* tab) {
375 DCHECK(tab);
376 // Tell any pending classification request that it is being canceled.
377 if (classification_request_.get()) {
378 classification_request_->Cancel();
380 // Cancel all pending feature extractions.
381 feature_extractor_.reset();
384 void ClientSideDetectionHost::OnPhishingDetectionDone(
385 const std::string& verdict_str) {
386 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
387 // There is something seriously wrong if there is no service class but
388 // this method is called. The renderer should not start phishing detection
389 // if there isn't any service class in the browser.
390 DCHECK(csd_service_);
391 // There shouldn't be any pending requests because we revoke them everytime
392 // we navigate away.
393 DCHECK(!weak_factory_.HasWeakPtrs());
394 DCHECK(browse_info_.get());
396 // We parse the protocol buffer here. If we're unable to parse it we won't
397 // send the verdict further.
398 scoped_ptr<ClientPhishingRequest> verdict(new ClientPhishingRequest);
399 if (csd_service_ &&
400 !weak_factory_.HasWeakPtrs() &&
401 browse_info_.get() &&
402 verdict->ParseFromString(verdict_str) &&
403 verdict->IsInitialized()) {
404 // We do the malware IP matching and request sending if the feature
405 // is enabled.
406 if (malware_report_enabled_ && !MalwareKillSwitchIsOn()) {
407 scoped_ptr<ClientMalwareRequest> malware_verdict(
408 new ClientMalwareRequest);
409 // Start browser-side malware feature extraction. Once we're done it will
410 // send the malware client verdict request.
411 malware_verdict->set_url(verdict->url());
412 const GURL& referrer = browse_info_->referrer;
413 if (referrer.SchemeIs("http")) { // Only send http urls.
414 malware_verdict->set_referrer_url(referrer.spec());
416 // This function doesn't expect browse_info_ to stay around after this
417 // function returns.
418 feature_extractor_->ExtractMalwareFeatures(
419 browse_info_.get(),
420 malware_verdict.release(),
421 base::Bind(&ClientSideDetectionHost::MalwareFeatureExtractionDone,
422 weak_factory_.GetWeakPtr()));
425 // We only send phishing verdict to the server if the verdict is phishing or
426 // if a SafeBrowsing interstitial was already shown for this site. E.g., a
427 // malware or phishing interstitial was shown but the user clicked
428 // through.
429 if (verdict->is_phishing() || DidShowSBInterstitial()) {
430 if (DidShowSBInterstitial()) {
431 browse_info_->unsafe_resource.reset(unsafe_resource_.release());
433 // Start browser-side feature extraction. Once we're done it will send
434 // the client verdict request.
435 feature_extractor_->ExtractFeatures(
436 browse_info_.get(),
437 verdict.release(),
438 base::Bind(&ClientSideDetectionHost::FeatureExtractionDone,
439 weak_factory_.GetWeakPtr()));
442 browse_info_.reset();
445 void ClientSideDetectionHost::MaybeShowPhishingWarning(GURL phishing_url,
446 bool is_phishing) {
447 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
448 VLOG(2) << "Received server phishing verdict for URL:" << phishing_url
449 << " is_phishing:" << is_phishing;
450 if (is_phishing) {
451 DCHECK(web_contents());
452 if (ui_manager_.get()) {
453 SafeBrowsingUIManager::UnsafeResource resource;
454 resource.url = phishing_url;
455 resource.original_url = phishing_url;
456 resource.is_subresource = false;
457 resource.threat_type = SB_THREAT_TYPE_CLIENT_SIDE_PHISHING_URL;
458 resource.render_process_host_id =
459 web_contents()->GetRenderProcessHost()->GetID();
460 resource.render_view_id =
461 web_contents()->GetRenderViewHost()->GetRoutingID();
462 if (!ui_manager_->IsWhitelisted(resource)) {
463 // We need to stop any pending navigations, otherwise the interstital
464 // might not get created properly.
465 web_contents()->GetController().DiscardNonCommittedEntries();
466 resource.callback = base::Bind(&EmptyUrlCheckCallback);
467 ui_manager_->DoDisplayBlockingPage(resource);
470 // If there is true phishing verdict, invalidate weakptr so that no longer
471 // consider the malware vedict.
472 weak_factory_.InvalidateWeakPtrs();
476 void ClientSideDetectionHost::MaybeShowMalwareWarning(GURL original_url,
477 GURL malware_url,
478 bool is_malware) {
479 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
480 VLOG(2) << "Received server malawre IP verdict for URL:" << malware_url
481 << " is_malware:" << is_malware;
482 if (is_malware && malware_url.is_valid() && original_url.is_valid()) {
483 DCHECK(web_contents());
484 if (ui_manager_.get()) {
485 SafeBrowsingUIManager::UnsafeResource resource;
486 resource.url = malware_url;
487 resource.original_url = original_url;
488 resource.is_subresource = (malware_url.host() != original_url.host());
489 resource.threat_type = SB_THREAT_TYPE_CLIENT_SIDE_MALWARE_URL;
490 resource.render_process_host_id =
491 web_contents()->GetRenderProcessHost()->GetID();
492 resource.render_view_id =
493 web_contents()->GetRenderViewHost()->GetRoutingID();
494 if (!ui_manager_->IsWhitelisted(resource)) {
495 // We need to stop any pending navigations, otherwise the interstital
496 // might not get created properly.
497 web_contents()->GetController().DiscardNonCommittedEntries();
498 resource.callback = base::Bind(&EmptyUrlCheckCallback);
499 ui_manager_->DoDisplayBlockingPage(resource);
502 // If there is true malware verdict, invalidate weakptr so that no longer
503 // consider the phishing vedict.
504 weak_factory_.InvalidateWeakPtrs();
508 void ClientSideDetectionHost::FeatureExtractionDone(
509 bool success,
510 ClientPhishingRequest* request) {
511 DCHECK(request);
512 VLOG(2) << "Feature extraction done (success:" << success << ") for URL: "
513 << request->url() << ". Start sending client phishing request.";
514 ClientSideDetectionService::ClientReportPhishingRequestCallback callback;
515 // If the client-side verdict isn't phishing we don't care about the server
516 // response because we aren't going to display a warning.
517 if (request->is_phishing()) {
518 callback = base::Bind(&ClientSideDetectionHost::MaybeShowPhishingWarning,
519 weak_factory_.GetWeakPtr());
521 // Send ping even if the browser feature extraction failed.
522 csd_service_->SendClientReportPhishingRequest(
523 request, // The service takes ownership of the request object.
524 callback);
527 void ClientSideDetectionHost::MalwareFeatureExtractionDone(
528 bool feature_extraction_success,
529 scoped_ptr<ClientMalwareRequest> request) {
530 DCHECK(request.get());
531 VLOG(2) << "Malware Feature extraction done for URL: " << request->url()
532 << ", with features count:" << request->feature_map_size();
534 // Send ping if there is matching features.
535 if (feature_extraction_success && request->feature_map_size() > 0) {
536 VLOG(1) << "Start sending client malware request.";
537 ClientSideDetectionService::ClientReportMalwareRequestCallback callback;
538 callback = base::Bind(&ClientSideDetectionHost::MaybeShowMalwareWarning,
539 weak_factory_.GetWeakPtr());
540 csd_service_->SendClientReportMalwareRequest(request.release(), callback);
544 void ClientSideDetectionHost::UpdateIPUrlMap(const std::string& ip,
545 const std::string& url) {
546 if (ip.empty() || url.empty())
547 return;
549 IPUrlMap::iterator it = browse_info_->ips.find(ip);
550 if (it == browse_info_->ips.end()) {
551 if (int(browse_info_->ips.size()) < kMaxIPsPerBrowse) {
552 std::set<std::string> urls;
553 urls.insert(url);
554 browse_info_->ips.insert(make_pair(ip, urls));
556 } else if (int(it->second.size()) < kMaxUrlsPerIP) {
557 it->second.insert(url);
561 void ClientSideDetectionHost::Observe(
562 int type,
563 const content::NotificationSource& source,
564 const content::NotificationDetails& details) {
565 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
566 DCHECK_EQ(type, content::NOTIFICATION_RESOURCE_RESPONSE_STARTED);
567 const ResourceRequestDetails* req = content::Details<ResourceRequestDetails>(
568 details).ptr();
569 if (req && browse_info_.get() && malware_report_enabled_ &&
570 !MalwareKillSwitchIsOn()) {
571 if (req->url.is_valid()) {
572 UpdateIPUrlMap(req->socket_address.host() /* ip */,
573 req->url.spec() /* url */);
578 bool ClientSideDetectionHost::DidShowSBInterstitial() {
579 if (unsafe_unique_page_id_ <= 0 || !web_contents()) {
580 return false;
582 const NavigationEntry* nav_entry =
583 web_contents()->GetController().GetActiveEntry();
584 return (nav_entry && nav_entry->GetUniqueID() == unsafe_unique_page_id_);
587 void ClientSideDetectionHost::set_client_side_detection_service(
588 ClientSideDetectionService* service) {
589 csd_service_ = service;
592 void ClientSideDetectionHost::set_safe_browsing_managers(
593 SafeBrowsingUIManager* ui_manager,
594 SafeBrowsingDatabaseManager* database_manager) {
595 if (ui_manager_.get())
596 ui_manager_->RemoveObserver(this);
598 ui_manager_ = ui_manager;
599 if (ui_manager)
600 ui_manager_->AddObserver(this);
602 database_manager_ = database_manager;
605 bool ClientSideDetectionHost::MalwareKillSwitchIsOn() {
606 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
607 return malware_killswitch_on_;
610 void ClientSideDetectionHost::SetMalwareKillSwitch(bool killswitch_on) {
611 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
612 malware_killswitch_on_ = killswitch_on;
615 } // namespace safe_browsing