Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(625)

Side by Side Diff: extensions/renderer/api_binding.cc

Issue 2704823002: [Extensions Bindings] Add support for custom property types (Closed)
Patch Set: jbroman's Created 3 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "extensions/renderer/api_binding.h" 5 #include "extensions/renderer/api_binding.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 8
9 #include "base/bind.h" 9 #include "base/bind.h"
10 #include "base/logging.h" 10 #include "base/logging.h"
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
65 if (!IsContextValid(args.isolate()->GetCurrentContext())) 65 if (!IsContextValid(args.isolate()->GetCurrentContext()))
66 return; 66 return;
67 67
68 v8::Local<v8::External> external; 68 v8::Local<v8::External> external;
69 CHECK(args.GetData(&external)); 69 CHECK(args.GetData(&external));
70 auto* callback = static_cast<APIBinding::HandlerCallback*>(external->Value()); 70 auto* callback = static_cast<APIBinding::HandlerCallback*>(external->Value());
71 71
72 callback->Run(&args); 72 callback->Run(&args);
73 } 73 }
74 74
75 // Decorates |object_template| with the properties specified by |properties|.
76 void DecorateTemplateWithProperties(
77 v8::Isolate* isolate,
78 v8::Local<v8::ObjectTemplate> object_template,
79 const base::DictionaryValue& properties) {
80 static const char kValueKey[] = "value";
81 for (base::DictionaryValue::Iterator iter(properties); !iter.IsAtEnd();
82 iter.Advance()) {
83 const base::DictionaryValue* dict = nullptr;
84 CHECK(iter.value().GetAsDictionary(&dict));
85 bool optional = false;
86 if (dict->GetBoolean("optional", &optional)) {
87 // TODO(devlin): What does optional even mean here? It's only used, it
88 // seems, for lastError and inIncognitoContext, which are both handled
89 // with custom bindings. Investigate, and remove.
90 continue;
91 }
92 std::string ref;
93 if (dict->GetString("$ref", &ref)) {
94 // TODO(devlin): Handle refs. These are tricky(er), because they represent
95 // a type that's defined in (currently) JS.
96 continue;
97 }
98 std::string type;
99 CHECK(dict->GetString("type", &type));
100 if (type != "object" && !dict->HasKey(kValueKey)) {
101 // TODO(devlin): What does a fundamental property not having a value mean?
102 // It doesn't seem useful, and looks like it's only used by runtime.id,
103 // which is set by custom bindings. Investigate, and remove.
104 continue;
105 }
106 if (type == "integer") {
107 int val = 0;
108 CHECK(dict->GetInteger(kValueKey, &val));
109 object_template->Set(isolate, iter.key().c_str(),
110 v8::Integer::New(isolate, val));
111 } else if (type == "boolean") {
112 bool val = false;
113 CHECK(dict->GetBoolean(kValueKey, &val));
114 object_template->Set(isolate, iter.key().c_str(),
115 v8::Boolean::New(isolate, val));
116 } else if (type == "string") {
117 std::string val;
118 CHECK(dict->GetString(kValueKey, &val)) << iter.key();
119 object_template->Set(isolate, iter.key().c_str(),
120 gin::StringToSymbol(isolate, val));
121 } else if (type == "object") {
122 v8::Local<v8::ObjectTemplate> property_template =
123 v8::ObjectTemplate::New(isolate);
124 const base::DictionaryValue* property_dict = nullptr;
125 CHECK(dict->GetDictionary("properties", &property_dict));
126 DecorateTemplateWithProperties(isolate, property_template,
127 *property_dict);
128 object_template->Set(isolate, iter.key().c_str(), property_template);
129 }
130 }
131 }
132
133 } // namespace 75 } // namespace
134 76
135 struct APIBinding::MethodData { 77 struct APIBinding::MethodData {
136 MethodData(std::string full_name, 78 MethodData(std::string full_name,
137 const base::ListValue& method_signature) 79 const base::ListValue& method_signature)
138 : full_name(std::move(full_name)), 80 : full_name(std::move(full_name)),
139 signature(method_signature) {} 81 signature(method_signature) {}
140 82
141 // The fully-qualified name of this api (e.g. runtime.sendMessage instead of 83 // The fully-qualified name of this api (e.g. runtime.sendMessage instead of
142 // sendMessage). 84 // sendMessage).
(...skipping 16 matching lines...) Expand all
159 std::string exposed_name; 101 std::string exposed_name;
160 // The fully-specified name of the event (e.g. tabs.onCreated). 102 // The fully-specified name of the event (e.g. tabs.onCreated).
161 std::string full_name; 103 std::string full_name;
162 // The associated event handler. This raw pointer is safe because the 104 // The associated event handler. This raw pointer is safe because the
163 // EventData is only accessed from the callbacks associated with the 105 // EventData is only accessed from the callbacks associated with the
164 // APIBinding, and both the APIBinding and APIEventHandler are owned by the 106 // APIBinding, and both the APIBinding and APIEventHandler are owned by the
165 // same object (the APIBindingsSystem). 107 // same object (the APIBindingsSystem).
166 APIEventHandler* event_handler; 108 APIEventHandler* event_handler;
167 }; 109 };
168 110
111 struct APIBinding::CustomPropertyData {
112 CustomPropertyData(const std::string& type_name,
113 const std::string& property_name,
114 const CreateCustomType& create_custom_type)
115 : type_name(type_name),
116 property_name(property_name),
117 create_custom_type(create_custom_type) {}
118
119 std::string type_name;
lazyboy 2017/02/22 03:37:03 I find giving examples of these are helpful to nav
Devlin 2017/02/22 20:56:45 Done.
120 std::string property_name;
121 CreateCustomType create_custom_type;
122 };
123
169 APIBinding::APIBinding(const std::string& api_name, 124 APIBinding::APIBinding(const std::string& api_name,
170 const base::ListValue* function_definitions, 125 const base::ListValue* function_definitions,
171 const base::ListValue* type_definitions, 126 const base::ListValue* type_definitions,
172 const base::ListValue* event_definitions, 127 const base::ListValue* event_definitions,
173 const base::DictionaryValue* property_definitions, 128 const base::DictionaryValue* property_definitions,
129 const CreateCustomType& create_custom_type,
174 std::unique_ptr<APIBindingHooks> binding_hooks, 130 std::unique_ptr<APIBindingHooks> binding_hooks,
175 APITypeReferenceMap* type_refs, 131 APITypeReferenceMap* type_refs,
176 APIRequestHandler* request_handler, 132 APIRequestHandler* request_handler,
177 APIEventHandler* event_handler) 133 APIEventHandler* event_handler)
178 : api_name_(api_name), 134 : api_name_(api_name),
179 property_definitions_(property_definitions), 135 property_definitions_(property_definitions),
136 create_custom_type_(create_custom_type),
180 binding_hooks_(std::move(binding_hooks)), 137 binding_hooks_(std::move(binding_hooks)),
181 type_refs_(type_refs), 138 type_refs_(type_refs),
182 request_handler_(request_handler), 139 request_handler_(request_handler),
183 weak_factory_(this) { 140 weak_factory_(this) {
184 // TODO(devlin): It might make sense to instantiate the object_template_ 141 // TODO(devlin): It might make sense to instantiate the object_template_
185 // directly here, which would avoid the need to hold on to 142 // directly here, which would avoid the need to hold on to
186 // |property_definitions_| and |enums_|. However, there are *some* cases where 143 // |property_definitions_| and |enums_|. However, there are *some* cases where
187 // we don't immediately stamp out an API from the template following 144 // we don't immediately stamp out an API from the template following
188 // construction. 145 // construction.
189 146
(...skipping 130 matching lines...) Expand 10 before | Expand all | Expand 10 after
320 } 277 }
321 278
322 if (property_definitions_) { 279 if (property_definitions_) {
323 DecorateTemplateWithProperties(isolate, object_template, 280 DecorateTemplateWithProperties(isolate, object_template,
324 *property_definitions_); 281 *property_definitions_);
325 } 282 }
326 283
327 object_template_.Set(isolate, object_template); 284 object_template_.Set(isolate, object_template);
328 } 285 }
329 286
287 void APIBinding::DecorateTemplateWithProperties(
288 v8::Isolate* isolate,
289 v8::Local<v8::ObjectTemplate> object_template,
290 const base::DictionaryValue& properties) {
291 static const char kValueKey[] = "value";
292 for (base::DictionaryValue::Iterator iter(properties); !iter.IsAtEnd();
293 iter.Advance()) {
294 const base::DictionaryValue* dict = nullptr;
295 CHECK(iter.value().GetAsDictionary(&dict));
296 bool optional = false;
297 if (dict->GetBoolean("optional", &optional)) {
298 // TODO(devlin): What does optional even mean here? It's only used, it
299 // seems, for lastError and inIncognitoContext, which are both handled
300 // with custom bindings. Investigate, and remove.
301 continue;
302 }
303
304 v8::Local<v8::String> v8_key = gin::StringToSymbol(isolate, iter.key());
305 std::string ref;
306 if (dict->GetString("$ref", &ref)) {
lazyboy 2017/02/22 03:37:03 For storage, we would get "StorageArea" as $ref in
Devlin 2017/02/22 20:56:45 In our json generation/compression, we fully speci
307 auto property_data = base::MakeUnique<CustomPropertyData>(
308 ref, iter.key(), create_custom_type_);
309 object_template->SetLazyDataProperty(
310 v8_key, &APIBinding::GetCustomPropertyObject,
311 v8::External::New(isolate, property_data.get()));
312 custom_properties_.push_back(std::move(property_data));
313 continue;
314 }
315
316 std::string type;
317 CHECK(dict->GetString("type", &type));
318 if (type != "object" && !dict->HasKey(kValueKey)) {
319 // TODO(devlin): What does a fundamental property not having a value mean?
320 // It doesn't seem useful, and looks like it's only used by runtime.id,
321 // which is set by custom bindings. Investigate, and remove.
322 continue;
323 }
324 if (type == "integer") {
325 int val = 0;
326 CHECK(dict->GetInteger(kValueKey, &val));
327 object_template->Set(v8_key, v8::Integer::New(isolate, val));
328 } else if (type == "boolean") {
329 bool val = false;
330 CHECK(dict->GetBoolean(kValueKey, &val));
331 object_template->Set(v8_key, v8::Boolean::New(isolate, val));
332 } else if (type == "string") {
333 std::string val;
334 CHECK(dict->GetString(kValueKey, &val)) << iter.key();
335 object_template->Set(v8_key, gin::StringToSymbol(isolate, val));
336 } else if (type == "object" || !ref.empty()) {
337 v8::Local<v8::ObjectTemplate> property_template =
338 v8::ObjectTemplate::New(isolate);
339 const base::DictionaryValue* property_dict = nullptr;
340 CHECK(dict->GetDictionary("properties", &property_dict));
341 DecorateTemplateWithProperties(isolate, property_template,
342 *property_dict);
343 object_template->Set(v8_key, property_template);
344 }
345 }
346 }
347
330 // static 348 // static
331 void APIBinding::GetEventObject( 349 void APIBinding::GetEventObject(
332 v8::Local<v8::Name> property, 350 v8::Local<v8::Name> property,
333 const v8::PropertyCallbackInfo<v8::Value>& info) { 351 const v8::PropertyCallbackInfo<v8::Value>& info) {
334 v8::Isolate* isolate = info.GetIsolate(); 352 v8::Isolate* isolate = info.GetIsolate();
335 v8::HandleScope handle_scope(isolate); 353 v8::HandleScope handle_scope(isolate);
336 v8::Local<v8::Context> context = info.Holder()->CreationContext(); 354 v8::Local<v8::Context> context = info.Holder()->CreationContext();
337 if (!IsContextValid(context)) 355 if (!IsContextValid(context))
338 return; 356 return;
339 357
340 CHECK(info.Data()->IsExternal()); 358 CHECK(info.Data()->IsExternal());
341 auto* event_data = 359 auto* event_data =
342 static_cast<EventData*>(info.Data().As<v8::External>()->Value()); 360 static_cast<EventData*>(info.Data().As<v8::External>()->Value());
343 info.GetReturnValue().Set(event_data->event_handler->CreateEventInstance( 361 info.GetReturnValue().Set(event_data->event_handler->CreateEventInstance(
344 event_data->full_name, context)); 362 event_data->full_name, context));
345 } 363 }
346 364
365 void APIBinding::GetCustomPropertyObject(
366 v8::Local<v8::Name> property_name,
367 const v8::PropertyCallbackInfo<v8::Value>& info) {
368 v8::Isolate* isolate = info.GetIsolate();
369 v8::HandleScope handle_scope(isolate);
370 v8::Local<v8::Context> context = info.Holder()->CreationContext();
371 if (!IsContextValid(context))
372 return;
373
374 CHECK(info.Data()->IsExternal());
375 auto* property_data =
376 static_cast<CustomPropertyData*>(info.Data().As<v8::External>()->Value());
377
378 v8::Local<v8::Object> property = property_data->create_custom_type.Run(
379 context, property_data->type_name, property_data->property_name);
380 if (property.IsEmpty())
381 return;
382
383 info.GetReturnValue().Set(property);
384 }
385
347 void APIBinding::HandleCall(const std::string& name, 386 void APIBinding::HandleCall(const std::string& name,
348 const APISignature* signature, 387 const APISignature* signature,
349 gin::Arguments* arguments) { 388 gin::Arguments* arguments) {
350 std::string error; 389 std::string error;
351 v8::Isolate* isolate = arguments->isolate(); 390 v8::Isolate* isolate = arguments->isolate();
352 v8::HandleScope handle_scope(isolate); 391 v8::HandleScope handle_scope(isolate);
353 392
354 // Since this is called synchronously from the JS entry point, 393 // Since this is called synchronously from the JS entry point,
355 // GetCurrentContext() should always be correct. 394 // GetCurrentContext() should always be correct.
356 v8::Local<v8::Context> context = isolate->GetCurrentContext(); 395 v8::Local<v8::Context> context = isolate->GetCurrentContext();
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
409 if (invalid_invocation) { 448 if (invalid_invocation) {
410 arguments->ThrowTypeError("Invalid invocation"); 449 arguments->ThrowTypeError("Invalid invocation");
411 return; 450 return;
412 } 451 }
413 452
414 request_handler_->StartRequest(context, name, std::move(converted_arguments), 453 request_handler_->StartRequest(context, name, std::move(converted_arguments),
415 callback, custom_callback); 454 callback, custom_callback);
416 } 455 }
417 456
418 } // namespace extensions 457 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698