| // Copyright 2017 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/runtime_hooks_delegate.h" | 
 |  | 
 | #include "base/containers/span.h" | 
 | #include "base/strings/string_piece.h" | 
 | #include "base/strings/stringprintf.h" | 
 | #include "content/public/renderer/v8_value_converter.h" | 
 | #include "extensions/common/api/messaging/message.h" | 
 | #include "extensions/common/extension.h" | 
 | #include "extensions/common/manifest.h" | 
 | #include "extensions/renderer/bindings/api_signature.h" | 
 | #include "extensions/renderer/get_script_context.h" | 
 | #include "extensions/renderer/message_target.h" | 
 | #include "extensions/renderer/messaging_util.h" | 
 | #include "extensions/renderer/native_renderer_messaging_service.h" | 
 | #include "extensions/renderer/script_context.h" | 
 | #include "extensions/renderer/script_context_set.h" | 
 | #include "gin/converter.h" | 
 |  | 
 | namespace extensions { | 
 |  | 
 | namespace { | 
 | using RequestResult = APIBindingHooks::RequestResult; | 
 |  | 
 | // Handler for the extensionId property on chrome.runtime. | 
 | void GetExtensionId(v8::Local<v8::Name> property_name, | 
 |                     const v8::PropertyCallbackInfo<v8::Value>& info) { | 
 |   v8::Isolate* isolate = info.GetIsolate(); | 
 |   v8::HandleScope handle_scope(isolate); | 
 |   v8::Local<v8::Context> context = info.Holder()->CreationContext(); | 
 |  | 
 |   ScriptContext* script_context = GetScriptContextFromV8Context(context); | 
 |   // This could potentially be invoked after the script context is removed | 
 |   // (unlike the handler calls, which should only be invoked for valid | 
 |   // contexts). | 
 |   if (script_context && script_context->extension()) { | 
 |     info.GetReturnValue().Set( | 
 |         gin::StringToSymbol(isolate, script_context->extension()->id())); | 
 |   } | 
 | } | 
 |  | 
 | constexpr char kGetManifest[] = "runtime.getManifest"; | 
 | constexpr char kGetURL[] = "runtime.getURL"; | 
 | constexpr char kConnect[] = "runtime.connect"; | 
 | constexpr char kConnectNative[] = "runtime.connectNative"; | 
 | constexpr char kSendMessage[] = "runtime.sendMessage"; | 
 | constexpr char kSendNativeMessage[] = "runtime.sendNativeMessage"; | 
 |  | 
 | }  // namespace | 
 |  | 
 | RuntimeHooksDelegate::RuntimeHooksDelegate( | 
 |     NativeRendererMessagingService* messaging_service) | 
 |     : messaging_service_(messaging_service) {} | 
 | RuntimeHooksDelegate::~RuntimeHooksDelegate() {} | 
 |  | 
 | RequestResult RuntimeHooksDelegate::HandleRequest( | 
 |     const std::string& method_name, | 
 |     const APISignature* signature, | 
 |     v8::Local<v8::Context> context, | 
 |     std::vector<v8::Local<v8::Value>>* arguments, | 
 |     const APITypeReferenceMap& refs) { | 
 |   using Handler = RequestResult (RuntimeHooksDelegate::*)( | 
 |       ScriptContext*, const std::vector<v8::Local<v8::Value>>&); | 
 |   static const struct { | 
 |     Handler handler; | 
 |     base::StringPiece method; | 
 |   } kHandlers[] = { | 
 |       {&RuntimeHooksDelegate::HandleSendMessage, kSendMessage}, | 
 |       {&RuntimeHooksDelegate::HandleConnect, kConnect}, | 
 |       {&RuntimeHooksDelegate::HandleGetURL, kGetURL}, | 
 |       {&RuntimeHooksDelegate::HandleGetManifest, kGetManifest}, | 
 |       {&RuntimeHooksDelegate::HandleConnectNative, kConnectNative}, | 
 |       {&RuntimeHooksDelegate::HandleSendNativeMessage, kSendNativeMessage}, | 
 |   }; | 
 |  | 
 |   ScriptContext* script_context = GetScriptContextFromV8ContextChecked(context); | 
 |  | 
 |   Handler handler = nullptr; | 
 |   for (const auto& handler_entry : kHandlers) { | 
 |     if (handler_entry.method == method_name) { | 
 |       handler = handler_entry.handler; | 
 |       break; | 
 |     } | 
 |   } | 
 |  | 
 |   if (!handler) | 
 |     return RequestResult(RequestResult::NOT_HANDLED); | 
 |  | 
 |   if (method_name == kSendMessage) { | 
 |     messaging_util::MassageSendMessageArguments(context->GetIsolate(), true, | 
 |                                                 arguments); | 
 |   } | 
 |  | 
 |   std::string error; | 
 |   std::vector<v8::Local<v8::Value>> parsed_arguments; | 
 |   if (!signature->ParseArgumentsToV8(context, *arguments, refs, | 
 |                                      &parsed_arguments, &error)) { | 
 |     RequestResult result(RequestResult::INVALID_INVOCATION); | 
 |     result.error = std::move(error); | 
 |     return result; | 
 |   } | 
 |  | 
 |   return (this->*handler)(script_context, parsed_arguments); | 
 | } | 
 |  | 
 | void RuntimeHooksDelegate::InitializeTemplate( | 
 |     v8::Isolate* isolate, | 
 |     v8::Local<v8::ObjectTemplate> object_template, | 
 |     const APITypeReferenceMap& type_refs) { | 
 |   object_template->SetAccessor(gin::StringToSymbol(isolate, "id"), | 
 |                                &GetExtensionId); | 
 | } | 
 |  | 
 | RequestResult RuntimeHooksDelegate::HandleGetManifest( | 
 |     ScriptContext* script_context, | 
 |     const std::vector<v8::Local<v8::Value>>& parsed_arguments) { | 
 |   DCHECK(script_context->extension()); | 
 |  | 
 |   RequestResult result(RequestResult::HANDLED); | 
 |   result.return_value = content::V8ValueConverter::Create()->ToV8Value( | 
 |       script_context->extension()->manifest()->value(), | 
 |       script_context->v8_context()); | 
 |  | 
 |   return result; | 
 | } | 
 |  | 
 | RequestResult RuntimeHooksDelegate::HandleGetURL( | 
 |     ScriptContext* script_context, | 
 |     const std::vector<v8::Local<v8::Value>>& arguments) { | 
 |   DCHECK_EQ(1u, arguments.size()); | 
 |   DCHECK(arguments[0]->IsString()); | 
 |   DCHECK(script_context->extension()); | 
 |  | 
 |   std::string path = gin::V8ToString(arguments[0]); | 
 |  | 
 |   RequestResult result(RequestResult::HANDLED); | 
 |   std::string url = base::StringPrintf( | 
 |       "chrome-extension://%s%s%s", script_context->extension()->id().c_str(), | 
 |       !path.empty() && path[0] == '/' ? "" : "/", path.c_str()); | 
 |   result.return_value = gin::StringToV8(script_context->isolate(), url); | 
 |  | 
 |   return result; | 
 | } | 
 |  | 
 | RequestResult RuntimeHooksDelegate::HandleSendMessage( | 
 |     ScriptContext* script_context, | 
 |     const std::vector<v8::Local<v8::Value>>& arguments) { | 
 |   DCHECK_EQ(4u, arguments.size()); | 
 |  | 
 |   std::string target_id; | 
 |   std::string error; | 
 |   if (!messaging_util::GetTargetExtensionId(script_context, arguments[0], | 
 |                                             "runtime.sendMessage", &target_id, | 
 |                                             &error)) { | 
 |     RequestResult result(RequestResult::INVALID_INVOCATION); | 
 |     result.error = std::move(error); | 
 |     return result; | 
 |   } | 
 |  | 
 |   v8::Local<v8::Context> v8_context = script_context->v8_context(); | 
 |   messaging_util::MessageOptions options; | 
 |   if (!arguments[2]->IsNull()) { | 
 |     options = messaging_util::ParseMessageOptions( | 
 |         v8_context, arguments[2].As<v8::Object>(), | 
 |         messaging_util::PARSE_INCLUDE_TLS_CHANNEL_ID); | 
 |   } | 
 |  | 
 |   v8::Local<v8::Value> v8_message = arguments[1]; | 
 |   std::unique_ptr<Message> message = | 
 |       messaging_util::MessageFromV8(v8_context, v8_message, &error); | 
 |   if (!message) { | 
 |     RequestResult result(RequestResult::INVALID_INVOCATION); | 
 |     result.error = std::move(error); | 
 |     return result; | 
 |   } | 
 |  | 
 |   v8::Local<v8::Function> response_callback; | 
 |   if (!arguments[3]->IsNull()) | 
 |     response_callback = arguments[3].As<v8::Function>(); | 
 |  | 
 |   messaging_service_->SendOneTimeMessage( | 
 |       script_context, MessageTarget::ForExtension(target_id), | 
 |       messaging_util::kSendMessageChannel, options.include_tls_channel_id, | 
 |       *message, response_callback); | 
 |  | 
 |   return RequestResult(RequestResult::HANDLED); | 
 | } | 
 |  | 
 | RequestResult RuntimeHooksDelegate::HandleSendNativeMessage( | 
 |     ScriptContext* script_context, | 
 |     const std::vector<v8::Local<v8::Value>>& arguments) { | 
 |   DCHECK_EQ(3u, arguments.size()); | 
 |  | 
 |   std::string application_name = gin::V8ToString(arguments[0]); | 
 |  | 
 |   v8::Local<v8::Value> v8_message = arguments[1]; | 
 |   DCHECK(!v8_message.IsEmpty()); | 
 |   std::string error; | 
 |   std::unique_ptr<Message> message = messaging_util::MessageFromV8( | 
 |       script_context->v8_context(), v8_message, &error); | 
 |   if (!message) { | 
 |     RequestResult result(RequestResult::INVALID_INVOCATION); | 
 |     result.error = std::move(error); | 
 |     return result; | 
 |   } | 
 |  | 
 |   v8::Local<v8::Function> response_callback; | 
 |   if (!arguments[2]->IsNull()) | 
 |     response_callback = arguments[2].As<v8::Function>(); | 
 |  | 
 |   messaging_service_->SendOneTimeMessage( | 
 |       script_context, MessageTarget::ForNativeApp(application_name), | 
 |       std::string(), false, *message, response_callback); | 
 |  | 
 |   return RequestResult(RequestResult::HANDLED); | 
 | } | 
 |  | 
 | RequestResult RuntimeHooksDelegate::HandleConnect( | 
 |     ScriptContext* script_context, | 
 |     const std::vector<v8::Local<v8::Value>>& arguments) { | 
 |   DCHECK_EQ(2u, arguments.size()); | 
 |  | 
 |   std::string target_id; | 
 |   std::string error; | 
 |   if (!messaging_util::GetTargetExtensionId(script_context, arguments[0], | 
 |                                             "runtime.connect", &target_id, | 
 |                                             &error)) { | 
 |     RequestResult result(RequestResult::INVALID_INVOCATION); | 
 |     result.error = std::move(error); | 
 |     return result; | 
 |   } | 
 |  | 
 |   messaging_util::MessageOptions options; | 
 |   if (!arguments[1]->IsNull()) { | 
 |     options = messaging_util::ParseMessageOptions( | 
 |         script_context->v8_context(), arguments[1].As<v8::Object>(), | 
 |         messaging_util::PARSE_INCLUDE_TLS_CHANNEL_ID | | 
 |             messaging_util::PARSE_CHANNEL_NAME); | 
 |   } | 
 |  | 
 |   gin::Handle<GinPort> port = messaging_service_->Connect( | 
 |       script_context, MessageTarget::ForExtension(target_id), | 
 |       options.channel_name, options.include_tls_channel_id); | 
 |   DCHECK(!port.IsEmpty()); | 
 |  | 
 |   RequestResult result(RequestResult::HANDLED); | 
 |   result.return_value = port.ToV8(); | 
 |   return result; | 
 | } | 
 |  | 
 | RequestResult RuntimeHooksDelegate::HandleConnectNative( | 
 |     ScriptContext* script_context, | 
 |     const std::vector<v8::Local<v8::Value>>& arguments) { | 
 |   DCHECK_EQ(1u, arguments.size()); | 
 |   DCHECK(arguments[0]->IsString()); | 
 |  | 
 |   std::string application_name = gin::V8ToString(arguments[0]); | 
 |   gin::Handle<GinPort> port = messaging_service_->Connect( | 
 |       script_context, MessageTarget::ForNativeApp(application_name), | 
 |       std::string(), false); | 
 |  | 
 |   RequestResult result(RequestResult::HANDLED); | 
 |   result.return_value = port.ToV8(); | 
 |   return result; | 
 | } | 
 |  | 
 | }  // namespace extensions |