OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 |
OLD | NEW |