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 "mojo/edk/js/core.h"
6
7 #include <stddef.h>
8 #include <stdint.h>
9
10 #include "base/bind.h"
11 #include "base/logging.h"
12 #include "gin/arguments.h"
13 #include "gin/array_buffer.h"
14 #include "gin/converter.h"
15 #include "gin/dictionary.h"
16 #include "gin/function_template.h"
17 #include "gin/handle.h"
18 #include "gin/object_template_builder.h"
19 #include "gin/per_isolate_data.h"
20 #include "gin/public/wrapper_info.h"
21 #include "gin/wrappable.h"
22 #include "mojo/edk/js/drain_data.h"
23 #include "mojo/edk/js/handle.h"
24
25 namespace mojo {
26 namespace edk {
27 namespace js {
28
29 namespace {
30
CloseHandle(gin::Handle<HandleWrapper> handle)31 MojoResult CloseHandle(gin::Handle<HandleWrapper> handle) {
32 if (!handle->get().is_valid())
33 return MOJO_RESULT_INVALID_ARGUMENT;
34 handle->Close();
35 return MOJO_RESULT_OK;
36 }
37
WaitHandle(const gin::Arguments & args,mojo::Handle handle,MojoHandleSignals signals,MojoDeadline deadline)38 gin::Dictionary WaitHandle(const gin::Arguments& args,
39 mojo::Handle handle,
40 MojoHandleSignals signals,
41 MojoDeadline deadline) {
42 v8::Isolate* isolate = args.isolate();
43 gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(isolate);
44
45 MojoHandleSignalsState signals_state;
46 MojoResult result = mojo::Wait(handle, signals, deadline, &signals_state);
47 dictionary.Set("result", result);
48
49 mojo::WaitManyResult wmv(result, 0);
50 if (!wmv.AreSignalsStatesValid()) {
51 dictionary.Set("signalsState", v8::Null(isolate).As<v8::Value>());
52 } else {
53 gin::Dictionary signalsStateDict = gin::Dictionary::CreateEmpty(isolate);
54 signalsStateDict.Set("satisfiedSignals", signals_state.satisfied_signals);
55 signalsStateDict.Set("satisfiableSignals",
56 signals_state.satisfiable_signals);
57 dictionary.Set("signalsState", signalsStateDict);
58 }
59
60 return dictionary;
61 }
62
WaitMany(const gin::Arguments & args,const std::vector<mojo::Handle> & handles,const std::vector<MojoHandleSignals> & signals,MojoDeadline deadline)63 gin::Dictionary WaitMany(const gin::Arguments& args,
64 const std::vector<mojo::Handle>& handles,
65 const std::vector<MojoHandleSignals>& signals,
66 MojoDeadline deadline) {
67 v8::Isolate* isolate = args.isolate();
68 gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(isolate);
69
70 std::vector<MojoHandleSignalsState> signals_states(signals.size());
71 mojo::WaitManyResult wmv =
72 mojo::WaitMany(handles, signals, deadline, &signals_states);
73 dictionary.Set("result", wmv.result);
74 if (wmv.IsIndexValid()) {
75 dictionary.Set("index", wmv.index);
76 } else {
77 dictionary.Set("index", v8::Null(isolate).As<v8::Value>());
78 }
79 if (wmv.AreSignalsStatesValid()) {
80 std::vector<gin::Dictionary> vec;
81 for (size_t i = 0; i < handles.size(); ++i) {
82 gin::Dictionary signalsStateDict = gin::Dictionary::CreateEmpty(isolate);
83 signalsStateDict.Set("satisfiedSignals",
84 signals_states[i].satisfied_signals);
85 signalsStateDict.Set("satisfiableSignals",
86 signals_states[i].satisfiable_signals);
87 vec.push_back(signalsStateDict);
88 }
89 dictionary.Set("signalsState", vec);
90 } else {
91 dictionary.Set("signalsState", v8::Null(isolate).As<v8::Value>());
92 }
93
94 return dictionary;
95 }
96
CreateMessagePipe(const gin::Arguments & args)97 gin::Dictionary CreateMessagePipe(const gin::Arguments& args) {
98 gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate());
99 dictionary.Set("result", MOJO_RESULT_INVALID_ARGUMENT);
100
101 MojoHandle handle0 = MOJO_HANDLE_INVALID;
102 MojoHandle handle1 = MOJO_HANDLE_INVALID;
103 MojoResult result = MOJO_RESULT_OK;
104
105 v8::Handle<v8::Value> options_value = args.PeekNext();
106 if (options_value.IsEmpty() || options_value->IsNull() ||
107 options_value->IsUndefined()) {
108 result = MojoCreateMessagePipe(NULL, &handle0, &handle1);
109 } else if (options_value->IsObject()) {
110 gin::Dictionary options_dict(args.isolate(), options_value->ToObject());
111 MojoCreateMessagePipeOptions options;
112 // For future struct_size, we can probably infer that from the presence of
113 // properties in options_dict. For now, it's always 8.
114 options.struct_size = 8;
115 // Ideally these would be optional. But the interface makes it hard to
116 // typecheck them then.
117 if (!options_dict.Get("flags", &options.flags)) {
118 return dictionary;
119 }
120
121 result = MojoCreateMessagePipe(&options, &handle0, &handle1);
122 } else {
123 return dictionary;
124 }
125
126 CHECK_EQ(MOJO_RESULT_OK, result);
127
128 dictionary.Set("result", result);
129 dictionary.Set("handle0", mojo::Handle(handle0));
130 dictionary.Set("handle1", mojo::Handle(handle1));
131 return dictionary;
132 }
133
WriteMessage(mojo::Handle handle,const gin::ArrayBufferView & buffer,const std::vector<gin::Handle<HandleWrapper>> & handles,MojoWriteMessageFlags flags)134 MojoResult WriteMessage(
135 mojo::Handle handle,
136 const gin::ArrayBufferView& buffer,
137 const std::vector<gin::Handle<HandleWrapper> >& handles,
138 MojoWriteMessageFlags flags) {
139 std::vector<MojoHandle> raw_handles(handles.size());
140 for (size_t i = 0; i < handles.size(); ++i)
141 raw_handles[i] = handles[i]->get().value();
142 MojoResult rv = MojoWriteMessage(handle.value(),
143 buffer.bytes(),
144 static_cast<uint32_t>(buffer.num_bytes()),
145 raw_handles.empty() ? NULL : &raw_handles[0],
146 static_cast<uint32_t>(raw_handles.size()),
147 flags);
148 // MojoWriteMessage takes ownership of the handles, so release them here.
149 for (size_t i = 0; i < handles.size(); ++i)
150 ignore_result(handles[i]->release());
151
152 return rv;
153 }
154
ReadMessage(const gin::Arguments & args,mojo::Handle handle,MojoReadMessageFlags flags)155 gin::Dictionary ReadMessage(const gin::Arguments& args,
156 mojo::Handle handle,
157 MojoReadMessageFlags flags) {
158 uint32_t num_bytes = 0;
159 uint32_t num_handles = 0;
160 MojoResult result = MojoReadMessage(
161 handle.value(), NULL, &num_bytes, NULL, &num_handles, flags);
162 if (result != MOJO_RESULT_RESOURCE_EXHAUSTED) {
163 gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate());
164 dictionary.Set("result", result);
165 return dictionary;
166 }
167
168 v8::Handle<v8::ArrayBuffer> array_buffer =
169 v8::ArrayBuffer::New(args.isolate(), num_bytes);
170 std::vector<mojo::Handle> handles(num_handles);
171
172 gin::ArrayBuffer buffer;
173 ConvertFromV8(args.isolate(), array_buffer, &buffer);
174 CHECK(buffer.num_bytes() == num_bytes);
175
176 result = MojoReadMessage(handle.value(),
177 buffer.bytes(),
178 &num_bytes,
179 handles.empty() ? NULL :
180 reinterpret_cast<MojoHandle*>(&handles[0]),
181 &num_handles,
182 flags);
183
184 CHECK(buffer.num_bytes() == num_bytes);
185 CHECK(handles.size() == num_handles);
186
187 gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate());
188 dictionary.Set("result", result);
189 dictionary.Set("buffer", array_buffer);
190 dictionary.Set("handles", handles);
191 return dictionary;
192 }
193
CreateDataPipe(const gin::Arguments & args)194 gin::Dictionary CreateDataPipe(const gin::Arguments& args) {
195 gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate());
196 dictionary.Set("result", MOJO_RESULT_INVALID_ARGUMENT);
197
198 MojoHandle producer_handle = MOJO_HANDLE_INVALID;
199 MojoHandle consumer_handle = MOJO_HANDLE_INVALID;
200 MojoResult result = MOJO_RESULT_OK;
201
202 v8::Handle<v8::Value> options_value = args.PeekNext();
203 if (options_value.IsEmpty() || options_value->IsNull() ||
204 options_value->IsUndefined()) {
205 result = MojoCreateDataPipe(NULL, &producer_handle, &consumer_handle);
206 } else if (options_value->IsObject()) {
207 gin::Dictionary options_dict(args.isolate(), options_value->ToObject());
208 MojoCreateDataPipeOptions options;
209 // For future struct_size, we can probably infer that from the presence of
210 // properties in options_dict. For now, it's always 16.
211 options.struct_size = 16;
212 // Ideally these would be optional. But the interface makes it hard to
213 // typecheck them then.
214 if (!options_dict.Get("flags", &options.flags) ||
215 !options_dict.Get("elementNumBytes", &options.element_num_bytes) ||
216 !options_dict.Get("capacityNumBytes", &options.capacity_num_bytes)) {
217 return dictionary;
218 }
219
220 result = MojoCreateDataPipe(&options, &producer_handle, &consumer_handle);
221 } else {
222 return dictionary;
223 }
224
225 CHECK_EQ(MOJO_RESULT_OK, result);
226
227 dictionary.Set("result", result);
228 dictionary.Set("producerHandle", mojo::Handle(producer_handle));
229 dictionary.Set("consumerHandle", mojo::Handle(consumer_handle));
230 return dictionary;
231 }
232
WriteData(const gin::Arguments & args,mojo::Handle handle,const gin::ArrayBufferView & buffer,MojoWriteDataFlags flags)233 gin::Dictionary WriteData(const gin::Arguments& args,
234 mojo::Handle handle,
235 const gin::ArrayBufferView& buffer,
236 MojoWriteDataFlags flags) {
237 uint32_t num_bytes = static_cast<uint32_t>(buffer.num_bytes());
238 MojoResult result =
239 MojoWriteData(handle.value(), buffer.bytes(), &num_bytes, flags);
240 gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate());
241 dictionary.Set("result", result);
242 dictionary.Set("numBytes", num_bytes);
243 return dictionary;
244 }
245
ReadData(const gin::Arguments & args,mojo::Handle handle,MojoReadDataFlags flags)246 gin::Dictionary ReadData(const gin::Arguments& args,
247 mojo::Handle handle,
248 MojoReadDataFlags flags) {
249 uint32_t num_bytes = 0;
250 MojoResult result = MojoReadData(
251 handle.value(), NULL, &num_bytes, MOJO_READ_DATA_FLAG_QUERY);
252 if (result != MOJO_RESULT_OK) {
253 gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate());
254 dictionary.Set("result", result);
255 return dictionary;
256 }
257
258 v8::Handle<v8::ArrayBuffer> array_buffer =
259 v8::ArrayBuffer::New(args.isolate(), num_bytes);
260 gin::ArrayBuffer buffer;
261 ConvertFromV8(args.isolate(), array_buffer, &buffer);
262 CHECK_EQ(num_bytes, buffer.num_bytes());
263
264 result = MojoReadData(handle.value(), buffer.bytes(), &num_bytes, flags);
265 CHECK_EQ(num_bytes, buffer.num_bytes());
266
267 gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate());
268 dictionary.Set("result", result);
269 dictionary.Set("buffer", array_buffer);
270 return dictionary;
271 }
272
273 // Asynchronously read all of the data available for the specified data pipe
274 // consumer handle until the remote handle is closed or an error occurs. A
275 // Promise is returned whose settled value is an object like this:
276 // {result: core.RESULT_OK, buffer: dataArrayBuffer}. If the read failed,
277 // then the Promise is rejected, the result will be the actual error code,
278 // and the buffer will contain whatever was read before the error occurred.
279 // The drainData data pipe handle argument is closed automatically.
280
DoDrainData(gin::Arguments * args,gin::Handle<HandleWrapper> handle)281 v8::Handle<v8::Value> DoDrainData(gin::Arguments* args,
282 gin::Handle<HandleWrapper> handle) {
283 return (new DrainData(args->isolate(), handle->release()))->GetPromise();
284 }
285
IsHandle(gin::Arguments * args,v8::Handle<v8::Value> val)286 bool IsHandle(gin::Arguments* args, v8::Handle<v8::Value> val) {
287 gin::Handle<mojo::edk::js::HandleWrapper> ignore_handle;
288 return gin::Converter<gin::Handle<mojo::edk::js::HandleWrapper>>::FromV8(
289 args->isolate(), val, &ignore_handle);
290 }
291
292
293 gin::WrapperInfo g_wrapper_info = { gin::kEmbedderNativeGin };
294
295 } // namespace
296
297 const char Core::kModuleName[] = "mojo/public/js/core";
298
GetModule(v8::Isolate * isolate)299 v8::Local<v8::Value> Core::GetModule(v8::Isolate* isolate) {
300 gin::PerIsolateData* data = gin::PerIsolateData::From(isolate);
301 v8::Local<v8::ObjectTemplate> templ = data->GetObjectTemplate(
302 &g_wrapper_info);
303
304 if (templ.IsEmpty()) {
305 templ =
306 gin::ObjectTemplateBuilder(isolate)
307 // TODO(mpcomplete): Should these just be methods on the JS Handle
308 // object?
309 .SetMethod("close", CloseHandle)
310 .SetMethod("wait", WaitHandle)
311 .SetMethod("waitMany", WaitMany)
312 .SetMethod("createMessagePipe", CreateMessagePipe)
313 .SetMethod("writeMessage", WriteMessage)
314 .SetMethod("readMessage", ReadMessage)
315 .SetMethod("createDataPipe", CreateDataPipe)
316 .SetMethod("writeData", WriteData)
317 .SetMethod("readData", ReadData)
318 .SetMethod("drainData", DoDrainData)
319 .SetMethod("isHandle", IsHandle)
320
321 .SetValue("RESULT_OK", MOJO_RESULT_OK)
322 .SetValue("RESULT_CANCELLED", MOJO_RESULT_CANCELLED)
323 .SetValue("RESULT_UNKNOWN", MOJO_RESULT_UNKNOWN)
324 .SetValue("RESULT_INVALID_ARGUMENT", MOJO_RESULT_INVALID_ARGUMENT)
325 .SetValue("RESULT_DEADLINE_EXCEEDED", MOJO_RESULT_DEADLINE_EXCEEDED)
326 .SetValue("RESULT_NOT_FOUND", MOJO_RESULT_NOT_FOUND)
327 .SetValue("RESULT_ALREADY_EXISTS", MOJO_RESULT_ALREADY_EXISTS)
328 .SetValue("RESULT_PERMISSION_DENIED", MOJO_RESULT_PERMISSION_DENIED)
329 .SetValue("RESULT_RESOURCE_EXHAUSTED",
330 MOJO_RESULT_RESOURCE_EXHAUSTED)
331 .SetValue("RESULT_FAILED_PRECONDITION",
332 MOJO_RESULT_FAILED_PRECONDITION)
333 .SetValue("RESULT_ABORTED", MOJO_RESULT_ABORTED)
334 .SetValue("RESULT_OUT_OF_RANGE", MOJO_RESULT_OUT_OF_RANGE)
335 .SetValue("RESULT_UNIMPLEMENTED", MOJO_RESULT_UNIMPLEMENTED)
336 .SetValue("RESULT_INTERNAL", MOJO_RESULT_INTERNAL)
337 .SetValue("RESULT_UNAVAILABLE", MOJO_RESULT_UNAVAILABLE)
338 .SetValue("RESULT_DATA_LOSS", MOJO_RESULT_DATA_LOSS)
339 .SetValue("RESULT_BUSY", MOJO_RESULT_BUSY)
340 .SetValue("RESULT_SHOULD_WAIT", MOJO_RESULT_SHOULD_WAIT)
341
342 .SetValue("DEADLINE_INDEFINITE", MOJO_DEADLINE_INDEFINITE)
343
344 .SetValue("HANDLE_SIGNAL_NONE", MOJO_HANDLE_SIGNAL_NONE)
345 .SetValue("HANDLE_SIGNAL_READABLE", MOJO_HANDLE_SIGNAL_READABLE)
346 .SetValue("HANDLE_SIGNAL_WRITABLE", MOJO_HANDLE_SIGNAL_WRITABLE)
347 .SetValue("HANDLE_SIGNAL_PEER_CLOSED",
348 MOJO_HANDLE_SIGNAL_PEER_CLOSED)
349
350 .SetValue("CREATE_MESSAGE_PIPE_OPTIONS_FLAG_NONE",
351 MOJO_CREATE_MESSAGE_PIPE_OPTIONS_FLAG_NONE)
352
353 .SetValue("WRITE_MESSAGE_FLAG_NONE", MOJO_WRITE_MESSAGE_FLAG_NONE)
354
355 .SetValue("READ_MESSAGE_FLAG_NONE", MOJO_READ_MESSAGE_FLAG_NONE)
356 .SetValue("READ_MESSAGE_FLAG_MAY_DISCARD",
357 MOJO_READ_MESSAGE_FLAG_MAY_DISCARD)
358
359 .SetValue("CREATE_DATA_PIPE_OPTIONS_FLAG_NONE",
360 MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE)
361
362 .SetValue("WRITE_DATA_FLAG_NONE", MOJO_WRITE_DATA_FLAG_NONE)
363 .SetValue("WRITE_DATA_FLAG_ALL_OR_NONE",
364 MOJO_WRITE_DATA_FLAG_ALL_OR_NONE)
365
366 .SetValue("READ_DATA_FLAG_NONE", MOJO_READ_DATA_FLAG_NONE)
367 .SetValue("READ_DATA_FLAG_ALL_OR_NONE",
368 MOJO_READ_DATA_FLAG_ALL_OR_NONE)
369 .SetValue("READ_DATA_FLAG_DISCARD", MOJO_READ_DATA_FLAG_DISCARD)
370 .SetValue("READ_DATA_FLAG_QUERY", MOJO_READ_DATA_FLAG_QUERY)
371 .SetValue("READ_DATA_FLAG_PEEK", MOJO_READ_DATA_FLAG_PEEK)
372 .Build();
373
374 data->SetObjectTemplate(&g_wrapper_info, templ);
375 }
376
377 return templ->NewInstance();
378 }
379
380 } // namespace js
381 } // namespace edk
382 } // namespace mojo
383