Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1159)

Side by Side Diff: chrome/browser/safe_browsing/incident_reporting_service.cc

Issue 411793004: More fine-grained pruning in safe browsing incident reporting service. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: shess improvements Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 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/incident_reporting_service.h" 5 #include "chrome/browser/safe_browsing/incident_reporting_service.h"
6 6
7 #include <math.h> 7 #include <math.h>
8 8
9 #include <algorithm> 9 #include <algorithm>
10 #include <vector> 10 #include <vector>
11 11
12 #include "base/metrics/histogram.h" 12 #include "base/metrics/histogram.h"
13 #include "base/prefs/pref_service.h" 13 #include "base/prefs/pref_service.h"
14 #include "base/prefs/scoped_user_pref_update.h"
14 #include "base/process/process_info.h" 15 #include "base/process/process_info.h"
15 #include "base/stl_util.h" 16 #include "base/stl_util.h"
17 #include "base/strings/string_number_conversions.h"
16 #include "base/threading/sequenced_worker_pool.h" 18 #include "base/threading/sequenced_worker_pool.h"
19 #include "base/values.h"
17 #include "chrome/browser/browser_process.h" 20 #include "chrome/browser/browser_process.h"
18 #include "chrome/browser/chrome_notification_types.h" 21 #include "chrome/browser/chrome_notification_types.h"
19 #include "chrome/browser/prefs/tracked/tracked_preference_validation_delegate.h" 22 #include "chrome/browser/prefs/tracked/tracked_preference_validation_delegate.h"
20 #include "chrome/browser/profiles/profile.h" 23 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/browser/safe_browsing/database_manager.h" 24 #include "chrome/browser/safe_browsing/database_manager.h"
22 #include "chrome/browser/safe_browsing/environment_data_collection.h" 25 #include "chrome/browser/safe_browsing/environment_data_collection.h"
23 #include "chrome/browser/safe_browsing/incident_report_uploader_impl.h" 26 #include "chrome/browser/safe_browsing/incident_report_uploader_impl.h"
24 #include "chrome/browser/safe_browsing/preference_validation_delegate.h" 27 #include "chrome/browser/safe_browsing/preference_validation_delegate.h"
25 #include "chrome/browser/safe_browsing/safe_browsing_service.h" 28 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
29 #include "chrome/browser/safe_browsing/tracked_preference_incident_handlers.h"
26 #include "chrome/common/pref_names.h" 30 #include "chrome/common/pref_names.h"
27 #include "chrome/common/safe_browsing/csd.pb.h" 31 #include "chrome/common/safe_browsing/csd.pb.h"
28 #include "content/public/browser/browser_thread.h" 32 #include "content/public/browser/browser_thread.h"
29 #include "content/public/browser/notification_service.h" 33 #include "content/public/browser/notification_service.h"
30 #include "net/url_request/url_request_context_getter.h" 34 #include "net/url_request/url_request_context_getter.h"
31 35
32 namespace safe_browsing { 36 namespace safe_browsing {
33 37
34 namespace { 38 namespace {
35 39
40 // The type of an incident. Used for user metrics and for pruning of
41 // previously-reported incidents.
36 enum IncidentType { 42 enum IncidentType {
37 // Start with 1 rather than zero; otherwise there won't be enough buckets for 43 // Start with 1 rather than zero; otherwise there won't be enough buckets for
38 // the histogram. 44 // the histogram.
39 TRACKED_PREFERENCE = 1, 45 TRACKED_PREFERENCE = 1,
46 // Values for new incident types go here.
40 NUM_INCIDENT_TYPES 47 NUM_INCIDENT_TYPES
41 }; 48 };
42 49
50 // The action taken for an incident; used for user metrics (see
51 // LogIncidentDataType).
43 enum IncidentDisposition { 52 enum IncidentDisposition {
44 DROPPED, 53 DROPPED,
45 ACCEPTED, 54 ACCEPTED,
46 }; 55 };
47 56
57 // The state persisted for a specific instance of an incident to enable pruning
58 // of previously-reported incidents.
59 struct PersistentIncidentState {
60 // The type of the incident.
61 IncidentType type;
62
63 // The key for a specific instance of an incident.
64 std::string key;
65
66 // A hash digest representing a specific instance of an incident.
67 uint32_t digest;
68 };
69
70 // The amount of time the service will wait to collate incidents.
48 const int64 kDefaultUploadDelayMs = 1000 * 60; // one minute 71 const int64 kDefaultUploadDelayMs = 1000 * 60; // one minute
49 72
73 // Returns the number of incidents contained in |incident|. The result is
74 // expected to be 1. Used in DCHECKs.
75 size_t CountIncidents(const ClientIncidentReport_IncidentData& incident) {
76 size_t result = 0;
77 if (incident.has_tracked_preference())
78 ++result;
79 // Add detection for new incident types here.
80 return result;
81 }
82
83 // Returns the type of incident contained in |incident_data|.
84 IncidentType GetIncidentType(
85 const ClientIncidentReport_IncidentData& incident_data) {
86 if (incident_data.has_tracked_preference())
87 return TRACKED_PREFERENCE;
88
89 // Add detection for new incident types here.
90 NOTREACHED();
91 return NUM_INCIDENT_TYPES;
92 }
93
94 // Logs the type of incident in |incident_data| to a user metrics histogram.
50 void LogIncidentDataType( 95 void LogIncidentDataType(
51 IncidentDisposition disposition, 96 IncidentDisposition disposition,
52 const ClientIncidentReport_IncidentData& incident_data) { 97 const ClientIncidentReport_IncidentData& incident_data) {
53 IncidentType type = TRACKED_PREFERENCE; 98 IncidentType type = GetIncidentType(incident_data);
54
55 // Add a switch statement once other types are supported.
56 DCHECK(incident_data.has_tracked_preference());
57
58 if (disposition == ACCEPTED) { 99 if (disposition == ACCEPTED) {
59 UMA_HISTOGRAM_ENUMERATION("SBIRS.Incident", type, NUM_INCIDENT_TYPES); 100 UMA_HISTOGRAM_ENUMERATION("SBIRS.Incident", type, NUM_INCIDENT_TYPES);
60 } else { 101 } else {
61 DCHECK_EQ(disposition, DROPPED); 102 DCHECK_EQ(disposition, DROPPED);
62 UMA_HISTOGRAM_ENUMERATION("SBIRS.DroppedIncident", type, 103 UMA_HISTOGRAM_ENUMERATION("SBIRS.DroppedIncident", type,
63 NUM_INCIDENT_TYPES); 104 NUM_INCIDENT_TYPES);
64 } 105 }
65 } 106 }
66 107
108 // Computes the persistent state for an incident.
109 PersistentIncidentState ComputeIncidentState(
110 const ClientIncidentReport_IncidentData& incident) {
111 PersistentIncidentState state = {GetIncidentType(incident)};
112 switch (state.type) {
113 case TRACKED_PREFERENCE:
114 state.key = GetTrackedPreferenceIncidentKey(incident);
115 state.digest = GetTrackedPreferenceIncidentDigest(incident);
116 break;
117 // Add handling for new incident types here.
118 default:
119 NOTREACHED();
120 break;
Scott Hess - ex-Googler 2014/07/24 23:09:54 The NOTREACHED() will only fire if an incident of
grt (UTC plus 2) 2014/07/24 23:22:08 i've added some COMPILE_ASSERTs. thanks for the id
121 }
122 return state;
123 }
124
125 // Returns true if the incident described by |state| has already been reported
126 // based on the bookkeeping in the |incidents_sent| preference dictionary.
127 bool IncidentHasBeenReported(const base::DictionaryValue* incidents_sent,
128 const PersistentIncidentState& state) {
129 const base::DictionaryValue* type_dict = NULL;
130 std::string digest_string;
131 return (incidents_sent &&
132 incidents_sent->GetDictionaryWithoutPathExpansion(
133 base::IntToString(state.type), &type_dict) &&
134 type_dict->GetStringWithoutPathExpansion(state.key, &digest_string) &&
135 digest_string == base::UintToString(state.digest));
136 }
137
138 // Marks the incidents described by |states| as having been reported
139 // in |incidents_set|.
140 void MarkIncidentsAsReported(const std::vector<PersistentIncidentState>& states,
141 base::DictionaryValue* incidents_sent) {
142 for (size_t i = 0; i < states.size(); ++i) {
143 const PersistentIncidentState& data = states[i];
144 base::DictionaryValue* type_dict = NULL;
145 const std::string type_string(base::IntToString(data.type));
146 if (!incidents_sent->GetDictionaryWithoutPathExpansion(type_string,
147 &type_dict)) {
148 type_dict = new base::DictionaryValue();
149 incidents_sent->SetWithoutPathExpansion(type_string, type_dict);
150 }
151 type_dict->SetStringWithoutPathExpansion(data.key,
152 base::UintToString(data.digest));
153 }
154 }
155
67 } // namespace 156 } // namespace
68 157
69 struct IncidentReportingService::ProfileContext { 158 struct IncidentReportingService::ProfileContext {
70 ProfileContext(); 159 ProfileContext();
71 ~ProfileContext(); 160 ~ProfileContext();
72 161
73 // The incidents collected for this profile pending creation and/or upload. 162 // The incidents collected for this profile pending creation and/or upload.
74 ScopedVector<ClientIncidentReport_IncidentData> incidents; 163 ScopedVector<ClientIncidentReport_IncidentData> incidents;
75 164
76 // False until PROFILE_ADDED notification is received. 165 // False until PROFILE_ADDED notification is received.
77 bool added; 166 bool added;
78 167
79 private: 168 private:
80 DISALLOW_COPY_AND_ASSIGN(ProfileContext); 169 DISALLOW_COPY_AND_ASSIGN(ProfileContext);
81 }; 170 };
82 171
83 class IncidentReportingService::UploadContext { 172 class IncidentReportingService::UploadContext {
84 public: 173 public:
174 typedef std::map<Profile*, std::vector<PersistentIncidentState> >
175 PersistentIncidentStateCollection;
176
85 explicit UploadContext(scoped_ptr<ClientIncidentReport> report); 177 explicit UploadContext(scoped_ptr<ClientIncidentReport> report);
86 ~UploadContext(); 178 ~UploadContext();
87 179
88 // The report being uploaded. 180 // The report being uploaded.
89 scoped_ptr<ClientIncidentReport> report; 181 scoped_ptr<ClientIncidentReport> report;
90 182
91 // The uploader in use. This is NULL until the CSD killswitch is checked. 183 // The uploader in use. This is NULL until the CSD killswitch is checked.
92 scoped_ptr<IncidentReportUploader> uploader; 184 scoped_ptr<IncidentReportUploader> uploader;
93 185
94 // The set of profiles from which incidents in |report| originated. 186 // A mapping of profiles to the data to be persisted upon successful upload.
95 std::vector<Profile*> profiles; 187 PersistentIncidentStateCollection profiles_to_state;
96 188
97 private: 189 private:
98 DISALLOW_COPY_AND_ASSIGN(UploadContext); 190 DISALLOW_COPY_AND_ASSIGN(UploadContext);
99 }; 191 };
100 192
101 IncidentReportingService::ProfileContext::ProfileContext() : added() { 193 IncidentReportingService::ProfileContext::ProfileContext() : added() {
102 } 194 }
103 195
104 IncidentReportingService::ProfileContext::~ProfileContext() { 196 IncidentReportingService::ProfileContext::~ProfileContext() {
105 } 197 }
(...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after
254 if (it == profiles_.end()) 346 if (it == profiles_.end())
255 return; 347 return;
256 348
257 // TODO(grt): Persist incidents for upload on future profile load. 349 // TODO(grt): Persist incidents for upload on future profile load.
258 350
259 // Forget about this profile. Incidents not yet sent for upload are lost. 351 // Forget about this profile. Incidents not yet sent for upload are lost.
260 // No new incidents will be accepted for it. 352 // No new incidents will be accepted for it.
261 delete it->second; 353 delete it->second;
262 profiles_.erase(it); 354 profiles_.erase(it);
263 355
264 // Remove the association with this profile from any pending uploads. 356 // Remove the association with this profile from all pending uploads.
265 for (size_t i = 0; i < uploads_.size(); ++i) { 357 for (size_t i = 0; i < uploads_.size(); ++i)
266 UploadContext* upload = uploads_[i]; 358 uploads_[i]->profiles_to_state.erase(profile);
267 std::vector<Profile*>::iterator it =
268 std::find(upload->profiles.begin(), upload->profiles.end(), profile);
269 if (it != upload->profiles.end()) {
270 *it = upload->profiles.back();
271 upload->profiles.resize(upload->profiles.size() - 1);
272 break;
273 }
274 }
275 } 359 }
276 360
277 void IncidentReportingService::AddIncident( 361 void IncidentReportingService::AddIncident(
278 Profile* profile, 362 Profile* profile,
279 scoped_ptr<ClientIncidentReport_IncidentData> incident_data) { 363 scoped_ptr<ClientIncidentReport_IncidentData> incident_data) {
280 DCHECK(thread_checker_.CalledOnValidThread()); 364 DCHECK(thread_checker_.CalledOnValidThread());
281 // Incidents outside the context of a profile are not supported at the moment. 365 // Incidents outside the context of a profile are not supported at the moment.
282 DCHECK(profile); 366 DCHECK(profile);
367 DCHECK_EQ(1U, CountIncidents(*incident_data));
283 368
284 ProfileContext* context = GetProfileContext(profile); 369 ProfileContext* context = GetProfileContext(profile);
285 // It is forbidden to call this function with a destroyed profile. 370 // It is forbidden to call this function with a destroyed profile.
286 DCHECK(context); 371 DCHECK(context);
287 372
288 // Drop the incident immediately if profile creation has completed and the 373 // Drop the incident immediately if profile creation has completed and the
289 // profile is not participating in safe browsing. Preference evaluation is 374 // profile is not participating in safe browsing. Preference evaluation is
290 // deferred until OnProfileAdded() if profile creation has not yet 375 // deferred until OnProfileAdded() if profile creation has not yet
291 // completed. 376 // completed.
292 if (context->added && 377 if (context->added &&
(...skipping 216 matching lines...) Expand 10 before | Expand all | Expand 10 after
509 if (g_browser_process->local_state()->FindPreference( 594 if (g_browser_process->local_state()->FindPreference(
510 prefs::kMetricsReportingEnabled)) { 595 prefs::kMetricsReportingEnabled)) {
511 process->set_metrics_consent(g_browser_process->local_state()->GetBoolean( 596 process->set_metrics_consent(g_browser_process->local_state()->GetBoolean(
512 prefs::kMetricsReportingEnabled)); 597 prefs::kMetricsReportingEnabled));
513 } 598 }
514 599
515 // Check for extended consent in any profile while collecting incidents. 600 // Check for extended consent in any profile while collecting incidents.
516 process->set_extended_consent(false); 601 process->set_extended_consent(false);
517 // Collect incidents across all profiles participating in safe browsing. Drop 602 // Collect incidents across all profiles participating in safe browsing. Drop
518 // incidents if the profile stopped participating before collection completed. 603 // incidents if the profile stopped participating before collection completed.
519 // Prune incidents if the profile has already submitted any incidents. 604 // Prune previously submitted incidents.
520 // Associate the participating profiles with the upload. 605 // Associate the profiles and their incident data with the upload.
521 size_t prune_count = 0; 606 size_t prune_count = 0;
522 std::vector<Profile*> profiles; 607 UploadContext::PersistentIncidentStateCollection profiles_to_state;
523 for (ProfileContextCollection::iterator scan = profiles_.begin(); 608 for (ProfileContextCollection::iterator scan = profiles_.begin();
524 scan != profiles_.end(); 609 scan != profiles_.end();
525 ++scan) { 610 ++scan) {
526 PrefService* prefs = scan->first->GetPrefs(); 611 PrefService* prefs = scan->first->GetPrefs();
527 if (process && 612 if (process &&
528 prefs->GetBoolean(prefs::kSafeBrowsingExtendedReportingEnabled)) { 613 prefs->GetBoolean(prefs::kSafeBrowsingExtendedReportingEnabled)) {
529 process->set_extended_consent(true); 614 process->set_extended_consent(true);
530 process = NULL; // Don't check any more once one is found. 615 process = NULL; // Don't check any more once one is found.
531 } 616 }
532 ProfileContext* context = scan->second; 617 ProfileContext* context = scan->second;
533 if (context->incidents.empty()) 618 if (context->incidents.empty())
534 continue; 619 continue;
535 if (!prefs->GetBoolean(prefs::kSafeBrowsingEnabled)) { 620 if (!prefs->GetBoolean(prefs::kSafeBrowsingEnabled)) {
536 for (size_t i = 0; i < context->incidents.size(); ++i) { 621 for (size_t i = 0; i < context->incidents.size(); ++i) {
537 LogIncidentDataType(DROPPED, *context->incidents[i]); 622 LogIncidentDataType(DROPPED, *context->incidents[i]);
538 } 623 }
539 context->incidents.clear(); 624 context->incidents.clear();
540 } else if (prefs->GetBoolean(prefs::kSafeBrowsingIncidentReportSent)) { 625 continue;
541 // Prune all incidents. 626 }
542 // TODO(grt): Only prune previously submitted incidents; 627 std::vector<PersistentIncidentState> states;
543 // http://crbug.com/383043. 628 const base::DictionaryValue* incidents_sent =
544 prune_count += context->incidents.size(); 629 prefs->GetDictionary(prefs::kSafeBrowsingIncidentsSent);
630 // Prep persistent data and prune any incidents already sent.
631 for (size_t i = 0; i < context->incidents.size(); ++i) {
632 ClientIncidentReport_IncidentData* incident = context->incidents[i];
633 const PersistentIncidentState state = ComputeIncidentState(*incident);
634 if (IncidentHasBeenReported(incidents_sent, state)) {
635 ++prune_count;
636 delete context->incidents[i];
637 context->incidents[i] = NULL;
638 } else {
639 states.push_back(state);
640 }
641 }
642 if (prefs->GetBoolean(prefs::kSafeBrowsingIncidentReportSent)) {
643 // Prune all incidents as if they had been reported, migrating to the new
644 // technique. TODO(grt): remove this branch after it has shipped.
645 for (size_t i = 0; i < context->incidents.size(); ++i) {
646 if (context->incidents[i])
647 ++prune_count;
648 }
545 context->incidents.clear(); 649 context->incidents.clear();
650 prefs->ClearPref(prefs::kSafeBrowsingIncidentReportSent);
651 DictionaryPrefUpdate pref_update(prefs,
652 prefs::kSafeBrowsingIncidentsSent);
653 MarkIncidentsAsReported(states, pref_update.Get());
546 } else { 654 } else {
547 for (size_t i = 0; i < context->incidents.size(); ++i) { 655 for (size_t i = 0; i < context->incidents.size(); ++i) {
548 ClientIncidentReport_IncidentData* incident = context->incidents[i]; 656 ClientIncidentReport_IncidentData* incident = context->incidents[i];
549 LogIncidentDataType(ACCEPTED, *incident); 657 if (incident) {
550 // Ownership of the incident is passed to the report. 658 LogIncidentDataType(ACCEPTED, *incident);
551 report->mutable_incident()->AddAllocated(incident); 659 // Ownership of the incident is passed to the report.
660 report->mutable_incident()->AddAllocated(incident);
661 }
552 } 662 }
553 context->incidents.weak_clear(); 663 context->incidents.weak_clear();
554 profiles.push_back(scan->first); 664 std::vector<PersistentIncidentState>& profile_states =
665 profiles_to_state[scan->first];
666 profile_states.swap(states);
555 } 667 }
556 } 668 }
557 669
558 const int count = report->incident_size(); 670 const int count = report->incident_size();
559 // Abandon the request if all incidents were dropped with none pruned. 671 // Abandon the request if all incidents were dropped with none pruned.
560 if (!count && !prune_count) 672 if (!count && !prune_count)
561 return; 673 return;
562 674
563 UMA_HISTOGRAM_COUNTS_100("SBIRS.IncidentCount", count + prune_count); 675 UMA_HISTOGRAM_COUNTS_100("SBIRS.IncidentCount", count + prune_count);
564 676
565 { 677 {
566 double prune_pct = static_cast<double>(prune_count); 678 double prune_pct = static_cast<double>(prune_count);
567 prune_pct = prune_pct * 100.0 / (count + prune_count); 679 prune_pct = prune_pct * 100.0 / (count + prune_count);
568 prune_pct = round(prune_pct); 680 prune_pct = round(prune_pct);
569 UMA_HISTOGRAM_PERCENTAGE("SBIRS.PruneRatio", static_cast<int>(prune_pct)); 681 UMA_HISTOGRAM_PERCENTAGE("SBIRS.PruneRatio", static_cast<int>(prune_pct));
570 } 682 }
571 // Abandon the report if all incidents were pruned. 683 // Abandon the report if all incidents were pruned.
572 if (!count) 684 if (!count)
573 return; 685 return;
574 686
575 scoped_ptr<UploadContext> context(new UploadContext(report.Pass())); 687 scoped_ptr<UploadContext> context(new UploadContext(report.Pass()));
576 context->profiles.swap(profiles); 688 context->profiles_to_state.swap(profiles_to_state);
577 if (!database_manager_) { 689 if (!database_manager_) {
578 // No database manager during testing. Take ownership of the context and 690 // No database manager during testing. Take ownership of the context and
579 // continue processing. 691 // continue processing.
580 UploadContext* temp_context = context.get(); 692 UploadContext* temp_context = context.get();
581 uploads_.push_back(context.release()); 693 uploads_.push_back(context.release());
582 IncidentReportingService::OnKillSwitchResult(temp_context, false); 694 IncidentReportingService::OnKillSwitchResult(temp_context, false);
583 } else { 695 } else {
584 if (content::BrowserThread::PostTaskAndReplyWithResult( 696 if (content::BrowserThread::PostTaskAndReplyWithResult(
585 content::BrowserThread::IO, 697 content::BrowserThread::IO,
586 FROM_HERE, 698 FROM_HERE,
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
621 scoped_ptr<ClientIncidentResponse>()); 733 scoped_ptr<ClientIncidentResponse>());
622 } 734 }
623 } else { 735 } else {
624 OnReportUploadResult(context, 736 OnReportUploadResult(context,
625 IncidentReportUploader::UPLOAD_SUPPRESSED, 737 IncidentReportUploader::UPLOAD_SUPPRESSED,
626 scoped_ptr<ClientIncidentResponse>()); 738 scoped_ptr<ClientIncidentResponse>());
627 } 739 }
628 } 740 }
629 741
630 void IncidentReportingService::HandleResponse(const UploadContext& context) { 742 void IncidentReportingService::HandleResponse(const UploadContext& context) {
631 for (size_t i = 0; i < context.profiles.size(); ++i) { 743 for (UploadContext::PersistentIncidentStateCollection::const_iterator scan =
632 context.profiles[i]->GetPrefs()->SetBoolean( 744 context.profiles_to_state.begin();
633 prefs::kSafeBrowsingIncidentReportSent, true); 745 scan != context.profiles_to_state.end();
746 ++scan) {
747 DictionaryPrefUpdate pref_update(scan->first->GetPrefs(),
748 prefs::kSafeBrowsingIncidentsSent);
749 MarkIncidentsAsReported(scan->second, pref_update.Get());
634 } 750 }
635 } 751 }
636 752
637 void IncidentReportingService::OnReportUploadResult( 753 void IncidentReportingService::OnReportUploadResult(
638 UploadContext* context, 754 UploadContext* context,
639 IncidentReportUploader::Result result, 755 IncidentReportUploader::Result result,
640 scoped_ptr<ClientIncidentResponse> response) { 756 scoped_ptr<ClientIncidentResponse> response) {
641 DCHECK(thread_checker_.CalledOnValidThread()); 757 DCHECK(thread_checker_.CalledOnValidThread());
642 758
643 UMA_HISTOGRAM_ENUMERATION( 759 UMA_HISTOGRAM_ENUMERATION(
(...skipping 29 matching lines...) Expand all
673 if (!profile->IsOffTheRecord()) 789 if (!profile->IsOffTheRecord())
674 OnProfileDestroyed(profile); 790 OnProfileDestroyed(profile);
675 break; 791 break;
676 } 792 }
677 default: 793 default:
678 break; 794 break;
679 } 795 }
680 } 796 }
681 797
682 } // namespace safe_browsing 798 } // namespace safe_browsing
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698