1 // Copyright 2014 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 "nacl_io/devfs/jspipe_event_emitter.h"
6
7 #include <assert.h>
8 #include <errno.h>
9 #include <string.h>
10
11 #include <algorithm>
12
13 #define TRACE(format, ...) \
14 LOG_TRACE("jspipe[%s]: " format, name_.c_str(), ##__VA_ARGS__)
15 #define ERROR(format, ...) \
16 LOG_ERROR("jspipe[%s]: " format, name_.c_str(), ##__VA_ARGS__)
17
18 #include "nacl_io/log.h"
19 #include "nacl_io/osinttypes.h"
20 #include "nacl_io/pepper_interface.h"
21
22 namespace {
23 const size_t kMaxPostMessageSize = 64 * 1024;
24 const char* kDictKeyPipe = "pipe";
25 const char* kDictKeyOperation = "operation";
26 const char* kDictKeyPayload = "payload";
27 const char* kOperationNameAck = "ack";
28 const char* kOperationNameWrite = "write";
29 }
30
31 namespace nacl_io {
32
JSPipeEventEmitter(PepperInterface * ppapi,size_t size)33 JSPipeEventEmitter::JSPipeEventEmitter(PepperInterface* ppapi, size_t size)
34 : input_fifo_(size),
35 post_message_buffer_size_(size),
36 bytes_sent_(0),
37 bytes_acked_(0),
38 bytes_read_(0),
39 ppapi_(ppapi),
40 messaging_iface_(NULL),
41 var_iface_(NULL),
42 array_iface_(NULL),
43 buffer_iface_(NULL),
44 dict_iface_(NULL),
45 pipe_name_var_(PP_MakeUndefined()),
46 pipe_key_(PP_MakeUndefined()),
47 operation_key_(PP_MakeUndefined()),
48 payload_key_(PP_MakeUndefined()),
49 write_var_(PP_MakeUndefined()),
50 ack_var_(PP_MakeUndefined()) {
51 UpdateStatus_Locked();
52 if (ppapi == NULL) {
53 TRACE("missing PPAPI provider");
54 return;
55 }
56 messaging_iface_ = ppapi->GetMessagingInterface();
57 var_iface_ = ppapi->GetVarInterface();
58 array_iface_ = ppapi->GetVarArrayInterface();
59 buffer_iface_ = ppapi->GetVarArrayBufferInterface();
60 dict_iface_ = ppapi->GetVarDictionaryInterface();
61
62 if (var_iface_ == NULL)
63 return;
64
65 pipe_key_ = VarFromCStr(kDictKeyPipe);
66 operation_key_ = VarFromCStr(kDictKeyOperation);
67 payload_key_ = VarFromCStr(kDictKeyPayload);
68 write_var_ = VarFromCStr(kOperationNameWrite);
69 ack_var_ = VarFromCStr(kOperationNameAck);
70 }
71
Destroy()72 void JSPipeEventEmitter::Destroy() {
73 if (var_iface_ == NULL)
74 return;
75 var_iface_->Release(pipe_name_var_);
76 var_iface_->Release(pipe_key_);
77 var_iface_->Release(operation_key_);
78 var_iface_->Release(payload_key_);
79 var_iface_->Release(write_var_);
80 var_iface_->Release(ack_var_);
81 }
82
VarFromCStr(const char * string)83 PP_Var JSPipeEventEmitter::VarFromCStr(const char* string) {
84 assert(var_iface_);
85 return var_iface_->VarFromUtf8(string, strlen(string));
86 }
87
UpdateStatus_Locked()88 void JSPipeEventEmitter::UpdateStatus_Locked() {
89 uint32_t status = 0;
90 if (!input_fifo_.IsEmpty())
91 status |= POLLIN;
92
93 if (GetOSpace() > 0)
94 status |= POLLOUT;
95
96 ClearEvents_Locked(~status);
97 RaiseEvents_Locked(status);
98 }
99
Read_Locked(char * data,size_t len,int * out_bytes)100 Error JSPipeEventEmitter::Read_Locked(char* data, size_t len, int* out_bytes) {
101 *out_bytes = input_fifo_.Read(data, len);
102 if (*out_bytes > 0) {
103 bytes_read_ += *out_bytes;
104 Error err = SendAckMessage(bytes_read_);
105 if (err != 0)
106 ERROR("Sending ACK failed: %d\n", err.error);
107 }
108
109 UpdateStatus_Locked();
110 return 0;
111 }
112
SendWriteMessage(const void * buf,size_t count)113 Error JSPipeEventEmitter::SendWriteMessage(const void* buf, size_t count) {
114 TRACE("SendWriteMessage [%" PRIuS "] total=%" PRIuS, count, bytes_sent_);
115 if (!var_iface_ || !buffer_iface_)
116 return EIO;
117
118 // Copy payload data in a new ArrayBuffer
119 PP_Var buffer = buffer_iface_->Create(count);
120 memcpy(buffer_iface_->Map(buffer), buf, count);
121 buffer_iface_->Unmap(buffer);
122
123 Error rtn = SendMessageToJS(write_var_, buffer);
124 var_iface_->Release(buffer);
125 return rtn;
126 }
127
SetName(const char * name)128 Error JSPipeEventEmitter::SetName(const char* name) {
129 if (var_iface_ == NULL)
130 return EIO;
131
132 // name can only be set once
133 if (!name_.empty())
134 return EIO;
135
136 // new name must not be empty
137 if (!name || strlen(name) == 0)
138 return EIO;
139
140 TRACE("set name: %s", name);
141 name_ = name;
142 pipe_name_var_ = VarFromCStr(name);
143 return 0;
144 }
145
SendMessageToJS(PP_Var operation,PP_Var payload)146 Error JSPipeEventEmitter::SendMessageToJS(PP_Var operation, PP_Var payload) {
147 if (!ppapi_ || !messaging_iface_ || !var_iface_ || !dict_iface_)
148 return EIO;
149
150 // Create dict object which will be sent to JavaScript.
151 PP_Var dict = dict_iface_->Create();
152
153 // Set three keys in the dictionary: 'pipe', 'operation', and 'payload'
154 dict_iface_->Set(dict, pipe_key_, pipe_name_var_);
155 dict_iface_->Set(dict, operation_key_, operation);
156 dict_iface_->Set(dict, payload_key_, payload);
157
158 // Send the dict via PostMessage
159 messaging_iface_->PostMessage(ppapi_->GetInstance(), dict);
160
161 // Release the dict
162 var_iface_->Release(dict);
163 return 0;
164 }
165
SendAckMessage(size_t byte_count)166 Error JSPipeEventEmitter::SendAckMessage(size_t byte_count) {
167 TRACE("SendAckMessage %" PRIuS, byte_count);
168 PP_Var payload;
169 payload.type = PP_VARTYPE_INT32;
170 payload.value.as_int = (int32_t)byte_count;
171
172 return SendMessageToJS(ack_var_, payload);
173 }
174
HandleJSWrite(const char * data,size_t len)175 size_t JSPipeEventEmitter::HandleJSWrite(const char* data, size_t len) {
176 size_t out_len = input_fifo_.Write(data, len);
177 UpdateStatus_Locked();
178 return out_len;
179 }
180
HandleJSAck(size_t byte_count)181 void JSPipeEventEmitter::HandleJSAck(size_t byte_count) {
182 if (byte_count > bytes_sent_) {
183 ERROR("HandleAck unexpected byte count: %" PRIuS, byte_count);
184 return;
185 }
186
187 bytes_acked_ = byte_count;
188 TRACE("HandleAck: %" SCNuS "/%" PRIuS, bytes_acked_, bytes_sent_);
189 UpdateStatus_Locked();
190 }
191
HandleJSWrite(struct PP_Var message)192 Error JSPipeEventEmitter::HandleJSWrite(struct PP_Var message) {
193 TRACE("HandleJSWrite");
194 if (message.type != PP_VARTYPE_ARRAY_BUFFER) {
195 TRACE("HandleJSWrite expected ArrayBuffer but got %d.", message.type);
196 return EINVAL;
197 }
198 uint32_t length;
199 if (buffer_iface_->ByteLength(message, &length) != PP_TRUE)
200 return EINVAL;
201
202 char* buffer = (char*)buffer_iface_->Map(message);
203
204 // Write data to the input fifo
205 size_t wrote = HandleJSWrite(buffer, length);
206 buffer_iface_->Unmap(message);
207 if (wrote != length) {
208 LOG_ERROR("Only wrote %d of %d bytes to pipe", (int)wrote, (int)length);
209 return EIO;
210 }
211 TRACE("done HandleWrite: %d", length);
212 return 0;
213 }
214
HandleJSAck(PP_Var message)215 Error JSPipeEventEmitter::HandleJSAck(PP_Var message) {
216 if (message.type != PP_VARTYPE_INT32) {
217 TRACE("HandleAck integer object expected but got %d.", message.type);
218 return EINVAL;
219 }
220 HandleJSAck(message.value.as_int);
221 return 0;
222 }
223
VarStrcmp(PP_Var a,PP_Var b)224 int JSPipeEventEmitter::VarStrcmp(PP_Var a, PP_Var b) {
225 uint32_t length_a = 0;
226 uint32_t length_b = 0;
227 const char* cstring_a = var_iface_->VarToUtf8(a, &length_a);
228 const char* cstring_b = var_iface_->VarToUtf8(a, &length_b);
229 std::string string_a(cstring_a, length_a);
230 std::string string_b(cstring_b, length_a);
231 return strcmp(string_a.c_str(), string_b.c_str());
232 }
233
HandleJSMessage(struct PP_Var message)234 Error JSPipeEventEmitter::HandleJSMessage(struct PP_Var message) {
235 Error err = 0;
236 if (!messaging_iface_ || !var_iface_ || !dict_iface_ || !buffer_iface_) {
237 TRACE("HandleJSMessage: missing PPAPI interfaces");
238 return ENOSYS;
239 }
240
241 // Verify that we have an array with size two.
242 if (message.type != PP_VARTYPE_DICTIONARY) {
243 TRACE("HandleJSMessage passed non-dictionary var");
244 return EINVAL;
245 }
246
247 #ifndef NDEBUG
248 PP_Var pipe_name_var = dict_iface_->Get(message, pipe_key_);
249 if (VarStrcmp(pipe_name_var, pipe_name_var_)) {
250 TRACE("HandleJSMessage wrong pipe name");
251 return EINVAL;
252 }
253 var_iface_->Release(pipe_name_var);
254 #endif
255
256 PP_Var operation_var = dict_iface_->Get(message, operation_key_);
257 if (operation_var.type != PP_VARTYPE_STRING) {
258 TRACE("HandleJSMessage invalid operation");
259 err = EINVAL;
260 } else {
261 uint32_t length;
262 const char* operation_string;
263 operation_string = var_iface_->VarToUtf8(operation_var, &length);
264 std::string message_type(operation_string, length);
265
266 TRACE("HandleJSMessage %s", message_type.c_str());
267 PP_Var payload = dict_iface_->Get(message, payload_key_);
268 if (message_type == kOperationNameWrite) {
269 err = HandleJSWrite(payload);
270 } else if (message_type == kOperationNameAck) {
271 err = HandleJSAck(payload);
272 } else {
273 TRACE("Unknown message type: %s", message_type.c_str());
274 err = EINVAL;
275 }
276 var_iface_->Release(payload);
277 }
278
279 var_iface_->Release(operation_var);
280 return err;
281 }
282
Write_Locked(const char * data,size_t len,int * out_bytes)283 Error JSPipeEventEmitter::Write_Locked(const char* data,
284 size_t len,
285 int* out_bytes) {
286 if (GetOSpace() == 0) {
287 *out_bytes = 0;
288 return 0;
289 }
290
291 if (len > GetOSpace())
292 len = GetOSpace();
293
294 // Limit the size of the data we send with PostMessage to kMaxPostMessageSize
295 if (len > kMaxPostMessageSize)
296 len = kMaxPostMessageSize;
297
298 Error err = SendWriteMessage(data, len);
299 if (err != 0)
300 return err;
301 *out_bytes = len;
302 bytes_sent_ += len;
303
304 UpdateStatus_Locked();
305 return 0;
306 }
307
308 } // namespace nacl_io
309