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::ToUpperASCII(app_id
.substr(app_id
.size() - kGuidLength
)));
91 const base::DictionaryValue
* map
=
92 profile
->GetPrefs()->GetDictionary(prefs::kPushMessagingAppIdentifierMap
);
94 std::string map_value
;
95 if (!map
->GetStringWithoutPathExpansion(app_id
, &map_value
))
96 return PushMessagingAppIdentifier();
99 int64_t service_worker_registration_id
;
100 if (!GetOriginAndSWRFromPrefValue(map_value
, &origin
,
101 &service_worker_registration_id
)) {
103 return PushMessagingAppIdentifier();
106 PushMessagingAppIdentifier
app_identifier(app_id
, origin
,
107 service_worker_registration_id
);
108 app_identifier
.DCheckValid();
109 return app_identifier
;
113 PushMessagingAppIdentifier
PushMessagingAppIdentifier::FindByServiceWorker(
114 Profile
* profile
, const GURL
& origin
,
115 int64_t service_worker_registration_id
)
117 const base::StringValue pref_value
=
118 base::StringValue(MakePrefValue(origin
, service_worker_registration_id
));
120 const base::DictionaryValue
* map
=
121 profile
->GetPrefs()->GetDictionary(prefs::kPushMessagingAppIdentifierMap
);
122 for (auto it
= base::DictionaryValue::Iterator(*map
); !it
.IsAtEnd();
124 if (it
.value().Equals(&pref_value
))
125 return FindByAppId(profile
, it
.key());
127 return PushMessagingAppIdentifier();
131 std::vector
<PushMessagingAppIdentifier
> PushMessagingAppIdentifier::GetAll(
133 std::vector
<PushMessagingAppIdentifier
> result
;
135 const base::DictionaryValue
* map
=
136 profile
->GetPrefs()->GetDictionary(prefs::kPushMessagingAppIdentifierMap
);
137 for (auto it
= base::DictionaryValue::Iterator(*map
); !it
.IsAtEnd();
139 result
.push_back(FindByAppId(profile
, it
.key()));
145 PushMessagingAppIdentifier::PushMessagingAppIdentifier()
146 : origin_(GURL::EmptyGURL()),
147 service_worker_registration_id_(-1) {
150 PushMessagingAppIdentifier::PushMessagingAppIdentifier(
151 const std::string
& app_id
,
153 int64_t service_worker_registration_id
)
156 service_worker_registration_id_(service_worker_registration_id
) {
159 PushMessagingAppIdentifier::~PushMessagingAppIdentifier() {
162 void PushMessagingAppIdentifier::PersistToPrefs(Profile
* profile
) const {
165 DictionaryPrefUpdate
update(profile
->GetPrefs(),
166 prefs::kPushMessagingAppIdentifierMap
);
167 base::DictionaryValue
* map
= update
.Get();
169 // Delete any stale entry with the same origin and Service Worker
170 // registration id (hence we ensure there is a 1:1 not 1:many mapping).
171 PushMessagingAppIdentifier old
= FindByServiceWorker(
172 profile
, origin_
, service_worker_registration_id_
);
174 map
->RemoveWithoutPathExpansion(old
.app_id_
, nullptr /* out_value */);
176 map
->SetStringWithoutPathExpansion(
177 app_id_
, MakePrefValue(origin_
, service_worker_registration_id_
));
180 void PushMessagingAppIdentifier::DeleteFromPrefs(Profile
* profile
) const {
183 DictionaryPrefUpdate
update(profile
->GetPrefs(),
184 prefs::kPushMessagingAppIdentifierMap
);
185 base::DictionaryValue
* map
= update
.Get();
186 map
->RemoveWithoutPathExpansion(app_id_
, nullptr /* out_value */);
189 void PushMessagingAppIdentifier::DCheckValid() const {
190 DCHECK_GE(service_worker_registration_id_
, 0);
192 DCHECK(origin_
.is_valid());
193 DCHECK_EQ(origin_
.GetOrigin(), origin_
);
196 DCHECK_EQ(kPushMessagingAppIdentifierPrefix
,
197 app_id_
.substr(0, kPrefixLength
));
198 // Optional (origin.spec() + '#')
199 if (app_id_
.size() != kPrefixLength
+ kGuidLength
) {
200 const size_t suffix_length
= 1 /* kSeparator */ + kGuidLength
;
201 DCHECK(app_id_
.size() > kPrefixLength
+ suffix_length
);
202 DCHECK_EQ(origin_
, GURL(app_id_
.substr(
203 kPrefixLength
, app_id_
.size() - kPrefixLength
- suffix_length
)));
204 DCHECK_EQ(std::string(1, kSeparator
),
205 app_id_
.substr(app_id_
.size() - suffix_length
, 1));
208 DCHECK(base::IsValidGUID(app_id_
.substr(app_id_
.size() - kGuidLength
)));