Allow only one bookmark to be added for multiple fast starring
[chromium-blink-merge.git] / chrome / browser / push_messaging / push_messaging_app_identifier.cc
blobcfb8c5f1732b1d9a9f7c857acef0acd07b082c80
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"
7 #include <string.h>
9 #include "base/guid.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:";
23 namespace {
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)
44 return false;
46 if (!base::StringToInt64(parts[1], service_worker_registration_id))
47 return false;
49 *origin = GURL(parts[0]);
50 return origin->is_valid();
53 } // namespace
55 // static
56 void PushMessagingAppIdentifier::RegisterProfilePrefs(
57 user_prefs::PrefRegistrySyncable* registry) {
58 registry->RegisterDictionaryPref(prefs::kPushMessagingAppIdentifierMap);
61 // static
62 PushMessagingAppIdentifier PushMessagingAppIdentifier::Generate(
63 const GURL& origin, int64_t service_worker_registration_id)
65 std::string guid = base::GenerateGUID();
66 CHECK(!guid.empty());
67 std::string app_id = kPushMessagingAppIdentifierPrefix + origin.spec()
68 + kSeparator + guid;
70 PushMessagingAppIdentifier app_identifier(app_id, origin,
71 service_worker_registration_id);
72 app_identifier.DCheckValid();
73 return app_identifier;
76 // static
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();
99 GURL origin;
100 int64_t service_worker_registration_id;
101 if (!GetOriginAndSWRFromPrefValue(map_value, &origin,
102 &service_worker_registration_id)) {
103 NOTREACHED();
104 return PushMessagingAppIdentifier();
107 PushMessagingAppIdentifier app_identifier(app_id, origin,
108 service_worker_registration_id);
109 app_identifier.DCheckValid();
110 return app_identifier;
113 // static
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();
124 it.Advance()) {
125 if (it.value().Equals(&pref_value))
126 return FindByAppId(profile, it.key());
128 return PushMessagingAppIdentifier();
131 // static
132 std::vector<PushMessagingAppIdentifier> PushMessagingAppIdentifier::GetAll(
133 Profile* profile) {
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();
139 it.Advance()) {
140 result.push_back(FindByAppId(profile, it.key()));
143 return result;
146 PushMessagingAppIdentifier::PushMessagingAppIdentifier()
147 : origin_(GURL::EmptyGURL()),
148 service_worker_registration_id_(-1) {
151 PushMessagingAppIdentifier::PushMessagingAppIdentifier(
152 const std::string& app_id,
153 const GURL& origin,
154 int64_t service_worker_registration_id)
155 : app_id_(app_id),
156 origin_(origin),
157 service_worker_registration_id_(service_worker_registration_id) {
160 PushMessagingAppIdentifier::~PushMessagingAppIdentifier() {
163 void PushMessagingAppIdentifier::PersistToPrefs(Profile* profile) const {
164 DCheckValid();
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_);
174 if (!old.is_null())
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 {
182 DCheckValid();
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_);
196 // "wp:"
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));
208 // GUID
209 DCHECK(base::IsValidGUID(app_id_.substr(app_id_.size() - kGuidLength)));