Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "chrome/browser/safe_browsing/srt_fetcher_win.h" | 5 #include "chrome/browser/safe_browsing/srt_fetcher_win.h" |
| 6 | 6 |
| 7 #include <stdint.h> | 7 #include <stdint.h> |
| 8 | 8 |
| 9 #include <memory> | 9 #include <memory> |
| 10 #include <vector> | 10 #include <vector> |
| 11 | 11 |
| 12 #include "base/bind.h" | 12 #include "base/bind.h" |
| 13 #include "base/bind_helpers.h" | 13 #include "base/bind_helpers.h" |
| 14 #include "base/callback_helpers.h" | 14 #include "base/callback_helpers.h" |
| 15 #include "base/command_line.h" | 15 #include "base/command_line.h" |
| 16 #include "base/feature_list.h" | |
| 16 #include "base/files/file_path.h" | 17 #include "base/files/file_path.h" |
| 17 #include "base/macros.h" | 18 #include "base/macros.h" |
| 18 #include "base/metrics/field_trial.h" | 19 #include "base/metrics/field_trial.h" |
| 19 #include "base/metrics/histogram_macros.h" | 20 #include "base/metrics/histogram_macros.h" |
| 20 #include "base/metrics/sparse_histogram.h" | 21 #include "base/metrics/sparse_histogram.h" |
| 21 #include "base/process/launch.h" | 22 #include "base/process/launch.h" |
| 22 #include "base/strings/string_number_conversions.h" | 23 #include "base/strings/string_number_conversions.h" |
| 23 #include "base/strings/stringprintf.h" | 24 #include "base/strings/stringprintf.h" |
| 24 #include "base/strings/utf_string_conversions.h" | 25 #include "base/strings/utf_string_conversions.h" |
| 25 #include "base/task_runner_util.h" | 26 #include "base/task_runner_util.h" |
| 26 #include "base/time/time.h" | 27 #include "base/time/time.h" |
| 27 #include "base/win/registry.h" | 28 #include "base/win/registry.h" |
| 28 #include "chrome/browser/browser_process.h" | 29 #include "chrome/browser/browser_process.h" |
| 29 #include "chrome/browser/metrics/chrome_metrics_service_accessor.h" | 30 #include "chrome/browser/metrics/chrome_metrics_service_accessor.h" |
| 30 #include "chrome/browser/profiles/profile.h" | 31 #include "chrome/browser/profiles/profile.h" |
| 31 #include "chrome/browser/profiles/profile_io_data.h" | 32 #include "chrome/browser/profiles/profile_io_data.h" |
| 33 #include "chrome/browser/safe_browsing/srt_client_info_win.h" | |
| 32 #include "chrome/browser/safe_browsing/srt_global_error_win.h" | 34 #include "chrome/browser/safe_browsing/srt_global_error_win.h" |
| 33 #include "chrome/browser/ui/browser_finder.h" | 35 #include "chrome/browser/ui/browser_finder.h" |
| 34 #include "chrome/browser/ui/browser_list.h" | 36 #include "chrome/browser/ui/browser_list.h" |
| 35 #include "chrome/browser/ui/browser_list_observer.h" | 37 #include "chrome/browser/ui/browser_list_observer.h" |
| 36 #include "chrome/browser/ui/global_error/global_error_service.h" | 38 #include "chrome/browser/ui/global_error/global_error_service.h" |
| 37 #include "chrome/browser/ui/global_error/global_error_service_factory.h" | 39 #include "chrome/browser/ui/global_error/global_error_service_factory.h" |
| 40 #include "chrome/common/pref_names.h" | |
| 38 #include "components/component_updater/pref_names.h" | 41 #include "components/component_updater/pref_names.h" |
| 39 #include "components/prefs/pref_service.h" | 42 #include "components/prefs/pref_service.h" |
| 40 #include "components/rappor/rappor_service.h" | 43 #include "components/rappor/rappor_service.h" |
| 41 #include "components/variations/net/variations_http_headers.h" | 44 #include "components/variations/net/variations_http_headers.h" |
| 45 #include "components/version_info/version_info.h" | |
| 42 #include "content/public/browser/browser_thread.h" | 46 #include "content/public/browser/browser_thread.h" |
| 43 #include "net/base/load_flags.h" | 47 #include "net/base/load_flags.h" |
| 44 #include "net/http/http_status_code.h" | 48 #include "net/http/http_status_code.h" |
| 45 #include "net/url_request/url_fetcher.h" | 49 #include "net/url_request/url_fetcher.h" |
| 46 #include "net/url_request/url_fetcher_delegate.h" | 50 #include "net/url_request/url_fetcher_delegate.h" |
| 47 #include "net/url_request/url_request_context_getter.h" | 51 #include "net/url_request/url_request_context_getter.h" |
| 48 | 52 |
| 49 using content::BrowserThread; | 53 using content::BrowserThread; |
| 50 | 54 |
| 51 namespace safe_browsing { | 55 namespace safe_browsing { |
| 52 | 56 |
| 53 const wchar_t kSoftwareRemovalToolRegistryKey[] = | 57 const wchar_t kSoftwareRemovalToolRegistryKey[] = |
| 54 L"Software\\Google\\Software Removal Tool"; | 58 L"Software\\Google\\Software Removal Tool"; |
| 55 | 59 |
| 56 const wchar_t kCleanerSubKey[] = L"Cleaner"; | 60 const wchar_t kCleanerSubKey[] = L"Cleaner"; |
| 57 | 61 |
| 58 const wchar_t kEndTimeValueName[] = L"EndTime"; | 62 const wchar_t kEndTimeValueName[] = L"EndTime"; |
| 59 const wchar_t kStartTimeValueName[] = L"StartTime"; | 63 const wchar_t kStartTimeValueName[] = L"StartTime"; |
| 60 | 64 |
| 65 const char kExtendedSafeBrowsingEnabledSwitch[] = | |
| 66 "extended-safebrowsing-enabled"; | |
| 67 | |
| 61 namespace { | 68 namespace { |
| 62 | 69 |
| 63 // Used to send UMA information about missing start and end time registry | 70 // Used to send UMA information about missing start and end time registry |
| 64 // values for the reporter. Replicated in the histograms.xml file, so the order | 71 // values for the reporter. Replicated in the histograms.xml file, so the order |
| 65 // MUST NOT CHANGE. | 72 // MUST NOT CHANGE. |
| 66 enum SwReporterRunningTimeRegistryError { | 73 enum SwReporterRunningTimeRegistryError { |
| 67 REPORTER_RUNNING_TIME_ERROR_NO_ERROR = 0, | 74 REPORTER_RUNNING_TIME_ERROR_NO_ERROR = 0, |
| 68 REPORTER_RUNNING_TIME_ERROR_REGISTRY_KEY_INVALID = 1, | 75 REPORTER_RUNNING_TIME_ERROR_REGISTRY_KEY_INVALID = 1, |
| 69 REPORTER_RUNNING_TIME_ERROR_MISSING_START_TIME = 2, | 76 REPORTER_RUNNING_TIME_ERROR_MISSING_START_TIME = 2, |
| 70 REPORTER_RUNNING_TIME_ERROR_MISSING_END_TIME = 3, | 77 REPORTER_RUNNING_TIME_ERROR_MISSING_END_TIME = 3, |
| (...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 108 const wchar_t kScanTimesSubKey[] = L"ScanTimes"; | 115 const wchar_t kScanTimesSubKey[] = L"ScanTimes"; |
| 109 const wchar_t kFoundUwsValueName[] = L"FoundUws"; | 116 const wchar_t kFoundUwsValueName[] = L"FoundUws"; |
| 110 const wchar_t kMemoryUsedValueName[] = L"MemoryUsed"; | 117 const wchar_t kMemoryUsedValueName[] = L"MemoryUsed"; |
| 111 | 118 |
| 112 const char kFoundUwsMetricName[] = "SoftwareReporter.FoundUwS"; | 119 const char kFoundUwsMetricName[] = "SoftwareReporter.FoundUwS"; |
| 113 const char kFoundUwsReadErrorMetricName[] = | 120 const char kFoundUwsReadErrorMetricName[] = |
| 114 "SoftwareReporter.FoundUwSReadError"; | 121 "SoftwareReporter.FoundUwSReadError"; |
| 115 const char kScanTimesMetricName[] = "SoftwareReporter.UwSScanTimes"; | 122 const char kScanTimesMetricName[] = "SoftwareReporter.UwSScanTimes"; |
| 116 const char kMemoryUsedMetricName[] = "SoftwareReporter.MemoryUsed"; | 123 const char kMemoryUsedMetricName[] = "SoftwareReporter.MemoryUsed"; |
| 117 | 124 |
| 125 const base::Feature kSwReporterExtendedSafeBrowsingFeature{ | |
| 126 "SwReporterExtendedSafeBrowsingFeature", base::FEATURE_DISABLED_BY_DEFAULT}; | |
| 127 | |
| 118 // Reports metrics about the software reporter via UMA (and sometimes Rappor). | 128 // Reports metrics about the software reporter via UMA (and sometimes Rappor). |
| 119 class UMAHistogramReporter { | 129 class UMAHistogramReporter { |
| 120 public: | 130 public: |
| 121 // Reports the software reporter tool's version via UMA. | 131 // Reports the software reporter tool's version via UMA. |
| 122 void ReportVersion(const base::Version& version) const { | 132 void ReportVersion(const base::Version& version) const { |
| 123 DCHECK(!version.components().empty()); | 133 DCHECK(!version.components().empty()); |
| 124 // The minor version is the 2nd last component of the version, | 134 // The minor version is the 2nd last component of the version, |
| 125 // or just the first component if there is only 1. | 135 // or just the first component if there is only 1. |
| 126 uint32_t minor_version = 0; | 136 uint32_t minor_version = 0; |
| 127 if (version.components().size() > 1) | 137 if (version.components().size() > 1) |
| (...skipping 219 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 347 if (error->GetBubbleView()) { | 357 if (error->GetBubbleView()) { |
| 348 show_bubble = false; | 358 show_bubble = false; |
| 349 break; | 359 break; |
| 350 } | 360 } |
| 351 } | 361 } |
| 352 } | 362 } |
| 353 if (show_bubble) | 363 if (show_bubble) |
| 354 global_error->ShowBubbleView(browser); | 364 global_error->ShowBubbleView(browser); |
| 355 } | 365 } |
| 356 | 366 |
| 367 // Returns true if there is a profile that is not in incognito mode and the user | |
| 368 // has opted into Safe Browsing extended reporting. | |
| 369 bool SafeBrowsingExtendedReportingEnabled() { | |
| 370 BrowserList* browser_list = BrowserList::GetInstance(); | |
| 371 return std::any_of(browser_list->begin_last_active(), | |
|
grt (UTC plus 2)
2016/08/29 06:56:14
#include <algorithm> for this
ftirelo
2016/08/29 15:30:55
Done.
| |
| 372 browser_list->end_last_active(), | |
| 373 [](const Browser* browser) { | |
| 374 const Profile* profile = browser->profile(); | |
| 375 return profile && !profile->IsOffTheRecord() && | |
| 376 profile->GetPrefs()->GetBoolean( | |
| 377 prefs::kSafeBrowsingExtendedReportingEnabled); | |
| 378 }); | |
| 379 } | |
| 380 | |
| 357 // This function is called from a worker thread to launch the SwReporter and | 381 // This function is called from a worker thread to launch the SwReporter and |
| 358 // wait for termination to collect its exit code. This task could be | 382 // wait for termination to collect its exit code. This task could be |
| 359 // interrupted by a shutdown at any time, so it shouldn't depend on anything | 383 // interrupted by a shutdown at any time, so it shouldn't depend on anything |
| 360 // external that could be shut down beforehand. | 384 // external that could be shut down beforehand. |
| 361 int LaunchAndWaitForExit(const SwReporterInvocation& invocation) { | 385 int LaunchAndWaitForExit(const SwReporterInvocation& invocation) { |
| 362 if (g_testing_delegate_) | 386 if (g_testing_delegate_) |
| 363 return g_testing_delegate_->LaunchReporter(invocation); | 387 return g_testing_delegate_->LaunchReporter(invocation); |
| 364 base::Process reporter_process = | 388 base::Process reporter_process = |
| 365 base::LaunchProcess(invocation.command_line, base::LaunchOptions()); | 389 base::LaunchProcess(invocation.command_line, base::LaunchOptions()); |
| 366 // This exit code is used to identify that a reporter run didn't happen, so | 390 // This exit code is used to identify that a reporter run didn't happen, so |
| (...skipping 175 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 542 DCHECK(browser); | 566 DCHECK(browser); |
| 543 MaybeFetchSRT(browser, version_); | 567 MaybeFetchSRT(browser, version_); |
| 544 BrowserList::RemoveObserver(this); | 568 BrowserList::RemoveObserver(this); |
| 545 } | 569 } |
| 546 | 570 |
| 547 // This method is called on the UI thread when the reporter run has completed. | 571 // This method is called on the UI thread when the reporter run has completed. |
| 548 // This is run as a task posted from an interruptible worker thread so should | 572 // This is run as a task posted from an interruptible worker thread so should |
| 549 // be resilient to unexpected shutdown. | 573 // be resilient to unexpected shutdown. |
| 550 void ReporterDone(const base::Time& reporter_start_time, | 574 void ReporterDone(const base::Time& reporter_start_time, |
| 551 const base::Version& version, | 575 const base::Version& version, |
| 576 bool logging_enabled, | |
| 552 int exit_code) { | 577 int exit_code) { |
| 553 DCHECK_CURRENTLY_ON(BrowserThread::UI); | 578 DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| 554 | 579 |
| 555 if (g_testing_delegate_) | 580 if (g_testing_delegate_) |
| 556 g_testing_delegate_->NotifyReporterDone(); | 581 g_testing_delegate_->NotifyReporterDone(); |
| 557 | 582 |
| 558 base::TimeDelta reporter_running_time = | 583 base::TimeDelta reporter_running_time = |
| 559 base::Time::Now() - reporter_start_time; | 584 base::Time::Now() - reporter_start_time; |
| 560 // Don't continue when the reporter process failed to launch, but still try | 585 // Don't continue when the reporter process failed to launch, but still try |
| 561 // again after the regular delay. It's not worth retrying earlier, risking | 586 // again after the regular delay. It's not worth retrying earlier, risking |
| 562 // running too often if it always fails, since not many users fail here. | 587 // running too often if it always fails, since not many users fail here. |
| 563 main_thread_task_runner_->PostDelayedTask( | 588 main_thread_task_runner_->PostDelayedTask( |
| 564 FROM_HERE, | 589 FROM_HERE, |
| 565 base::Bind(&ReporterRunner::TryToRun, base::Unretained(this)), | 590 base::Bind(&ReporterRunner::TryToRun, base::Unretained(this)), |
| 566 base::TimeDelta::FromDays(days_between_reporter_runs_)); | 591 base::TimeDelta::FromDays(days_between_reporter_runs_)); |
| 567 if (exit_code == kReporterFailureExitCode) | 592 if (exit_code == kReporterFailureExitCode) |
| 568 return; | 593 return; |
| 569 | 594 |
| 570 UMAHistogramReporter uma; | 595 UMAHistogramReporter uma; |
| 571 uma.ReportVersion(version); | 596 uma.ReportVersion(version); |
| 572 uma.ReportExitCode(exit_code); | 597 uma.ReportExitCode(exit_code); |
| 573 uma.ReportFoundUwS(); | 598 uma.ReportFoundUwS(); |
| 574 PrefService* local_state = g_browser_process->local_state(); | 599 PrefService* local_state = g_browser_process->local_state(); |
| 575 if (local_state) { | 600 if (local_state) { |
| 576 local_state->SetInteger(prefs::kSwReporterLastExitCode, exit_code); | 601 local_state->SetInteger(prefs::kSwReporterLastExitCode, exit_code); |
| 577 local_state->SetInt64(prefs::kSwReporterLastTimeTriggered, | 602 local_state->SetInt64(prefs::kSwReporterLastTimeTriggered, |
| 578 base::Time::Now().ToInternalValue()); | 603 base::Time::Now().ToInternalValue()); |
| 604 if (logging_enabled) { | |
| 605 local_state->SetInt64(prefs::kSwReporterLastTimeSentReport, | |
| 606 base::Time::Now().ToInternalValue()); | |
| 607 } | |
| 579 } | 608 } |
| 580 uma.ReportRuntime(reporter_running_time); | 609 uma.ReportRuntime(reporter_running_time); |
| 581 uma.ReportScanTimes(); | 610 uma.ReportScanTimes(); |
| 582 uma.ReportMemoryUsage(); | 611 uma.ReportMemoryUsage(); |
| 583 | 612 |
| 584 if (!IsInSRTPromptFieldTrialGroups()) { | 613 if (!IsInSRTPromptFieldTrialGroups()) { |
| 585 // Knowing about disabled field trial is more important than reporter not | 614 // Knowing about disabled field trial is more important than reporter not |
| 586 // finding anything to remove, so check this case first. | 615 // finding anything to remove, so check this case first. |
| 587 RecordReporterStepHistogram(SW_REPORTER_NO_PROMPT_FIELD_TRIAL); | 616 RecordReporterStepHistogram(SW_REPORTER_NO_PROMPT_FIELD_TRIAL); |
| 588 return; | 617 return; |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 632 last_time_triggered + | 661 last_time_triggered + |
| 633 base::TimeDelta::FromDays(days_between_reporter_runs_) - | 662 base::TimeDelta::FromDays(days_between_reporter_runs_) - |
| 634 base::Time::Now()); | 663 base::Time::Now()); |
| 635 if (next_trigger_delay.ToInternalValue() <= 0 || | 664 if (next_trigger_delay.ToInternalValue() <= 0 || |
| 636 // Also make sure the kSwReporterLastTimeTriggered value is not set in | 665 // Also make sure the kSwReporterLastTimeTriggered value is not set in |
| 637 // the future. | 666 // the future. |
| 638 last_time_triggered > base::Time::Now()) { | 667 last_time_triggered > base::Time::Now()) { |
| 639 if (g_testing_delegate_) | 668 if (g_testing_delegate_) |
| 640 g_testing_delegate_->NotifyLaunchReady(); | 669 g_testing_delegate_->NotifyLaunchReady(); |
| 641 | 670 |
| 671 // Creating a new invocation to add switches for users who opted into | |
| 672 // extended Safe Browsing reporting. The invocation object is changed | |
| 673 // locally right before the actual process is launched because user status | |
| 674 // can between this and the next run for this ReporterRunner object. For | |
| 675 // example, the ReporterDone() callback schedules the next run for a few | |
| 676 // days later, and the user might have changed settings in the meantime. | |
| 677 SwReporterInvocation actual_invocation = | |
| 678 SwReporterInvocation::FromCommandLine(invocation_.command_line); | |
| 679 bool logging_enabled = ShouldSendReporterLogs(*local_state); | |
| 680 if (logging_enabled) | |
| 681 AddSwitchesForExtendedReporterUser(&actual_invocation); | |
| 682 | |
| 642 // It's OK to simply |PostTaskAndReplyWithResult| so that | 683 // It's OK to simply |PostTaskAndReplyWithResult| so that |
| 643 // |LaunchAndWaitForExit| doesn't need to access | 684 // |LaunchAndWaitForExit| doesn't need to access |
| 644 // |main_thread_task_runner_| since the callback is not delayed and the | 685 // |main_thread_task_runner_| since the callback is not delayed and the |
| 645 // test task runner won't need to force it. | 686 // test task runner won't need to force it. |
| 646 base::PostTaskAndReplyWithResult( | 687 base::PostTaskAndReplyWithResult( |
| 647 blocking_task_runner_.get(), FROM_HERE, | 688 blocking_task_runner_.get(), FROM_HERE, |
| 648 base::Bind(&LaunchAndWaitForExit, invocation_), | 689 base::Bind(&LaunchAndWaitForExit, actual_invocation), |
| 649 base::Bind(&ReporterRunner::ReporterDone, base::Unretained(this), | 690 base::Bind(&ReporterRunner::ReporterDone, base::Unretained(this), |
| 650 base::Time::Now(), version_)); | 691 base::Time::Now(), version_, logging_enabled)); |
| 651 } else { | 692 } else { |
| 652 main_thread_task_runner_->PostDelayedTask( | 693 main_thread_task_runner_->PostDelayedTask( |
| 653 FROM_HERE, | 694 FROM_HERE, |
| 654 base::Bind(&ReporterRunner::TryToRun, base::Unretained(this)), | 695 base::Bind(&ReporterRunner::TryToRun, base::Unretained(this)), |
| 655 next_trigger_delay); | 696 next_trigger_delay); |
| 656 } | 697 } |
| 657 } | 698 } |
| 658 | 699 |
| 700 // Returns true if the experiment to send reporter logs is enabled, the user | |
| 701 // opted into Safe Browsing extended reporting, and logs have been sent at | |
| 702 // least |kSwReporterLastTimeSentReport| days ago. | |
| 703 bool ShouldSendReporterLogs(const PrefService& local_state) { | |
| 704 if (!base::FeatureList::IsEnabled(kSwReporterExtendedSafeBrowsingFeature) || | |
| 705 !SafeBrowsingExtendedReportingEnabled()) { | |
| 706 return false; | |
| 707 } | |
| 708 | |
| 709 const base::Time now = base::Time::Now(); | |
| 710 const base::Time last_time_sent_logs = base::Time::FromInternalValue( | |
| 711 local_state.GetInt64(prefs::kSwReporterLastTimeSentReport)); | |
| 712 // Send the logs if the last send was in the future. | |
| 713 if (last_time_sent_logs > now) | |
| 714 return true; | |
| 715 // Otherwise, send them if the interval has passed. | |
| 716 base::Time time_until_next_logs(last_time_sent_logs + | |
| 717 base::TimeDelta::FromDays(kDaysBetweenReporterLogsSent)); | |
| 718 return time_until_next_logs <= now; | |
|
grt (UTC plus 2)
2016/08/29 06:56:14
fwiw: i find the code clear enough without the int
ftirelo
2016/08/29 15:30:55
Done.
| |
| 719 } | |
| 720 | |
| 721 void AddSwitchesForExtendedReporterUser(SwReporterInvocation* invocation) { | |
| 722 invocation->command_line.AppendSwitch(kExtendedSafeBrowsingEnabledSwitch); | |
| 723 invocation->command_line.AppendSwitchASCII( | |
| 724 kChromeVersionSwitch, version_info::GetVersionNumber()); | |
| 725 invocation->command_line.AppendSwitchNative( | |
| 726 kChromeChannelSwitch, base::IntToString16(ChannelAsInt())); | |
| 727 } | |
| 728 | |
| 659 bool first_run_ = true; | 729 bool first_run_ = true; |
| 660 SwReporterInvocation invocation_; | 730 SwReporterInvocation invocation_; |
| 661 base::Version version_; | 731 base::Version version_; |
| 662 scoped_refptr<base::TaskRunner> main_thread_task_runner_; | 732 scoped_refptr<base::TaskRunner> main_thread_task_runner_; |
| 663 scoped_refptr<base::TaskRunner> blocking_task_runner_; | 733 scoped_refptr<base::TaskRunner> blocking_task_runner_; |
| 664 | 734 |
| 665 // This value is used to identify how long to wait before starting a new run | 735 // This value is used to identify how long to wait before starting a new run |
| 666 // of the reporter. It's initialized with the default value and may be changed | 736 // of the reporter. It's initialized with the default value and may be changed |
| 667 // to a different value when a prompt is pending and the reporter should be | 737 // to a different value when a prompt is pending and the reporter should be |
| 668 // run before adding the global error to the Chrome menu. | 738 // run before adding the global error to the Chrome menu. |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 723 KEY_QUERY_VALUE); | 793 KEY_QUERY_VALUE); |
| 724 | 794 |
| 725 return srt_cleaner_key.Valid() && srt_cleaner_key.GetValueCount() > 0; | 795 return srt_cleaner_key.Valid() && srt_cleaner_key.GetValueCount() > 0; |
| 726 } | 796 } |
| 727 | 797 |
| 728 void SetSwReporterTestingDelegate(SwReporterTestingDelegate* delegate) { | 798 void SetSwReporterTestingDelegate(SwReporterTestingDelegate* delegate) { |
| 729 g_testing_delegate_ = delegate; | 799 g_testing_delegate_ = delegate; |
| 730 } | 800 } |
| 731 | 801 |
| 732 } // namespace safe_browsing | 802 } // namespace safe_browsing |
| OLD | NEW |