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