blob: 6b0e675a97cbc49008b8fd34c5522a73e22c95a8 [file] [log] [blame]
// Copyright 2020 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/buckets/storage_bucket.h"
#include "third_party/blink/public/platform/task_type.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_storage_estimate.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_storage_usage_details.h"
#include "third_party/blink/renderer/core/dom/dom_exception.h"
#include "third_party/blink/renderer/core/dom/dom_time_stamp.h"
#include "third_party/blink/renderer/core/frame/navigator.h"
#include "third_party/blink/renderer/modules/indexeddb/idb_factory.h"
#include "third_party/blink/renderer/modules/locks/lock_manager.h"
namespace blink {
StorageBucket::StorageBucket(
NavigatorBase* navigator,
mojo::PendingRemote<mojom::blink::BucketHost> remote)
: ExecutionContextLifecycleObserver(navigator->GetExecutionContext()),
navigator_base_(navigator) {
remote_.Bind(std::move(remote), GetExecutionContext()->GetTaskRunner(
TaskType::kInternalDefault));
}
ScriptPromise StorageBucket::persist(ScriptState* script_state) {
auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
ScriptPromise promise = resolver->Promise();
// The context may be destroyed and the mojo connection unbound. However the
// object may live on, reject any requests after the context is destroyed.
if (!remote_.is_bound()) {
resolver->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kInvalidStateError));
return promise;
}
remote_->Persist(WTF::Bind(&StorageBucket::DidRequestPersist,
WrapPersistent(this), WrapPersistent(resolver)));
return promise;
}
ScriptPromise StorageBucket::persisted(ScriptState* script_state) {
auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
ScriptPromise promise = resolver->Promise();
// The context may be destroyed and the mojo connection unbound. However the
// object may live on, reject any requests after the context is destroyed.
if (!remote_.is_bound()) {
resolver->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kInvalidStateError));
return promise;
}
remote_->Persisted(WTF::Bind(&StorageBucket::DidGetPersisted,
WrapPersistent(this), WrapPersistent(resolver)));
return promise;
}
ScriptPromise StorageBucket::estimate(ScriptState* script_state) {
auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
ScriptPromise promise = resolver->Promise();
// The context may be destroyed and the mojo connection unbound. However the
// object may live on, reject any requests after the context is destroyed.
if (!remote_.is_bound()) {
resolver->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kInvalidStateError));
return promise;
}
remote_->Estimate(WTF::Bind(&StorageBucket::DidGetEstimate,
WrapPersistent(this), WrapPersistent(resolver)));
return promise;
}
ScriptPromise StorageBucket::durability(ScriptState* script_state) {
auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
ScriptPromise promise = resolver->Promise();
// The context may be destroyed and the mojo connection unbound. However the
// object may live on, reject any requests after the context is destroyed.
if (!remote_.is_bound()) {
resolver->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kInvalidStateError));
return promise;
}
remote_->Durability(WTF::Bind(&StorageBucket::DidGetDurability,
WrapPersistent(this),
WrapPersistent(resolver)));
return promise;
}
ScriptPromise StorageBucket::setExpires(ScriptState* script_state,
const DOMTimeStamp& expires) {
auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
ScriptPromise promise = resolver->Promise();
// The context may be destroyed and the mojo connection unbound. However the
// object may live on, reject any requests after the context is destroyed.
if (!remote_.is_bound()) {
resolver->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kInvalidStateError));
return promise;
}
remote_->SetExpires(
base::Time::FromJavaTime(expires),
WTF::Bind(&StorageBucket::DidSetExpires, WrapPersistent(this),
WrapPersistent(resolver)));
return promise;
}
ScriptPromise StorageBucket::expires(ScriptState* script_state) {
auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
ScriptPromise promise = resolver->Promise();
// The context may be destroyed and the mojo connection unbound. However the
// object may live on, reject any requests after the context is destroyed.
if (!remote_.is_bound()) {
resolver->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kInvalidStateError));
return promise;
}
remote_->Expires(WTF::Bind(&StorageBucket::DidGetExpires,
WrapPersistent(this), WrapPersistent(resolver)));
return promise;
}
IDBFactory* StorageBucket::indexedDB() {
if (!idb_factory_) {
idb_factory_ = MakeGarbageCollected<IDBFactory>();
mojo::PendingRemote<mojom::blink::IDBFactory> factory;
remote_->GetIdbFactory(factory.InitWithNewPipeAndPassReceiver());
idb_factory_->SetFactory(std::move(factory), GetExecutionContext());
}
return idb_factory_;
}
LockManager* StorageBucket::locks() {
if (!lock_manager_) {
mojo::PendingRemote<mojom::blink::LockManager> lock_manager;
remote_->GetLockManager(lock_manager.InitWithNewPipeAndPassReceiver());
lock_manager_ = MakeGarbageCollected<LockManager>(*navigator_base_);
lock_manager_->SetManager(std::move(lock_manager), GetExecutionContext());
}
return lock_manager_;
}
bool StorageBucket::HasPendingActivity() const {
return GetExecutionContext();
}
void StorageBucket::Trace(Visitor* visitor) const {
visitor->Trace(idb_factory_);
visitor->Trace(lock_manager_);
visitor->Trace(navigator_base_);
ScriptWrappable::Trace(visitor);
ExecutionContextLifecycleObserver::Trace(visitor);
}
void StorageBucket::DidRequestPersist(ScriptPromiseResolver* resolver,
bool persisted,
bool success) {
ScriptState* script_state = resolver->GetScriptState();
if (!script_state->ContextIsValid())
return;
if (!success) {
resolver->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kUnknownError,
"Unknown error occured while requesting persist."));
return;
}
ScriptState::Scope scope(script_state);
resolver->Resolve(persisted);
}
void StorageBucket::DidGetPersisted(ScriptPromiseResolver* resolver,
bool persisted,
bool success) {
ScriptState* script_state = resolver->GetScriptState();
if (!script_state->ContextIsValid())
return;
if (!success) {
resolver->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kUnknownError,
"Unknown error occured while getting persisted."));
return;
}
ScriptState::Scope scope(script_state);
resolver->Resolve(persisted);
}
void StorageBucket::DidGetEstimate(ScriptPromiseResolver* resolver,
int64_t current_usage,
int64_t current_quota,
bool success) {
ScriptState* script_state = resolver->GetScriptState();
if (!script_state->ContextIsValid())
return;
ScriptState::Scope scope(script_state);
if (!success) {
resolver->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kUnknownError,
"Unknown error occured while getting estimate."));
return;
}
// TODO(ayui): Pass correct values once connected to quota.
StorageEstimate* estimate = StorageEstimate::Create();
estimate->setUsage(0);
estimate->setQuota(0);
StorageUsageDetails* details = StorageUsageDetails::Create();
estimate->setUsageDetails(details);
resolver->Resolve(estimate);
}
void StorageBucket::DidGetDurability(ScriptPromiseResolver* resolver,
mojom::blink::BucketDurability durability,
bool success) {
ScriptState* script_state = resolver->GetScriptState();
if (!script_state->ContextIsValid())
return;
if (!success) {
resolver->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kUnknownError,
"Unknown error occured while getting durability."));
return;
}
ScriptState::Scope scope(script_state);
if (durability == mojom::blink::BucketDurability::kRelaxed)
resolver->Resolve("relaxed");
resolver->Resolve("strict");
}
void StorageBucket::DidSetExpires(ScriptPromiseResolver* resolver,
bool success) {
ScriptState* script_state = resolver->GetScriptState();
if (!script_state->ContextIsValid())
return;
ScriptState::Scope scope(script_state);
if (success) {
resolver->Resolve();
} else {
resolver->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kUnknownError,
"Unknown error occured while setting expires."));
}
}
void StorageBucket::DidGetExpires(ScriptPromiseResolver* resolver,
const absl::optional<base::Time> expires,
bool success) {
ScriptState* script_state = resolver->GetScriptState();
if (!script_state->ContextIsValid())
return;
ScriptState::Scope scope(script_state);
if (!success) {
resolver->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kUnknownError,
"Unknown error occured while getting expires."));
} else if (expires.has_value()) {
resolver->Resolve(
ConvertSecondsToDOMTimeStamp(expires.value().ToDoubleT()));
} else {
resolver->Resolve(v8::Null(script_state->GetIsolate()));
}
}
void StorageBucket::ContextDestroyed() {
remote_.reset();
}
} // namespace blink