123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189 |
- // 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 "extensions/renderer/v8_schema_registry.h"
- #include <stddef.h>
- #include <memory>
- #include <utility>
- #include "base/bind.h"
- #include "base/check.h"
- #include "base/values.h"
- #include "content/public/renderer/v8_value_converter.h"
- #include "extensions/common/extension_api.h"
- #include "extensions/renderer/object_backed_native_handler.h"
- #include "extensions/renderer/script_context.h"
- #include "extensions/renderer/static_v8_external_one_byte_string_resource.h"
- #include "extensions/renderer/v8_helpers.h"
- #include "v8/include/v8-container.h"
- #include "v8/include/v8-context.h"
- #include "v8/include/v8-function-callback.h"
- #include "v8/include/v8-isolate.h"
- #include "v8/include/v8-json.h"
- #include "v8/include/v8-object.h"
- #include "v8/include/v8-value.h"
- using content::V8ValueConverter;
- namespace extensions {
- namespace {
- // Recursively freezes every v8 object on |object|.
- void DeepFreeze(const v8::Local<v8::Object>& object,
- const v8::Local<v8::Context>& context) {
- // Don't let the object trace upwards via the prototype.
- v8::Maybe<bool> maybe =
- object->SetPrototype(context, v8::Null(context->GetIsolate()));
- CHECK(maybe.IsJust() && maybe.FromJust());
- v8::Local<v8::Array> property_names =
- object->GetOwnPropertyNames(context).ToLocalChecked();
- for (uint32_t i = 0; i < property_names->Length(); ++i) {
- v8::Local<v8::Value> child =
- object->Get(context, property_names->Get(context, i).ToLocalChecked())
- .ToLocalChecked();
- if (child->IsObject())
- DeepFreeze(v8::Local<v8::Object>::Cast(child), context);
- }
- object->SetIntegrityLevel(context, v8::IntegrityLevel::kFrozen);
- }
- class SchemaRegistryNativeHandler : public ObjectBackedNativeHandler {
- public:
- SchemaRegistryNativeHandler(V8SchemaRegistry* registry,
- std::unique_ptr<ScriptContext> context)
- : ObjectBackedNativeHandler(context.get()),
- context_(std::move(context)),
- registry_(registry) {}
- // ObjectBackedNativeHandler:
- void AddRoutes() override {
- RouteHandlerFunction(
- "GetSchema",
- base::BindRepeating(&SchemaRegistryNativeHandler::GetSchema,
- base::Unretained(this)));
- RouteHandlerFunction(
- "GetObjectType",
- base::BindRepeating(&SchemaRegistryNativeHandler::GetObjectType,
- base::Unretained(this)));
- }
- ~SchemaRegistryNativeHandler() override { context_->Invalidate(); }
- private:
- void GetSchema(const v8::FunctionCallbackInfo<v8::Value>& args) {
- args.GetReturnValue().Set(registry_->GetSchema(
- *v8::String::Utf8Value(args.GetIsolate(), args[0])));
- }
- void GetObjectType(const v8::FunctionCallbackInfo<v8::Value>& args) {
- CHECK(args.Length() == 1 && args[0]->IsObject());
- std::string type;
- if (args[0]->IsArray())
- type = "array";
- else if (args[0]->IsArrayBuffer() || args[0]->IsArrayBufferView())
- type = "binary";
- else
- type = "object";
- args.GetReturnValue().Set(
- v8_helpers::ToV8StringUnsafe(context()->isolate(), type.c_str()));
- }
- std::unique_ptr<ScriptContext> context_;
- V8SchemaRegistry* registry_;
- };
- } // namespace
- V8SchemaRegistry::V8SchemaRegistry() {
- }
- V8SchemaRegistry::~V8SchemaRegistry() {
- }
- std::unique_ptr<NativeHandler> V8SchemaRegistry::AsNativeHandler() {
- std::unique_ptr<ScriptContext> context(
- new ScriptContext(GetOrCreateContext(v8::Isolate::GetCurrent()),
- NULL, // no frame
- NULL, // no extension
- Feature::UNSPECIFIED_CONTEXT,
- NULL, // no effective extension
- Feature::UNSPECIFIED_CONTEXT));
- return std::unique_ptr<NativeHandler>(
- new SchemaRegistryNativeHandler(this, std::move(context)));
- }
- v8::Local<v8::Array> V8SchemaRegistry::GetSchemas(
- const std::vector<std::string>& apis) {
- v8::Isolate* isolate = v8::Isolate::GetCurrent();
- v8::EscapableHandleScope handle_scope(isolate);
- v8::Local<v8::Context> context = GetOrCreateContext(isolate);
- v8::Context::Scope context_scope(context);
- v8::Local<v8::Array> v8_apis(v8::Array::New(isolate, apis.size()));
- size_t api_index = 0;
- for (auto i = apis.cbegin(); i != apis.cend(); ++i) {
- bool set_result =
- v8_apis->Set(context, api_index++, GetSchema(*i)).ToChecked();
- // Set() should never return false without throwing an exception (which
- // would be caught by the ToChecked() above).
- DCHECK(set_result);
- }
- return handle_scope.Escape(v8_apis);
- }
- v8::Local<v8::Object> V8SchemaRegistry::GetSchema(const std::string& api) {
- if (schema_cache_ != NULL) {
- v8::Local<v8::Object> cached_schema = schema_cache_->Get(api);
- if (!cached_schema.IsEmpty()) {
- return cached_schema;
- }
- }
- // Slow path: Need to build schema first.
- v8::Isolate* isolate = v8::Isolate::GetCurrent();
- v8::EscapableHandleScope handle_scope(isolate);
- v8::Local<v8::Context> context = GetOrCreateContext(isolate);
- v8::Context::Scope context_scope(context);
- base::StringPiece schema_string =
- ExtensionAPI::GetSharedInstance()->GetSchemaStringPiece(api);
- CHECK(!schema_string.empty());
- v8::MaybeLocal<v8::String> v8_maybe_string = v8::String::NewExternalOneByte(
- isolate, new StaticV8ExternalOneByteStringResource(schema_string));
- v8::Local<v8::String> v8_schema_string;
- CHECK(v8_maybe_string.ToLocal(&v8_schema_string));
- v8::MaybeLocal<v8::Value> v8_maybe_schema_value =
- v8::JSON::Parse(context, v8_schema_string);
- v8::Local<v8::Value> v8_schema_value;
- CHECK(v8_maybe_schema_value.ToLocal(&v8_schema_value));
- CHECK(v8_schema_value->IsObject());
- v8::Local<v8::Object> v8_schema_object(
- v8::Local<v8::Object>::Cast(v8_schema_value));
- DeepFreeze(v8_schema_object, context);
- schema_cache_->Set(api, v8_schema_object);
- return handle_scope.Escape(v8_schema_object);
- }
- v8::Local<v8::Context> V8SchemaRegistry::GetOrCreateContext(
- v8::Isolate* isolate) {
- // It's ok to create local handles in this function, since this is only called
- // when we have a HandleScope.
- if (!context_holder_) {
- context_holder_ = std::make_unique<gin::ContextHolder>(isolate);
- context_holder_->SetContext(v8::Context::New(isolate));
- schema_cache_ = std::make_unique<SchemaCache>(isolate);
- return context_holder_->context();
- }
- return context_holder_->context();
- }
- } // namespace extensions
|