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
= base::SplitString(
41 pref_value
, std::string(1, kSeparator
),
42 base::TRIM_WHITESPACE
, base::SPLIT_WANT_ALL
);
43 if (parts
.size() != 2)
46 if (!base::StringToInt64(parts
[1], service_worker_registration_id
))
49 *origin
= GURL(parts
[0]);
50 return origin
->is_valid();
56 void PushMessagingAppIdentifier::RegisterProfilePrefs(
57 user_prefs::PrefRegistrySyncable
* registry
) {
58 registry
->RegisterDictionaryPref(prefs::kPushMessagingAppIdentifierMap
);
62 PushMessagingAppIdentifier
PushMessagingAppIdentifier::Generate(
63 const GURL
& origin
, int64_t service_worker_registration_id
)
65 std::string guid
= base::GenerateGUID();
67 std::string app_id
= kPushMessagingAppIdentifierPrefix
+ origin
.spec()
70 PushMessagingAppIdentifier
app_identifier(app_id
, origin
,
71 service_worker_registration_id
);
72 app_identifier
.DCheckValid();
73 return app_identifier
;
77 PushMessagingAppIdentifier
PushMessagingAppIdentifier::FindByAppId(
78 Profile
* profile
, const std::string
& app_id
) {
79 if (!base::StartsWith(app_id
, kPushMessagingAppIdentifierPrefix
,
80 base::CompareCase::INSENSITIVE_ASCII
)) {
81 return PushMessagingAppIdentifier();
84 // Since we now know this is a Push Messaging app_id, check the case hasn't
85 // been mangled (crbug.com/461867).
86 DCHECK_EQ(kPushMessagingAppIdentifierPrefix
, app_id
.substr(0, kPrefixLength
));
87 DCHECK_GE(app_id
.size(), kPrefixLength
+ kGuidLength
);
88 DCHECK_EQ(app_id
.substr(app_id
.size() - kGuidLength
),
89 base::StringToUpperASCII(
90 app_id
.substr(app_id
.size() - kGuidLength
)));
92 const base::DictionaryValue
* map
=
93 profile
->GetPrefs()->GetDictionary(prefs::kPushMessagingAppIdentifierMap
);
95 std::string map_value
;
96 if (!map
->GetStringWithoutPathExpansion(app_id
, &map_value
))
97 return PushMessagingAppIdentifier();
100 int64_t service_worker_registration_id
;
101 if (!GetOriginAndSWRFromPrefValue(map_value
, &origin
,
102 &service_worker_registration_id
)) {
104 return PushMessagingAppIdentifier();
107 PushMessagingAppIdentifier
app_identifier(app_id
, origin
,
108 service_worker_registration_id
);
109 app_identifier
.DCheckValid();
110 return app_identifier
;
114 PushMessagingAppIdentifier
PushMessagingAppIdentifier::FindByServiceWorker(
115 Profile
* profile
, const GURL
& origin
,
116 int64_t service_worker_registration_id
)
118 const base::StringValue pref_value
=
119 base::StringValue(MakePrefValue(origin
, service_worker_registration_id
));
121 const base::DictionaryValue
* map
=
122 profile
->GetPrefs()->GetDictionary(prefs::kPushMessagingAppIdentifierMap
);
123 for (auto it
= base::DictionaryValue::Iterator(*map
); !it
.IsAtEnd();
125 if (it
.value().Equals(&pref_value
))
126 return FindByAppId(profile
, it
.key());
128 return PushMessagingAppIdentifier();
132 std::vector
<PushMessagingAppIdentifier
> PushMessagingAppIdentifier::GetAll(
134 std::vector
<PushMessagingAppIdentifier
> result
;
136 const base::DictionaryValue
* map
=
137 profile
->GetPrefs()->GetDictionary(prefs::kPushMessagingAppIdentifierMap
);
138 for (auto it
= base::DictionaryValue::Iterator(*map
); !it
.IsAtEnd();
140 result
.push_back(FindByAppId(profile
, it
.key()));
146 PushMessagingAppIdentifier::PushMessagingAppIdentifier()
147 : origin_(GURL::EmptyGURL()),
148 service_worker_registration_id_(-1) {
151 PushMessagingAppIdentifier::PushMessagingAppIdentifier(
152 const std::string
& app_id
,
154 int64_t service_worker_registration_id
)
157 service_worker_registration_id_(service_worker_registration_id
) {
160 PushMessagingAppIdentifier::~PushMessagingAppIdentifier() {
163 void PushMessagingAppIdentifier::PersistToPrefs(Profile
* profile
) const {
166 DictionaryPrefUpdate
update(profile
->GetPrefs(),
167 prefs::kPushMessagingAppIdentifierMap
);
168 base::DictionaryValue
* map
= update
.Get();
170 // Delete any stale entry with the same origin and Service Worker
171 // registration id (hence we ensure there is a 1:1 not 1:many mapping).
172 PushMessagingAppIdentifier old
= FindByServiceWorker(
173 profile
, origin_
, service_worker_registration_id_
);
175 map
->RemoveWithoutPathExpansion(old
.app_id_
, nullptr /* out_value */);
177 map
->SetStringWithoutPathExpansion(
178 app_id_
, MakePrefValue(origin_
, service_worker_registration_id_
));
181 void PushMessagingAppIdentifier::DeleteFromPrefs(Profile
* profile
) const {
184 DictionaryPrefUpdate
update(profile
->GetPrefs(),
185 prefs::kPushMessagingAppIdentifierMap
);
186 base::DictionaryValue
* map
= update
.Get();
187 map
->RemoveWithoutPathExpansion(app_id_
, nullptr /* out_value */);
190 void PushMessagingAppIdentifier::DCheckValid() const {
191 DCHECK_GE(service_worker_registration_id_
, 0);
193 DCHECK(origin_
.is_valid());
194 DCHECK_EQ(origin_
.GetOrigin(), origin_
);
197 DCHECK_EQ(kPushMessagingAppIdentifierPrefix
,
198 app_id_
.substr(0, kPrefixLength
));
199 // Optional (origin.spec() + '#')
200 if (app_id_
.size() != kPrefixLength
+ kGuidLength
) {
201 const size_t suffix_length
= 1 /* kSeparator */ + kGuidLength
;
202 DCHECK(app_id_
.size() > kPrefixLength
+ suffix_length
);
203 DCHECK_EQ(origin_
, GURL(app_id_
.substr(
204 kPrefixLength
, app_id_
.size() - kPrefixLength
- suffix_length
)));
205 DCHECK_EQ(std::string(1, kSeparator
),
206 app_id_
.substr(app_id_
.size() - suffix_length
, 1));
209 DCHECK(base::IsValidGUID(app_id_
.substr(app_id_
.size() - kGuidLength
)));