1 // Copyright 2019 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 
5 #include "chromecast/renderer/extensions/tabs_hooks_delegate.h"
6 
7 #include <string>
8 #include <memory>
9 #include <utility>
10 
11 #include "base/strings/stringprintf.h"
12 #include "content/public/renderer/v8_value_converter.h"
13 #include "extensions/common/api/messaging/message.h"
14 #include "extensions/common/extension.h"
15 #include "extensions/common/manifest.h"
16 #include "extensions/renderer/bindings/api_signature.h"
17 #include "extensions/renderer/get_script_context.h"
18 #include "extensions/renderer/message_target.h"
19 #include "extensions/renderer/messaging_util.h"
20 #include "extensions/renderer/native_renderer_messaging_service.h"
21 #include "extensions/renderer/script_context.h"
22 #include "gin/converter.h"
23 
24 namespace extensions {
25 
26 namespace {
27 
28 using RequestResult = APIBindingHooks::RequestResult;
29 
30 constexpr char kConnect[] = "tabs.connect";
31 constexpr char kSendMessage[] = "tabs.sendMessage";
32 constexpr char kSendTabsRequest[] = "tabs.sendRequest";
33 
34 }  // namespace
35 
TabsHooksDelegate(NativeRendererMessagingService * messaging_service)36 TabsHooksDelegate::TabsHooksDelegate(
37     NativeRendererMessagingService* messaging_service)
38     : messaging_service_(messaging_service) {}
~TabsHooksDelegate()39 TabsHooksDelegate::~TabsHooksDelegate() {}
40 
HandleRequest(const std::string & method_name,const APISignature * signature,v8::Local<v8::Context> context,std::vector<v8::Local<v8::Value>> * arguments,const APITypeReferenceMap & refs)41 RequestResult TabsHooksDelegate::HandleRequest(
42     const std::string& method_name,
43     const APISignature* signature,
44     v8::Local<v8::Context> context,
45     std::vector<v8::Local<v8::Value>>* arguments,
46     const APITypeReferenceMap& refs) {
47   // TODO(devlin): This logic is the same in the RuntimeCustomHooksDelegate -
48   // would it make sense to share it?
49   using Handler = RequestResult (TabsHooksDelegate::*)(
50       ScriptContext*, const std::vector<v8::Local<v8::Value>>&);
51   static const struct {
52     Handler handler;
53     base::StringPiece method;
54   } kHandlers[] = {
55       {&TabsHooksDelegate::HandleSendMessage, kSendMessage},
56       {&TabsHooksDelegate::HandleSendRequest, kSendTabsRequest},
57       {&TabsHooksDelegate::HandleConnect, kConnect},
58   };
59 
60   ScriptContext* script_context = GetScriptContextFromV8ContextChecked(context);
61 
62   Handler handler = nullptr;
63   for (const auto& handler_entry : kHandlers) {
64     if (handler_entry.method == method_name) {
65       handler = handler_entry.handler;
66       break;
67     }
68   }
69 
70   if (!handler)
71     return RequestResult(RequestResult::NOT_HANDLED);
72 
73   APISignature::V8ParseResult parse_result =
74       signature->ParseArgumentsToV8(context, *arguments, refs);
75   if (!parse_result.succeeded()) {
76     RequestResult result(RequestResult::INVALID_INVOCATION);
77     result.error = std::move(*parse_result.error);
78     return result;
79   }
80 
81   return (this->*handler)(script_context, *parse_result.arguments);
82 }
83 
HandleSendRequest(ScriptContext * script_context,const std::vector<v8::Local<v8::Value>> & arguments)84 RequestResult TabsHooksDelegate::HandleSendRequest(
85     ScriptContext* script_context,
86     const std::vector<v8::Local<v8::Value>>& arguments) {
87   DCHECK_EQ(3u, arguments.size());
88 
89   int tab_id = messaging_util::ExtractIntegerId(arguments[0]);
90   v8::Local<v8::Value> v8_message = arguments[1];
91   std::string error;
92   std::unique_ptr<Message> message = messaging_util::MessageFromV8(
93       script_context->v8_context(), v8_message, &error);
94   if (!message) {
95     RequestResult result(RequestResult::INVALID_INVOCATION);
96     result.error = std::move(error);
97     return result;
98   }
99 
100   v8::Local<v8::Function> response_callback;
101   if (!arguments[2]->IsNull())
102     response_callback = arguments[2].As<v8::Function>();
103 
104   messaging_service_->SendOneTimeMessage(
105       script_context, MessageTarget::ForTab(tab_id, messaging_util::kNoFrameId),
106       messaging_util::kSendRequestChannel, *message, response_callback);
107 
108   return RequestResult(RequestResult::HANDLED);
109 }
110 
HandleSendMessage(ScriptContext * script_context,const std::vector<v8::Local<v8::Value>> & arguments)111 RequestResult TabsHooksDelegate::HandleSendMessage(
112     ScriptContext* script_context,
113     const std::vector<v8::Local<v8::Value>>& arguments) {
114   DCHECK_EQ(4u, arguments.size());
115 
116   int tab_id = messaging_util::ExtractIntegerId(arguments[0]);
117   messaging_util::MessageOptions options;
118   if (!arguments[2]->IsNull()) {
119     options = messaging_util::ParseMessageOptions(
120         script_context->v8_context(), arguments[2].As<v8::Object>(),
121         messaging_util::PARSE_FRAME_ID);
122   }
123 
124   v8::Local<v8::Value> v8_message = arguments[1];
125   DCHECK(!v8_message.IsEmpty());
126   std::string error;
127   std::unique_ptr<Message> message = messaging_util::MessageFromV8(
128       script_context->v8_context(), v8_message, &error);
129   if (!message) {
130     RequestResult result(RequestResult::INVALID_INVOCATION);
131     result.error = std::move(error);
132     return result;
133   }
134 
135   v8::Local<v8::Function> response_callback;
136   if (!arguments[3]->IsNull())
137     response_callback = arguments[3].As<v8::Function>();
138 
139   messaging_service_->SendOneTimeMessage(
140       script_context, MessageTarget::ForTab(tab_id, options.frame_id),
141       messaging_util::kSendMessageChannel, *message, response_callback);
142 
143   return RequestResult(RequestResult::HANDLED);
144 }
145 
HandleConnect(ScriptContext * script_context,const std::vector<v8::Local<v8::Value>> & arguments)146 RequestResult TabsHooksDelegate::HandleConnect(
147     ScriptContext* script_context,
148     const std::vector<v8::Local<v8::Value>>& arguments) {
149   DCHECK_EQ(2u, arguments.size());
150 
151   int tab_id = messaging_util::ExtractIntegerId(arguments[0]);
152 
153   messaging_util::MessageOptions options;
154   if (!arguments[1]->IsNull()) {
155     options = messaging_util::ParseMessageOptions(
156         script_context->v8_context(), arguments[1].As<v8::Object>(),
157         messaging_util::PARSE_FRAME_ID | messaging_util::PARSE_CHANNEL_NAME);
158   }
159 
160   gin::Handle<GinPort> port = messaging_service_->Connect(
161       script_context, MessageTarget::ForTab(tab_id, options.frame_id),
162       options.channel_name);
163   DCHECK(!port.IsEmpty());
164 
165   RequestResult result(RequestResult::HANDLED);
166   result.return_value = port.ToV8();
167   return result;
168 }
169 
170 }  // namespace extensions
171