diff options
Diffstat (limited to 'chromium/mojo/bindings')
-rw-r--r-- | chromium/mojo/bindings/js/DEPS | 4 | ||||
-rw-r--r-- | chromium/mojo/bindings/js/codec_unittests.js | 222 | ||||
-rw-r--r-- | chromium/mojo/bindings/js/core.cc | 278 | ||||
-rw-r--r-- | chromium/mojo/bindings/js/core.h | 22 | ||||
-rw-r--r-- | chromium/mojo/bindings/js/core_unittests.js | 91 | ||||
-rw-r--r-- | chromium/mojo/bindings/js/handle.cc | 41 | ||||
-rw-r--r-- | chromium/mojo/bindings/js/handle.h | 75 | ||||
-rw-r--r-- | chromium/mojo/bindings/js/run_js_tests.cc | 64 | ||||
-rw-r--r-- | chromium/mojo/bindings/js/support.cc | 59 | ||||
-rw-r--r-- | chromium/mojo/bindings/js/support.h | 22 | ||||
-rw-r--r-- | chromium/mojo/bindings/js/waiting_callback.cc | 84 | ||||
-rw-r--r-- | chromium/mojo/bindings/js/waiting_callback.h | 52 |
12 files changed, 1014 insertions, 0 deletions
diff --git a/chromium/mojo/bindings/js/DEPS b/chromium/mojo/bindings/js/DEPS new file mode 100644 index 00000000000..d974b6806fc --- /dev/null +++ b/chromium/mojo/bindings/js/DEPS @@ -0,0 +1,4 @@ +include_rules = [ + "+gin", + "+v8", +] diff --git a/chromium/mojo/bindings/js/codec_unittests.js b/chromium/mojo/bindings/js/codec_unittests.js new file mode 100644 index 00000000000..6a223ffe89b --- /dev/null +++ b/chromium/mojo/bindings/js/codec_unittests.js @@ -0,0 +1,222 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +define([ + "gin/test/expect", + "mojo/public/js/bindings/codec", + "mojo/public/interfaces/bindings/tests/sample_service.mojom", + ], function(expect, codec, sample) { + testBar(); + testFoo(); + testTypes(); + testAlign(); + testUtf8(); + this.result = "PASS"; + + function testBar() { + var bar = new sample.Bar(); + bar.alpha = 1; + bar.beta = 2; + bar.gamma = 3; + bar.type = 0x08070605; + bar.extraProperty = "banana"; + + var messageName = 42; + var payloadSize = sample.Bar.encodedSize; + + var builder = new codec.MessageBuilder(messageName, payloadSize); + builder.encodeStruct(sample.Bar, bar); + + var message = builder.finish(); + + var expectedMemory = new Uint8Array([ + 16, 0, 0, 0, + 2, 0, 0, 0, + 42, 0, 0, 0, + 0, 0, 0, 0, + + 16, 0, 0, 0, + 4, 0, 0, 0, + + 1, 2, 3, 0, + 5, 6, 7, 8, + ]); + + var actualMemory = new Uint8Array(message.buffer.arrayBuffer); + expect(actualMemory).toEqual(expectedMemory); + + var reader = new codec.MessageReader(message); + + expect(reader.payloadSize).toBe(payloadSize); + expect(reader.messageName).toBe(messageName); + + var bar2 = reader.decodeStruct(sample.Bar); + + expect(bar2.alpha).toBe(bar.alpha); + expect(bar2.beta).toBe(bar.beta); + expect(bar2.gamma).toBe(bar.gamma); + expect("extraProperty" in bar2).toBeFalsy(); + } + + function testFoo() { + var foo = new sample.Foo(); + foo.x = 0x212B4D5; + foo.y = 0x16E93; + foo.a = 1; + foo.b = 0; + foo.c = 3; // This will get truncated to one bit. + foo.bar = new sample.Bar(); + foo.bar.alpha = 91; + foo.bar.beta = 82; + foo.bar.gamma = 73; + foo.data = [ + 4, 5, 6, 7, 8, + ]; + foo.extra_bars = [ + new sample.Bar(), new sample.Bar(), new sample.Bar(), + ]; + for (var i = 0; i < foo.extra_bars.length; ++i) { + foo.extra_bars[i].alpha = 1 * i; + foo.extra_bars[i].beta = 2 * i; + foo.extra_bars[i].gamma = 3 * i; + } + foo.name = "I am a banana"; + // This is supposed to be a handle, but we fake it with an integer. + foo.source = 23423782; + foo.array_of_array_of_bools = [ + [true], [false, true] + ]; + + var messageName = 31; + var payloadSize = 304; + + var builder = new codec.MessageBuilder(messageName, payloadSize); + builder.encodeStruct(sample.Foo, foo); + + var message = builder.finish(); + + var expectedMemory = new Uint8Array([ + /* 0: */ 16, 0, 0, 0, 2, 0, 0, 0, + /* 8: */ 31, 0, 0, 0, 0, 0, 0, 0, + /* 16: */ 88, 0, 0, 0, 14, 0, 0, 0, + /* 24: */ 0xD5, 0xB4, 0x12, 0x02, 0x93, 0x6E, 0x01, 0, + /* 32: */ 5, 0, 0, 0, 0, 0, 0, 0, + /* 40: */ 64, 0, 0, 0, 0, 0, 0, 0, + ]); + // TODO(abarth): Test more of the message's raw memory. + var actualMemory = new Uint8Array(message.buffer.arrayBuffer, + 0, expectedMemory.length); + expect(actualMemory).toEqual(expectedMemory); + + var expectedHandles = [ + 23423782, + ]; + + expect(message.handles).toEqual(expectedHandles); + + var reader = new codec.MessageReader(message); + + expect(reader.payloadSize).toBe(payloadSize); + expect(reader.messageName).toBe(messageName); + + var foo2 = reader.decodeStruct(sample.Foo); + + expect(foo2.x).toBe(foo.x); + expect(foo2.y).toBe(foo.y); + + expect(foo2.a).toBe(foo.a & 1 ? true : false); + expect(foo2.b).toBe(foo.b & 1 ? true : false); + expect(foo2.c).toBe(foo.c & 1 ? true : false); + + expect(foo2.bar).toEqual(foo.bar); + expect(foo2.data).toEqual(foo.data); + + expect(foo2.extra_bars).toEqual(foo.extra_bars); + expect(foo2.name).toBe(foo.name); + expect(foo2.source).toEqual(foo.source); + } + + function testTypes() { + function encodeDecode(cls, input, expectedResult, encodedSize) { + var messageName = 42; + var payloadSize = encodedSize || cls.encodedSize; + + var builder = new codec.MessageBuilder(messageName, payloadSize); + builder.encodeStruct(cls, input) + var message = builder.finish(); + + var reader = new codec.MessageReader(message); + expect(reader.payloadSize).toBe(payloadSize); + expect(reader.messageName).toBe(messageName); + var result = reader.decodeStruct(cls); + expect(result).toEqual(expectedResult); + } + encodeDecode(codec.String, "banana", "banana", 24); + encodeDecode(codec.Int8, -1, -1); + encodeDecode(codec.Int8, 0xff, -1); + encodeDecode(codec.Int16, -1, -1); + encodeDecode(codec.Int16, 0xff, 0xff); + encodeDecode(codec.Int16, 0xffff, -1); + encodeDecode(codec.Int32, -1, -1); + encodeDecode(codec.Int32, 0xffff, 0xffff); + encodeDecode(codec.Int32, 0xffffffff, -1); + encodeDecode(codec.Float, 1.0, 1.0); + encodeDecode(codec.Double, 1.0, 1.0); + } + + function testAlign() { + var aligned = [ + 0, // 0 + 8, // 1 + 8, // 2 + 8, // 3 + 8, // 4 + 8, // 5 + 8, // 6 + 8, // 7 + 8, // 8 + 16, // 9 + 16, // 10 + 16, // 11 + 16, // 12 + 16, // 13 + 16, // 14 + 16, // 15 + 16, // 16 + 24, // 17 + 24, // 18 + 24, // 19 + 24, // 20 + ]; + for (var i = 0; i < aligned.length; ++i) + expect(codec.align(i)).toBe(aligned[i]); + } + + function testUtf8() { + var str = "B\u03ba\u1f79"; // some UCS-2 codepoints + var messageName = 42; + var payloadSize = 24; + + var builder = new codec.MessageBuilder(messageName, payloadSize); + var encoder = builder.createEncoder(8); + encoder.encodeStringPointer(str); + var message = builder.finish(); + var expectedMemory = new Uint8Array([ + /* 0: */ 16, 0, 0, 0, 2, 0, 0, 0, + /* 8: */ 42, 0, 0, 0, 0, 0, 0, 0, + /* 16: */ 8, 0, 0, 0, 0, 0, 0, 0, + /* 24: */ 14, 0, 0, 0, 6, 0, 0, 0, + /* 32: */ 0x42, 0xCE, 0xBA, 0xE1, 0xBD, 0xB9, 0, 0, + ]); + var actualMemory = new Uint8Array(message.buffer.arrayBuffer); + expect(actualMemory.length).toEqual(expectedMemory.length); + expect(actualMemory).toEqual(expectedMemory); + + var reader = new codec.MessageReader(message); + expect(reader.payloadSize).toBe(payloadSize); + expect(reader.messageName).toBe(messageName); + var str2 = reader.decoder.decodeStringPointer(); + expect(str2).toEqual(str); + } +}); diff --git a/chromium/mojo/bindings/js/core.cc b/chromium/mojo/bindings/js/core.cc new file mode 100644 index 00000000000..d6bd2ea68a8 --- /dev/null +++ b/chromium/mojo/bindings/js/core.cc @@ -0,0 +1,278 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "mojo/bindings/js/core.h" + +#include "base/bind.h" +#include "base/logging.h" +#include "gin/arguments.h" +#include "gin/array_buffer.h" +#include "gin/converter.h" +#include "gin/dictionary.h" +#include "gin/function_template.h" +#include "gin/handle.h" +#include "gin/object_template_builder.h" +#include "gin/per_isolate_data.h" +#include "gin/public/wrapper_info.h" +#include "gin/wrappable.h" +#include "mojo/bindings/js/handle.h" + +namespace mojo { +namespace js { + +namespace { + +MojoResult CloseHandle(gin::Handle<gin::HandleWrapper> handle) { + if (!handle->get().is_valid()) + return MOJO_RESULT_INVALID_ARGUMENT; + handle->Close(); + return MOJO_RESULT_OK; +} + +MojoResult WaitHandle(mojo::Handle handle, + MojoHandleSignals signals, + MojoDeadline deadline) { + return MojoWait(handle.value(), signals, deadline); +} + +MojoResult WaitMany( + const std::vector<mojo::Handle>& handles, + const std::vector<MojoHandleSignals>& signals, + MojoDeadline deadline) { + return mojo::WaitMany(handles, signals, deadline); +} + +gin::Dictionary CreateMessagePipe(const gin::Arguments& args) { + MojoHandle handle0 = MOJO_HANDLE_INVALID; + MojoHandle handle1 = MOJO_HANDLE_INVALID; + // TODO(vtl): Add support for the options struct. + MojoResult result = MojoCreateMessagePipe(NULL, &handle0, &handle1); + CHECK(result == MOJO_RESULT_OK); + + gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate()); + dictionary.Set("handle0", mojo::Handle(handle0)); + dictionary.Set("handle1", mojo::Handle(handle1)); + return dictionary; +} + +MojoResult WriteMessage( + mojo::Handle handle, + const gin::ArrayBufferView& buffer, + const std::vector<gin::Handle<gin::HandleWrapper> >& handles, + MojoWriteMessageFlags flags) { + std::vector<MojoHandle> raw_handles(handles.size()); + for (size_t i = 0; i < handles.size(); ++i) + raw_handles[i] = handles[i]->get().value(); + MojoResult rv = MojoWriteMessage(handle.value(), + buffer.bytes(), + static_cast<uint32_t>(buffer.num_bytes()), + raw_handles.empty() ? NULL : &raw_handles[0], + static_cast<uint32_t>(raw_handles.size()), + flags); + // MojoWriteMessage takes ownership of the handles upon success, so + // release them here. + if (rv == MOJO_RESULT_OK) { + for (size_t i = 0; i < handles.size(); ++i) + ignore_result(handles[i]->release()); + } + return rv; +} + +gin::Dictionary ReadMessage(const gin::Arguments& args, + mojo::Handle handle, + MojoReadMessageFlags flags) { + uint32_t num_bytes = 0; + uint32_t num_handles = 0; + MojoResult result = MojoReadMessage( + handle.value(), NULL, &num_bytes, NULL, &num_handles, flags); + if (result != MOJO_RESULT_RESOURCE_EXHAUSTED) { + gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate()); + dictionary.Set("result", result); + return dictionary; + } + + v8::Handle<v8::ArrayBuffer> array_buffer = + v8::ArrayBuffer::New(args.isolate(), num_bytes); + std::vector<mojo::Handle> handles(num_handles); + + gin::ArrayBuffer buffer; + ConvertFromV8(args.isolate(), array_buffer, &buffer); + CHECK(buffer.num_bytes() == num_bytes); + + result = MojoReadMessage(handle.value(), + buffer.bytes(), + &num_bytes, + handles.empty() ? NULL : + reinterpret_cast<MojoHandle*>(&handles[0]), + &num_handles, + flags); + + CHECK(buffer.num_bytes() == num_bytes); + CHECK(handles.size() == num_handles); + + gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate()); + dictionary.Set("result", result); + dictionary.Set("buffer", array_buffer); + dictionary.Set("handles", handles); + return dictionary; +} + +gin::Dictionary CreateDataPipe(const gin::Arguments& args, + v8::Handle<v8::Value> options_value) { + gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate()); + dictionary.Set("result", MOJO_RESULT_INVALID_ARGUMENT); + + MojoHandle producer_handle = MOJO_HANDLE_INVALID; + MojoHandle consumer_handle = MOJO_HANDLE_INVALID; + MojoResult result = MOJO_RESULT_OK; + + if (options_value->IsObject()) { + gin::Dictionary options_dict(args.isolate(), options_value->ToObject()); + MojoCreateDataPipeOptions options; + // For future struct_size, we can probably infer that from the presence of + // properties in options_dict. For now, it's always 16. + options.struct_size = 16; + // Ideally these would be optional. But the interface makes it hard to + // typecheck them then. + if (!options_dict.Get("flags", &options.flags) || + !options_dict.Get("elementNumBytes", &options.element_num_bytes) || + !options_dict.Get("capacityNumBytes", &options.capacity_num_bytes)) { + return dictionary; + } + + result = MojoCreateDataPipe(&options, &producer_handle, &consumer_handle); + } else if (options_value->IsNull() || options_value->IsUndefined()) { + result = MojoCreateDataPipe(NULL, &producer_handle, &consumer_handle); + } else { + return dictionary; + } + + CHECK_EQ(MOJO_RESULT_OK, result); + + dictionary.Set("result", result); + dictionary.Set("producerHandle", mojo::Handle(producer_handle)); + dictionary.Set("consumerHandle", mojo::Handle(consumer_handle)); + return dictionary; +} + +gin::Dictionary WriteData(const gin::Arguments& args, + mojo::Handle handle, + const gin::ArrayBufferView& buffer, + MojoWriteDataFlags flags) { + uint32_t num_bytes = static_cast<uint32_t>(buffer.num_bytes()); + MojoResult result = + MojoWriteData(handle.value(), buffer.bytes(), &num_bytes, flags); + gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate()); + dictionary.Set("result", result); + dictionary.Set("numBytes", num_bytes); + return dictionary; +} + +gin::Dictionary ReadData(const gin::Arguments& args, + mojo::Handle handle, + MojoReadDataFlags flags) { + uint32_t num_bytes = 0; + MojoResult result = MojoReadData( + handle.value(), NULL, &num_bytes, MOJO_READ_DATA_FLAG_QUERY); + if (result != MOJO_RESULT_OK) { + gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate()); + dictionary.Set("result", result); + return dictionary; + } + + v8::Handle<v8::ArrayBuffer> array_buffer = + v8::ArrayBuffer::New(args.isolate(), num_bytes); + gin::ArrayBuffer buffer; + ConvertFromV8(args.isolate(), array_buffer, &buffer); + CHECK_EQ(num_bytes, buffer.num_bytes()); + + result = MojoReadData(handle.value(), buffer.bytes(), &num_bytes, flags); + CHECK_EQ(num_bytes, buffer.num_bytes()); + + gin::Dictionary dictionary = gin::Dictionary::CreateEmpty(args.isolate()); + dictionary.Set("result", result); + dictionary.Set("buffer", array_buffer); + return dictionary; +} + +gin::WrapperInfo g_wrapper_info = { gin::kEmbedderNativeGin }; + +} // namespace + +const char Core::kModuleName[] = "mojo/public/js/bindings/core"; + +v8::Local<v8::Value> Core::GetModule(v8::Isolate* isolate) { + gin::PerIsolateData* data = gin::PerIsolateData::From(isolate); + v8::Local<v8::ObjectTemplate> templ = data->GetObjectTemplate( + &g_wrapper_info); + + if (templ.IsEmpty()) { + templ = gin::ObjectTemplateBuilder(isolate) + // TODO(mpcomplete): Should these just be methods on the JS Handle + // object? + .SetMethod("close", CloseHandle) + .SetMethod("wait", WaitHandle) + .SetMethod("waitMany", WaitMany) + .SetMethod("createMessagePipe", CreateMessagePipe) + .SetMethod("writeMessage", WriteMessage) + .SetMethod("readMessage", ReadMessage) + .SetMethod("createDataPipe", CreateDataPipe) + .SetMethod("writeData", WriteData) + .SetMethod("readData", ReadData) + + .SetValue("RESULT_OK", MOJO_RESULT_OK) + .SetValue("RESULT_CANCELLED", MOJO_RESULT_CANCELLED) + .SetValue("RESULT_UNKNOWN", MOJO_RESULT_UNKNOWN) + .SetValue("RESULT_INVALID_ARGUMENT", MOJO_RESULT_INVALID_ARGUMENT) + .SetValue("RESULT_DEADLINE_EXCEEDED", MOJO_RESULT_DEADLINE_EXCEEDED) + .SetValue("RESULT_NOT_FOUND", MOJO_RESULT_NOT_FOUND) + .SetValue("RESULT_ALREADY_EXISTS", MOJO_RESULT_ALREADY_EXISTS) + .SetValue("RESULT_PERMISSION_DENIED", MOJO_RESULT_PERMISSION_DENIED) + .SetValue("RESULT_RESOURCE_EXHAUSTED", MOJO_RESULT_RESOURCE_EXHAUSTED) + .SetValue("RESULT_FAILED_PRECONDITION", MOJO_RESULT_FAILED_PRECONDITION) + .SetValue("RESULT_ABORTED", MOJO_RESULT_ABORTED) + .SetValue("RESULT_OUT_OF_RANGE", MOJO_RESULT_OUT_OF_RANGE) + .SetValue("RESULT_UNIMPLEMENTED", MOJO_RESULT_UNIMPLEMENTED) + .SetValue("RESULT_INTERNAL", MOJO_RESULT_INTERNAL) + .SetValue("RESULT_UNAVAILABLE", MOJO_RESULT_UNAVAILABLE) + .SetValue("RESULT_DATA_LOSS", MOJO_RESULT_DATA_LOSS) + .SetValue("RESULT_BUSY", MOJO_RESULT_BUSY) + .SetValue("RESULT_SHOULD_WAIT", MOJO_RESULT_SHOULD_WAIT) + + .SetValue("DEADLINE_INDEFINITE", MOJO_DEADLINE_INDEFINITE) + + .SetValue("HANDLE_SIGNAL_NONE", MOJO_HANDLE_SIGNAL_NONE) + .SetValue("HANDLE_SIGNAL_READABLE", MOJO_HANDLE_SIGNAL_READABLE) + .SetValue("HANDLE_SIGNAL_READABLE", MOJO_HANDLE_SIGNAL_READABLE) + + .SetValue("WRITE_MESSAGE_FLAG_NONE", MOJO_WRITE_MESSAGE_FLAG_NONE) + + .SetValue("READ_MESSAGE_FLAG_NONE", MOJO_READ_MESSAGE_FLAG_NONE) + .SetValue("READ_MESSAGE_FLAG_MAY_DISCARD", + MOJO_READ_MESSAGE_FLAG_MAY_DISCARD) + + .SetValue("CREATE_DATA_PIPE_OPTIONS_FLAG_NONE", + MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_NONE) + .SetValue("CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD", + MOJO_CREATE_DATA_PIPE_OPTIONS_FLAG_MAY_DISCARD) + + .SetValue("WRITE_DATA_FLAG_NONE", MOJO_WRITE_DATA_FLAG_NONE) + .SetValue("WRITE_DATA_FLAG_ALL_OR_NONE", + MOJO_WRITE_DATA_FLAG_ALL_OR_NONE) + + .SetValue("READ_DATA_FLAG_NONE", MOJO_READ_DATA_FLAG_NONE) + .SetValue("READ_DATA_FLAG_ALL_OR_NONE", + MOJO_READ_DATA_FLAG_ALL_OR_NONE) + .SetValue("READ_DATA_FLAG_DISCARD", MOJO_READ_DATA_FLAG_DISCARD) + .SetValue("READ_DATA_FLAG_QUERY", MOJO_READ_DATA_FLAG_QUERY) + .Build(); + + data->SetObjectTemplate(&g_wrapper_info, templ); + } + + return templ->NewInstance(); +} + +} // namespace js +} // namespace mojo diff --git a/chromium/mojo/bindings/js/core.h b/chromium/mojo/bindings/js/core.h new file mode 100644 index 00000000000..bde327ce5ad --- /dev/null +++ b/chromium/mojo/bindings/js/core.h @@ -0,0 +1,22 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_BINDINGS_JS_CORE_H_ +#define MOJO_BINDINGS_JS_CORE_H_ + +#include "v8/include/v8.h" + +namespace mojo { +namespace js { + +class Core { + public: + static const char kModuleName[]; + static v8::Local<v8::Value> GetModule(v8::Isolate* isolate); +}; + +} // namespace js +} // namespace mojo + +#endif // MOJO_BINDINGS_JS_CORE_H_ diff --git a/chromium/mojo/bindings/js/core_unittests.js b/chromium/mojo/bindings/js/core_unittests.js new file mode 100644 index 00000000000..20446dbc260 --- /dev/null +++ b/chromium/mojo/bindings/js/core_unittests.js @@ -0,0 +1,91 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +define([ + "gin/test/expect", + "mojo/public/js/bindings/core", + "gc", + ], function(expect, core, gc) { + runWithMessagePipe(testNop); + runWithMessagePipe(testReadAndWriteMessage); + runWithDataPipe(testNop); + runWithDataPipe(testReadAndWriteDataPipe); + gc.collectGarbage(); // should not crash + this.result = "PASS"; + + function runWithMessagePipe(test) { + var pipe = core.createMessagePipe(); + + test(pipe); + + expect(core.close(pipe.handle0)).toBe(core.RESULT_OK); + expect(core.close(pipe.handle1)).toBe(core.RESULT_OK); + } + + function runWithDataPipe(test) { + var pipe = core.createDataPipe({ + flags: core.CREATE_DATA_PIPE_OPTIONS_FLAG_NONE, + elementNumBytes: 1, + capacityNumBytes: 64 + }); + expect(pipe.result).toBe(core.RESULT_OK); + + test(pipe); + + expect(core.close(pipe.producerHandle)).toBe(core.RESULT_OK); + expect(core.close(pipe.consumerHandle)).toBe(core.RESULT_OK); + } + + function testNop(pipe) { + } + + function testReadAndWriteMessage(pipe) { + var senderData = new Uint8Array(42); + for (var i = 0; i < senderData.length; ++i) { + senderData[i] = i * i; + } + + var result = core.writeMessage( + pipe.handle0, senderData, [], + core.WRITE_MESSAGE_FLAG_NONE); + + expect(result).toBe(core.RESULT_OK); + + var read = core.readMessage( + pipe.handle1, core.READ_MESSAGE_FLAG_NONE); + + expect(read.result).toBe(core.RESULT_OK); + expect(read.buffer.byteLength).toBe(42); + expect(read.handles.length).toBe(0); + + var memory = new Uint8Array(read.buffer); + for (var i = 0; i < memory.length; ++i) + expect(memory[i]).toBe((i * i) & 0xFF); + } + + function testReadAndWriteDataPipe(pipe) { + var senderData = new Uint8Array(42); + for (var i = 0; i < senderData.length; ++i) { + senderData[i] = i * i; + } + + var write = core.writeData( + pipe.producerHandle, senderData, + core.WRITE_DATA_FLAG_ALL_OR_NONE); + + expect(write.result).toBe(core.RESULT_OK); + expect(write.numBytes).toBe(42); + + var read = core.readData( + pipe.consumerHandle, core.READ_DATA_FLAG_ALL_OR_NONE); + + expect(read.result).toBe(core.RESULT_OK); + expect(read.buffer.byteLength).toBe(42); + + var memory = new Uint8Array(read.buffer); + for (var i = 0; i < memory.length; ++i) + expect(memory[i]).toBe((i * i) & 0xFF); + } + +}); diff --git a/chromium/mojo/bindings/js/handle.cc b/chromium/mojo/bindings/js/handle.cc new file mode 100644 index 00000000000..e1d0fb04b60 --- /dev/null +++ b/chromium/mojo/bindings/js/handle.cc @@ -0,0 +1,41 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "mojo/bindings/js/handle.h" + +namespace gin { + +gin::WrapperInfo HandleWrapper::kWrapperInfo = { gin::kEmbedderNativeGin }; + +HandleWrapper::HandleWrapper(MojoHandle handle) + : handle_(mojo::Handle(handle)) { +} + +HandleWrapper::~HandleWrapper() { +} + +v8::Handle<v8::Value> Converter<mojo::Handle>::ToV8(v8::Isolate* isolate, + const mojo::Handle& val) { + if (!val.is_valid()) + return v8::Null(isolate); + return HandleWrapper::Create(isolate, val.value()).ToV8(); +} + +bool Converter<mojo::Handle>::FromV8(v8::Isolate* isolate, + v8::Handle<v8::Value> val, + mojo::Handle* out) { + if (val->IsNull()) { + *out = mojo::Handle(); + return true; + } + + gin::Handle<HandleWrapper> handle; + if (!Converter<gin::Handle<HandleWrapper> >::FromV8(isolate, val, &handle)) + return false; + + *out = handle->get(); + return true; +} + +} // namespace gin diff --git a/chromium/mojo/bindings/js/handle.h b/chromium/mojo/bindings/js/handle.h new file mode 100644 index 00000000000..35202b07ea6 --- /dev/null +++ b/chromium/mojo/bindings/js/handle.h @@ -0,0 +1,75 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_BINDINGS_JS_HANDLE_H_ +#define MOJO_BINDINGS_JS_HANDLE_H_ + +#include "gin/converter.h" +#include "gin/handle.h" +#include "gin/wrappable.h" +#include "mojo/public/cpp/system/core.h" + +namespace gin { + +// Wrapper for mojo Handles exposed to JavaScript. This ensures the Handle +// is Closed when its JS object is garbage collected. +class HandleWrapper : public gin::Wrappable<HandleWrapper> { + public: + static gin::WrapperInfo kWrapperInfo; + + static gin::Handle<HandleWrapper> Create(v8::Isolate* isolate, + MojoHandle handle) { + return gin::CreateHandle(isolate, new HandleWrapper(handle)); + } + + mojo::Handle get() const { return handle_.get(); } + mojo::Handle release() { return handle_.release(); } + void Close() { handle_.reset(); } + + protected: + HandleWrapper(MojoHandle handle); + virtual ~HandleWrapper(); + mojo::ScopedHandle handle_; +}; + +// Note: It's important to use this converter rather than the one for +// MojoHandle, since that will do a simple int32 conversion. It's unfortunate +// there's no way to prevent against accidental use. +// TODO(mpcomplete): define converters for all Handle subtypes. +template<> +struct Converter<mojo::Handle> { + static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate, + const mojo::Handle& val); + static bool FromV8(v8::Isolate* isolate, v8::Handle<v8::Value> val, + mojo::Handle* out); +}; + +// We need to specialize the normal gin::Handle converter in order to handle +// converting |null| to a wrapper for an empty mojo::Handle. +template<> +struct Converter<gin::Handle<gin::HandleWrapper> > { + static v8::Handle<v8::Value> ToV8( + v8::Isolate* isolate, const gin::Handle<gin::HandleWrapper>& val) { + return val.ToV8(); + } + + static bool FromV8(v8::Isolate* isolate, v8::Handle<v8::Value> val, + gin::Handle<gin::HandleWrapper>* out) { + if (val->IsNull()) { + *out = HandleWrapper::Create(isolate, MOJO_HANDLE_INVALID); + return true; + } + + gin::HandleWrapper* object = NULL; + if (!Converter<gin::HandleWrapper*>::FromV8(isolate, val, &object)) { + return false; + } + *out = gin::Handle<gin::HandleWrapper>(val, object); + return true; + } +}; + +} // namespace gin + +#endif // MOJO_BINDINGS_JS_HANDLE_H_ diff --git a/chromium/mojo/bindings/js/run_js_tests.cc b/chromium/mojo/bindings/js/run_js_tests.cc new file mode 100644 index 00000000000..976a0b17591 --- /dev/null +++ b/chromium/mojo/bindings/js/run_js_tests.cc @@ -0,0 +1,64 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "base/file_util.h" +#include "base/files/file_path.h" +#include "base/path_service.h" +#include "gin/modules/console.h" +#include "gin/modules/module_registry.h" +#include "gin/modules/timer.h" +#include "gin/test/file_runner.h" +#include "gin/test/gtest.h" +#include "mojo/bindings/js/core.h" +#include "mojo/common/test/test_utils.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace mojo { +namespace js { +namespace { + +class TestRunnerDelegate : public gin::FileRunnerDelegate { + public: + TestRunnerDelegate() { + AddBuiltinModule(gin::Console::kModuleName, gin::Console::GetModule); + AddBuiltinModule(Core::kModuleName, Core::GetModule); + } + + private: + DISALLOW_COPY_AND_ASSIGN(TestRunnerDelegate); +}; + +void RunTest(std::string test, bool run_until_idle) { + base::FilePath path; + PathService::Get(base::DIR_SOURCE_ROOT, &path); + path = path.AppendASCII("mojo") + .AppendASCII("bindings") + .AppendASCII("js") + .AppendASCII(test); + TestRunnerDelegate delegate; + gin::RunTestFromFile(path, &delegate, run_until_idle); +} + +// TODO(abarth): Should we autogenerate these stubs from GYP? +TEST(JSTest, core) { + RunTest("core_unittests.js", true); +} + +TEST(JSTest, codec) { + // TODO(yzshen): Remove this check once isolated tests are supported on the + // Chromium waterfall. (http://crbug.com/351214) + const base::FilePath test_file_path( + test::GetFilePathForJSResource( + "mojo/public/interfaces/bindings/tests/sample_service.mojom")); + if (!base::PathExists(test_file_path)) { + LOG(WARNING) << "Mojom binding files don't exist. Skipping the test."; + return; + } + + RunTest("codec_unittests.js", true); +} + +} // namespace +} // namespace js +} // namespace mojo diff --git a/chromium/mojo/bindings/js/support.cc b/chromium/mojo/bindings/js/support.cc new file mode 100644 index 00000000000..dc97865cca7 --- /dev/null +++ b/chromium/mojo/bindings/js/support.cc @@ -0,0 +1,59 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "mojo/bindings/js/support.h" + +#include "base/bind.h" +#include "gin/arguments.h" +#include "gin/converter.h" +#include "gin/function_template.h" +#include "gin/object_template_builder.h" +#include "gin/per_isolate_data.h" +#include "gin/public/wrapper_info.h" +#include "gin/wrappable.h" +#include "mojo/bindings/js/handle.h" +#include "mojo/bindings/js/waiting_callback.h" +#include "mojo/public/cpp/system/core.h" + +namespace mojo { +namespace js { + +namespace { + +WaitingCallback* AsyncWait(const gin::Arguments& args, mojo::Handle handle, + MojoHandleSignals signals, + v8::Handle<v8::Function> callback) { + return WaitingCallback::Create(args.isolate(), callback, handle, signals) + .get(); +} + +void CancelWait(WaitingCallback* waiting_callback) { + waiting_callback->Cancel(); +} + +gin::WrapperInfo g_wrapper_info = { gin::kEmbedderNativeGin }; + +} // namespace + +const char Support::kModuleName[] = "mojo/public/js/bindings/support"; + +v8::Local<v8::Value> Support::GetModule(v8::Isolate* isolate) { + gin::PerIsolateData* data = gin::PerIsolateData::From(isolate); + v8::Local<v8::ObjectTemplate> templ = data->GetObjectTemplate( + &g_wrapper_info); + + if (templ.IsEmpty()) { + templ = gin::ObjectTemplateBuilder(isolate) + .SetMethod("asyncWait", AsyncWait) + .SetMethod("cancelWait", CancelWait) + .Build(); + + data->SetObjectTemplate(&g_wrapper_info, templ); + } + + return templ->NewInstance(); +} + +} // namespace js +} // namespace mojo diff --git a/chromium/mojo/bindings/js/support.h b/chromium/mojo/bindings/js/support.h new file mode 100644 index 00000000000..0f6eb07c2b1 --- /dev/null +++ b/chromium/mojo/bindings/js/support.h @@ -0,0 +1,22 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_BINDINGS_JS_SUPPORT_H_ +#define MOJO_BINDINGS_JS_SUPPORT_H_ + +#include "v8/include/v8.h" + +namespace mojo { +namespace js { + +class Support { + public: + static const char kModuleName[]; + static v8::Local<v8::Value> GetModule(v8::Isolate* isolate); +}; + +} // namespace js +} // namespace mojo + +#endif // MOJO_BINDINGS_JS_SUPPORT_H_ diff --git a/chromium/mojo/bindings/js/waiting_callback.cc b/chromium/mojo/bindings/js/waiting_callback.cc new file mode 100644 index 00000000000..692551bb0e7 --- /dev/null +++ b/chromium/mojo/bindings/js/waiting_callback.cc @@ -0,0 +1,84 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "mojo/bindings/js/waiting_callback.h" + +#include "gin/per_context_data.h" +#include "mojo/public/cpp/environment/environment.h" + +namespace mojo { +namespace js { + +namespace { + +v8::Handle<v8::String> GetHiddenPropertyName(v8::Isolate* isolate) { + return gin::StringToSymbol(isolate, "::mojo::js::WaitingCallback"); +} + +} // namespace + +gin::WrapperInfo WaitingCallback::kWrapperInfo = { gin::kEmbedderNativeGin }; + +// static +gin::Handle<WaitingCallback> WaitingCallback::Create( + v8::Isolate* isolate, + v8::Handle<v8::Function> callback, + mojo::Handle handle, + MojoHandleSignals signals) { + gin::Handle<WaitingCallback> waiting_callback = + gin::CreateHandle(isolate, new WaitingCallback(isolate, callback)); + waiting_callback->wait_id_ = Environment::GetDefaultAsyncWaiter()->AsyncWait( + handle.value(), + signals, + MOJO_DEADLINE_INDEFINITE, + &WaitingCallback::CallOnHandleReady, + waiting_callback.get()); + return waiting_callback; +} + +void WaitingCallback::Cancel() { + if (!wait_id_) + return; + + Environment::GetDefaultAsyncWaiter()->CancelWait(wait_id_); + wait_id_ = 0; +} + +WaitingCallback::WaitingCallback(v8::Isolate* isolate, + v8::Handle<v8::Function> callback) + : wait_id_() { + v8::Handle<v8::Context> context = isolate->GetCurrentContext(); + runner_ = gin::PerContextData::From(context)->runner()->GetWeakPtr(); + GetWrapper(isolate)->SetHiddenValue(GetHiddenPropertyName(isolate), callback); +} + +WaitingCallback::~WaitingCallback() { + Cancel(); +} + +// static +void WaitingCallback::CallOnHandleReady(void* closure, MojoResult result) { + static_cast<WaitingCallback*>(closure)->OnHandleReady(result); +} + +void WaitingCallback::OnHandleReady(MojoResult result) { + wait_id_ = 0; + + if (!runner_) + return; + + gin::Runner::Scope scope(runner_.get()); + v8::Isolate* isolate = runner_->GetContextHolder()->isolate(); + + v8::Handle<v8::Value> hidden_value = + GetWrapper(isolate)->GetHiddenValue(GetHiddenPropertyName(isolate)); + v8::Handle<v8::Function> callback; + CHECK(gin::ConvertFromV8(isolate, hidden_value, &callback)); + + v8::Handle<v8::Value> args[] = { gin::ConvertToV8(isolate, result) }; + runner_->Call(callback, runner_->global(), 1, args); +} + +} // namespace js +} // namespace mojo diff --git a/chromium/mojo/bindings/js/waiting_callback.h b/chromium/mojo/bindings/js/waiting_callback.h new file mode 100644 index 00000000000..973a500cdf0 --- /dev/null +++ b/chromium/mojo/bindings/js/waiting_callback.h @@ -0,0 +1,52 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MOJO_BINDINGS_JS_WAITING_CALLBACK_H_ +#define MOJO_BINDINGS_JS_WAITING_CALLBACK_H_ + +#include "gin/handle.h" +#include "gin/runner.h" +#include "gin/wrappable.h" +#include "mojo/public/c/environment/async_waiter.h" +#include "mojo/public/cpp/system/core.h" + +namespace mojo { +namespace js { + +class WaitingCallback : public gin::Wrappable<WaitingCallback> { + public: + static gin::WrapperInfo kWrapperInfo; + + // Creates a new WaitingCallback. + static gin::Handle<WaitingCallback> Create( + v8::Isolate* isolate, + v8::Handle<v8::Function> callback, + mojo::Handle handle, + MojoHandleSignals signals); + + // Cancels the callback. Does nothing if a callback is not pending. This is + // implicitly invoked from the destructor but can be explicitly invoked as + // necessary. + void Cancel(); + + private: + WaitingCallback(v8::Isolate* isolate, v8::Handle<v8::Function> callback); + virtual ~WaitingCallback(); + + // Callback from MojoAsyncWaiter. |closure| is the WaitingCallback. + static void CallOnHandleReady(void* closure, MojoResult result); + + // Invoked from CallOnHandleReady() (CallOnHandleReady() must be static). + void OnHandleReady(MojoResult result); + + base::WeakPtr<gin::Runner> runner_; + MojoAsyncWaitID wait_id_; + + DISALLOW_COPY_AND_ASSIGN(WaitingCallback); +}; + +} // namespace js +} // namespace mojo + +#endif // MOJO_BINDINGS_JS_WAITING_CALLBACK_H_ |