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