blob: ef2bfaa395b7c1dac5562edb00498e20b860e408 [file] [log] [blame]
// Copyright 2014 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 "third_party/blink/renderer/modules/push_messaging/push_subscription.h"
#include <memory>
#include "third_party/blink/renderer/bindings/core/v8/callback_promise_adapter.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
#include "third_party/blink/renderer/bindings/core/v8/v8_object_builder.h"
#include "third_party/blink/renderer/core/dom/dom_exception.h"
#include "third_party/blink/renderer/modules/push_messaging/push_error.h"
#include "third_party/blink/renderer/modules/push_messaging/push_provider.h"
#include "third_party/blink/renderer/modules/push_messaging/push_subscription_options.h"
#include "third_party/blink/renderer/modules/service_worker/service_worker_registration.h"
#include "third_party/blink/renderer/platform/heap/heap.h"
#include "third_party/blink/renderer/platform/scheduler/public/thread.h"
#include "third_party/blink/renderer/platform/wtf/assertions.h"
#include "third_party/blink/renderer/platform/wtf/std_lib_extras.h"
#include "third_party/blink/renderer/platform/wtf/text/base64.h"
#include "third_party/blink/renderer/platform/wtf/vector.h"
namespace blink {
namespace {
// This method and its dependencies must remain constant time, thus not branch
// based on the value of |buffer| while encoding, assuming a known length.
String ToBase64URLWithoutPadding(DOMArrayBuffer* buffer) {
String value = WTF::Base64URLEncode(
static_cast<const char*>(buffer->Data()),
// The size of {buffer} should always fit into into {wtf_size_t}, because
// the buffer content itself origins from a WTF::Vector.
base::checked_cast<wtf_size_t>(buffer->ByteLength()));
DCHECK_GT(value.length(), 0u);
unsigned padding_to_remove = 0;
for (unsigned position = value.length() - 1; position; --position) {
if (value[position] != '=')
break;
++padding_to_remove;
}
DCHECK_LT(padding_to_remove, 4u);
DCHECK_GT(value.length(), padding_to_remove);
value.Truncate(value.length() - padding_to_remove);
return value;
}
// Converts a {base::Optional<base::Time>} into a
// {base::Optional<base::DOMTimeStamp>} object.
// base::Time is in milliseconds from Windows epoch (1601-01-01 00:00:00 UTC)
// while blink::DOMTimeStamp is in milliseconds from UNIX epoch (1970-01-01
// 00:00:00 UTC)
base::Optional<blink::DOMTimeStamp> ToDOMTimeStamp(
const base::Optional<base::Time>& time) {
if (time)
return ConvertSecondsToDOMTimeStamp(time->ToDoubleT());
return base::nullopt;
}
} // namespace
// static
PushSubscription* PushSubscription::Create(
mojom::blink::PushSubscriptionPtr subscription,
ServiceWorkerRegistration* service_worker_registration) {
return MakeGarbageCollected<PushSubscription>(
subscription->endpoint, subscription->options->user_visible_only,
subscription->options->application_server_key, subscription->p256dh,
subscription->auth, ToDOMTimeStamp(subscription->expirationTime),
service_worker_registration);
}
PushSubscription::PushSubscription(
const KURL& endpoint,
bool user_visible_only,
const WTF::Vector<uint8_t>& application_server_key,
const WTF::Vector<unsigned char>& p256dh,
const WTF::Vector<unsigned char>& auth,
const base::Optional<DOMTimeStamp>& expiration_time,
ServiceWorkerRegistration* service_worker_registration)
: endpoint_(endpoint),
options_(MakeGarbageCollected<PushSubscriptionOptions>(
user_visible_only,
application_server_key)),
p256dh_(DOMArrayBuffer::Create(p256dh.data(),
SafeCast<unsigned>(p256dh.size()))),
auth_(
DOMArrayBuffer::Create(auth.data(), SafeCast<unsigned>(auth.size()))),
expiration_time_(expiration_time),
service_worker_registration_(service_worker_registration) {}
PushSubscription::~PushSubscription() = default;
base::Optional<DOMTimeStamp> PushSubscription::expirationTime() const {
// This attribute reflects the time at which the subscription will expire,
// which is not relevant to this implementation yet as subscription refreshes
// are not supported.
return expiration_time_;
}
DOMArrayBuffer* PushSubscription::getKey(const AtomicString& name) const {
if (name == "p256dh")
return p256dh_;
if (name == "auth")
return auth_;
return nullptr;
}
ScriptPromise PushSubscription::unsubscribe(ScriptState* script_state) {
auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
ScriptPromise promise = resolver->Promise();
PushProvider* push_provider =
PushProvider::From(service_worker_registration_);
DCHECK(push_provider);
push_provider->Unsubscribe(
std::make_unique<CallbackPromiseAdapter<bool, DOMException*>>(resolver));
return promise;
}
ScriptValue PushSubscription::toJSONForBinding(ScriptState* script_state) {
DCHECK(p256dh_);
V8ObjectBuilder result(script_state);
result.AddString("endpoint", endpoint());
if (expiration_time_) {
result.AddNumber("expirationTime", *expiration_time_);
} else {
result.AddNull("expirationTime");
}
V8ObjectBuilder keys(script_state);
keys.Add("p256dh", ToBase64URLWithoutPadding(p256dh_));
keys.Add("auth", ToBase64URLWithoutPadding(auth_));
result.Add("keys", keys);
return result.GetScriptValue();
}
void PushSubscription::Trace(Visitor* visitor) const {
visitor->Trace(options_);
visitor->Trace(p256dh_);
visitor->Trace(auth_);
visitor->Trace(service_worker_registration_);
ScriptWrappable::Trace(visitor);
}
} // namespace blink