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
3 // found in the LICENSE file.
5 #include "chrome/browser/push_messaging/push_messaging_app_identifier.h"
10 #include "base/logging.h"
11 #include "base/prefs/pref_service.h"
12 #include "base/prefs/scoped_user_pref_update.h"
13 #include "base/strings/string_number_conversions.h"
14 #include "base/strings/string_split.h"
15 #include "base/strings/string_util.h"
16 #include "base/values.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/common/pref_names.h"
19 #include "components/pref_registry/pref_registry_syncable.h"
21 const char kPushMessagingAppIdentifierPrefix
[] = "wp:";
25 // sizeof is strlen + 1 since it's null-terminated.
26 const size_t kPrefixLength
= sizeof(kPushMessagingAppIdentifierPrefix
) - 1;
28 const char kSeparator
= '#'; // Ok as only the origin of the url is used.
29 const size_t kGuidLength
= 36; // "%08X-%04X-%04X-%04X-%012llX"
31 std::string
MakePrefValue(const GURL
& origin
,
32 int64_t service_worker_registration_id
) {
33 return origin
.spec() + kSeparator
34 + base::Int64ToString(service_worker_registration_id
);
37 bool GetOriginAndSWRFromPrefValue(
38 const std::string
& pref_value
, GURL
* origin
,
39 int64_t* service_worker_registration_id
) {
40 std::vector
<std::string
> parts
;
41 base::SplitString(pref_value
, kSeparator
, &parts
);
42 if (parts
.size() != 2)
45 if (!base::StringToInt64(parts
[1], service_worker_registration_id
))
48 *origin
= GURL(parts
[0]);
49 return origin
->is_valid();
55 void PushMessagingAppIdentifier::RegisterProfilePrefs(
56 user_prefs::PrefRegistrySyncable
* registry
) {
57 registry
->RegisterDictionaryPref(prefs::kPushMessagingAppIdentifierMap
);
61 PushMessagingAppIdentifier
PushMessagingAppIdentifier::Generate(
62 const GURL
& origin
, int64_t service_worker_registration_id
)
64 std::string guid
= base::GenerateGUID();
66 std::string app_id
= kPushMessagingAppIdentifierPrefix
+ origin
.spec()
69 PushMessagingAppIdentifier
app_identifier(app_id
, origin
,
70 service_worker_registration_id
);
71 app_identifier
.DCheckValid();
72 return app_identifier
;
76 PushMessagingAppIdentifier
PushMessagingAppIdentifier::FindByAppId(
77 Profile
* profile
, const std::string
& app_id
) {
78 if (!StartsWithASCII(app_id
, kPushMessagingAppIdentifierPrefix
,
79 false /* case_sensitive */)) {
80 return PushMessagingAppIdentifier();
83 // Since we now know this is a Push Messaging app_id, check the case hasn't
84 // been mangled (crbug.com/461867).
85 DCHECK_EQ(kPushMessagingAppIdentifierPrefix
, app_id
.substr(0, kPrefixLength
));
86 DCHECK_GE(app_id
.size(), kPrefixLength
+ kGuidLength
);
87 DCHECK_EQ(app_id
.substr(app_id
.size() - kGuidLength
),
88 StringToUpperASCII(app_id
.substr(app_id
.size() - kGuidLength
)));
90 const base::DictionaryValue
* map
=
91 profile
->GetPrefs()->GetDictionary(prefs::kPushMessagingAppIdentifierMap
);
93 std::string map_value
;
94 if (!map
->GetStringWithoutPathExpansion(app_id
, &map_value
))
95 return PushMessagingAppIdentifier();
98 int64_t service_worker_registration_id
;
99 if (!GetOriginAndSWRFromPrefValue(map_value
, &origin
,
100 &service_worker_registration_id
)) {
102 return PushMessagingAppIdentifier();
105 PushMessagingAppIdentifier
app_identifier(app_id
, origin
,
106 service_worker_registration_id
);
107 app_identifier
.DCheckValid();
108 return app_identifier
;
112 PushMessagingAppIdentifier
PushMessagingAppIdentifier::FindByServiceWorker(
113 Profile
* profile
, const GURL
& origin
,
114 int64_t service_worker_registration_id
)
116 const base::StringValue pref_value
=
117 base::StringValue(MakePrefValue(origin
, service_worker_registration_id
));
119 const base::DictionaryValue
* map
=
120 profile
->GetPrefs()->GetDictionary(prefs::kPushMessagingAppIdentifierMap
);
121 for (auto it
= base::DictionaryValue::Iterator(*map
); !it
.IsAtEnd();
123 if (it
.value().Equals(&pref_value
))
124 return FindByAppId(profile
, it
.key());
126 return PushMessagingAppIdentifier();
130 std::vector
<PushMessagingAppIdentifier
> PushMessagingAppIdentifier::GetAll(
132 std::vector
<PushMessagingAppIdentifier
> result
;
134 const base::DictionaryValue
* map
=
135 profile
->GetPrefs()->GetDictionary(prefs::kPushMessagingAppIdentifierMap
);
136 for (auto it
= base::DictionaryValue::Iterator(*map
); !it
.IsAtEnd();
138 result
.push_back(FindByAppId(profile
, it
.key()));
144 PushMessagingAppIdentifier::PushMessagingAppIdentifier()
145 : origin_(GURL::EmptyGURL()),
146 service_worker_registration_id_(-1) {
149 PushMessagingAppIdentifier::PushMessagingAppIdentifier(
150 const std::string
& app_id
,
152 int64_t service_worker_registration_id
)
155 service_worker_registration_id_(service_worker_registration_id
) {
158 PushMessagingAppIdentifier::~PushMessagingAppIdentifier() {
161 void PushMessagingAppIdentifier::PersistToPrefs(Profile
* profile
) const {
164 DictionaryPrefUpdate
update(profile
->GetPrefs(),
165 prefs::kPushMessagingAppIdentifierMap
);
166 base::DictionaryValue
* map
= update
.Get();
168 // Delete any stale entry with the same origin and Service Worker
169 // registration id (hence we ensure there is a 1:1 not 1:many mapping).
170 PushMessagingAppIdentifier old
= FindByServiceWorker(
171 profile
, origin_
, service_worker_registration_id_
);
173 map
->RemoveWithoutPathExpansion(old
.app_id_
, nullptr /* out_value */);
175 map
->SetStringWithoutPathExpansion(
176 app_id_
, MakePrefValue(origin_
, service_worker_registration_id_
));
179 void PushMessagingAppIdentifier::DeleteFromPrefs(Profile
* profile
) const {
182 DictionaryPrefUpdate
update(profile
->GetPrefs(),
183 prefs::kPushMessagingAppIdentifierMap
);
184 base::DictionaryValue
* map
= update
.Get();
185 map
->RemoveWithoutPathExpansion(app_id_
, nullptr /* out_value */);
188 void PushMessagingAppIdentifier::DCheckValid() const {
189 DCHECK_GE(service_worker_registration_id_
, 0);
191 DCHECK(origin_
.is_valid());
192 DCHECK_EQ(origin_
.GetOrigin(), origin_
);
195 DCHECK_EQ(kPushMessagingAppIdentifierPrefix
,
196 app_id_
.substr(0, kPrefixLength
));
197 // Optional (origin.spec() + '#')
198 if (app_id_
.size() != kPrefixLength
+ kGuidLength
) {
199 const size_t suffix_length
= 1 /* kSeparator */ + kGuidLength
;
200 DCHECK(app_id_
.size() > kPrefixLength
+ suffix_length
);
201 DCHECK_EQ(origin_
, GURL(app_id_
.substr(
202 kPrefixLength
, app_id_
.size() - kPrefixLength
- suffix_length
)));
203 DCHECK_EQ(std::string(1, kSeparator
),
204 app_id_
.substr(app_id_
.size() - suffix_length
, 1));
207 DCHECK(base::IsValidGUID(app_id_
.substr(app_id_
.size() - kGuidLength
)));