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 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 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 83 PP_Var JSPipeEventEmitter::VarFromCStr(const char* string) { 84 assert(var_iface_); 85 return var_iface_->VarFromUtf8(string, strlen(string)); 86 } 87 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 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 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 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 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 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 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 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 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 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 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 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 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