v8_schema_registry.cc 6.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. // Copyright 2014 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "extensions/renderer/v8_schema_registry.h"
  5. #include <stddef.h>
  6. #include <memory>
  7. #include <utility>
  8. #include "base/bind.h"
  9. #include "base/check.h"
  10. #include "base/values.h"
  11. #include "content/public/renderer/v8_value_converter.h"
  12. #include "extensions/common/extension_api.h"
  13. #include "extensions/renderer/object_backed_native_handler.h"
  14. #include "extensions/renderer/script_context.h"
  15. #include "extensions/renderer/static_v8_external_one_byte_string_resource.h"
  16. #include "extensions/renderer/v8_helpers.h"
  17. #include "v8/include/v8-container.h"
  18. #include "v8/include/v8-context.h"
  19. #include "v8/include/v8-function-callback.h"
  20. #include "v8/include/v8-isolate.h"
  21. #include "v8/include/v8-json.h"
  22. #include "v8/include/v8-object.h"
  23. #include "v8/include/v8-value.h"
  24. using content::V8ValueConverter;
  25. namespace extensions {
  26. namespace {
  27. // Recursively freezes every v8 object on |object|.
  28. void DeepFreeze(const v8::Local<v8::Object>& object,
  29. const v8::Local<v8::Context>& context) {
  30. // Don't let the object trace upwards via the prototype.
  31. v8::Maybe<bool> maybe =
  32. object->SetPrototype(context, v8::Null(context->GetIsolate()));
  33. CHECK(maybe.IsJust() && maybe.FromJust());
  34. v8::Local<v8::Array> property_names =
  35. object->GetOwnPropertyNames(context).ToLocalChecked();
  36. for (uint32_t i = 0; i < property_names->Length(); ++i) {
  37. v8::Local<v8::Value> child =
  38. object->Get(context, property_names->Get(context, i).ToLocalChecked())
  39. .ToLocalChecked();
  40. if (child->IsObject())
  41. DeepFreeze(v8::Local<v8::Object>::Cast(child), context);
  42. }
  43. object->SetIntegrityLevel(context, v8::IntegrityLevel::kFrozen);
  44. }
  45. class SchemaRegistryNativeHandler : public ObjectBackedNativeHandler {
  46. public:
  47. SchemaRegistryNativeHandler(V8SchemaRegistry* registry,
  48. std::unique_ptr<ScriptContext> context)
  49. : ObjectBackedNativeHandler(context.get()),
  50. context_(std::move(context)),
  51. registry_(registry) {}
  52. // ObjectBackedNativeHandler:
  53. void AddRoutes() override {
  54. RouteHandlerFunction(
  55. "GetSchema",
  56. base::BindRepeating(&SchemaRegistryNativeHandler::GetSchema,
  57. base::Unretained(this)));
  58. RouteHandlerFunction(
  59. "GetObjectType",
  60. base::BindRepeating(&SchemaRegistryNativeHandler::GetObjectType,
  61. base::Unretained(this)));
  62. }
  63. ~SchemaRegistryNativeHandler() override { context_->Invalidate(); }
  64. private:
  65. void GetSchema(const v8::FunctionCallbackInfo<v8::Value>& args) {
  66. args.GetReturnValue().Set(registry_->GetSchema(
  67. *v8::String::Utf8Value(args.GetIsolate(), args[0])));
  68. }
  69. void GetObjectType(const v8::FunctionCallbackInfo<v8::Value>& args) {
  70. CHECK(args.Length() == 1 && args[0]->IsObject());
  71. std::string type;
  72. if (args[0]->IsArray())
  73. type = "array";
  74. else if (args[0]->IsArrayBuffer() || args[0]->IsArrayBufferView())
  75. type = "binary";
  76. else
  77. type = "object";
  78. args.GetReturnValue().Set(
  79. v8_helpers::ToV8StringUnsafe(context()->isolate(), type.c_str()));
  80. }
  81. std::unique_ptr<ScriptContext> context_;
  82. V8SchemaRegistry* registry_;
  83. };
  84. } // namespace
  85. V8SchemaRegistry::V8SchemaRegistry() {
  86. }
  87. V8SchemaRegistry::~V8SchemaRegistry() {
  88. }
  89. std::unique_ptr<NativeHandler> V8SchemaRegistry::AsNativeHandler() {
  90. std::unique_ptr<ScriptContext> context(
  91. new ScriptContext(GetOrCreateContext(v8::Isolate::GetCurrent()),
  92. NULL, // no frame
  93. NULL, // no extension
  94. Feature::UNSPECIFIED_CONTEXT,
  95. NULL, // no effective extension
  96. Feature::UNSPECIFIED_CONTEXT));
  97. return std::unique_ptr<NativeHandler>(
  98. new SchemaRegistryNativeHandler(this, std::move(context)));
  99. }
  100. v8::Local<v8::Array> V8SchemaRegistry::GetSchemas(
  101. const std::vector<std::string>& apis) {
  102. v8::Isolate* isolate = v8::Isolate::GetCurrent();
  103. v8::EscapableHandleScope handle_scope(isolate);
  104. v8::Local<v8::Context> context = GetOrCreateContext(isolate);
  105. v8::Context::Scope context_scope(context);
  106. v8::Local<v8::Array> v8_apis(v8::Array::New(isolate, apis.size()));
  107. size_t api_index = 0;
  108. for (auto i = apis.cbegin(); i != apis.cend(); ++i) {
  109. bool set_result =
  110. v8_apis->Set(context, api_index++, GetSchema(*i)).ToChecked();
  111. // Set() should never return false without throwing an exception (which
  112. // would be caught by the ToChecked() above).
  113. DCHECK(set_result);
  114. }
  115. return handle_scope.Escape(v8_apis);
  116. }
  117. v8::Local<v8::Object> V8SchemaRegistry::GetSchema(const std::string& api) {
  118. if (schema_cache_ != NULL) {
  119. v8::Local<v8::Object> cached_schema = schema_cache_->Get(api);
  120. if (!cached_schema.IsEmpty()) {
  121. return cached_schema;
  122. }
  123. }
  124. // Slow path: Need to build schema first.
  125. v8::Isolate* isolate = v8::Isolate::GetCurrent();
  126. v8::EscapableHandleScope handle_scope(isolate);
  127. v8::Local<v8::Context> context = GetOrCreateContext(isolate);
  128. v8::Context::Scope context_scope(context);
  129. base::StringPiece schema_string =
  130. ExtensionAPI::GetSharedInstance()->GetSchemaStringPiece(api);
  131. CHECK(!schema_string.empty());
  132. v8::MaybeLocal<v8::String> v8_maybe_string = v8::String::NewExternalOneByte(
  133. isolate, new StaticV8ExternalOneByteStringResource(schema_string));
  134. v8::Local<v8::String> v8_schema_string;
  135. CHECK(v8_maybe_string.ToLocal(&v8_schema_string));
  136. v8::MaybeLocal<v8::Value> v8_maybe_schema_value =
  137. v8::JSON::Parse(context, v8_schema_string);
  138. v8::Local<v8::Value> v8_schema_value;
  139. CHECK(v8_maybe_schema_value.ToLocal(&v8_schema_value));
  140. CHECK(v8_schema_value->IsObject());
  141. v8::Local<v8::Object> v8_schema_object(
  142. v8::Local<v8::Object>::Cast(v8_schema_value));
  143. DeepFreeze(v8_schema_object, context);
  144. schema_cache_->Set(api, v8_schema_object);
  145. return handle_scope.Escape(v8_schema_object);
  146. }
  147. v8::Local<v8::Context> V8SchemaRegistry::GetOrCreateContext(
  148. v8::Isolate* isolate) {
  149. // It's ok to create local handles in this function, since this is only called
  150. // when we have a HandleScope.
  151. if (!context_holder_) {
  152. context_holder_ = std::make_unique<gin::ContextHolder>(isolate);
  153. context_holder_->SetContext(v8::Context::New(isolate));
  154. schema_cache_ = std::make_unique<SchemaCache>(isolate);
  155. return context_holder_->context();
  156. }
  157. return context_holder_->context();
  158. }
  159. } // namespace extensions