Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / native_client_sdk / src / libraries / nacl_io / devfs / jspipe_event_emitter.cc
blobc3d1a3f7a78d5e3d3daa4ff9003811d6fda5df55
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.
5 #include "nacl_io/devfs/jspipe_event_emitter.h"
7 #include <assert.h>
8 #include <errno.h>
9 #include <string.h>
11 #include <algorithm>
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__)
18 #include "nacl_io/log.h"
19 #include "nacl_io/osinttypes.h"
20 #include "nacl_io/pepper_interface.h"
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";
31 namespace nacl_io {
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;
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();
62 if (var_iface_ == NULL)
63 return;
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);
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_);
83 PP_Var JSPipeEventEmitter::VarFromCStr(const char* string) {
84 assert(var_iface_);
85 return var_iface_->VarFromUtf8(string, strlen(string));
88 void JSPipeEventEmitter::UpdateStatus_Locked() {
89 uint32_t status = 0;
90 if (!input_fifo_.IsEmpty())
91 status |= POLLIN;
93 if (GetOSpace() > 0)
94 status |= POLLOUT;
96 ClearEvents_Locked(~status);
97 RaiseEvents_Locked(status);
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);
109 UpdateStatus_Locked();
110 return 0;
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 ERROR("Got NULL interface(s): %s%s",
117 var_iface_ ? "" : "Var ",
118 buffer_iface_ ? "" : "ArrayBuffer");
119 return EIO;
122 // Copy payload data in a new ArrayBuffer
123 PP_Var buffer = buffer_iface_->Create(count);
124 memcpy(buffer_iface_->Map(buffer), buf, count);
125 buffer_iface_->Unmap(buffer);
127 Error rtn = SendMessageToJS(write_var_, buffer);
128 var_iface_->Release(buffer);
129 return rtn;
132 Error JSPipeEventEmitter::SetName(const char* name) {
133 if (var_iface_ == NULL) {
134 // No error here: many of the tests trigger this message.
135 LOG_TRACE("Got NULL interface: Var");
136 return EIO;
139 // name can only be set once
140 if (!name_.empty()) {
141 LOG_ERROR("Attempting to set name more than once.");
142 return EIO;
145 // new name must not be empty
146 if (!name || strlen(name) == 0) {
147 LOG_ERROR("Empty name is invalid.");
148 return EIO;
151 TRACE("set name: %s", name);
152 name_ = name;
153 pipe_name_var_ = VarFromCStr(name);
154 return 0;
157 Error JSPipeEventEmitter::SendMessageToJS(PP_Var operation, PP_Var payload) {
158 if (!ppapi_) {
159 LOG_ERROR("ppapi_ is NULL.");
160 return EIO;
163 if (!messaging_iface_ || !var_iface_ || !dict_iface_) {
164 LOG_ERROR("Got NULL interface(s): %s%s%s",
165 messaging_iface_ ? "" : "Messaging ",
166 dict_iface_ ? "" : "Dictionary ",
167 var_iface_ ? "" : "Var");
168 return EIO;
171 // Create dict object which will be sent to JavaScript.
172 PP_Var dict = dict_iface_->Create();
174 // Set three keys in the dictionary: 'pipe', 'operation', and 'payload'
175 dict_iface_->Set(dict, pipe_key_, pipe_name_var_);
176 dict_iface_->Set(dict, operation_key_, operation);
177 dict_iface_->Set(dict, payload_key_, payload);
179 // Send the dict via PostMessage
180 messaging_iface_->PostMessage(ppapi_->GetInstance(), dict);
182 // Release the dict
183 var_iface_->Release(dict);
184 return 0;
187 Error JSPipeEventEmitter::SendAckMessage(size_t byte_count) {
188 TRACE("SendAckMessage %" PRIuS, byte_count);
189 PP_Var payload;
190 payload.type = PP_VARTYPE_INT32;
191 payload.value.as_int = (int32_t)byte_count;
193 return SendMessageToJS(ack_var_, payload);
196 size_t JSPipeEventEmitter::HandleJSWrite(const char* data, size_t len) {
197 AUTO_LOCK(GetLock());
198 size_t out_len = input_fifo_.Write(data, len);
199 UpdateStatus_Locked();
200 return out_len;
203 void JSPipeEventEmitter::HandleJSAck(size_t byte_count) {
204 AUTO_LOCK(GetLock());
205 if (byte_count > bytes_sent_) {
206 ERROR("Unexpected byte count: %" PRIuS, byte_count);
207 return;
210 bytes_acked_ = byte_count;
211 TRACE("HandleAck: %" SCNuS "/%" PRIuS, bytes_acked_, bytes_sent_);
212 UpdateStatus_Locked();
215 Error JSPipeEventEmitter::HandleJSWrite(struct PP_Var message) {
216 TRACE("HandleJSWrite");
217 if (message.type != PP_VARTYPE_ARRAY_BUFFER) {
218 ERROR("Expected ArrayBuffer but got %d.", message.type);
219 return EINVAL;
221 uint32_t length;
222 if (buffer_iface_->ByteLength(message, &length) != PP_TRUE) {
223 ERROR("ArrayBuffer.ByteLength returned PP_FALSE");
224 return EINVAL;
227 char* buffer = (char*)buffer_iface_->Map(message);
229 // Write data to the input fifo
230 size_t wrote = HandleJSWrite(buffer, length);
231 buffer_iface_->Unmap(message);
232 if (wrote != length) {
233 ERROR("Only wrote %d of %d bytes to pipe", (int)wrote, (int)length);
234 return EIO;
236 TRACE("done HandleWrite: %d", length);
237 return 0;
240 Error JSPipeEventEmitter::HandleJSAck(PP_Var message) {
241 if (message.type != PP_VARTYPE_INT32) {
242 ERROR("Integer object expected but got %d.", message.type);
243 return EINVAL;
245 HandleJSAck(message.value.as_int);
246 return 0;
249 int JSPipeEventEmitter::VarStrcmp(PP_Var a, PP_Var b) {
250 uint32_t length_a = 0;
251 uint32_t length_b = 0;
252 const char* cstring_a = var_iface_->VarToUtf8(a, &length_a);
253 const char* cstring_b = var_iface_->VarToUtf8(a, &length_b);
254 std::string string_a(cstring_a, length_a);
255 std::string string_b(cstring_b, length_a);
256 return strcmp(string_a.c_str(), string_b.c_str());
259 Error JSPipeEventEmitter::HandleJSMessage(struct PP_Var message) {
260 Error err = 0;
261 if (!messaging_iface_ || !var_iface_ || !dict_iface_ || !buffer_iface_) {
262 ERROR("Got NULL interface(s): %s%s%s%s",
263 messaging_iface_ ? "" : "Messaging ",
264 var_iface_ ? "" : "Var ",
265 dict_iface_ ? "" : "Dictionary ",
266 buffer_iface_ ? "" : "ArrayBuffer");
267 return ENOSYS;
270 // Verify that we have an array with size two.
271 if (message.type != PP_VARTYPE_DICTIONARY) {
272 ERROR("Expected Dictionary but got %d.", message.type);
273 return EINVAL;
276 #ifndef NDEBUG
277 PP_Var pipe_name_var = dict_iface_->Get(message, pipe_key_);
278 if (VarStrcmp(pipe_name_var, pipe_name_var_)) {
279 ERROR("Wrong pipe name.");
280 return EINVAL;
282 var_iface_->Release(pipe_name_var);
283 #endif
285 PP_Var operation_var = dict_iface_->Get(message, operation_key_);
286 if (operation_var.type != PP_VARTYPE_STRING) {
287 ERROR("Expected String but got %d.", operation_var.type);
288 err = EINVAL;
289 } else {
290 uint32_t length;
291 const char* operation_string;
292 operation_string = var_iface_->VarToUtf8(operation_var, &length);
293 std::string message_type(operation_string, length);
295 TRACE("HandleJSMessage %s", message_type.c_str());
296 PP_Var payload = dict_iface_->Get(message, payload_key_);
297 if (message_type == kOperationNameWrite) {
298 err = HandleJSWrite(payload);
299 } else if (message_type == kOperationNameAck) {
300 err = HandleJSAck(payload);
301 } else {
302 ERROR("Unknown message type: %s", message_type.c_str());
303 err = EINVAL;
305 var_iface_->Release(payload);
308 var_iface_->Release(operation_var);
309 return err;
312 Error JSPipeEventEmitter::Write_Locked(const char* data,
313 size_t len,
314 int* out_bytes) {
315 if (GetOSpace() == 0) {
316 *out_bytes = 0;
317 return 0;
320 if (len > GetOSpace())
321 len = GetOSpace();
323 // Limit the size of the data we send with PostMessage to kMaxPostMessageSize
324 if (len > kMaxPostMessageSize)
325 len = kMaxPostMessageSize;
327 Error err = SendWriteMessage(data, len);
328 if (err != 0)
329 return err;
330 *out_bytes = len;
331 bytes_sent_ += len;
333 UpdateStatus_Locked();
334 return 0;
337 } // namespace nacl_io