blob: 7c60bf1180876dc2e07eceb5d477a5700c721c02 [file] [log] [blame]
// Copyright 2017 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 "extensions/renderer/storage_area.h"
#include "base/strings/stringprintf.h"
#include "extensions/common/api/storage.h"
#include "extensions/renderer/bindings/api_binding_util.h"
#include "extensions/renderer/bindings/api_event_handler.h"
#include "extensions/renderer/bindings/api_invocation_errors.h"
#include "extensions/renderer/bindings/api_request_handler.h"
#include "extensions/renderer/bindings/api_signature.h"
#include "extensions/renderer/bindings/api_type_reference_map.h"
#include "extensions/renderer/bindings/binding_access_checker.h"
#include "gin/arguments.h"
#include "gin/handle.h"
#include "gin/object_template_builder.h"
#include "gin/wrappable.h"
namespace extensions {
namespace {
#define DEFINE_STORAGE_AREA_HANDLERS() \
const char* GetTypeName() override { return "StorageArea"; } \
void Get(gin::Arguments* arguments) { \
storage_area_.HandleFunctionCall("get", arguments); \
} \
void Set(gin::Arguments* arguments) { \
storage_area_.HandleFunctionCall("set", arguments); \
} \
void Remove(gin::Arguments* arguments) { \
storage_area_.HandleFunctionCall("remove", arguments); \
} \
void Clear(gin::Arguments* arguments) { \
storage_area_.HandleFunctionCall("clear", arguments); \
} \
void GetBytesInUse(gin::Arguments* arguments) { \
storage_area_.HandleFunctionCall("getBytesInUse", arguments); \
} \
v8::Local<v8::Value> GetOnChangedEvent(gin::Arguments* arguments) { \
v8::Isolate* isolate = arguments->isolate(); \
v8::Local<v8::Context> context = arguments->GetHolderCreationContext(); \
v8::Local<v8::Object> wrapper = GetWrapper(isolate).ToLocalChecked(); \
return storage_area_.GetOnChangedEvent(isolate, context, wrapper); \
}
// gin::Wrappables for each of the storage areas. Since each has slightly
// different properties, and the object template is shared between all
// instances, this is a little verbose.
class LocalStorageArea final : public gin::Wrappable<LocalStorageArea> {
public:
LocalStorageArea(APIRequestHandler* request_handler,
APIEventHandler* event_handler,
const APITypeReferenceMap* type_refs,
const BindingAccessChecker* access_checker)
: storage_area_(request_handler,
event_handler,
type_refs,
"local",
access_checker) {}
~LocalStorageArea() override = default;
static gin::WrapperInfo kWrapperInfo;
gin::ObjectTemplateBuilder GetObjectTemplateBuilder(
v8::Isolate* isolate) override {
return Wrappable<LocalStorageArea>::GetObjectTemplateBuilder(isolate)
.SetMethod("get", &LocalStorageArea::Get)
.SetMethod("set", &LocalStorageArea::Set)
.SetMethod("remove", &LocalStorageArea::Remove)
.SetMethod("clear", &LocalStorageArea::Clear)
.SetMethod("getBytesInUse", &LocalStorageArea::GetBytesInUse)
.SetProperty("onChanged", &LocalStorageArea::GetOnChangedEvent)
.SetValue("QUOTA_BYTES", api::storage::local::QUOTA_BYTES);
}
private:
DEFINE_STORAGE_AREA_HANDLERS()
StorageArea storage_area_;
DISALLOW_COPY_AND_ASSIGN(LocalStorageArea);
};
gin::WrapperInfo LocalStorageArea::kWrapperInfo = {gin::kEmbedderNativeGin};
class SyncStorageArea final : public gin::Wrappable<SyncStorageArea> {
public:
SyncStorageArea(APIRequestHandler* request_handler,
APIEventHandler* event_handler,
const APITypeReferenceMap* type_refs,
const BindingAccessChecker* access_checker)
: storage_area_(request_handler,
event_handler,
type_refs,
"sync",
access_checker) {}
~SyncStorageArea() override = default;
static gin::WrapperInfo kWrapperInfo;
gin::ObjectTemplateBuilder GetObjectTemplateBuilder(
v8::Isolate* isolate) override {
return Wrappable<SyncStorageArea>::GetObjectTemplateBuilder(isolate)
.SetMethod("get", &SyncStorageArea::Get)
.SetMethod("set", &SyncStorageArea::Set)
.SetMethod("remove", &SyncStorageArea::Remove)
.SetMethod("clear", &SyncStorageArea::Clear)
.SetMethod("getBytesInUse", &SyncStorageArea::GetBytesInUse)
.SetProperty("onChanged", &SyncStorageArea::GetOnChangedEvent)
.SetValue("QUOTA_BYTES", api::storage::sync::QUOTA_BYTES)
.SetValue("QUOTA_BYTES_PER_ITEM",
api::storage::sync::QUOTA_BYTES_PER_ITEM)
.SetValue("MAX_ITEMS", api::storage::sync::MAX_ITEMS)
.SetValue("MAX_WRITE_OPERATIONS_PER_HOUR",
api::storage::sync::MAX_WRITE_OPERATIONS_PER_HOUR)
.SetValue("MAX_WRITE_OPERATIONS_PER_MINUTE",
api::storage::sync::MAX_WRITE_OPERATIONS_PER_MINUTE)
.SetValue(
"MAX_SUSTAINED_WRITE_OPERATIONS_PER_MINUTE",
api::storage::sync::MAX_SUSTAINED_WRITE_OPERATIONS_PER_MINUTE);
}
private:
DEFINE_STORAGE_AREA_HANDLERS()
StorageArea storage_area_;
DISALLOW_COPY_AND_ASSIGN(SyncStorageArea);
};
gin::WrapperInfo SyncStorageArea::kWrapperInfo = {gin::kEmbedderNativeGin};
class ManagedStorageArea final : public gin::Wrappable<ManagedStorageArea> {
public:
ManagedStorageArea(APIRequestHandler* request_handler,
APIEventHandler* event_handler,
const APITypeReferenceMap* type_refs,
const BindingAccessChecker* access_checker)
: storage_area_(request_handler,
event_handler,
type_refs,
"managed",
access_checker) {}
~ManagedStorageArea() override = default;
static gin::WrapperInfo kWrapperInfo;
gin::ObjectTemplateBuilder GetObjectTemplateBuilder(
v8::Isolate* isolate) override {
return Wrappable<ManagedStorageArea>::GetObjectTemplateBuilder(isolate)
.SetMethod("get", &ManagedStorageArea::Get)
.SetMethod("set", &ManagedStorageArea::Set)
.SetMethod("remove", &ManagedStorageArea::Remove)
.SetMethod("clear", &ManagedStorageArea::Clear)
.SetMethod("getBytesInUse", &ManagedStorageArea::GetBytesInUse)
.SetProperty("onChanged", &ManagedStorageArea::GetOnChangedEvent);
}
private:
DEFINE_STORAGE_AREA_HANDLERS()
StorageArea storage_area_;
DISALLOW_COPY_AND_ASSIGN(ManagedStorageArea);
};
gin::WrapperInfo ManagedStorageArea::kWrapperInfo = {gin::kEmbedderNativeGin};
#undef DEFINE_STORAGE_AREA_HANDLERS
} // namespace
StorageArea::StorageArea(APIRequestHandler* request_handler,
APIEventHandler* event_handler,
const APITypeReferenceMap* type_refs,
const std::string& name,
const BindingAccessChecker* access_checker)
: request_handler_(request_handler),
event_handler_(event_handler),
type_refs_(type_refs),
name_(name),
access_checker_(access_checker) {}
StorageArea::~StorageArea() = default;
// static
v8::Local<v8::Object> StorageArea::CreateStorageArea(
v8::Isolate* isolate,
const std::string& property_name,
const base::ListValue* property_values,
APIRequestHandler* request_handler,
APIEventHandler* event_handler,
APITypeReferenceMap* type_refs,
const BindingAccessChecker* access_checker) {
v8::Local<v8::Object> object;
if (property_name == "local") {
gin::Handle<LocalStorageArea> handle = gin::CreateHandle(
isolate, new LocalStorageArea(request_handler, event_handler, type_refs,
access_checker));
object = handle.ToV8().As<v8::Object>();
} else if (property_name == "sync") {
gin::Handle<SyncStorageArea> handle = gin::CreateHandle(
isolate, new SyncStorageArea(request_handler, event_handler, type_refs,
access_checker));
object = handle.ToV8().As<v8::Object>();
} else {
CHECK_EQ("managed", property_name);
gin::Handle<ManagedStorageArea> handle = gin::CreateHandle(
isolate, new ManagedStorageArea(request_handler, event_handler,
type_refs, access_checker));
object = handle.ToV8().As<v8::Object>();
}
return object;
}
void StorageArea::HandleFunctionCall(const std::string& method_name,
gin::Arguments* arguments) {
v8::Isolate* isolate = arguments->isolate();
v8::HandleScope handle_scope(isolate);
v8::Local<v8::Context> context = arguments->GetHolderCreationContext();
// The context may have been invalidated, as in the case where this could be
// a reference to an object from a removed frame.
if (!binding::IsContextValidOrThrowError(context))
return;
std::string full_method_name = "storage." + method_name;
if (!access_checker_->HasAccessOrThrowError(context, full_method_name))
return;
std::vector<v8::Local<v8::Value>> argument_list = arguments->GetAll();
const APISignature* signature = type_refs_->GetTypeMethodSignature(
base::StringPrintf("%s.%s", "storage.StorageArea", method_name.c_str()));
DCHECK(signature);
APISignature::JSONParseResult parse_result =
signature->ParseArgumentsToJSON(context, argument_list, *type_refs_);
if (!parse_result.succeeded()) {
arguments->ThrowTypeError(api_errors::InvocationError(
full_method_name, signature->GetExpectedSignature(),
*parse_result.error));
return;
}
parse_result.arguments->Insert(0u, std::make_unique<base::Value>(name_));
request_handler_->StartRequest(
context, full_method_name, std::move(parse_result.arguments),
parse_result.callback, v8::Local<v8::Function>());
}
v8::Local<v8::Value> StorageArea::GetOnChangedEvent(
v8::Isolate* isolate,
v8::Local<v8::Context> context,
v8::Local<v8::Object> wrapper) {
if (!binding::IsContextValidOrThrowError(context))
return v8::Undefined(isolate);
v8::Local<v8::Private> key = v8::Private::ForApi(
isolate, gin::StringToSymbol(isolate, "onChangedEvent"));
v8::Local<v8::Value> event;
if (!wrapper->GetPrivate(context, key).ToLocal(&event)) {
NOTREACHED();
return v8::Local<v8::Value>();
}
DCHECK(!event.IsEmpty());
if (event->IsUndefined()) {
constexpr bool kSupportsFilters = false;
constexpr bool kSupportsLazyListeners = true;
event = event_handler_->CreateEventInstance(
base::StringPrintf("storage.%s.onChanged", name_.c_str()),
kSupportsFilters, kSupportsLazyListeners, binding::kNoListenerMax, true,
context);
v8::Maybe<bool> set_result = wrapper->SetPrivate(context, key, event);
if (!set_result.IsJust() || !set_result.FromJust()) {
NOTREACHED();
return v8::Local<v8::Value>();
}
}
return event;
}
} // namespace extensions