• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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