blob: 8c6053999e71eaa915d4b3966d3212ff6b86193a [file] [log] [blame]
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/notifications/notification_storage.h"
#include "base/metrics/histogram_macros.h"
#include "base/time/time.h"
#include "content/browser/notifications/notification_database_data_conversions.h"
namespace content {
namespace {
// Schema is as follows:
// KEY: NOTIFICATION_<|data.notification_id|>
// VALUE: <serialized content::proto::NotificationDatabaseData>
const char kNotificationPrefix[] = "NOTIFICATION_";
// Create the key that will be used for the service worker database.
std::string CreateDataKey(const std::string& notification_id) {
DCHECK(!notification_id.empty());
return kNotificationPrefix + notification_id;
}
// Updates the time of the last click on the notification, and the first if
// necessary.
void UpdateNotificationClickTimestamps(NotificationDatabaseData* data) {
base::TimeDelta delta = base::Time::Now() - data->creation_time_millis;
if (!data->time_until_first_click_millis.has_value())
data->time_until_first_click_millis = delta;
data->time_until_last_click_millis = delta;
}
} // namespace
NotificationStorage::NotificationStorage(
scoped_refptr<ServiceWorkerContextWrapper> service_worker_context)
: service_worker_context_(std::move(service_worker_context)),
weak_ptr_factory_(this) {}
NotificationStorage::~NotificationStorage() = default;
void NotificationStorage::WriteNotificationData(
const NotificationDatabaseData& data,
const PlatformNotificationContext::WriteResultCallback& callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
std::string serialized_data;
if (!SerializeNotificationDatabaseData(data, &serialized_data)) {
DLOG(ERROR) << "Unable to serialize data for a notification belonging "
<< "to: " << data.origin;
callback.Run(/* success= */ false, std::string());
return;
}
service_worker_context_->StoreRegistrationUserData(
data.service_worker_registration_id, data.origin,
{{CreateDataKey(data.notification_id), std::move(serialized_data)}},
base::BindOnce(&NotificationStorage::OnWriteComplete,
weak_ptr_factory_.GetWeakPtr(), data, callback));
}
void NotificationStorage::OnWriteComplete(
const NotificationDatabaseData& data,
const PlatformNotificationContext::WriteResultCallback& callback,
blink::ServiceWorkerStatusCode status) {
if (status == blink::ServiceWorkerStatusCode::kOk) {
callback.Run(/* success= */ true, data.notification_id);
} else {
callback.Run(/* success= */ false,
/* notification_id= */ std::string());
}
}
void NotificationStorage::ReadNotificationDataAndRecordInteraction(
int64_t service_worker_registration_id,
const std::string& notification_id,
PlatformNotificationContext::Interaction interaction,
const PlatformNotificationContext::ReadResultCallback& callback) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
service_worker_context_->GetRegistrationUserData(
service_worker_registration_id, {CreateDataKey(notification_id)},
base::BindOnce(&NotificationStorage::OnReadCompleteUpdateInteraction,
weak_ptr_factory_.GetWeakPtr(),
service_worker_registration_id, interaction, callback));
}
void NotificationStorage::OnReadCompleteUpdateInteraction(
int64_t service_worker_registration_id,
PlatformNotificationContext::Interaction interaction,
const PlatformNotificationContext::ReadResultCallback& callback,
const std::vector<std::string>& database_data,
blink::ServiceWorkerStatusCode status) {
if (status != blink::ServiceWorkerStatusCode::kOk || database_data.empty()) {
callback.Run(/* success= */ false, NotificationDatabaseData());
return;
}
auto data = std::make_unique<NotificationDatabaseData>();
if (!DeserializeNotificationDatabaseData(database_data[0], data.get())) {
DLOG(ERROR) << "Unable to deserialize data for a notification belonging "
<< "to: " << data->origin;
callback.Run(/* success= */ false, NotificationDatabaseData());
return;
}
switch (interaction) {
case PlatformNotificationContext::Interaction::CLOSED:
data->time_until_close_millis =
base::Time::Now() - data->creation_time_millis;
break;
case PlatformNotificationContext::Interaction::NONE:
break;
case PlatformNotificationContext::Interaction::ACTION_BUTTON_CLICKED:
data->num_action_button_clicks += 1;
UpdateNotificationClickTimestamps(data.get());
break;
case PlatformNotificationContext::Interaction::CLICKED:
data->num_clicks += 1;
UpdateNotificationClickTimestamps(data.get());
break;
}
std::string serialized_data;
if (!SerializeNotificationDatabaseData(*data, &serialized_data)) {
DLOG(ERROR) << "Unable to serialize data for a notification belonging "
<< "to: " << data->origin;
callback.Run(/* success= */ false, NotificationDatabaseData());
return;
}
GURL origin = data->origin;
std::string notification_id = data->notification_id;
service_worker_context_->StoreRegistrationUserData(
service_worker_registration_id, origin,
{{CreateDataKey(notification_id), std::move(serialized_data)}},
base::BindOnce(&NotificationStorage::OnInteractionUpdateComplete,
weak_ptr_factory_.GetWeakPtr(), std::move(data),
callback));
}
void NotificationStorage::OnInteractionUpdateComplete(
std::unique_ptr<NotificationDatabaseData> data,
const PlatformNotificationContext::ReadResultCallback& callback,
blink::ServiceWorkerStatusCode status) {
DCHECK(data);
if (status == blink::ServiceWorkerStatusCode::kOk)
callback.Run(/* success= */ true, *data);
else
callback.Run(/* success= */ false, NotificationDatabaseData());
}
} // namespace content