| // Copyright 2016 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/bindings/api_bindings_system.h" |
| |
| #include "base/bind.h" |
| #include "base/values.h" |
| #include "extensions/renderer/bindings/api_binding_hooks.h" |
| #include "extensions/renderer/bindings/api_binding_util.h" |
| #include "extensions/renderer/bindings/api_response_validator.h" |
| #include "extensions/renderer/bindings/interaction_provider.h" |
| |
| namespace extensions { |
| |
| APIBindingsSystem::APIBindingsSystem( |
| GetAPISchemaMethod get_api_schema, |
| BindingAccessChecker::AvailabilityCallback is_available, |
| APIRequestHandler::SendRequestMethod send_request, |
| std::unique_ptr<InteractionProvider> interaction_provider, |
| APIEventListeners::ListenersUpdated event_listeners_changed, |
| APIEventHandler::ContextOwnerIdGetter context_owner_getter, |
| APIBinding::OnSilentRequest on_silent_request, |
| binding::AddConsoleError add_console_error, |
| APILastError last_error) |
| : type_reference_map_( |
| base::BindRepeating(&APIBindingsSystem::InitializeType, |
| base::Unretained(this))), |
| exception_handler_(std::move(add_console_error)), |
| interaction_provider_(std::move(interaction_provider)), |
| request_handler_(std::move(send_request), |
| std::move(last_error), |
| &exception_handler_, |
| interaction_provider_.get()), |
| event_handler_(std::move(event_listeners_changed), |
| std::move(context_owner_getter), |
| &exception_handler_), |
| access_checker_(std::move(is_available)), |
| get_api_schema_(std::move(get_api_schema)), |
| on_silent_request_(std::move(on_silent_request)) { |
| if (binding::IsResponseValidationEnabled()) { |
| request_handler_.SetResponseValidator( |
| std::make_unique<APIResponseValidator>(&type_reference_map_)); |
| } |
| } |
| |
| APIBindingsSystem::~APIBindingsSystem() {} |
| |
| v8::Local<v8::Object> APIBindingsSystem::CreateAPIInstance( |
| const std::string& api_name, |
| v8::Local<v8::Context> context, |
| APIBindingHooks** hooks_out) { |
| std::unique_ptr<APIBinding>& binding = api_bindings_[api_name]; |
| if (!binding) |
| binding = CreateNewAPIBinding(api_name); |
| if (hooks_out) |
| *hooks_out = binding->hooks(); |
| return binding->CreateInstance(context); |
| } |
| |
| std::unique_ptr<APIBinding> APIBindingsSystem::CreateNewAPIBinding( |
| const std::string& api_name) { |
| const base::DictionaryValue& api_schema = get_api_schema_.Run(api_name); |
| |
| const base::ListValue* function_definitions = nullptr; |
| api_schema.GetList("functions", &function_definitions); |
| const base::ListValue* type_definitions = nullptr; |
| api_schema.GetList("types", &type_definitions); |
| const base::ListValue* event_definitions = nullptr; |
| api_schema.GetList("events", &event_definitions); |
| const base::DictionaryValue* property_definitions = nullptr; |
| api_schema.GetDictionary("properties", &property_definitions); |
| |
| // Find the hooks for the API. If none exist, an empty set will be created so |
| // we can use JS custom bindings. |
| // TODO(devlin): Once all legacy custom bindings are converted, we don't have |
| // to unconditionally pass in binding hooks. |
| std::unique_ptr<APIBindingHooks> hooks; |
| auto iter = binding_hooks_.find(api_name); |
| if (iter != binding_hooks_.end()) { |
| hooks = std::move(iter->second); |
| binding_hooks_.erase(iter); |
| } else { |
| hooks = std::make_unique<APIBindingHooks>(api_name); |
| } |
| |
| return std::make_unique<APIBinding>( |
| api_name, function_definitions, type_definitions, event_definitions, |
| property_definitions, |
| base::BindRepeating(&APIBindingsSystem::CreateCustomType, |
| base::Unretained(this)), |
| on_silent_request_, std::move(hooks), &type_reference_map_, |
| &request_handler_, &event_handler_, &access_checker_); |
| } |
| |
| void APIBindingsSystem::InitializeType(const std::string& type_name) { |
| // In order to initialize the type, we just initialize the full binding. This |
| // seems like a lot of work, but in practice, trying to extract out only the |
| // types from the schema, and then update the reference map based on that, is |
| // close enough to the same cost. Additionally, this happens lazily on API |
| // use, and relatively few APIs specify types from another API. Finally, this |
| // will also go away if/when we generate all these specifications. |
| std::string::size_type dot = type_name.rfind('.'); |
| // The type name should be fully qualified (include the API name). |
| DCHECK_NE(std::string::npos, dot) << type_name; |
| DCHECK_LT(dot, type_name.size() - 1); |
| std::string api_name = type_name.substr(0, dot); |
| // If we've already instantiated the binding, the type should have been in |
| // there. |
| DCHECK(api_bindings_.find(api_name) == api_bindings_.end()) << api_name; |
| |
| api_bindings_[api_name] = CreateNewAPIBinding(api_name); |
| } |
| |
| void APIBindingsSystem::CompleteRequest(int request_id, |
| const base::ListValue& response, |
| const std::string& error) { |
| request_handler_.CompleteRequest(request_id, response, error); |
| } |
| |
| void APIBindingsSystem::FireEventInContext(const std::string& event_name, |
| v8::Local<v8::Context> context, |
| const base::ListValue& response, |
| const EventFilteringInfo* filter) { |
| event_handler_.FireEventInContext(event_name, context, response, filter); |
| } |
| |
| APIBindingHooks* APIBindingsSystem::GetHooksForAPI( |
| const std::string& api_name) { |
| DCHECK(api_bindings_.empty()) |
| << "Hook registration must happen before creating any binding instances."; |
| std::unique_ptr<APIBindingHooks>& hooks = binding_hooks_[api_name]; |
| if (!hooks) |
| hooks = std::make_unique<APIBindingHooks>(api_name); |
| return hooks.get(); |
| } |
| |
| void APIBindingsSystem::RegisterCustomType(const std::string& type_name, |
| CustomTypeHandler function) { |
| DCHECK(custom_types_.find(type_name) == custom_types_.end()) |
| << "Custom type already registered: " << type_name; |
| custom_types_[type_name] = std::move(function); |
| } |
| |
| void APIBindingsSystem::WillReleaseContext(v8::Local<v8::Context> context) { |
| binding::InvalidateContext(context); |
| request_handler_.InvalidateContext(context); |
| event_handler_.InvalidateContext(context); |
| } |
| |
| v8::Local<v8::Object> APIBindingsSystem::CreateCustomType( |
| v8::Isolate* isolate, |
| const std::string& type_name, |
| const std::string& property_name, |
| const base::ListValue* property_values) { |
| auto iter = custom_types_.find(type_name); |
| DCHECK(iter != custom_types_.end()) << "Custom type not found: " << type_name; |
| return iter->second.Run(isolate, property_name, property_values, |
| &request_handler_, &event_handler_, |
| &type_reference_map_, &access_checker_); |
| } |
| |
| } // namespace extensions |