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"
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"
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";
33 JSPipeEventEmitter::JSPipeEventEmitter(PepperInterface
* ppapi
, size_t size
)
35 post_message_buffer_size_(size
),
40 messaging_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();
53 TRACE("missing PPAPI provider");
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
)
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
)
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
) {
85 return var_iface_
->VarFromUtf8(string
, strlen(string
));
88 void JSPipeEventEmitter::UpdateStatus_Locked() {
90 if (!input_fifo_
.IsEmpty())
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_
);
106 ERROR("Sending ACK failed: %d\n", err
.error
);
109 UpdateStatus_Locked();
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");
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
);
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");
139 // name can only be set once
140 if (!name_
.empty()) {
141 LOG_ERROR("Attempting to set name more than once.");
145 // new name must not be empty
146 if (!name
|| strlen(name
) == 0) {
147 LOG_ERROR("Empty name is invalid.");
151 TRACE("set name: %s", name
);
153 pipe_name_var_
= VarFromCStr(name
);
157 Error
JSPipeEventEmitter::SendMessageToJS(PP_Var operation
, PP_Var payload
) {
159 LOG_ERROR("ppapi_ is NULL.");
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");
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
);
183 var_iface_
->Release(dict
);
187 Error
JSPipeEventEmitter::SendAckMessage(size_t byte_count
) {
188 TRACE("SendAckMessage %" PRIuS
, byte_count
);
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();
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
);
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
);
222 if (buffer_iface_
->ByteLength(message
, &length
) != PP_TRUE
) {
223 ERROR("ArrayBuffer.ByteLength returned PP_FALSE");
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
);
236 TRACE("done HandleWrite: %d", length
);
240 Error
JSPipeEventEmitter::HandleJSAck(PP_Var message
) {
241 if (message
.type
!= PP_VARTYPE_INT32
) {
242 ERROR("Integer object expected but got %d.", message
.type
);
245 HandleJSAck(message
.value
.as_int
);
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
) {
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");
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
);
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.");
282 var_iface_
->Release(pipe_name_var
);
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
);
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
);
302 ERROR("Unknown message type: %s", message_type
.c_str());
305 var_iface_
->Release(payload
);
308 var_iface_
->Release(operation_var
);
312 Error
JSPipeEventEmitter::Write_Locked(const char* data
,
315 if (GetOSpace() == 0) {
320 if (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
);
333 UpdateStatus_Locked();
337 } // namespace nacl_io