blob: 1c3046553436cf1372a8812c8df85a0a92aac0ca [file] [log] [blame]
Rayan Kanso296eb3d2019-06-26 11:32:041// Copyright 2019 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.
4
5#include "content/browser/content_index/content_index_database.h"
6
7#include <string>
8
Rayan Kanso24f7d412019-07-11 10:09:309#include "base/task/post_task.h"
Rayan Kanso296eb3d2019-06-26 11:32:0410#include "base/time/time.h"
Rayan Kanso837b7952019-07-01 10:31:2911#include "content/browser/background_fetch/storage/image_helpers.h"
Rayan Kanso296eb3d2019-06-26 11:32:0412#include "content/browser/content_index/content_index.pb.h"
Rayan Kanso03a847b2019-06-27 21:00:0913#include "content/public/browser/browser_context.h"
Rayan Kanso24f7d412019-07-11 10:09:3014#include "content/public/browser/browser_task_traits.h"
Rayan Kanso296eb3d2019-06-26 11:32:0415#include "content/public/browser/browser_thread.h"
Rayan Kansod33e2772019-06-27 16:52:4116#include "url/origin.h"
Rayan Kanso296eb3d2019-06-26 11:32:0417
Rayan Kanso837b7952019-07-01 10:31:2918// TODO(crbug.com/973844): Move image utility functions to common library.
19using content::background_fetch::DeserializeIcon;
20using content::background_fetch::SerializeIcon;
21
Rayan Kanso296eb3d2019-06-26 11:32:0422namespace content {
23
24namespace {
25
26constexpr char kEntryPrefix[] = "content_index:entry_";
Rayan Kanso837b7952019-07-01 10:31:2927constexpr char kIconPrefix[] = "content_index:icon_";
Rayan Kanso296eb3d2019-06-26 11:32:0428
29std::string EntryKey(const std::string& id) {
30 return kEntryPrefix + id;
31}
32
Rayan Kanso837b7952019-07-01 10:31:2933std::string IconKey(const std::string& id) {
34 return kIconPrefix + id;
35}
36
Rayan Kanso296eb3d2019-06-26 11:32:0437std::string CreateSerializedContentEntry(
Rayan Kanso03a847b2019-06-27 21:00:0938 const blink::mojom::ContentDescription& description,
39 base::Time entry_time) {
Rayan Kanso296eb3d2019-06-26 11:32:0440 // Convert description.
41 proto::ContentDescription description_proto;
Rayan Kanso475b85b2019-06-28 01:56:4442 description_proto.set_id(description.id);
43 description_proto.set_title(description.title);
44 description_proto.set_description(description.description);
Rayan Kanso03a847b2019-06-27 21:00:0945 description_proto.set_category(static_cast<int>(description.category));
Rayan Kanso475b85b2019-06-28 01:56:4446 description_proto.set_icon_url(description.icon_url);
47 description_proto.set_launch_url(description.launch_url);
Rayan Kanso296eb3d2019-06-26 11:32:0448
49 // Create entry.
50 proto::ContentEntry entry;
51 *entry.mutable_description() = std::move(description_proto);
Rayan Kanso03a847b2019-06-27 21:00:0952 entry.set_timestamp(entry_time.ToDeltaSinceWindowsEpoch().InMicroseconds());
Rayan Kanso296eb3d2019-06-26 11:32:0453
54 return entry.SerializeAsString();
55}
56
57blink::mojom::ContentDescriptionPtr DescriptionFromProto(
58 const proto::ContentDescription& description) {
59 // Validate.
60 if (description.category() <
61 static_cast<int>(blink::mojom::ContentCategory::kMinValue) ||
62 description.category() >
63 static_cast<int>(blink::mojom::ContentCategory::kMaxValue)) {
64 return nullptr;
65 }
66
67 // Convert.
68 auto result = blink::mojom::ContentDescription::New();
69 result->id = description.id();
70 result->title = description.title();
71 result->description = description.description();
72 result->category =
73 static_cast<blink::mojom::ContentCategory>(description.category());
Rayan Kanso475b85b2019-06-28 01:56:4474 result->icon_url = description.icon_url();
75 result->launch_url = description.launch_url();
Rayan Kanso296eb3d2019-06-26 11:32:0476 return result;
77}
78
79} // namespace
80
81ContentIndexDatabase::ContentIndexDatabase(
Rayan Kanso03a847b2019-06-27 21:00:0982 BrowserContext* browser_context,
Rayan Kanso296eb3d2019-06-26 11:32:0483 scoped_refptr<ServiceWorkerContextWrapper> service_worker_context)
Rayan Kansoba075802019-06-28 16:33:4884 : provider_(browser_context->GetContentIndexProvider()),
Rayan Kanso24f7d412019-07-11 10:09:3085 service_worker_context_(std::move(service_worker_context)),
86 weak_ptr_factory_io_(this),
87 weak_ptr_factory_ui_(this) {}
Rayan Kanso296eb3d2019-06-26 11:32:0488
89ContentIndexDatabase::~ContentIndexDatabase() = default;
90
91void ContentIndexDatabase::AddEntry(
92 int64_t service_worker_registration_id,
Rayan Kansod33e2772019-06-27 16:52:4193 const url::Origin& origin,
Rayan Kanso296eb3d2019-06-26 11:32:0494 blink::mojom::ContentDescriptionPtr description,
95 const SkBitmap& icon,
96 blink::mojom::ContentIndexService::AddCallback callback) {
Rayan Kanso837b7952019-07-01 10:31:2997 SerializeIcon(icon,
98 base::BindOnce(&ContentIndexDatabase::DidSerializeIcon,
Rayan Kanso24f7d412019-07-11 10:09:3099 weak_ptr_factory_io_.GetWeakPtr(),
Rayan Kanso837b7952019-07-01 10:31:29100 service_worker_registration_id, origin,
101 std::move(description), std::move(callback)));
102}
103
104void ContentIndexDatabase::DidSerializeIcon(
105 int64_t service_worker_registration_id,
106 const url::Origin& origin,
107 blink::mojom::ContentDescriptionPtr description,
108 blink::mojom::ContentIndexService::AddCallback callback,
109 std::string serialized_icon) {
Rayan Kanso03a847b2019-06-27 21:00:09110 base::Time entry_time = base::Time::Now();
Rayan Kanso837b7952019-07-01 10:31:29111 std::string entry_key = EntryKey(description->id);
112 std::string icon_key = IconKey(description->id);
113 std::string entry_value =
114 CreateSerializedContentEntry(*description, entry_time);
Rayan Kanso296eb3d2019-06-26 11:32:04115
Rayan Kanso03a847b2019-06-27 21:00:09116 // Entry to pass over to the provider.
117 ContentIndexEntry entry(service_worker_registration_id,
118 std::move(description), entry_time);
119
Rayan Kanso296eb3d2019-06-26 11:32:04120 service_worker_context_->StoreRegistrationUserData(
Rayan Kansod33e2772019-06-27 16:52:41121 service_worker_registration_id, origin.GetURL(),
Rayan Kanso837b7952019-07-01 10:31:29122 {{std::move(entry_key), std::move(entry_value)},
123 {std::move(icon_key), std::move(serialized_icon)}},
Rayan Kanso296eb3d2019-06-26 11:32:04124 base::BindOnce(&ContentIndexDatabase::DidAddEntry,
Rayan Kanso24f7d412019-07-11 10:09:30125 weak_ptr_factory_io_.GetWeakPtr(), std::move(callback),
Rayan Kanso03a847b2019-06-27 21:00:09126 std::move(entry)));
Rayan Kanso296eb3d2019-06-26 11:32:04127}
128
129void ContentIndexDatabase::DidAddEntry(
130 blink::mojom::ContentIndexService::AddCallback callback,
Rayan Kanso03a847b2019-06-27 21:00:09131 ContentIndexEntry entry,
Rayan Kanso296eb3d2019-06-26 11:32:04132 blink::ServiceWorkerStatusCode status) {
Rayan Kanso03a847b2019-06-27 21:00:09133 if (status != blink::ServiceWorkerStatusCode::kOk) {
Rayan Kanso296eb3d2019-06-26 11:32:04134 std::move(callback).Run(blink::mojom::ContentIndexError::STORAGE_ERROR);
Rayan Kanso03a847b2019-06-27 21:00:09135 return;
136 }
137
138 std::move(callback).Run(blink::mojom::ContentIndexError::NONE);
139
Rayan Kanso24f7d412019-07-11 10:09:30140 std::vector<ContentIndexEntry> entries;
141 entries.push_back(std::move(entry));
142 base::PostTaskWithTraits(
143 FROM_HERE, {BrowserThread::UI},
144 base::BindOnce(&ContentIndexDatabase::NotifyProviderContentAdded,
145 weak_ptr_factory_ui_.GetWeakPtr(), std::move(entries)));
Rayan Kanso296eb3d2019-06-26 11:32:04146}
147
148void ContentIndexDatabase::DeleteEntry(
149 int64_t service_worker_registration_id,
150 const std::string& entry_id,
151 blink::mojom::ContentIndexService::DeleteCallback callback) {
152 service_worker_context_->ClearRegistrationUserData(
Rayan Kanso837b7952019-07-01 10:31:29153 service_worker_registration_id, {EntryKey(entry_id), IconKey(entry_id)},
Rayan Kanso24f7d412019-07-11 10:09:30154 base::BindOnce(&ContentIndexDatabase::DidDeleteEntry,
155 weak_ptr_factory_io_.GetWeakPtr(),
156 service_worker_registration_id, entry_id,
157 std::move(callback)));
Rayan Kanso296eb3d2019-06-26 11:32:04158}
159
160void ContentIndexDatabase::DidDeleteEntry(
Rayan Kanso03a847b2019-06-27 21:00:09161 int64_t service_worker_registration_id,
162 const std::string& entry_id,
Rayan Kanso296eb3d2019-06-26 11:32:04163 blink::mojom::ContentIndexService::DeleteCallback callback,
164 blink::ServiceWorkerStatusCode status) {
Rayan Kanso03a847b2019-06-27 21:00:09165 if (status != blink::ServiceWorkerStatusCode::kOk) {
Rayan Kanso296eb3d2019-06-26 11:32:04166 std::move(callback).Run(blink::mojom::ContentIndexError::STORAGE_ERROR);
Rayan Kanso03a847b2019-06-27 21:00:09167 return;
168 }
169
170 std::move(callback).Run(blink::mojom::ContentIndexError::NONE);
Rayan Kanso24f7d412019-07-11 10:09:30171
172 base::PostTaskWithTraits(
173 FROM_HERE, {BrowserThread::UI},
174 base::BindOnce(&ContentIndexDatabase::NotifyProviderContentDeleted,
175 weak_ptr_factory_ui_.GetWeakPtr(),
176 service_worker_registration_id, entry_id));
Rayan Kanso296eb3d2019-06-26 11:32:04177}
178
179void ContentIndexDatabase::GetDescriptions(
180 int64_t service_worker_registration_id,
181 blink::mojom::ContentIndexService::GetDescriptionsCallback callback) {
182 service_worker_context_->GetRegistrationUserDataByKeyPrefix(
183 service_worker_registration_id, kEntryPrefix,
184 base::BindOnce(&ContentIndexDatabase::DidGetDescriptions,
Rayan Kanso24f7d412019-07-11 10:09:30185 weak_ptr_factory_io_.GetWeakPtr(), std::move(callback)));
Rayan Kanso296eb3d2019-06-26 11:32:04186}
187
188void ContentIndexDatabase::DidGetDescriptions(
189 blink::mojom::ContentIndexService::GetDescriptionsCallback callback,
190 const std::vector<std::string>& data,
191 blink::ServiceWorkerStatusCode status) {
192 if (status == blink::ServiceWorkerStatusCode::kErrorNotFound) {
193 std::move(callback).Run(blink::mojom::ContentIndexError::NONE,
194 /* descriptions= */ {});
195 return;
196 } else if (status != blink::ServiceWorkerStatusCode::kOk) {
197 std::move(callback).Run(blink::mojom::ContentIndexError::STORAGE_ERROR,
198 /* descriptions= */ {});
199 return;
200 }
201
202 std::vector<blink::mojom::ContentDescriptionPtr> descriptions;
203 descriptions.reserve(data.size());
204
205 // TODO(crbug.com/973844): Clear the storage if there is data corruption.
206 for (const auto& serialized_entry : data) {
207 proto::ContentEntry entry;
208 if (!entry.ParseFromString(serialized_entry)) {
209 std::move(callback).Run(blink::mojom::ContentIndexError::STORAGE_ERROR,
210 /* descriptions= */ {});
211 return;
212 }
213
214 auto description = DescriptionFromProto(entry.description());
215 if (!description) {
216 std::move(callback).Run(blink::mojom::ContentIndexError::STORAGE_ERROR,
217 /* descriptions= */ {});
218 return;
219 }
220
221 descriptions.push_back(std::move(description));
222 }
223
224 std::move(callback).Run(blink::mojom::ContentIndexError::NONE,
225 std::move(descriptions));
226}
227
Rayan Kansoba075802019-06-28 16:33:48228void ContentIndexDatabase::InitializeProviderWithEntries() {
Rayan Kansoba075802019-06-28 16:33:48229 service_worker_context_->GetUserDataForAllRegistrationsByKeyPrefix(
230 kEntryPrefix, base::BindOnce(&ContentIndexDatabase::DidGetAllEntries,
Rayan Kanso24f7d412019-07-11 10:09:30231 weak_ptr_factory_io_.GetWeakPtr()));
Rayan Kansoba075802019-06-28 16:33:48232}
233
234void ContentIndexDatabase::DidGetAllEntries(
235 const std::vector<std::pair<int64_t, std::string>>& user_data,
236 blink::ServiceWorkerStatusCode status) {
237 if (status != blink::ServiceWorkerStatusCode::kOk) {
238 // TODO(crbug.com/973844): Handle or report this error.
239 return;
240 }
241
Rayan Kanso24f7d412019-07-11 10:09:30242 if (user_data.empty())
Rayan Kansoba075802019-06-28 16:33:48243 return;
244
245 std::vector<ContentIndexEntry> entries;
246 entries.reserve(user_data.size());
247
248 for (const auto& ud : user_data) {
249 proto::ContentEntry entry_proto;
250 if (!entry_proto.ParseFromString(ud.second)) {
251 // TODO(crbug.com/973844): Handle or report this error.
252 return;
253 }
254
255 int64_t service_worker_registration_id = ud.first;
256 auto description = DescriptionFromProto(entry_proto.description());
257 base::Time registration_time = base::Time::FromDeltaSinceWindowsEpoch(
258 base::TimeDelta::FromMicroseconds(entry_proto.timestamp()));
259
260 entries.emplace_back(service_worker_registration_id, std::move(description),
261 registration_time);
262 }
263
Rayan Kanso24f7d412019-07-11 10:09:30264 base::PostTaskWithTraits(
265 FROM_HERE, {BrowserThread::UI},
266 base::BindOnce(&ContentIndexDatabase::NotifyProviderContentAdded,
267 weak_ptr_factory_ui_.GetWeakPtr(), std::move(entries)));
Rayan Kansoba075802019-06-28 16:33:48268}
269
Rayan Kanso03a847b2019-06-27 21:00:09270void ContentIndexDatabase::GetIcon(
271 int64_t service_worker_registration_id,
272 const std::string& description_id,
273 base::OnceCallback<void(SkBitmap)> icon_callback) {
Rayan Kanso24f7d412019-07-11 10:09:30274 DCHECK_CURRENTLY_ON(BrowserThread::UI);
275
276 base::PostTaskWithTraits(
277 FROM_HERE, {BrowserThread::IO},
278 base::BindOnce(&ContentIndexDatabase::GetIconOnIO,
279 weak_ptr_factory_io_.GetWeakPtr(),
280 service_worker_registration_id, description_id,
281 std::move(icon_callback)));
282}
283
284void ContentIndexDatabase::GetIconOnIO(
285 int64_t service_worker_registration_id,
286 const std::string& description_id,
287 base::OnceCallback<void(SkBitmap)> icon_callback) {
288 DCHECK_CURRENTLY_ON(BrowserThread::IO);
289
Rayan Kanso837b7952019-07-01 10:31:29290 service_worker_context_->GetRegistrationUserData(
291 service_worker_registration_id, {IconKey(description_id)},
292 base::BindOnce(&ContentIndexDatabase::DidGetSerializedIcon,
Rayan Kanso24f7d412019-07-11 10:09:30293 weak_ptr_factory_io_.GetWeakPtr(),
294 std::move(icon_callback)));
Rayan Kanso837b7952019-07-01 10:31:29295}
296
297void ContentIndexDatabase::DidGetSerializedIcon(
298 base::OnceCallback<void(SkBitmap)> icon_callback,
299 const std::vector<std::string>& data,
300 blink::ServiceWorkerStatusCode status) {
301 if (status != blink::ServiceWorkerStatusCode::kOk || data.empty()) {
302 std::move(icon_callback).Run(SkBitmap());
303 return;
304 }
305
306 DCHECK_EQ(data.size(), 1u);
307
Rayan Kanso24f7d412019-07-11 10:09:30308 base::PostTaskWithTraits(
309 FROM_HERE, {BrowserThread::UI},
310 base::BindOnce(&DeserializeIcon, std::make_unique<std::string>(data[0]),
311 std::move(icon_callback)));
Rayan Kanso03a847b2019-06-27 21:00:09312}
313
Rayan Kansoba075802019-06-28 16:33:48314void ContentIndexDatabase::Shutdown() {
Rayan Kanso24f7d412019-07-11 10:09:30315 DCHECK_CURRENTLY_ON(BrowserThread::UI);
Rayan Kansoba075802019-06-28 16:33:48316
317 provider_ = nullptr;
318}
319
Rayan Kanso24f7d412019-07-11 10:09:30320void ContentIndexDatabase::NotifyProviderContentAdded(
321 std::vector<ContentIndexEntry> entries) {
322 DCHECK_CURRENTLY_ON(BrowserThread::UI);
323
324 if (!provider_)
325 return;
326
327 for (auto& entry : entries) {
328 provider_->OnContentAdded(std::move(entry),
329 weak_ptr_factory_ui_.GetWeakPtr());
330 }
331}
332
333void ContentIndexDatabase::NotifyProviderContentDeleted(
334 int64_t service_worker_registration_id,
335 const std::string& entry_id) {
336 DCHECK_CURRENTLY_ON(BrowserThread::UI);
337
338 if (!provider_)
339 return;
340
341 provider_->OnContentDeleted(service_worker_registration_id, entry_id);
342}
343
Rayan Kanso296eb3d2019-06-26 11:32:04344} // namespace content