blob: 3dee4ac447fa7dfa72927db0df17825c84d59fdd [file] [log] [blame]
Devlin Croninc7d8fd062017-09-16 03:36:431// Copyright 2017 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 "extensions/renderer/gin_port.h"
6
7#include <cstring>
8#include <vector>
9
10#include "extensions/common/api/messaging/message.h"
11#include "extensions/renderer/bindings/api_event_handler.h"
12#include "extensions/renderer/bindings/event_emitter.h"
Devlin Cronin0b875672017-10-06 00:49:2113#include "extensions/renderer/messaging_util.h"
Devlin Croninc7d8fd062017-09-16 03:36:4314#include "gin/arguments.h"
15#include "gin/converter.h"
16#include "gin/object_template_builder.h"
Devlin Croninc7d8fd062017-09-16 03:36:4317
18namespace extensions {
19
20namespace {
21
22constexpr char kSenderKey[] = "sender";
23constexpr char kOnMessageEvent[] = "onMessage";
24constexpr char kOnDisconnectEvent[] = "onDisconnect";
25
26} // namespace
27
28GinPort::GinPort(const PortId& port_id,
Devlin Cronin60b72962017-09-29 00:11:4129 int routing_id,
Devlin Croninc7d8fd062017-09-16 03:36:4330 const std::string& name,
31 APIEventHandler* event_handler,
32 Delegate* delegate)
33 : port_id_(port_id),
Devlin Cronin60b72962017-09-29 00:11:4134 routing_id_(routing_id),
Devlin Croninc7d8fd062017-09-16 03:36:4335 name_(name),
36 event_handler_(event_handler),
37 delegate_(delegate) {}
38
39GinPort::~GinPort() {}
40
41gin::WrapperInfo GinPort::kWrapperInfo = {gin::kEmbedderNativeGin};
42
43gin::ObjectTemplateBuilder GinPort::GetObjectTemplateBuilder(
44 v8::Isolate* isolate) {
45 return Wrappable<GinPort>::GetObjectTemplateBuilder(isolate)
46 .SetMethod("disconnect", &GinPort::DisconnectHandler)
47 .SetMethod("postMessage", &GinPort::PostMessageHandler)
48 .SetProperty("name", &GinPort::GetName)
49 .SetProperty("onDisconnect", &GinPort::GetOnDisconnectEvent)
50 .SetProperty("onMessage", &GinPort::GetOnMessageEvent)
51 .SetProperty("sender", &GinPort::GetSender);
52}
53
54void GinPort::DispatchOnMessage(v8::Local<v8::Context> context,
55 const Message& message) {
56 v8::Isolate* isolate = context->GetIsolate();
57 v8::HandleScope handle_scope(isolate);
58 v8::Context::Scope context_scope(context);
59
Devlin Cronin0b875672017-10-06 00:49:2160 v8::Local<v8::Value> parsed_message =
61 messaging_util::MessageToV8(context, message);
62 if (parsed_message.IsEmpty()) {
63 NOTREACHED();
64 return;
Devlin Croninc7d8fd062017-09-16 03:36:4365 }
Devlin Cronin0b875672017-10-06 00:49:2166
Devlin Croninc7d8fd062017-09-16 03:36:4367 v8::Local<v8::Object> self = GetWrapper(isolate).ToLocalChecked();
68 std::vector<v8::Local<v8::Value>> args = {parsed_message, self};
69 DispatchEvent(context, &args, kOnMessageEvent);
70}
71
Devlin Cronin60b72962017-09-29 00:11:4172void GinPort::DispatchOnDisconnect(v8::Local<v8::Context> context) {
Devlin Croninc7d8fd062017-09-16 03:36:4373 DCHECK(!is_closed_);
74
75 v8::Isolate* isolate = context->GetIsolate();
76 v8::HandleScope handle_scope(isolate);
77 v8::Context::Scope context_scope(context);
78
79 v8::Local<v8::Object> self = GetWrapper(isolate).ToLocalChecked();
80 std::vector<v8::Local<v8::Value>> args = {self};
81 DispatchEvent(context, &args, kOnDisconnectEvent);
82
83 Invalidate(context);
84}
85
86void GinPort::SetSender(v8::Local<v8::Context> context,
87 v8::Local<v8::Value> sender) {
88 v8::Isolate* isolate = context->GetIsolate();
89 v8::HandleScope handle_scope(isolate);
90
91 v8::Local<v8::Object> wrapper = GetWrapper(isolate).ToLocalChecked();
92 v8::Local<v8::Private> key =
93 v8::Private::ForApi(isolate, gin::StringToSymbol(isolate, kSenderKey));
94 v8::Maybe<bool> set_result = wrapper->SetPrivate(context, key, sender);
95 DCHECK(set_result.IsJust() && set_result.FromJust());
96}
97
98void GinPort::DisconnectHandler(gin::Arguments* arguments) {
99 if (is_closed_)
100 return;
101
102 v8::Local<v8::Context> context = arguments->GetHolderCreationContext();
103 Invalidate(context);
Devlin Cronin60b72962017-09-29 00:11:41104
105 delegate_->ClosePort(context, port_id_, routing_id_);
Devlin Croninc7d8fd062017-09-16 03:36:43106}
107
108void GinPort::PostMessageHandler(gin::Arguments* arguments,
109 v8::Local<v8::Value> v8_message) {
110 v8::Isolate* isolate = arguments->isolate();
Devlin Cronin60b72962017-09-29 00:11:41111 v8::Local<v8::Context> context = arguments->GetHolderCreationContext();
Devlin Croninc7d8fd062017-09-16 03:36:43112 if (is_closed_) {
113 ThrowError(isolate, "Attempting to use a disconnected port object");
114 return;
115 }
116
Devlin Croninfe7aae62017-11-16 03:49:55117 std::string error;
Devlin Cronin0b875672017-10-06 00:49:21118 std::unique_ptr<Message> message =
Devlin Croninfe7aae62017-11-16 03:49:55119 messaging_util::MessageFromV8(context, v8_message, &error);
Devlin Cronin0b875672017-10-06 00:49:21120 if (!message) {
Devlin Croninfe7aae62017-11-16 03:49:55121 ThrowError(isolate, error);
Devlin Croninc7d8fd062017-09-16 03:36:43122 return;
123 }
124
Devlin Cronin0b875672017-10-06 00:49:21125 delegate_->PostMessageToPort(context, port_id_, routing_id_,
126 std::move(message));
Devlin Croninc7d8fd062017-09-16 03:36:43127}
128
129std::string GinPort::GetName() {
130 return name_;
131}
132
133v8::Local<v8::Value> GinPort::GetOnDisconnectEvent(gin::Arguments* arguments) {
134 return GetEvent(arguments->GetHolderCreationContext(), kOnDisconnectEvent);
135}
136
137v8::Local<v8::Value> GinPort::GetOnMessageEvent(gin::Arguments* arguments) {
138 return GetEvent(arguments->GetHolderCreationContext(), kOnMessageEvent);
139}
140
141v8::Local<v8::Value> GinPort::GetSender(gin::Arguments* arguments) {
142 v8::Isolate* isolate = arguments->isolate();
143 v8::Local<v8::Object> wrapper = GetWrapper(isolate).ToLocalChecked();
144 v8::Local<v8::Private> key =
145 v8::Private::ForApi(isolate, gin::StringToSymbol(isolate, kSenderKey));
146 v8::Local<v8::Value> sender;
147 if (!wrapper->GetPrivate(arguments->GetHolderCreationContext(), key)
148 .ToLocal(&sender)) {
149 NOTREACHED();
150 return v8::Undefined(isolate);
151 }
152
153 return sender;
154}
155
156v8::Local<v8::Object> GinPort::GetEvent(v8::Local<v8::Context> context,
157 base::StringPiece event_name) {
158 DCHECK(event_name == kOnMessageEvent || event_name == kOnDisconnectEvent);
159 v8::Isolate* isolate = context->GetIsolate();
160 v8::Local<v8::Object> wrapper = GetWrapper(isolate).ToLocalChecked();
161 v8::Local<v8::Private> key =
162 v8::Private::ForApi(isolate, gin::StringToSymbol(isolate, event_name));
163 v8::Local<v8::Value> event_val;
164 if (!wrapper->GetPrivate(context, key).ToLocal(&event_val)) {
165 NOTREACHED();
166 return v8::Local<v8::Object>();
167 }
168
169 DCHECK(!event_val.IsEmpty());
170 v8::Local<v8::Object> event_object;
171 if (event_val->IsUndefined()) {
172 event_object = event_handler_->CreateAnonymousEventInstance(context);
173 v8::Maybe<bool> set_result =
174 wrapper->SetPrivate(context, key, event_object);
175 if (!set_result.IsJust() || !set_result.FromJust()) {
176 NOTREACHED();
177 return v8::Local<v8::Object>();
178 }
179 } else {
180 event_object = event_val.As<v8::Object>();
181 }
182 return event_object;
183}
184
185void GinPort::DispatchEvent(v8::Local<v8::Context> context,
186 std::vector<v8::Local<v8::Value>>* args,
187 base::StringPiece event_name) {
188 v8::Isolate* isolate = context->GetIsolate();
189 v8::Local<v8::Value> on_message = GetEvent(context, event_name);
190 EventEmitter* emitter = nullptr;
191 gin::Converter<EventEmitter*>::FromV8(isolate, on_message, &emitter);
192 CHECK(emitter);
193
194 emitter->Fire(context, args, nullptr);
195}
196
197void GinPort::Invalidate(v8::Local<v8::Context> context) {
198 is_closed_ = true;
199 event_handler_->InvalidateCustomEvent(context,
200 GetEvent(context, kOnMessageEvent));
201 event_handler_->InvalidateCustomEvent(context,
202 GetEvent(context, kOnDisconnectEvent));
Devlin Croninc7d8fd062017-09-16 03:36:43203}
204
205void GinPort::ThrowError(v8::Isolate* isolate, base::StringPiece error) {
206 isolate->ThrowException(
207 v8::Exception::Error(gin::StringToV8(isolate, error)));
208}
209
210} // namespace extensions