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
3 // found in the LICENSE file.
5 #include "chrome/browser/safe_browsing/incident_reporting/state_store.h"
7 #include "base/metrics/histogram_macros.h"
8 #include "base/prefs/pref_service.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "base/values.h"
11 #include "chrome/browser/profiles/profile.h"
12 #include "chrome/browser/safe_browsing/incident_reporting/incident.h"
13 #include "chrome/browser/safe_browsing/incident_reporting/platform_state_store.h"
14 #include "chrome/common/pref_names.h"
16 namespace safe_browsing
{
18 // StateStore::Transaction -----------------------------------------------------
20 StateStore::Transaction::Transaction(StateStore
* store
) : store_(store
) {
22 DCHECK(!store_
->has_transaction_
);
23 store_
->has_transaction_
= true;
27 StateStore::Transaction::~Transaction() {
29 store_
->has_transaction_
= false;
32 platform_state_store::Store(store_
->profile_
, store_
->incidents_sent_
);
35 void StateStore::Transaction::MarkAsReported(IncidentType type
,
36 const std::string
& key
,
37 IncidentDigest digest
) {
38 std::string
type_string(base::IntToString(static_cast<int32_t>(type
)));
39 base::DictionaryValue
* incidents_sent
= GetPrefDict();
40 base::DictionaryValue
* type_dict
= nullptr;
41 if (!incidents_sent
->GetDictionaryWithoutPathExpansion(type_string
,
43 type_dict
= new base::DictionaryValue();
44 incidents_sent
->SetWithoutPathExpansion(type_string
, type_dict
);
46 type_dict
->SetStringWithoutPathExpansion(key
, base::UintToString(digest
));
49 void StateStore::Transaction::ClearForType(IncidentType type
) {
50 // Nothing to do if the pref dict does not exist.
51 if (!store_
->incidents_sent_
)
54 // Use the read-only view on the preference to figure out if there is a value
55 // to remove before committing to making a change since any use of GetPrefDict
56 // will result in a full serialize-and-write operation on the preferences
58 std::string
type_string(base::IntToString(static_cast<int32_t>(type
)));
59 const base::DictionaryValue
* type_dict
= nullptr;
60 if (store_
->incidents_sent_
->GetDictionaryWithoutPathExpansion(type_string
,
62 GetPrefDict()->RemoveWithoutPathExpansion(type_string
, nullptr);
66 void StateStore::Transaction::ClearAll() {
67 // Clear the preference if it exists and contains any values.
68 if (store_
->incidents_sent_
&& !store_
->incidents_sent_
->empty())
69 GetPrefDict()->Clear();
72 base::DictionaryValue
* StateStore::Transaction::GetPrefDict() {
74 pref_update_
.reset(new DictionaryPrefUpdate(
75 store_
->profile_
->GetPrefs(), prefs::kSafeBrowsingIncidentsSent
));
76 // Getting the dict will cause it to be created if it doesn't exist.
77 // Unconditionally refresh the store's read-only view on the preference so
78 // that it will always be correct.
79 store_
->incidents_sent_
= pref_update_
->Get();
81 return pref_update_
->Get();
84 void StateStore::Transaction::ReplacePrefDict(
85 scoped_ptr
<base::DictionaryValue
> pref_dict
) {
86 GetPrefDict()->Swap(pref_dict
.get());
90 // StateStore ------------------------------------------------------------------
92 StateStore::StateStore(Profile
* profile
)
94 incidents_sent_(nullptr)
97 has_transaction_(false)
100 // Cache a read-only view of the preference.
101 const base::Value
* value
=
102 profile_
->GetPrefs()->GetUserPrefValue(prefs::kSafeBrowsingIncidentsSent
);
104 value
->GetAsDictionary(&incidents_sent_
);
106 // Apply the platform data.
107 Transaction
transaction(this);
108 scoped_ptr
<base::DictionaryValue
> value_dict(
109 platform_state_store::Load(profile_
));
111 InitializationResult state_store_init_result
= PSS_MATCHES
;
113 state_store_init_result
= PSS_NULL
;
114 } else if (value_dict
->empty()) {
115 if (incidents_sent_
&& !incidents_sent_
->empty())
116 state_store_init_result
= PSS_EMPTY
;
117 transaction
.ClearAll();
118 } else if (!incidents_sent_
|| !incidents_sent_
->Equals(value_dict
.get())) {
119 state_store_init_result
= PSS_DIFFERS
;
120 transaction
.ReplacePrefDict(value_dict
.Pass());
122 UMA_HISTOGRAM_ENUMERATION("SBIRS.StateStoreInit", state_store_init_result
,
123 NUM_INITIALIZATION_RESULTS
);
125 CleanLegacyValues(&transaction
);
128 StateStore::~StateStore() {
130 DCHECK(!has_transaction_
);
134 bool StateStore::HasBeenReported(IncidentType type
,
135 const std::string
& key
,
136 IncidentDigest digest
) {
137 const base::DictionaryValue
* type_dict
= nullptr;
138 std::string digest_string
;
139 return (incidents_sent_
&&
140 incidents_sent_
->GetDictionaryWithoutPathExpansion(
141 base::IntToString(static_cast<int32_t>(type
)), &type_dict
) &&
142 type_dict
->GetStringWithoutPathExpansion(key
, &digest_string
) &&
143 digest_string
== base::UintToString(digest
));
146 void StateStore::CleanLegacyValues(Transaction
* transaction
) {
147 static const IncidentType kLegacyTypes
[] = {
148 // TODO(grt): remove in M44 (crbug.com/451173).
149 IncidentType::OMNIBOX_INTERACTION
,
152 for (IncidentType type
: kLegacyTypes
)
153 transaction
->ClearForType(type
);
156 } // namespace safe_browsing