diff options
Diffstat (limited to 'mojo/public/js')
-rw-r--r-- | mojo/public/js/BUILD.gn | 29 | ||||
-rw-r--r-- | mojo/public/js/bindings.js | 336 | ||||
-rw-r--r-- | mojo/public/js/codec.js | 81 | ||||
-rw-r--r-- | mojo/public/js/codec_unittests.js | 296 | ||||
-rw-r--r-- | mojo/public/js/connection.js | 176 | ||||
-rw-r--r-- | mojo/public/js/connector.js | 27 | ||||
-rw-r--r-- | mojo/public/js/constants.cc | 11 | ||||
-rw-r--r-- | mojo/public/js/constants.h | 7 | ||||
-rw-r--r-- | mojo/public/js/core.js | 75 | ||||
-rw-r--r-- | mojo/public/js/core_unittests.js | 198 | ||||
-rw-r--r-- | mojo/public/js/router.js | 91 | ||||
-rw-r--r-- | mojo/public/js/struct_unittests.js | 279 | ||||
-rw-r--r-- | mojo/public/js/test/validation_test_input_parser.js | 299 | ||||
-rw-r--r-- | mojo/public/js/threading.js | 2 | ||||
-rw-r--r-- | mojo/public/js/union_unittests.js | 184 | ||||
-rw-r--r-- | mojo/public/js/validation_unittests.js | 349 | ||||
-rw-r--r-- | mojo/public/js/validator.js | 153 |
17 files changed, 627 insertions, 1966 deletions
diff --git a/mojo/public/js/BUILD.gn b/mojo/public/js/BUILD.gn index eda7e04..0fae4b4 100644 --- a/mojo/public/js/BUILD.gn +++ b/mojo/public/js/BUILD.gn @@ -2,6 +2,8 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. +interfaces_bindings_gen_dir = "$root_gen_dir/mojo/public/interfaces/bindings" + source_set("js") { sources = [ "constants.cc", @@ -11,34 +13,43 @@ source_set("js") { group("bindings") { data = [ + "$interfaces_bindings_gen_dir/interface_control_messages.mojom.js", "bindings.js", "buffer.js", "codec.js", - "connection.js", "connector.js", - "constants.cc", - "constants.h", "core.js", + "interface_types.js", + "lib/control_message_handler.js", + "lib/control_message_proxy.js", "router.js", "support.js", "threading.js", "unicode.js", "validator.js", ] + + deps = [ + "//mojo/public/interfaces/bindings:bindings__generator", + ] } group("tests") { testonly = true data = [ - "codec_unittests.js", - "core_unittests.js", - "struct_unittests.js", - "test/validation_test_input_parser.js", - "union_unittests.js", - "validation_unittests.js", "//mojo/public/interfaces/bindings/tests/data/validation/", + "tests/codec_unittest.js", + "tests/connection_unittest.js", + "tests/core_unittest.js", + "tests/interface_ptr_unittest.js", + "tests/sample_service_unittest.js", + "tests/struct_unittest.js", + "tests/union_unittest.js", + "tests/validation_test_input_parser.js", + "tests/validation_unittest.js", ] + public_deps = [ ":bindings", ] diff --git a/mojo/public/js/bindings.js b/mojo/public/js/bindings.js index 2fdcae3..f3e40d2 100644 --- a/mojo/public/js/bindings.js +++ b/mojo/public/js/bindings.js @@ -3,115 +3,283 @@ // found in the LICENSE file. define("mojo/public/js/bindings", [ - "mojo/public/js/router", "mojo/public/js/core", -], function(router, core) { - - var Router = router.Router; + "mojo/public/js/lib/control_message_proxy", + "mojo/public/js/interface_types", + "mojo/public/js/router", +], function(core, controlMessageProxy, types, router) { - var kProxyProperties = Symbol("proxyProperties"); - var kStubProperties = Symbol("stubProperties"); + // --------------------------------------------------------------------------- - // Public proxy class properties that are managed at runtime by the JS - // bindings. See ProxyBindings below. - function ProxyProperties(receiver) { - this.receiver = receiver; + function makeRequest(interfacePtr) { + var pipe = core.createMessagePipe(); + interfacePtr.ptr.bind(new types.InterfacePtrInfo(pipe.handle0, 0)); + return new types.InterfaceRequest(pipe.handle1); } - // TODO(hansmuller): remove then after 'Client=' has been removed from Mojom. - ProxyProperties.prototype.getLocalDelegate = function() { - return this.local && StubBindings(this.local).delegate; - } + // --------------------------------------------------------------------------- - // TODO(hansmuller): remove then after 'Client=' has been removed from Mojom. - ProxyProperties.prototype.setLocalDelegate = function(impl) { - if (this.local) - StubBindings(this.local).delegate = impl; - else - throw new Error("no stub object"); - } + // Operations used to setup/configure an interface pointer. Exposed as the + // |ptr| field of generated interface pointer classes. + // |ptrInfoOrHandle| could be omitted and passed into bind() later. + function InterfacePtrController(interfaceType, ptrInfoOrHandle) { + this.version = 0; - ProxyProperties.prototype.close = function() { - this.connection.close(); - } + this.interfaceType_ = interfaceType; + this.router_ = null; + this.proxy_ = null; - // Public stub class properties that are managed at runtime by the JS - // bindings. See StubBindings below. - function StubProperties(delegate) { - this.delegate = delegate; - } + // |router_| is lazily initialized. |handle_| is valid between bind() and + // the initialization of |router_|. + this.handle_ = null; + this.controlMessageProxy_ = null; - StubProperties.prototype.close = function() { - this.connection.close(); + if (ptrInfoOrHandle) + this.bind(ptrInfoOrHandle); } - // The base class for generated proxy classes. - function ProxyBase(receiver) { - this[kProxyProperties] = new ProxyProperties(receiver); + InterfacePtrController.prototype.bind = function(ptrInfoOrHandle) { + this.reset(); - // TODO(hansmuller): Temporary, for Chrome backwards compatibility. - if (receiver instanceof Router) - this.receiver_ = receiver; - } + if (ptrInfoOrHandle instanceof types.InterfacePtrInfo) { + this.version = ptrInfoOrHandle.version; + this.handle_ = ptrInfoOrHandle.handle; + } else { + this.handle_ = ptrInfoOrHandle; + } + }; - // The base class for generated stub classes. - function StubBase(delegate) { - this[kStubProperties] = new StubProperties(delegate); - } + InterfacePtrController.prototype.isBound = function() { + return this.router_ !== null || this.handle_ !== null; + }; - // TODO(hansmuller): remove everything except the connection property doc - // after 'Client=' has been removed from Mojom. + // Although users could just discard the object, reset() closes the pipe + // immediately. + InterfacePtrController.prototype.reset = function() { + this.version = 0; + if (this.router_) { + this.router_.close(); + this.router_ = null; - // Provides access to properties added to a proxy object without risking - // Mojo interface name collisions. Unless otherwise specified, the initial - // value of all properties is undefined. - // - // ProxyBindings(proxy).connection - The Connection object that links the - // proxy for a remote Mojo service to an optional local stub for a local - // service. The value of ProxyBindings(proxy).connection.remote == proxy. + this.proxy_ = null; + } + if (this.handle_) { + core.close(this.handle_); + this.handle_ = null; + } + }; + + InterfacePtrController.prototype.setConnectionErrorHandler + = function(callback) { + if (!this.isBound()) + throw new Error("Cannot set connection error handler if not bound."); + + this.configureProxyIfNecessary_(); + this.router_.setErrorHandler(callback); + }; + + InterfacePtrController.prototype.passInterface = function() { + var result; + if (this.router_) { + // TODO(yzshen): Fix Router interface to support extracting handle. + result = new types.InterfacePtrInfo( + this.router_.connector_.handle_, this.version); + this.router_.connector_.handle_ = null; + } else { + // This also handles the case when this object is not bound. + result = new types.InterfacePtrInfo(this.handle_, this.version); + this.handle_ = null; + } + + this.reset(); + return result; + }; + + InterfacePtrController.prototype.getProxy = function() { + this.configureProxyIfNecessary_(); + return this.proxy_; + }; + + InterfacePtrController.prototype.enableTestingMode = function() { + this.configureProxyIfNecessary_(); + return this.router_.enableTestingMode(); + }; + + InterfacePtrController.prototype.configureProxyIfNecessary_ = function() { + if (!this.handle_) + return; + + this.router_ = new router.Router(this.handle_); + this.handle_ = null; + this.router_ .setPayloadValidators([this.interfaceType_.validateResponse]); + + this.controlMessageProxy_ = new + controlMessageProxy.ControlMessageProxy(this.router_); + + this.proxy_ = new this.interfaceType_.proxyClass(this.router_); + }; + + InterfacePtrController.prototype.queryVersion = function() { + function onQueryVersion(version) { + this.version = version; + return version; + } + + this.configureProxyIfNecessary_(); + return this.controlMessageProxy_.queryVersion().then( + onQueryVersion.bind(this)); + }; + + InterfacePtrController.prototype.requireVersion = function(version) { + this.configureProxyIfNecessary_(); + + if (this.version >= version) { + return; + } + this.version = version; + this.controlMessageProxy_.requireVersion(version); + }; + + // --------------------------------------------------------------------------- + + // |request| could be omitted and passed into bind() later. // - // ProxyBindings(proxy).local - The "local" stub object whose delegate - // implements the proxy's Mojo client interface. + // Example: // - // ProxyBindings(proxy).setLocalDelegate(impl) - Sets the implementation - // delegate of the proxy's client stub object. This is just shorthand - // for |StubBindings(ProxyBindings(proxy).local).delegate = impl|. + // // FooImpl implements mojom.Foo. + // function FooImpl() { ... } + // FooImpl.prototype.fooMethod1 = function() { ... } + // FooImpl.prototype.fooMethod2 = function() { ... } // - // ProxyBindings(proxy).getLocalDelegate() - Returns the implementation - // delegate of the proxy's client stub object. This is just shorthand - // for |StubBindings(ProxyBindings(proxy).local).delegate|. + // var fooPtr = new mojom.FooPtr(); + // var request = makeRequest(fooPtr); + // var binding = new Binding(mojom.Foo, new FooImpl(), request); + // fooPtr.fooMethod1(); + function Binding(interfaceType, impl, requestOrHandle) { + this.interfaceType_ = interfaceType; + this.impl_ = impl; + this.router_ = null; + this.stub_ = null; - function ProxyBindings(proxy) { - return (proxy instanceof ProxyBase) ? proxy[kProxyProperties] : proxy; + if (requestOrHandle) + this.bind(requestOrHandle); } - // TODO(hansmuller): remove the remote doc after 'Client=' has been - // removed from Mojom. + Binding.prototype.isBound = function() { + return this.router_ !== null; + }; - // Provides access to properties added to a stub object without risking - // Mojo interface name collisions. Unless otherwise specified, the initial - // value of all properties is undefined. - // - // StubBindings(stub).delegate - The optional implementation delegate for - // the Mojo interface stub. - // - // StubBindings(stub).connection - The Connection object that links an - // optional proxy for a remote service to this stub. The value of - // StubBindings(stub).connection.local == stub. - // - // StubBindings(stub).remote - A proxy for the the stub's Mojo client - // service. + Binding.prototype.createInterfacePtrAndBind = function() { + var ptr = new this.interfaceType_.ptrClass(); + // TODO(yzshen): Set the version of the interface pointer. + this.bind(makeRequest(ptr)); + return ptr; + } + + Binding.prototype.bind = function(requestOrHandle) { + this.close(); + + var handle = requestOrHandle instanceof types.InterfaceRequest ? + requestOrHandle.handle : requestOrHandle; + if (!core.isHandle(handle)) + return; + + this.stub_ = new this.interfaceType_.stubClass(this.impl_); + this.router_ = new router.Router(handle, this.interfaceType_.kVersion); + this.router_.setIncomingReceiver(this.stub_); + this.router_ .setPayloadValidators([this.interfaceType_.validateRequest]); + }; + + Binding.prototype.close = function() { + if (!this.isBound()) + return; + + this.router_.close(); + this.router_ = null; + this.stub_ = null; + }; + + Binding.prototype.setConnectionErrorHandler + = function(callback) { + if (!this.isBound()) + throw new Error("Cannot set connection error handler if not bound."); + this.router_.setErrorHandler(callback); + }; + + Binding.prototype.unbind = function() { + if (!this.isBound()) + return new types.InterfaceRequest(null); + + var result = new types.InterfaceRequest(this.router_.connector_.handle_); + this.router_.connector_.handle_ = null; + this.close(); + return result; + }; + + Binding.prototype.enableTestingMode = function() { + return this.router_.enableTestingMode(); + }; - function StubBindings(stub) { - return stub instanceof StubBase ? stub[kStubProperties] : stub; + // --------------------------------------------------------------------------- + + function BindingSetEntry(bindingSet, interfaceType, impl, requestOrHandle, + bindingId) { + this.bindingSet_ = bindingSet; + this.bindingId_ = bindingId; + this.binding_ = new Binding(interfaceType, impl, requestOrHandle); + + this.binding_.setConnectionErrorHandler(function() { + this.bindingSet_.onConnectionError(bindingId); + }.bind(this)); + } + + BindingSetEntry.prototype.close = function() { + this.binding_.close(); + }; + + function BindingSet(interfaceType) { + this.interfaceType_ = interfaceType; + this.nextBindingId_ = 0; + this.bindings_ = new Map(); + this.errorHandler_ = null; } + BindingSet.prototype.isEmpty = function() { + return this.bindings_.size == 0; + }; + + BindingSet.prototype.addBinding = function(impl, requestOrHandle) { + this.bindings_.set( + this.nextBindingId_, + new BindingSetEntry(this, this.interfaceType_, impl, requestOrHandle, + this.nextBindingId_)); + ++this.nextBindingId_; + }; + + BindingSet.prototype.closeAllBindings = function() { + for (var entry of this.bindings_.values()) + entry.close(); + this.bindings_.clear(); + }; + + BindingSet.prototype.setConnectionErrorHandler = function(callback) { + this.errorHandler_ = callback; + }; + + BindingSet.prototype.onConnectionError = function(bindingId) { + this.bindings_.delete(bindingId); + + if (this.errorHandler_) + this.errorHandler_(); + }; + var exports = {}; - exports.EmptyProxy = ProxyBase; - exports.EmptyStub = StubBase; - exports.ProxyBase = ProxyBase; - exports.ProxyBindings = ProxyBindings; - exports.StubBase = StubBase; - exports.StubBindings = StubBindings; + exports.InterfacePtrInfo = types.InterfacePtrInfo; + exports.InterfaceRequest = types.InterfaceRequest; + exports.makeRequest = makeRequest; + exports.InterfacePtrController = InterfacePtrController; + exports.Binding = Binding; + exports.BindingSet = BindingSet; + return exports; }); diff --git a/mojo/public/js/codec.js b/mojo/public/js/codec.js index 4003b51..ff5d31a 100644 --- a/mojo/public/js/codec.js +++ b/mojo/public/js/codec.js @@ -3,9 +3,10 @@ // found in the LICENSE file. define("mojo/public/js/codec", [ - "mojo/public/js/unicode", "mojo/public/js/buffer", -], function(unicode, buffer) { + "mojo/public/js/interface_types", + "mojo/public/js/unicode", +], function(buffer, types, unicode) { var kErrorUnsigned = "Passing negative value to unsigned"; var kErrorArray = "Passing non Array for array type"; @@ -303,8 +304,12 @@ define("mojo/public/js/codec", [ }; Encoder.prototype.encodeHandle = function(handle) { - this.handles.push(handle); - this.writeUint32(this.handles.length - 1); + if (handle) { + this.handles.push(handle); + this.writeUint32(this.handles.length - 1); + } else { + this.writeUint32(kEncodedInvalidHandleValue); + } }; Encoder.prototype.encodeString = function(val) { @@ -711,6 +716,20 @@ define("mojo/public/js/codec", [ encoder.writeDouble(val); }; + function Enum(cls) { + this.cls = cls; + } + + Enum.prototype.encodedSize = 4; + + Enum.prototype.decode = function(decoder) { + return decoder.readInt32(); + }; + + Enum.prototype.encode = function(encoder, val) { + encoder.writeInt32(val); + }; + function PointerTo(cls) { this.cls = cls; } @@ -788,33 +807,54 @@ define("mojo/public/js/codec", [ NullableHandle.encode = Handle.encode; - function Interface() { + function Interface(cls) { + this.cls = cls; } - Interface.encodedSize = 8; + Interface.prototype.encodedSize = 8; - Interface.decode = function(decoder) { - var handle = decoder.decodeHandle(); - // Ignore the version field for now. - decoder.readUint32(); + Interface.prototype.decode = function(decoder) { + var interfacePtrInfo = new types.InterfacePtrInfo( + decoder.decodeHandle(), decoder.readUint32()); + var interfacePtr = new this.cls(); + interfacePtr.ptr.bind(interfacePtrInfo); + return interfacePtr; + }; - return handle; + Interface.prototype.encode = function(encoder, val) { + var interfacePtrInfo = + val ? val.ptr.passInterface() : new types.InterfacePtrInfo(null, 0); + encoder.encodeHandle(interfacePtrInfo.handle); + encoder.writeUint32(interfacePtrInfo.version); }; - Interface.encode = function(encoder, val) { - encoder.encodeHandle(val); - // Set the version field to 0 for now. - encoder.writeUint32(0); + function NullableInterface(cls) { + Interface.call(this, cls); + } + + NullableInterface.prototype = Object.create(Interface.prototype); + + function InterfaceRequest() { + } + + InterfaceRequest.encodedSize = 4; + + InterfaceRequest.decode = function(decoder) { + return new types.InterfaceRequest(decoder.decodeHandle()); + }; + + InterfaceRequest.encode = function(encoder, val) { + encoder.encodeHandle(val ? val.handle : null); }; - function NullableInterface() { + function NullableInterfaceRequest() { } - NullableInterface.encodedSize = Interface.encodedSize; + NullableInterfaceRequest.encodedSize = InterfaceRequest.encodedSize; - NullableInterface.decode = Interface.decode; + NullableInterfaceRequest.decode = InterfaceRequest.decode; - NullableInterface.encode = Interface.encode; + NullableInterfaceRequest.encode = InterfaceRequest.encode; function MapOf(keyClass, valueClass) { this.keyClass = keyClass; @@ -863,6 +903,7 @@ define("mojo/public/js/codec", [ exports.Float = Float; exports.Double = Double; exports.String = String; + exports.Enum = Enum; exports.NullableString = NullableString; exports.PointerTo = PointerTo; exports.NullablePointerTo = NullablePointerTo; @@ -873,6 +914,8 @@ define("mojo/public/js/codec", [ exports.NullableHandle = NullableHandle; exports.Interface = Interface; exports.NullableInterface = NullableInterface; + exports.InterfaceRequest = InterfaceRequest; + exports.NullableInterfaceRequest = NullableInterfaceRequest; exports.MapOf = MapOf; exports.NullableMapOf = NullableMapOf; return exports; diff --git a/mojo/public/js/codec_unittests.js b/mojo/public/js/codec_unittests.js deleted file mode 100644 index b610d9a..0000000 --- a/mojo/public/js/codec_unittests.js +++ /dev/null @@ -1,296 +0,0 @@ -// 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. - -define([ - "gin/test/expect", - "mojo/public/js/codec", - "mojo/public/interfaces/bindings/tests/rect.mojom", - "mojo/public/interfaces/bindings/tests/sample_service.mojom", - "mojo/public/interfaces/bindings/tests/test_structs.mojom", - ], function(expect, codec, rect, sample, structs) { - testBar(); - testFoo(); - testNamedRegion(); - testTypes(); - testAlign(); - testUtf8(); - testTypedPointerValidation(); - 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([ - 24, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - 42, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - - 16, 0, 0, 0, - 0, 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] - ]; - foo.array_of_bools = [ - true, false, true, false, true, false, true, 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: */ 24, 0, 0, 0, 0, 0, 0, 0, - /* 8: */ 0, 0, 0, 0, 31, 0, 0, 0, - /* 16: */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 24: */ 96, 0, 0, 0, 0, 0, 0, 0, - /* 32: */ 0xD5, 0xB4, 0x12, 0x02, 0x93, 0x6E, 0x01, 0, - /* 40: */ 5, 0, 0, 0, 0, 0, 0, 0, - /* 48: */ 72, 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); - - expect(foo2.array_of_bools).toEqual(foo.array_of_bools); - } - - function createRect(x, y, width, height) { - var r = new rect.Rect(); - r.x = x; - r.y = y; - r.width = width; - r.height = height; - return r; - } - - // Verify that the references to the imported Rect type in test_structs.mojom - // are generated correctly. - function testNamedRegion() { - var r = new structs.NamedRegion(); - r.name = "rectangle"; - r.rects = new Array(createRect(1, 2, 3, 4), createRect(10, 20, 30, 40)); - - var builder = new codec.MessageBuilder(1, structs.NamedRegion.encodedSize); - builder.encodeStruct(structs.NamedRegion, r); - var reader = new codec.MessageReader(builder.finish()); - var result = reader.decodeStruct(structs.NamedRegion); - - expect(result.name).toEqual("rectangle"); - expect(result.rects[0]).toEqual(createRect(1, 2, 3, 4)); - expect(result.rects[1]).toEqual(createRect(10, 20, 30, 40)); - } - - 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.NullableString, null, null, 8); - 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: */ 24, 0, 0, 0, 0, 0, 0, 0, - /* 8: */ 0, 0, 0, 0, 42, 0, 0, 0, - /* 16: */ 0, 0, 0, 0, 0, 0, 0, 0, - /* 24: */ 8, 0, 0, 0, 0, 0, 0, 0, - /* 32: */ 14, 0, 0, 0, 6, 0, 0, 0, - /* 40: */ 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); - } - - function testTypedPointerValidation() { - var encoder = new codec.MessageBuilder(42, 24).createEncoder(8); - function DummyClass() {}; - var testCases = [ - // method, args, invalid examples, valid examples - [encoder.encodeArrayPointer, [DummyClass], [75], - [[], null, undefined, new Uint8Array([])]], - [encoder.encodeStringPointer, [], [75, new String("foo")], - ["", "bar", null, undefined]], - [encoder.encodeMapPointer, [DummyClass, DummyClass], [75], - [new Map(), null, undefined]], - ]; - - testCases.forEach(function(test) { - var method = test[0]; - var baseArgs = test[1]; - var invalidExamples = test[2]; - var validExamples = test[3]; - - var encoder = new codec.MessageBuilder(42, 24).createEncoder(8); - invalidExamples.forEach(function(invalid) { - expect(function() { - method.apply(encoder, baseArgs.concat(invalid)); - }).toThrow(); - }); - - validExamples.forEach(function(valid) { - var encoder = new codec.MessageBuilder(42, 24).createEncoder(8); - method.apply(encoder, baseArgs.concat(valid)); - }); - }); - } -}); diff --git a/mojo/public/js/connection.js b/mojo/public/js/connection.js deleted file mode 100644 index 3f7e839..0000000 --- a/mojo/public/js/connection.js +++ /dev/null @@ -1,176 +0,0 @@ -// 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. - -define("mojo/public/js/connection", [ - "mojo/public/js/bindings", - "mojo/public/js/connector", - "mojo/public/js/core", - "mojo/public/js/router", -], function(bindings, connector, core, router) { - - var Router = router.Router; - var EmptyProxy = bindings.EmptyProxy; - var EmptyStub = bindings.EmptyStub; - var ProxyBindings = bindings.ProxyBindings; - var StubBindings = bindings.StubBindings; - var TestConnector = connector.TestConnector; - var TestRouter = router.TestRouter; - - // TODO(hansmuller): the proxy receiver_ property should be receiver$ - - function BaseConnection(localStub, remoteProxy, router) { - this.router_ = router; - this.local = localStub; - this.remote = remoteProxy; - - this.router_.setIncomingReceiver(localStub); - this.router_.setErrorHandler(function() { - if (StubBindings(this.local) && - StubBindings(this.local).connectionErrorHandler) - StubBindings(this.local).connectionErrorHandler(); - }.bind(this)); - if (this.remote) - this.remote.receiver_ = router; - - // Validate incoming messages: remote responses and local requests. - var validateRequest = localStub && localStub.validator; - var validateResponse = remoteProxy && remoteProxy.validator; - var payloadValidators = []; - if (validateRequest) - payloadValidators.push(validateRequest); - if (validateResponse) - payloadValidators.push(validateResponse); - this.router_.setPayloadValidators(payloadValidators); - } - - BaseConnection.prototype.close = function() { - this.router_.close(); - this.router_ = null; - this.local = null; - this.remote = null; - }; - - BaseConnection.prototype.encounteredError = function() { - return this.router_.encounteredError(); - }; - - function Connection( - handle, localFactory, remoteFactory, routerFactory, connectorFactory) { - var routerClass = routerFactory || Router; - var router = new routerClass(handle, connectorFactory); - var remoteProxy = remoteFactory && new remoteFactory(router); - var localStub = localFactory && new localFactory(remoteProxy); - BaseConnection.call(this, localStub, remoteProxy, router); - } - - Connection.prototype = Object.create(BaseConnection.prototype); - - // The TestConnection subclass is only intended to be used in unit tests. - function TestConnection(handle, localFactory, remoteFactory) { - Connection.call(this, - handle, - localFactory, - remoteFactory, - TestRouter, - TestConnector); - } - - TestConnection.prototype = Object.create(Connection.prototype); - - // Return a handle for a message pipe that's connected to a proxy - // for remoteInterface. Used by generated code for outgoing interface& - // (request) parameters: the caller is given the generated proxy via - // |proxyCallback(proxy)| and the generated code sends the handle - // returned by this function. - function bindProxy(proxyCallback, remoteInterface) { - var messagePipe = core.createMessagePipe(); - if (messagePipe.result != core.RESULT_OK) - throw new Error("createMessagePipe failed " + messagePipe.result); - - var proxy = new remoteInterface.proxyClass; - var router = new Router(messagePipe.handle0); - var connection = new BaseConnection(undefined, proxy, router); - ProxyBindings(proxy).connection = connection; - if (proxyCallback) - proxyCallback(proxy); - - return messagePipe.handle1; - } - - // Return a handle for a message pipe that's connected to a stub for - // localInterface. Used by generated code for outgoing interface - // parameters: the caller is given the generated stub via - // |stubCallback(stub)| and the generated code sends the handle - // returned by this function. The caller is responsible for managing - // the lifetime of the stub and for setting it's implementation - // delegate with: StubBindings(stub).delegate = myImpl; - function bindImpl(stubCallback, localInterface) { - var messagePipe = core.createMessagePipe(); - if (messagePipe.result != core.RESULT_OK) - throw new Error("createMessagePipe failed " + messagePipe.result); - - var stub = new localInterface.stubClass; - var router = new Router(messagePipe.handle0); - var connection = new BaseConnection(stub, undefined, router); - StubBindings(stub).connection = connection; - if (stubCallback) - stubCallback(stub); - - return messagePipe.handle1; - } - - // Return a remoteInterface proxy for handle. Used by generated code - // for converting incoming interface parameters to proxies. - function bindHandleToProxy(handle, remoteInterface) { - if (!core.isHandle(handle)) - throw new Error("Not a handle " + handle); - - var proxy = new remoteInterface.proxyClass; - var router = new Router(handle); - var connection = new BaseConnection(undefined, proxy, router); - ProxyBindings(proxy).connection = connection; - return proxy; - } - - // Return a localInterface stub for handle. Used by generated code - // for converting incoming interface& request parameters to localInterface - // stubs. The caller can specify the stub's implementation of localInterface - // like this: StubBindings(stub).delegate = myStubImpl. - function bindHandleToStub(handle, localInterface) { - if (!core.isHandle(handle)) - throw new Error("Not a handle " + handle); - - var stub = new localInterface.stubClass; - var router = new Router(handle); - var connection = new BaseConnection(stub, undefined, router); - StubBindings(stub).connection = connection; - return stub; - } - - /** - * Creates a messape pipe and links one end of the pipe to the given object. - * @param {!Object} obj The object to create a handle for. Must be a subclass - * of an auto-generated stub class. - * @return {!MojoHandle} The other (not yet connected) end of the message - * pipe. - */ - function bindStubDerivedImpl(obj) { - var pipe = core.createMessagePipe(); - var router = new Router(pipe.handle0); - var connection = new BaseConnection(obj, undefined, router); - obj.connection = connection; - return pipe.handle1; - } - - var exports = {}; - exports.Connection = Connection; - exports.TestConnection = TestConnection; - - exports.bindProxy = bindProxy; - exports.bindImpl = bindImpl; - exports.bindHandleToProxy = bindHandleToProxy; - exports.bindHandleToStub = bindHandleToStub; - exports.bindStubDerivedImpl = bindStubDerivedImpl; - return exports; -}); diff --git a/mojo/public/js/connector.js b/mojo/public/js/connector.js index 674f36b..ee16be8 100644 --- a/mojo/public/js/connector.js +++ b/mojo/public/js/connector.js @@ -82,6 +82,12 @@ define("mojo/public/js/connector", [ return this.error_; }; + Connector.prototype.waitForNextMessageForTesting = function() { + var wait = core.wait(this.handle_, core.HANDLE_SIGNAL_READABLE, + core.DEADLINE_INDEFINITE); + this.readMore_(wait.result); + }; + Connector.prototype.readMore_ = function(result) { for (;;) { var read = core.readMessage(this.handle_, @@ -98,29 +104,12 @@ define("mojo/public/js/connector", [ } var messageBuffer = new buffer.Buffer(read.buffer); var message = new codec.Message(messageBuffer, read.handles); - if (this.incomingReceiver_) { - this.incomingReceiver_.accept(message); - } + if (this.incomingReceiver_) + this.incomingReceiver_.accept(message); } }; - // The TestConnector subclass is only intended to be used in unit tests. It - // doesn't automatically listen for input messages. Instead, you need to - // call waitForNextMessage to block and wait for the next incoming message. - function TestConnector(handle) { - Connector.call(this, handle); - } - - TestConnector.prototype = Object.create(Connector.prototype); - - TestConnector.prototype.waitForNextMessage = function() { - var wait = core.wait(this.handle_, core.HANDLE_SIGNAL_READABLE, - core.DEADLINE_INDEFINITE); - this.readMore_(wait.result); - } - var exports = {}; exports.Connector = Connector; - exports.TestConnector = TestConnector; return exports; }); diff --git a/mojo/public/js/constants.cc b/mojo/public/js/constants.cc index d29f5cb..58cf274 100644 --- a/mojo/public/js/constants.cc +++ b/mojo/public/js/constants.cc @@ -9,10 +9,15 @@ namespace mojo { const char kBindingsModuleName[] = "mojo/public/js/bindings"; const char kBufferModuleName[] = "mojo/public/js/buffer"; const char kCodecModuleName[] = "mojo/public/js/codec"; -const char kConnectionModuleName[] = "mojo/public/js/connection"; const char kConnectorModuleName[] = "mojo/public/js/connector"; -const char kUnicodeModuleName[] = "mojo/public/js/unicode"; +const char kControlMessageHandlerModuleName[] = + "mojo/public/js/lib/control_message_handler"; +const char kControlMessageProxyModuleName[] = + "mojo/public/js/lib/control_message_proxy"; +const char kInterfaceControlMessagesMojom[] = + "mojo/public/interfaces/bindings/interface_control_messages.mojom"; +const char kInterfaceTypesModuleName[] = "mojo/public/js/interface_types"; const char kRouterModuleName[] = "mojo/public/js/router"; +const char kUnicodeModuleName[] = "mojo/public/js/unicode"; const char kValidatorModuleName[] = "mojo/public/js/validator"; - } // namespace mojo diff --git a/mojo/public/js/constants.h b/mojo/public/js/constants.h index de75a90..9d32d20 100644 --- a/mojo/public/js/constants.h +++ b/mojo/public/js/constants.h @@ -11,10 +11,13 @@ namespace mojo { extern const char kBindingsModuleName[]; extern const char kBufferModuleName[]; extern const char kCodecModuleName[]; -extern const char kConnectionModuleName[]; extern const char kConnectorModuleName[]; -extern const char kUnicodeModuleName[]; +extern const char kControlMessageHandlerModuleName[]; +extern const char kControlMessageProxyModuleName[]; +extern const char kInterfaceControlMessagesMojom[]; +extern const char kInterfaceTypesModuleName[]; extern const char kRouterModuleName[]; +extern const char kUnicodeModuleName[]; extern const char kValidatorModuleName[]; } // namespace mojo diff --git a/mojo/public/js/core.js b/mojo/public/js/core.js index b89a956..ef480ee 100644 --- a/mojo/public/js/core.js +++ b/mojo/public/js/core.js @@ -114,6 +114,27 @@ var READ_DATA_FLAG_QUERY; var READ_DATA_FLAG_PEEK; /** + * MojoCreateSharedBufferOptionsFlags: Used to specify options to + * |createSharedBuffer()|. + * See core.h for more information. + */ +var CREATE_SHARED_BUFFER_OPTIONS_FLAG_NONE; + +/** + * MojoDuplicateBufferHandleOptionsFlags: Used to specify options to + * |duplicateBufferHandle()|. + * See core.h for more information. + */ +var DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_NONE; +var DUPLICATE_BUFFER_HANDLE_OPTIONS_FLAG_READ_ONLY; + +/** + * MojoMapBufferFlags: Used to specify options to |mapBuffer()|. + * See core.h for more information. + */ +var MAP_BUFFER_FLAG_NONE; + +/** * Closes the given |handle|. See MojoClose for more info. * @param {MojoHandle} Handle to close. * @return {MojoResult} Result code. @@ -236,3 +257,57 @@ function readData(handle, flags) { [native code] } * @return true or false */ function isHandle(value) { [native code] } + +/** + * Creates shared buffer of specified size |num_bytes|. + * See MojoCreateSharedBuffer for more information including error codes. + * + * @param {number} num_bytes Size of the memory to be allocated for shared + * @param {MojoCreateSharedBufferOptionsFlags} flags Flags. + * buffer. + * @return {object} An object of the form { + * result, // |RESULT_OK| on success, error code otherwise. + * handle, // An MojoHandle for shared buffer (only on success). + * } + */ +function createSharedBuffer(num_bytes, flags) { [native code] } + +/** + * Duplicates the |buffer_handle| to a shared buffer. Duplicated handle can be + * sent to another process over message pipe. See MojoDuplicateBufferHandle for + * more information including error codes. + * + * @param {MojoHandle} buffer_handle MojoHandle. + * @param {MojoCreateSharedBufferOptionsFlags} flags Flags. + * @return {object} An object of the form { + * result, // |RESULT_OK| on success, error code otherwise. + * handle, // A duplicated MojoHandle for shared buffer (only on success). + * } + */ +function duplicateBufferHandle(buffer_handle, flags) { [native code] } + +/** + * Maps the part (at offset |offset| of length |num_bytes|) of the buffer given + * by |buffer_handle| into ArrayBuffer memory |buffer|, with options specified + * by |flags|. See MojoMapBuffer for more information including error codes. + * + * @param {MojoHandle} buffer_handle A sharedBufferHandle returned by + * createSharedBuffer. + * @param {number} offset Offset. + * @param {number} num_bytes Size of the memory to be mapped. + * @param {MojoMapBufferFlags} flags Flags. + * @return {object} An object of the form { + * result, // |RESULT_OK| on success, error code otherwise. + * buffer, // An ArrayBuffer (only on success). + * } + */ +function mapBuffer(buffer_handle, offset, num_bytes, flags) { [native code] } + +/** + * Unmaps buffer that was mapped using mapBuffer. + * See MojoUnmapBuffer for more information including error codes. + * + * @param {ArrayBuffer} buffer ArrayBuffer. + * @return {MojoResult} Result code. + */ +function unmapBuffer(buffer) { [native code] } diff --git a/mojo/public/js/core_unittests.js b/mojo/public/js/core_unittests.js deleted file mode 100644 index 12364dc..0000000 --- a/mojo/public/js/core_unittests.js +++ /dev/null @@ -1,198 +0,0 @@ -// 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. - -define([ - "gin/test/expect", - "mojo/public/js/core", - "gc", - ], function(expect, core, gc) { - - var HANDLE_SIGNAL_READWRITABLE = core.HANDLE_SIGNAL_WRITABLE | - core.HANDLE_SIGNAL_READABLE; - var HANDLE_SIGNAL_ALL = core.HANDLE_SIGNAL_WRITABLE | - core.HANDLE_SIGNAL_READABLE | - core.HANDLE_SIGNAL_PEER_CLOSED; - - runWithMessagePipe(testNop); - runWithMessagePipe(testReadAndWriteMessage); - runWithMessagePipeWithOptions(testNop); - runWithMessagePipeWithOptions(testReadAndWriteMessage); - runWithDataPipe(testNop); - runWithDataPipe(testReadAndWriteDataPipe); - runWithDataPipeWithOptions(testNop); - runWithDataPipeWithOptions(testReadAndWriteDataPipe); - runWithMessagePipe(testIsHandleMessagePipe); - runWithDataPipe(testIsHandleDataPipe); - gc.collectGarbage(); // should not crash - this.result = "PASS"; - - function runWithMessagePipe(test) { - var pipe = core.createMessagePipe(); - expect(pipe.result).toBe(core.RESULT_OK); - - test(pipe); - - expect(core.close(pipe.handle0)).toBe(core.RESULT_OK); - expect(core.close(pipe.handle1)).toBe(core.RESULT_OK); - } - - function runWithMessagePipeWithOptions(test) { - var pipe = core.createMessagePipe({ - flags: core.CREATE_MESSAGE_PIPE_OPTIONS_FLAG_NONE - }); - expect(pipe.result).toBe(core.RESULT_OK); - - 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(); - 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 runWithDataPipeWithOptions(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 wait = core.waitMany([], [], 0); - expect(wait.result).toBe(core.RESULT_INVALID_ARGUMENT); - expect(wait.index).toBe(null); - expect(wait.signalsState).toBe(null); - - wait = core.wait(pipe.handle0, core.HANDLE_SIGNAL_READABLE, 0); - expect(wait.result).toBe(core.RESULT_DEADLINE_EXCEEDED); - expect(wait.signalsState.satisfiedSignals).toBe( - core.HANDLE_SIGNAL_WRITABLE); - expect(wait.signalsState.satisfiableSignals).toBe(HANDLE_SIGNAL_ALL); - - wait = core.waitMany( - [pipe.handle0, pipe.handle1], - [core.HANDLE_SIGNAL_READABLE,core.HANDLE_SIGNAL_READABLE], - 0); - expect(wait.result).toBe(core.RESULT_DEADLINE_EXCEEDED); - expect(wait.index).toBe(null); - expect(wait.signalsState[0].satisfiedSignals).toBe( - core.HANDLE_SIGNAL_WRITABLE); - expect(wait.signalsState[0].satisfiableSignals).toBe(HANDLE_SIGNAL_ALL); - expect(wait.signalsState[1].satisfiedSignals).toBe( - core.HANDLE_SIGNAL_WRITABLE); - expect(wait.signalsState[1].satisfiableSignals).toBe(HANDLE_SIGNAL_ALL); - - wait = core.wait(pipe.handle0, core.HANDLE_SIGNAL_WRITABLE, 0); - expect(wait.result).toBe(core.RESULT_OK); - expect(wait.signalsState.satisfiedSignals).toBe( - core.HANDLE_SIGNAL_WRITABLE); - expect(wait.signalsState.satisfiableSignals).toBe(HANDLE_SIGNAL_ALL); - - 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); - - wait = core.wait(pipe.handle0, core.HANDLE_SIGNAL_WRITABLE, 0); - expect(wait.result).toBe(core.RESULT_OK); - expect(wait.signalsState.satisfiedSignals).toBe( - core.HANDLE_SIGNAL_WRITABLE); - expect(wait.signalsState.satisfiableSignals).toBe(HANDLE_SIGNAL_ALL); - - wait = core.wait(pipe.handle1, core.HANDLE_SIGNAL_READABLE, - core.DEADLINE_INDEFINITE); - expect(wait.result).toBe(core.RESULT_OK); - expect(wait.signalsState.satisfiedSignals).toBe(HANDLE_SIGNAL_READWRITABLE); - expect(wait.signalsState.satisfiableSignals).toBe(HANDLE_SIGNAL_ALL); - - 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 wait = core.wait(pipe.consumerHandle, core.HANDLE_SIGNAL_READABLE, - core.DEADLINE_INDEFINITE); - expect(wait.result).toBe(core.RESULT_OK); - var peeked = core.readData( - pipe.consumerHandle, - core.READ_DATA_FLAG_PEEK | core.READ_DATA_FLAG_ALL_OR_NONE); - expect(peeked.result).toBe(core.RESULT_OK); - expect(peeked.buffer.byteLength).toBe(42); - - var peeked_memory = new Uint8Array(peeked.buffer); - for (var i = 0; i < peeked_memory.length; ++i) - expect(peeked_memory[i]).toBe((i * i) & 0xFF); - - 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); - } - - function testIsHandleMessagePipe(pipe) { - expect(core.isHandle(123).toBeFalsy); - expect(core.isHandle("123").toBeFalsy); - expect(core.isHandle({}).toBeFalsy); - expect(core.isHandle([]).toBeFalsy); - expect(core.isHandle(undefined).toBeFalsy); - expect(core.isHandle(pipe).toBeFalsy); - expect(core.isHandle(pipe.handle0)).toBeTruthy(); - expect(core.isHandle(pipe.handle1)).toBeTruthy(); - expect(core.isHandle(null)).toBeTruthy(); - } - - function testIsHandleDataPipe(pipe) { - expect(core.isHandle(pipe.consumerHandle)).toBeTruthy(); - expect(core.isHandle(pipe.producerHandle)).toBeTruthy(); - } - -}); diff --git a/mojo/public/js/router.js b/mojo/public/js/router.js index e3db0a6..e94c5eb 100644 --- a/mojo/public/js/router.js +++ b/mojo/public/js/router.js @@ -3,17 +3,20 @@ // found in the LICENSE file. define("mojo/public/js/router", [ + "console", "mojo/public/js/codec", "mojo/public/js/core", "mojo/public/js/connector", + "mojo/public/js/lib/control_message_handler", "mojo/public/js/validator", -], function(codec, core, connector, validator) { +], function(console, codec, core, connector, controlMessageHandler, validator) { var Connector = connector.Connector; var MessageReader = codec.MessageReader; var Validator = validator.Validator; + var ControlMessageHandler = controlMessageHandler.ControlMessageHandler; - function Router(handle, connectorFactory) { + function Router(handle, interface_version, connectorFactory) { if (!core.isHandle(handle)) throw new Error("Router constructor: Not a handle"); if (connectorFactory === undefined) @@ -24,6 +27,12 @@ define("mojo/public/js/router", [ this.nextRequestID_ = 0; this.completers_ = new Map(); this.payloadValidators_ = []; + this.testingController_ = null; + + if (interface_version !== undefined) { + this.controlMessageHandler_ = new + ControlMessageHandler(interface_version); + } this.connector_.setIncomingReceiver({ accept: this.handleIncomingMessage_.bind(this), @@ -36,6 +45,7 @@ define("mojo/public/js/router", [ Router.prototype.close = function() { this.completers_.clear(); // Drop any responders. this.connector_.close(); + this.testingController_ = null; }; Router.prototype.accept = function(message) { @@ -81,6 +91,11 @@ define("mojo/public/js/router", [ return this.connector_.encounteredError(); }; + Router.prototype.enableTestingMode = function() { + this.testingController_ = new RouterTestingController(this.connector_); + return this.testingController_; + }; + Router.prototype.handleIncomingMessage_ = function(message) { var noError = validator.validationError.NONE; var messageValidator = new Validator(message); @@ -95,8 +110,17 @@ define("mojo/public/js/router", [ }; Router.prototype.handleValidIncomingMessage_ = function(message) { + if (this.testingController_) + return; + if (message.expectsResponse()) { - if (this.incomingReceiver_) { + if (controlMessageHandler.isControlMessage(message)) { + if (this.controlMessageHandler_) { + this.controlMessageHandler_.acceptWithResponder(message, this); + } else { + this.close(); + } + } else if (this.incomingReceiver_) { this.incomingReceiver_.acceptWithResponder(message, this); } else { // If we receive a request expecting a response when the client is not @@ -107,17 +131,39 @@ define("mojo/public/js/router", [ var reader = new MessageReader(message); var requestID = reader.requestID; var completer = this.completers_.get(requestID); - this.completers_.delete(requestID); - completer.resolve(message); + if (completer) { + this.completers_.delete(requestID); + completer.resolve(message); + } else { + console.log("Unexpected response with request ID: " + requestID); + } } else { - if (this.incomingReceiver_) + if (controlMessageHandler.isControlMessage(message)) { + if (this.controlMessageHandler_) { + var ok = this.controlMessageHandler_.accept(message); + if (ok) return; + } + this.close(); + } else if (this.incomingReceiver_) { this.incomingReceiver_.accept(message); + } } - } + }; Router.prototype.handleInvalidIncomingMessage_ = function(message, error) { - this.close(); - } + if (!this.testingController_) { + // TODO(yzshen): Consider notifying the embedder. + // TODO(yzshen): This should also trigger connection error handler. + // Consider making accept() return a boolean and let the connector deal + // with this, as the C++ code does. + console.log("Invalid message: " + validator.validationError[error]); + + this.close(); + return; + } + + this.testingController_.onInvalidIncomingMessage(error); + }; Router.prototype.handleConnectionError_ = function(result) { this.completers_.forEach(function(value) { @@ -128,25 +174,30 @@ define("mojo/public/js/router", [ this.close(); }; - // The TestRouter subclass is only intended to be used in unit tests. - // It defeats valid message handling and delgates invalid message handling. + // The RouterTestingController is used in unit tests. It defeats valid message + // handling and delgates invalid message handling. - function TestRouter(handle, connectorFactory) { - Router.call(this, handle, connectorFactory); + function RouterTestingController(connector) { + this.connector_ = connector; + this.invalidMessageHandler_ = null; } - TestRouter.prototype = Object.create(Router.prototype); + RouterTestingController.prototype.waitForNextMessage = function() { + this.connector_.waitForNextMessageForTesting(); + }; - TestRouter.prototype.handleValidIncomingMessage_ = function() { + RouterTestingController.prototype.setInvalidIncomingMessageHandler = + function(callback) { + this.invalidMessageHandler_ = callback; }; - TestRouter.prototype.handleInvalidIncomingMessage_ = - function(message, error) { - this.validationErrorHandler(error); - }; + RouterTestingController.prototype.onInvalidIncomingMessage = + function(error) { + if (this.invalidMessageHandler_) + this.invalidMessageHandler_(error); + }; var exports = {}; exports.Router = Router; - exports.TestRouter = TestRouter; return exports; }); diff --git a/mojo/public/js/struct_unittests.js b/mojo/public/js/struct_unittests.js deleted file mode 100644 index 691d51b..0000000 --- a/mojo/public/js/struct_unittests.js +++ /dev/null @@ -1,279 +0,0 @@ -// 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. - -define([ - "gin/test/expect", - "mojo/public/interfaces/bindings/tests/rect.mojom", - "mojo/public/interfaces/bindings/tests/test_structs.mojom", - "mojo/public/js/codec", - "mojo/public/js/validator", -], function(expect, - rect, - testStructs, - codec, - validator) { - - function testConstructors() { - var r = new rect.Rect(); - expect(r).toEqual(new rect.Rect({x:0, y:0, width:0, height:0})); - expect(r).toEqual(new rect.Rect({foo:100, bar:200})); - - r.x = 10; - r.y = 20; - r.width = 30; - r.height = 40; - var rp = new testStructs.RectPair({first: r, second: r}); - expect(rp.first).toEqual(r); - expect(rp.second).toEqual(r); - - expect(new testStructs.RectPair({second: r}).first).toBeNull(); - - var nr = new testStructs.NamedRegion(); - expect(nr.name).toBeNull(); - expect(nr.rects).toBeNull(); - expect(nr).toEqual(new testStructs.NamedRegion({})); - - nr.name = "foo"; - nr.rects = [r, r, r]; - expect(nr).toEqual(new testStructs.NamedRegion({ - name: "foo", - rects: [r, r, r], - })); - - var e = new testStructs.EmptyStruct(); - expect(e).toEqual(new testStructs.EmptyStruct({foo:123})); - } - - function testNoDefaultFieldValues() { - var s = new testStructs.NoDefaultFieldValues(); - expect(s.f0).toEqual(false); - - // f1 - f10, number type fields - for (var i = 1; i <= 10; i++) - expect(s["f" + i]).toEqual(0); - - // f11,12 strings, f13-22 handles, f23-f26 arrays, f27,28 structs - for (var i = 11; i <= 28; i++) - expect(s["f" + i]).toBeNull(); - } - - function testDefaultFieldValues() { - var s = new testStructs.DefaultFieldValues(); - expect(s.f0).toEqual(true); - - // f1 - f12, number type fields - for (var i = 1; i <= 12; i++) - expect(s["f" + i]).toEqual(100); - - // f13,14 "foo" - for (var i = 13; i <= 14; i++) - expect(s["f" + i]).toEqual("foo"); - - // f15,16 a default instance of Rect - var r = new rect.Rect(); - expect(s.f15).toEqual(r); - expect(s.f16).toEqual(r); - } - - function testScopedConstants() { - expect(testStructs.ScopedConstants.TEN).toEqual(10); - expect(testStructs.ScopedConstants.ALSO_TEN).toEqual(10); - - expect(testStructs.ScopedConstants.EType.E0).toEqual(0); - expect(testStructs.ScopedConstants.EType.E1).toEqual(1); - expect(testStructs.ScopedConstants.EType.E2).toEqual(10); - expect(testStructs.ScopedConstants.EType.E3).toEqual(10); - expect(testStructs.ScopedConstants.EType.E4).toEqual(11); - - var s = new testStructs.ScopedConstants(); - expect(s.f0).toEqual(0); - expect(s.f1).toEqual(1); - expect(s.f2).toEqual(10); - expect(s.f3).toEqual(10); - expect(s.f4).toEqual(11); - expect(s.f5).toEqual(10); - expect(s.f6).toEqual(10); - } - - function structEncodeDecode(struct) { - var structClass = struct.constructor; - var builder = new codec.MessageBuilder(1234, structClass.encodedSize); - builder.encodeStruct(structClass, struct); - var message = builder.finish(); - - var messageValidator = new validator.Validator(message); - var err = structClass.validate(messageValidator, codec.kMessageHeaderSize); - expect(err).toEqual(validator.validationError.NONE); - - var reader = new codec.MessageReader(message); - return reader.decodeStruct(structClass); - } - - function testMapKeyTypes() { - var mapFieldsStruct = new testStructs.MapKeyTypes({ - f0: new Map([[true, false], [false, true]]), // map<bool, bool> - f1: new Map([[0, 0], [1, 127], [-1, -128]]), // map<int8, int8> - f2: new Map([[0, 0], [1, 127], [2, 255]]), // map<uint8, uint8> - f3: new Map([[0, 0], [1, 32767], [2, -32768]]), // map<int16, int16> - f4: new Map([[0, 0], [1, 32768], [2, 0xFFFF]]), // map<uint16, uint16> - f5: new Map([[0, 0], [1, 32767], [2, -32768]]), // map<int32, int32> - f6: new Map([[0, 0], [1, 32768], [2, 0xFFFF]]), // map<uint32, uint32> - f7: new Map([[0, 0], [1, 32767], [2, -32768]]), // map<int64, int64> - f8: new Map([[0, 0], [1, 32768], [2, 0xFFFF]]), // map<uint64, uint64> - f9: new Map([[1000.5, -50000], [100.5, 5000]]), // map<float, float> - f10: new Map([[-100.5, -50000], [0, 50000000]]), // map<double, double> - f11: new Map([["one", "two"], ["free", "four"]]), // map<string, string> - }); - var decodedStruct = structEncodeDecode(mapFieldsStruct); - expect(decodedStruct.f0).toEqual(mapFieldsStruct.f0); - expect(decodedStruct.f1).toEqual(mapFieldsStruct.f1); - expect(decodedStruct.f2).toEqual(mapFieldsStruct.f2); - expect(decodedStruct.f3).toEqual(mapFieldsStruct.f3); - expect(decodedStruct.f4).toEqual(mapFieldsStruct.f4); - expect(decodedStruct.f5).toEqual(mapFieldsStruct.f5); - expect(decodedStruct.f6).toEqual(mapFieldsStruct.f6); - expect(decodedStruct.f7).toEqual(mapFieldsStruct.f7); - expect(decodedStruct.f8).toEqual(mapFieldsStruct.f8); - expect(decodedStruct.f9).toEqual(mapFieldsStruct.f9); - expect(decodedStruct.f10).toEqual(mapFieldsStruct.f10); - expect(decodedStruct.f11).toEqual(mapFieldsStruct.f11); - } - - function testMapValueTypes() { - var mapFieldsStruct = new testStructs.MapValueTypes({ - // map<string, array<string>> - f0: new Map([["a", ["b", "c"]], ["d", ["e"]]]), - // map<string, array<string>?> - f1: new Map([["a", null], ["b", ["c", "d"]]]), - // map<string, array<string?>> - f2: new Map([["a", [null]], ["b", [null, "d"]]]), - // map<string, array<string,2>> - f3: new Map([["a", ["1", "2"]], ["b", ["1", "2"]]]), - // map<string, array<array<string, 2>?>> - f4: new Map([["a", [["1", "2"]]], ["b", [null]]]), - // map<string, array<array<string, 2>, 1>> - f5: new Map([["a", [["1", "2"]]]]), - // map<string, Rect?> - f6: new Map([["a", null]]), - // map<string, map<string, string>> - f7: new Map([["a", new Map([["b", "c"]])]]), - // map<string, array<map<string, string>>> - f8: new Map([["a", [new Map([["b", "c"]])]]]), - // map<string, handle> - f9: new Map([["a", 1234]]), - // map<string, array<handle>> - f10: new Map([["a", [1234, 5678]]]), - // map<string, map<string, handle>> - f11: new Map([["a", new Map([["b", 1234]])]]), - }); - var decodedStruct = structEncodeDecode(mapFieldsStruct); - expect(decodedStruct.f0).toEqual(mapFieldsStruct.f0); - expect(decodedStruct.f1).toEqual(mapFieldsStruct.f1); - expect(decodedStruct.f2).toEqual(mapFieldsStruct.f2); - expect(decodedStruct.f3).toEqual(mapFieldsStruct.f3); - expect(decodedStruct.f4).toEqual(mapFieldsStruct.f4); - expect(decodedStruct.f5).toEqual(mapFieldsStruct.f5); - expect(decodedStruct.f6).toEqual(mapFieldsStruct.f6); - expect(decodedStruct.f7).toEqual(mapFieldsStruct.f7); - expect(decodedStruct.f8).toEqual(mapFieldsStruct.f8); - expect(decodedStruct.f9).toEqual(mapFieldsStruct.f9); - expect(decodedStruct.f10).toEqual(mapFieldsStruct.f10); - expect(decodedStruct.f11).toEqual(mapFieldsStruct.f11); - } - - function testFloatNumberValues() { - var decodedStruct = structEncodeDecode(new testStructs.FloatNumberValues); - expect(decodedStruct.f0).toEqual(testStructs.FloatNumberValues.V0); - expect(decodedStruct.f1).toEqual(testStructs.FloatNumberValues.V1); - expect(decodedStruct.f2).toEqual(testStructs.FloatNumberValues.V2); - expect(decodedStruct.f3).toEqual(testStructs.FloatNumberValues.V3); - expect(decodedStruct.f4).toEqual(testStructs.FloatNumberValues.V4); - expect(decodedStruct.f5).toEqual(testStructs.FloatNumberValues.V5); - expect(decodedStruct.f6).toEqual(testStructs.FloatNumberValues.V6); - expect(decodedStruct.f7).toEqual(testStructs.FloatNumberValues.V7); - expect(decodedStruct.f8).toEqual(testStructs.FloatNumberValues.V8); - expect(decodedStruct.f9).toEqual(testStructs.FloatNumberValues.V9); - } - - function testIntegerNumberValues() { - var decodedStruct = structEncodeDecode(new testStructs.IntegerNumberValues); - expect(decodedStruct.f0).toEqual(testStructs.IntegerNumberValues.V0); - expect(decodedStruct.f1).toEqual(testStructs.IntegerNumberValues.V1); - expect(decodedStruct.f2).toEqual(testStructs.IntegerNumberValues.V2); - expect(decodedStruct.f3).toEqual(testStructs.IntegerNumberValues.V3); - expect(decodedStruct.f4).toEqual(testStructs.IntegerNumberValues.V4); - expect(decodedStruct.f5).toEqual(testStructs.IntegerNumberValues.V5); - expect(decodedStruct.f6).toEqual(testStructs.IntegerNumberValues.V6); - expect(decodedStruct.f7).toEqual(testStructs.IntegerNumberValues.V7); - expect(decodedStruct.f8).toEqual(testStructs.IntegerNumberValues.V8); - expect(decodedStruct.f9).toEqual(testStructs.IntegerNumberValues.V9); - expect(decodedStruct.f10).toEqual(testStructs.IntegerNumberValues.V10); - expect(decodedStruct.f11).toEqual(testStructs.IntegerNumberValues.V11); - expect(decodedStruct.f12).toEqual(testStructs.IntegerNumberValues.V12); - expect(decodedStruct.f13).toEqual(testStructs.IntegerNumberValues.V13); - expect(decodedStruct.f14).toEqual(testStructs.IntegerNumberValues.V14); - expect(decodedStruct.f15).toEqual(testStructs.IntegerNumberValues.V15); - expect(decodedStruct.f16).toEqual(testStructs.IntegerNumberValues.V16); - expect(decodedStruct.f17).toEqual(testStructs.IntegerNumberValues.V17); - expect(decodedStruct.f18).toEqual(testStructs.IntegerNumberValues.V18); - expect(decodedStruct.f19).toEqual(testStructs.IntegerNumberValues.V19); - } - - function testUnsignedNumberValues() { - var decodedStruct = - structEncodeDecode(new testStructs.UnsignedNumberValues); - expect(decodedStruct.f0).toEqual(testStructs.UnsignedNumberValues.V0); - expect(decodedStruct.f1).toEqual(testStructs.UnsignedNumberValues.V1); - expect(decodedStruct.f2).toEqual(testStructs.UnsignedNumberValues.V2); - expect(decodedStruct.f3).toEqual(testStructs.UnsignedNumberValues.V3); - expect(decodedStruct.f4).toEqual(testStructs.UnsignedNumberValues.V4); - expect(decodedStruct.f5).toEqual(testStructs.UnsignedNumberValues.V5); - expect(decodedStruct.f6).toEqual(testStructs.UnsignedNumberValues.V6); - expect(decodedStruct.f7).toEqual(testStructs.UnsignedNumberValues.V7); - expect(decodedStruct.f8).toEqual(testStructs.UnsignedNumberValues.V8); - expect(decodedStruct.f9).toEqual(testStructs.UnsignedNumberValues.V9); - expect(decodedStruct.f10).toEqual(testStructs.UnsignedNumberValues.V10); - expect(decodedStruct.f11).toEqual(testStructs.UnsignedNumberValues.V11); - } - - - function testBitArrayValues() { - var bitArraysStruct = new testStructs.BitArrayValues({ - // array<bool, 1> f0; - f0: [true], - // array<bool, 7> f1; - f1: [true, false, true, false, true, false, true], - // array<bool, 9> f2; - f2: [true, false, true, false, true, false, true, false, true], - // array<bool> f3; - f3: [true, false, true, false, true, false, true, false], - // array<array<bool>> f4; - f4: [[true], [false], [true, false], [true, false, true, false]], - // array<array<bool>?> f5; - f5: [[true], null, null, [true, false, true, false]], - // array<array<bool, 2>?> f6; - f6: [[true, false], [true, false], [true, false]], - }); - var decodedStruct = structEncodeDecode(bitArraysStruct); - expect(decodedStruct.f0).toEqual(bitArraysStruct.f0); - expect(decodedStruct.f1).toEqual(bitArraysStruct.f1); - expect(decodedStruct.f2).toEqual(bitArraysStruct.f2); - expect(decodedStruct.f3).toEqual(bitArraysStruct.f3); - expect(decodedStruct.f4).toEqual(bitArraysStruct.f4); - expect(decodedStruct.f5).toEqual(bitArraysStruct.f5); - expect(decodedStruct.f6).toEqual(bitArraysStruct.f6); - } - - testConstructors(); - testNoDefaultFieldValues(); - testDefaultFieldValues(); - testScopedConstants(); - testMapKeyTypes(); - testMapValueTypes(); - testFloatNumberValues(); - testIntegerNumberValues(); - testUnsignedNumberValues(); - testBitArrayValues(); - this.result = "PASS"; -}); diff --git a/mojo/public/js/test/validation_test_input_parser.js b/mojo/public/js/test/validation_test_input_parser.js deleted file mode 100644 index f5a57f9..0000000 --- a/mojo/public/js/test/validation_test_input_parser.js +++ /dev/null @@ -1,299 +0,0 @@ -// 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. - -// Support for parsing binary sequences encoded as readable strings -// or ".data" files. The input format is described here: -// mojo/public/cpp/bindings/tests/validation_test_input_parser.h - -define([ - "mojo/public/js/buffer" - ], function(buffer) { - - // Files and Lines represent the raw text from an input string - // or ".data" file. - - function InputError(message, line) { - this.message = message; - this.line = line; - } - - InputError.prototype.toString = function() { - var s = 'Error: ' + this.message; - if (this.line) - s += ', at line ' + - (this.line.number + 1) + ': "' + this.line.contents + '"'; - return s; - } - - function File(contents) { - this.contents = contents; - this.index = 0; - this.lineNumber = 0; - } - - File.prototype.endReached = function() { - return this.index >= this.contents.length; - } - - File.prototype.nextLine = function() { - if (this.endReached()) - return null; - var start = this.index; - var end = this.contents.indexOf('\n', start); - if (end == -1) - end = this.contents.length; - this.index = end + 1; - return new Line(this.contents.substring(start, end), this.lineNumber++); - } - - function Line(contents, number) { - var i = contents.indexOf('//'); - var s = (i == -1) ? contents.trim() : contents.substring(0, i).trim(); - this.contents = contents; - this.items = (s.length > 0) ? s.split(/\s+/) : []; - this.index = 0; - this.number = number; - } - - Line.prototype.endReached = function() { - return this.index >= this.items.length; - } - - var ITEM_TYPE_SIZES = { - u1: 1, u2: 2, u4: 4, u8: 8, s1: 1, s2: 2, s4: 4, s8: 8, b: 1, f: 4, d: 8, - dist4: 4, dist8: 8, anchr: 0, handles: 0 - }; - - function isValidItemType(type) { - return ITEM_TYPE_SIZES[type] !== undefined; - } - - Line.prototype.nextItem = function() { - if (this.endReached()) - return null; - - var itemString = this.items[this.index++]; - var type = 'u1'; - var value = itemString; - - if (itemString.charAt(0) == '[') { - var i = itemString.indexOf(']'); - if (i != -1 && i + 1 < itemString.length) { - type = itemString.substring(1, i); - value = itemString.substring(i + 1); - } else { - throw new InputError('invalid item', this); - } - } - if (!isValidItemType(type)) - throw new InputError('invalid item type', this); - - return new Item(this, type, value); - } - - // The text for each whitespace delimited binary data "item" is represented - // by an Item. - - function Item(line, type, value) { - this.line = line; - this.type = type; - this.value = value; - this.size = ITEM_TYPE_SIZES[type]; - } - - Item.prototype.isFloat = function() { - return this.type == 'f' || this.type == 'd'; - } - - Item.prototype.isInteger = function() { - return ['u1', 'u2', 'u4', 'u8', - 's1', 's2', 's4', 's8'].indexOf(this.type) != -1; - } - - Item.prototype.isNumber = function() { - return this.isFloat() || this.isInteger(); - } - - Item.prototype.isByte = function() { - return this.type == 'b'; - } - - Item.prototype.isDistance = function() { - return this.type == 'dist4' || this.type == 'dist8'; - } - - Item.prototype.isAnchor = function() { - return this.type == 'anchr'; - } - - Item.prototype.isHandles = function() { - return this.type == 'handles'; - } - - // A TestMessage represents the complete binary message loaded from an input - // string or ".data" file. The parseTestMessage() function below constructs - // a TestMessage from a File. - - function TestMessage(byteLength) { - this.index = 0; - this.buffer = new buffer.Buffer(byteLength); - this.distances = {}; - this.handleCount = 0; - } - - function checkItemNumberValue(item, n, min, max) { - if (n < min || n > max) - throw new InputError('invalid item value', item.line); - } - - TestMessage.prototype.addNumber = function(item) { - var n = item.isInteger() ? parseInt(item.value) : parseFloat(item.value); - if (Number.isNaN(n)) - throw new InputError("can't parse item value", item.line); - - switch(item.type) { - case 'u1': - checkItemNumberValue(item, n, 0, 0xFF); - this.buffer.setUint8(this.index, n); - break; - case 'u2': - checkItemNumberValue(item, n, 0, 0xFFFF); - this.buffer.setUint16(this.index, n); - break; - case 'u4': - checkItemNumberValue(item, n, 0, 0xFFFFFFFF); - this.buffer.setUint32(this.index, n); - break; - case 'u8': - checkItemNumberValue(item, n, 0, Number.MAX_SAFE_INTEGER); - this.buffer.setUint64(this.index, n); - break; - case 's1': - checkItemNumberValue(item, n, -128, 127); - this.buffer.setInt8(this.index, n); - break; - case 's2': - checkItemNumberValue(item, n, -32768, 32767); - this.buffer.setInt16(this.index, n); - break; - case 's4': - checkItemNumberValue(item, n, -2147483648, 2147483647); - this.buffer.setInt32(this.index, n); - break; - case 's8': - checkItemNumberValue(item, n, - Number.MIN_SAFE_INTEGER, - Number.MAX_SAFE_INTEGER); - this.buffer.setInt64(this.index, n); - break; - case 'f': - this.buffer.setFloat32(this.index, n); - break; - case 'd': - this.buffer.setFloat64(this.index, n); - break; - - default: - throw new InputError('unrecognized item type', item.line); - } - } - - TestMessage.prototype.addByte = function(item) { - if (!/^[01]{8}$/.test(item.value)) - throw new InputError('invalid byte item value', item.line); - function b(i) { - return (item.value.charAt(7 - i) == '1') ? 1 << i : 0; - } - var n = b(0) | b(1) | b(2) | b(3) | b(4) | b(5) | b(6) | b(7); - this.buffer.setUint8(this.index, n); - } - - TestMessage.prototype.addDistance = function(item) { - if (this.distances[item.value]) - throw new InputError('duplicate distance item', item.line); - this.distances[item.value] = {index: this.index, item: item}; - } - - TestMessage.prototype.addAnchor = function(item) { - var dist = this.distances[item.value]; - if (!dist) - throw new InputError('unmatched anchor item', item.line); - delete this.distances[item.value]; - - var n = this.index - dist.index; - // TODO(hansmuller): validate n - - if (dist.item.type == 'dist4') - this.buffer.setUint32(dist.index, n); - else if (dist.item.type == 'dist8') - this.buffer.setUint64(dist.index, n); - else - throw new InputError('unrecognzed distance item type', dist.item.line); - } - - TestMessage.prototype.addHandles = function(item) { - this.handleCount = parseInt(item.value); - if (Number.isNaN(this.handleCount)) - throw new InputError("can't parse handleCount", item.line); - } - - TestMessage.prototype.addItem = function(item) { - if (item.isNumber()) - this.addNumber(item); - else if (item.isByte()) - this.addByte(item); - else if (item.isDistance()) - this.addDistance(item); - else if (item.isAnchor()) - this.addAnchor(item); - else if (item.isHandles()) - this.addHandles(item); - else - throw new InputError('unrecognized item type', item.line); - - this.index += item.size; - } - - TestMessage.prototype.unanchoredDistances = function() { - var names = null; - for (var name in this.distances) { - if (this.distances.hasOwnProperty(name)) - names = (names === null) ? name : names + ' ' + name; - } - return names; - } - - function parseTestMessage(text) { - var file = new File(text); - var items = []; - var messageLength = 0; - while(!file.endReached()) { - var line = file.nextLine(); - while (!line.endReached()) { - var item = line.nextItem(); - if (item.isHandles() && items.length > 0) - throw new InputError('handles item is not first'); - messageLength += item.size; - items.push(item); - } - } - - var msg = new TestMessage(messageLength); - for (var i = 0; i < items.length; i++) - msg.addItem(items[i]); - - if (messageLength != msg.index) - throw new InputError('failed to compute message length'); - var names = msg.unanchoredDistances(); - if (names) - throw new InputError('no anchors for ' + names, 0); - - return msg; - } - - var exports = {}; - exports.parseTestMessage = parseTestMessage; - exports.InputError = InputError; - return exports; -}); diff --git a/mojo/public/js/threading.js b/mojo/public/js/threading.js index cfe5037..49ab5c9 100644 --- a/mojo/public/js/threading.js +++ b/mojo/public/js/threading.js @@ -7,7 +7,7 @@ // Note: This file is for documentation purposes only. The code here is not // actually executed. The real module is implemented natively in Mojo. // -// This module provides a way for a Mojo application implemented in JS +// This module provides a way for a Service implemented in JS // to exit by quitting the current message loop. This module is not // intended to be used by Mojo JS application started by the JS // content handler. diff --git a/mojo/public/js/union_unittests.js b/mojo/public/js/union_unittests.js deleted file mode 100644 index 5dcda7d..0000000 --- a/mojo/public/js/union_unittests.js +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright 2015 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/interfaces/bindings/tests/test_unions.mojom", - "mojo/public/js/codec", - "mojo/public/js/validator", -], function(expect, - unions, - codec, - validator) { - function testConstructors() { - var u = new unions.PodUnion(); - expect(u.$data).toEqual(null); - expect(u.$tag).toBeUndefined(); - - u.f_uint32 = 32; - - expect(u.f_uint32).toEqual(32); - expect(u.$tag).toEqual(unions.PodUnion.Tags.f_uint32); - - var u = new unions.PodUnion({f_uint64: 64}); - expect(u.f_uint64).toEqual(64); - expect(u.$tag).toEqual(unions.PodUnion.Tags.f_uint64); - expect(function() {var v = u.f_uint32;}).toThrow(); - - expect(function() { - var u = new unions.PodUnion({ - f_uint64: 64, - f_uint32: 32, - }); - }).toThrow(); - - expect(function() { - var u = new unions.PodUnion({ foo: 64 }); }).toThrow(); - - expect(function() { - var u = new unions.PodUnion([1,2,3,4]); }).toThrow(); - } - - function structEncodeDecode(struct) { - var structClass = struct.constructor; - var builder = new codec.MessageBuilder(1234, structClass.encodedSize); - builder.encodeStruct(structClass, struct); - - var message = builder.finish(); - - var messageValidator = new validator.Validator(message); - var err = structClass.validate(messageValidator, codec.kMessageHeaderSize); - expect(err).toEqual(validator.validationError.NONE); - - var reader = new codec.MessageReader(message); - var view = reader.decoder.buffer.dataView; - - return reader.decodeStruct(structClass); - } - - function testBasicEncoding() { - var s = new unions.WrapperStruct({ - pod_union: new unions.PodUnion({ - f_uint64: 64})}); - - var decoded = structEncodeDecode(s); - expect(decoded).toEqual(s); - - var s = new unions.WrapperStruct({ - object_union: new unions.ObjectUnion({ - f_dummy: new unions.DummyStruct({ - f_int8: 8})})}); - - var decoded = structEncodeDecode(s); - expect(decoded).toEqual(s); - - var s = new unions.WrapperStruct({ - object_union: new unions.ObjectUnion({ - f_array_int8: [1, 2, 3]})}); - - var decoded = structEncodeDecode(s); - expect(decoded).toEqual(s); - - var s = new unions.WrapperStruct({ - object_union: new unions.ObjectUnion({ - f_map_int8: new Map([ - ["first", 1], - ["second", 2], - ])})}); - - var decoded = structEncodeDecode(s); - expect(decoded).toEqual(s); - - // Encoding a union with no member set is an error. - var s = new unions.WrapperStruct({ - object_union: new unions.ObjectUnion()}); - expect(function() { - structEncodeDecode(s); }).toThrow(); - } - - function testUnionsInArrayEncoding() { - var s = new unions.SmallStruct({ - pod_union_array: [ - new unions.PodUnion({f_uint32: 32}), - new unions.PodUnion({f_uint64: 64}), - ] - }); - - var decoded = structEncodeDecode(s); - expect(decoded).toEqual(s); - } - - function testUnionsInMapEncoding() { - var s = new unions.SmallStruct({ - pod_union_map: new Map([ - ["thirty-two", new unions.PodUnion({f_uint32: 32})], - ["sixty-four", new unions.PodUnion({f_uint64: 64})], - ]) - }); - - var decoded = structEncodeDecode(s); - expect(decoded).toEqual(s); - } - - function testNestedUnionsEncoding() { - var s = new unions.WrapperStruct({ - object_union: new unions.ObjectUnion({ - f_pod_union: new unions.PodUnion({f_uint32: 32}) - })}); - var decoded = structEncodeDecode(s); - expect(decoded).toEqual(s); - } - - function structValidate(struct) { - var structClass = struct.constructor; - var builder = new codec.MessageBuilder(1234, structClass.encodedSize); - builder.encodeStruct(structClass, struct); - - var message = builder.finish(); - - var messageValidator = new validator.Validator(message); - return structClass.validate(messageValidator, codec.kMessageHeaderSize); - } - - function testNullUnionMemberValidation() { - var s = new unions.WrapperStruct({ - object_union: new unions.ObjectUnion({ - f_dummy: null})}); - - var err = structValidate(s); - expect(err).toEqual(validator.validationError.UNEXPECTED_NULL_POINTER); - - var s = new unions.WrapperStruct({ - object_union: new unions.ObjectUnion({ - f_nullable: null})}); - - var err = structValidate(s); - expect(err).toEqual(validator.validationError.NONE); - } - - function testNullUnionValidation() { - var s = new unions.SmallStructNonNullableUnion({ - pod_union: null}); - - var err = structValidate(s); - expect(err).toEqual(validator.validationError.UNEXPECTED_NULL_UNION); - - var s = new unions.WrapperStruct({ - object_union: new unions.ObjectUnion({ - f_pod_union: null}) - }); - - var err = structValidate(s); - expect(err).toEqual(validator.validationError.UNEXPECTED_NULL_UNION); - } - - testConstructors(); - testBasicEncoding(); - testUnionsInArrayEncoding(); - testUnionsInMapEncoding(); - testNestedUnionsEncoding(); - testNullUnionMemberValidation(); - testNullUnionValidation(); - this.result = "PASS"; -}); diff --git a/mojo/public/js/validation_unittests.js b/mojo/public/js/validation_unittests.js deleted file mode 100644 index 817d42c..0000000 --- a/mojo/public/js/validation_unittests.js +++ /dev/null @@ -1,349 +0,0 @@ -// 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. - -define([ - "console", - "file", - "gin/test/expect", - "mojo/public/interfaces/bindings/tests/validation_test_interfaces.mojom", - "mojo/public/js/buffer", - "mojo/public/js/codec", - "mojo/public/js/connection", - "mojo/public/js/connector", - "mojo/public/js/core", - "mojo/public/js/test/validation_test_input_parser", - "mojo/public/js/router", - "mojo/public/js/validator", -], function(console, - file, - expect, - testInterface, - buffer, - codec, - connection, - connector, - core, - parser, - router, - validator) { - - var noError = validator.validationError.NONE; - - function checkTestMessageParser() { - function TestMessageParserFailure(message, input) { - this.message = message; - this.input = input; - } - - TestMessageParserFailure.prototype.toString = function() { - return 'Error: ' + this.message + ' for "' + this.input + '"'; - } - - function checkData(data, expectedData, input) { - if (data.byteLength != expectedData.byteLength) { - var s = "message length (" + data.byteLength + ") doesn't match " + - "expected length: " + expectedData.byteLength; - throw new TestMessageParserFailure(s, input); - } - - for (var i = 0; i < data.byteLength; i++) { - if (data.getUint8(i) != expectedData.getUint8(i)) { - var s = 'message data mismatch at byte offset ' + i; - throw new TestMessageParserFailure(s, input); - } - } - } - - function testFloatItems() { - var input = '[f]+.3e9 [d]-10.03'; - var msg = parser.parseTestMessage(input); - var expectedData = new buffer.Buffer(12); - expectedData.setFloat32(0, +.3e9); - expectedData.setFloat64(4, -10.03); - checkData(msg.buffer, expectedData, input); - } - - function testUnsignedIntegerItems() { - var input = '[u1]0x10// hello world !! \n\r \t [u2]65535 \n' + - '[u4]65536 [u8]0xFFFFFFFFFFFFF 0 0Xff'; - var msg = parser.parseTestMessage(input); - var expectedData = new buffer.Buffer(17); - expectedData.setUint8(0, 0x10); - expectedData.setUint16(1, 65535); - expectedData.setUint32(3, 65536); - expectedData.setUint64(7, 0xFFFFFFFFFFFFF); - expectedData.setUint8(15, 0); - expectedData.setUint8(16, 0xff); - checkData(msg.buffer, expectedData, input); - } - - function testSignedIntegerItems() { - var input = '[s8]-0x800 [s1]-128\t[s2]+0 [s4]-40'; - var msg = parser.parseTestMessage(input); - var expectedData = new buffer.Buffer(15); - expectedData.setInt64(0, -0x800); - expectedData.setInt8(8, -128); - expectedData.setInt16(9, 0); - expectedData.setInt32(11, -40); - checkData(msg.buffer, expectedData, input); - } - - function testByteItems() { - var input = '[b]00001011 [b]10000000 // hello world\n [b]00000000'; - var msg = parser.parseTestMessage(input); - var expectedData = new buffer.Buffer(3); - expectedData.setUint8(0, 11); - expectedData.setUint8(1, 128); - expectedData.setUint8(2, 0); - checkData(msg.buffer, expectedData, input); - } - - function testAnchors() { - var input = '[dist4]foo 0 [dist8]bar 0 [anchr]foo [anchr]bar'; - var msg = parser.parseTestMessage(input); - var expectedData = new buffer.Buffer(14); - expectedData.setUint32(0, 14); - expectedData.setUint8(4, 0); - expectedData.setUint64(5, 9); - expectedData.setUint8(13, 0); - checkData(msg.buffer, expectedData, input); - } - - function testHandles() { - var input = '// This message has handles! \n[handles]50 [u8]2'; - var msg = parser.parseTestMessage(input); - var expectedData = new buffer.Buffer(8); - expectedData.setUint64(0, 2); - - if (msg.handleCount != 50) { - var s = 'wrong handle count (' + msg.handleCount + ')'; - throw new TestMessageParserFailure(s, input); - } - checkData(msg.buffer, expectedData, input); - } - - function testEmptyInput() { - var msg = parser.parseTestMessage(''); - if (msg.buffer.byteLength != 0) - throw new TestMessageParserFailure('expected empty message', ''); - } - - function testBlankInput() { - var input = ' \t // hello world \n\r \t// the answer is 42 '; - var msg = parser.parseTestMessage(input); - if (msg.buffer.byteLength != 0) - throw new TestMessageParserFailure('expected empty message', input); - } - - function testInvalidInput() { - function parserShouldFail(input) { - try { - parser.parseTestMessage(input); - } catch (e) { - if (e instanceof parser.InputError) - return; - throw new TestMessageParserFailure( - 'unexpected exception ' + e.toString(), input); - } - throw new TestMessageParserFailure("didn't detect invalid input", file); - } - - ['/ hello world', - '[u1]x', - '[u2]-1000', - '[u1]0x100', - '[s2]-0x8001', - '[b]1', - '[b]1111111k', - '[dist4]unmatched', - '[anchr]hello [dist8]hello', - '[dist4]a [dist4]a [anchr]a', - // '[dist4]a [anchr]a [dist4]a [anchr]a', - '0 [handles]50' - ].forEach(parserShouldFail); - } - - try { - testFloatItems(); - testUnsignedIntegerItems(); - testSignedIntegerItems(); - testByteItems(); - testInvalidInput(); - testEmptyInput(); - testBlankInput(); - testHandles(); - testAnchors(); - } catch (e) { - return e.toString(); - } - return null; - } - - function getMessageTestFiles(prefix) { - var sourceRoot = file.getSourceRootDirectory(); - expect(sourceRoot).not.toBeNull(); - - var testDir = sourceRoot + - "/mojo/public/interfaces/bindings/tests/data/validation/"; - var testFiles = file.getFilesInDirectory(testDir); - expect(testFiles).not.toBeNull(); - expect(testFiles.length).toBeGreaterThan(0); - - // The matching ".data" pathnames with the extension removed. - return testFiles.filter(function(s) { - return s.substr(-5) == ".data" && s.indexOf(prefix) == 0; - }).map(function(s) { - return testDir + s.slice(0, -5); - }); - } - - function readTestMessage(filename) { - var contents = file.readFileToString(filename + ".data"); - expect(contents).not.toBeNull(); - return parser.parseTestMessage(contents); - } - - function readTestExpected(filename) { - var contents = file.readFileToString(filename + ".expected"); - expect(contents).not.toBeNull(); - return contents.trim(); - } - - function checkValidationResult(testFile, err) { - var actualResult = (err === noError) ? "PASS" : err; - var expectedResult = readTestExpected(testFile); - if (actualResult != expectedResult) - console.log("[Test message validation failed: " + testFile + " ]"); - expect(actualResult).toEqual(expectedResult); - } - - function testMessageValidation(prefix, filters) { - var testFiles = getMessageTestFiles(prefix); - expect(testFiles.length).toBeGreaterThan(0); - - for (var i = 0; i < testFiles.length; i++) { - // TODO(hansmuller) Temporarily skipping array pointer overflow tests - // because JS numbers are limited to 53 bits. - // TODO(yzshen) Skipping struct versioning tests (tests with "mthd11" - // in the name) because the feature is not supported in JS yet. - // TODO(yzshen) Skipping enum validation tests (tests with "enum" in the - // name) because the feature is not supported in JS yet. crbug.com/581390 - // TODO(rudominer): Temporarily skipping 'no-such-method', - // 'invalid_request_flags', and 'invalid_response_flags' until additional - // logic in *RequestValidator and *ResponseValidator is ported from - // cpp to js. - if (testFiles[i].indexOf("overflow") != -1 || - testFiles[i].indexOf("mthd11") != -1 || - testFiles[i].indexOf("enum") != -1 || - testFiles[i].indexOf("no_such_method") != -1 || - testFiles[i].indexOf("invalid_request_flags") != -1 || - testFiles[i].indexOf("invalid_response_flags") != -1) { - console.log("[Skipping " + testFiles[i] + "]"); - continue; - } - - var testMessage = readTestMessage(testFiles[i]); - var handles = new Array(testMessage.handleCount); - var message = new codec.Message(testMessage.buffer, handles); - var messageValidator = new validator.Validator(message); - - var err = messageValidator.validateMessageHeader(); - for (var j = 0; err === noError && j < filters.length; ++j) - err = filters[j](messageValidator); - - checkValidationResult(testFiles[i], err); - } - } - - function testConformanceMessageValidation() { - testMessageValidation("conformance_", [ - testInterface.ConformanceTestInterface.validateRequest]); - } - - function testBoundsCheckMessageValidation() { - testMessageValidation("boundscheck_", [ - testInterface.BoundsCheckTestInterface.validateRequest]); - } - - function testResponseConformanceMessageValidation() { - testMessageValidation("resp_conformance_", [ - testInterface.ConformanceTestInterface.validateResponse]); - } - - function testResponseBoundsCheckMessageValidation() { - testMessageValidation("resp_boundscheck_", [ - testInterface.BoundsCheckTestInterface.validateResponse]); - } - - function testIntegratedMessageValidation(testFilesPattern, - localFactory, - remoteFactory) { - var testFiles = getMessageTestFiles(testFilesPattern); - expect(testFiles.length).toBeGreaterThan(0); - - var testMessagePipe = core.createMessagePipe(); - expect(testMessagePipe.result).toBe(core.RESULT_OK); - var testConnection = new connection.TestConnection( - testMessagePipe.handle1, localFactory, remoteFactory); - - for (var i = 0; i < testFiles.length; i++) { - var testMessage = readTestMessage(testFiles[i]); - var handles = new Array(testMessage.handleCount); - - var writeMessageValue = core.writeMessage( - testMessagePipe.handle0, - new Uint8Array(testMessage.buffer.arrayBuffer), - new Array(testMessage.handleCount), - core.WRITE_MESSAGE_FLAG_NONE); - expect(writeMessageValue).toBe(core.RESULT_OK); - - var validationError = noError; - testConnection.router_.validationErrorHandler = function(err) { - validationError = err; - } - - testConnection.router_.connector_.waitForNextMessage(); - checkValidationResult(testFiles[i], validationError); - } - - testConnection.close(); - expect(core.close(testMessagePipe.handle0)).toBe(core.RESULT_OK); - } - - function testIntegratedMessageHeaderValidation() { - testIntegratedMessageValidation( - "integration_msghdr", - testInterface.IntegrationTestInterface.stubClass, - undefined); - testIntegratedMessageValidation( - "integration_msghdr", - undefined, - testInterface.IntegrationTestInterface.proxyClass); - } - - function testIntegratedRequestMessageValidation() { - testIntegratedMessageValidation( - "integration_intf_rqst", - testInterface.IntegrationTestInterface.stubClass, - undefined); - } - - function testIntegratedResponseMessageValidation() { - testIntegratedMessageValidation( - "integration_intf_resp", - undefined, - testInterface.IntegrationTestInterface.proxyClass); - } - - expect(checkTestMessageParser()).toBeNull(); - testConformanceMessageValidation(); - testBoundsCheckMessageValidation(); - testResponseConformanceMessageValidation(); - testResponseBoundsCheckMessageValidation(); - testIntegratedMessageHeaderValidation(); - testIntegratedResponseMessageValidation(); - testIntegratedRequestMessageValidation(); - - this.result = "PASS"; -}); diff --git a/mojo/public/js/validator.js b/mojo/public/js/validator.js index cbf7521..fee742d 100644 --- a/mojo/public/js/validator.js +++ b/mojo/public/js/validator.js @@ -24,10 +24,15 @@ define("mojo/public/js/validator", [ 'VALIDATION_ERROR_DIFFERENT_SIZED_ARRAYS_IN_MAP', INVALID_UNION_SIZE: 'VALIDATION_ERROR_INVALID_UNION_SIZE', UNEXPECTED_NULL_UNION: 'VALIDATION_ERROR_UNEXPECTED_NULL_UNION', + UNKNOWN_ENUM_VALUE: 'VALIDATION_ERROR_UNKNOWN_ENUM_VALUE', }; var NULL_MOJO_POINTER = "NULL_MOJO_POINTER"; + function isEnumClass(cls) { + return cls instanceof codec.Enum; + } + function isStringClass(cls) { return cls === codec.String || cls === codec.NullableString; } @@ -37,12 +42,18 @@ define("mojo/public/js/validator", [ } function isInterfaceClass(cls) { - return cls === codec.Interface || cls === codec.NullableInterface; + return cls instanceof codec.Interface; + } + + function isInterfaceRequestClass(cls) { + return cls === codec.InterfaceRequest || + cls === codec.NullableInterfaceRequest; } function isNullable(type) { return type === codec.NullableString || type === codec.NullableHandle || type === codec.NullableInterface || + type === codec.NullableInterfaceRequest || type instanceof codec.NullableArrayOf || type instanceof codec.NullablePointerTo; } @@ -76,7 +87,7 @@ define("mojo/public/js/validator", [ return false; return true; - } + }; Validator.prototype.claimRange = function(start, numBytes) { if (this.isValidRange(start, numBytes)) { @@ -84,7 +95,7 @@ define("mojo/public/js/validator", [ return true; } return false; - } + }; Validator.prototype.claimHandle = function(index) { if (index === codec.kEncodedInvalidHandleValue) @@ -96,6 +107,13 @@ define("mojo/public/js/validator", [ // This is safe because handle indices are uint32. this.handleIndex = index + 1; return true; + }; + + Validator.prototype.validateEnum = function(offset, enumClass) { + // Note: Assumes that enums are always 32 bits! But this matches + // mojom::generate::pack::PackedField::GetSizeForKind, so it should be okay. + var value = this.message.buffer.getInt32(offset); + return enumClass.validate(value); } Validator.prototype.validateHandle = function(offset, nullable) { @@ -107,15 +125,19 @@ define("mojo/public/js/validator", [ if (!this.claimHandle(index)) return validationError.ILLEGAL_HANDLE; + return validationError.NONE; - } + }; Validator.prototype.validateInterface = function(offset, nullable) { return this.validateHandle(offset, nullable); - } + }; + + Validator.prototype.validateInterfaceRequest = function(offset, nullable) { + return this.validateHandle(offset, nullable); + }; - Validator.prototype.validateStructHeader = - function(offset, minNumBytes, minVersion) { + Validator.prototype.validateStructHeader = function(offset, minNumBytes) { if (!codec.isAligned(offset)) return validationError.MISALIGNED_OBJECT; @@ -123,20 +145,44 @@ define("mojo/public/js/validator", [ return validationError.ILLEGAL_MEMORY_RANGE; var numBytes = this.message.buffer.getUint32(offset); - var version = this.message.buffer.getUint32(offset + 4); - // Backward compatibility is not yet supported. - if (numBytes < minNumBytes || version < minVersion) + if (numBytes < minNumBytes) return validationError.UNEXPECTED_STRUCT_HEADER; if (!this.claimRange(offset, numBytes)) return validationError.ILLEGAL_MEMORY_RANGE; return validationError.NONE; - } + }; + + Validator.prototype.validateStructVersion = function(offset, versionSizes) { + var numBytes = this.message.buffer.getUint32(offset); + var version = this.message.buffer.getUint32(offset + 4); + + if (version <= versionSizes[versionSizes.length - 1].version) { + // Scan in reverse order to optimize for more recent versionSizes. + for (var i = versionSizes.length - 1; i >= 0; --i) { + if (version >= versionSizes[i].version) { + if (numBytes == versionSizes[i].numBytes) + break; + return validationError.UNEXPECTED_STRUCT_HEADER; + } + } + } else if (numBytes < versionSizes[versionSizes.length-1].numBytes) { + return validationError.UNEXPECTED_STRUCT_HEADER; + } + + return validationError.NONE; + }; + + Validator.prototype.isFieldInStructVersion = function(offset, fieldVersion) { + var structVersion = this.message.buffer.getUint32(offset + 4); + return fieldVersion <= structVersion; + }; Validator.prototype.validateMessageHeader = function() { - var err = this.validateStructHeader(0, codec.kMessageHeaderSize, 0); + + var err = this.validateStructHeader(0, codec.kMessageHeaderSize); if (err != validationError.NONE) return err; @@ -162,7 +208,28 @@ define("mojo/public/js/validator", [ return validationError.MESSAGE_HEADER_INVALID_FLAGS; return validationError.NONE; - } + }; + + Validator.prototype.validateMessageIsRequestWithoutResponse = function() { + if (this.message.isResponse() || this.message.expectsResponse()) { + return validationError.MESSAGE_HEADER_INVALID_FLAGS; + } + return validationError.NONE; + }; + + Validator.prototype.validateMessageIsRequestExpectingResponse = function() { + if (this.message.isResponse() || !this.message.expectsResponse()) { + return validationError.MESSAGE_HEADER_INVALID_FLAGS; + } + return validationError.NONE; + }; + + Validator.prototype.validateMessageIsResponse = function() { + if (this.message.expectsResponse() || !this.message.isResponse()) { + return validationError.MESSAGE_HEADER_INVALID_FLAGS; + } + return validationError.NONE; + }; // Returns the message.buffer relative offset this pointer "points to", // NULL_MOJO_POINTER if the pointer represents a null, or JS null if the @@ -173,7 +240,7 @@ define("mojo/public/js/validator", [ return NULL_MOJO_POINTER; var bufferOffset = offset + pointerValue; return Number.isSafeInteger(bufferOffset) ? bufferOffset : null; - } + }; Validator.prototype.decodeUnionSize = function(offset) { return this.message.buffer.getUint32(offset); @@ -196,7 +263,7 @@ define("mojo/public/js/validator", [ return this.validateArray(arrayOffset, elementSize, elementType, expectedDimensionSizes, currentDimension); - } + }; Validator.prototype.validateStructPointer = function( offset, structClass, nullable) { @@ -209,7 +276,7 @@ define("mojo/public/js/validator", [ validationError.NONE : validationError.UNEXPECTED_NULL_POINTER; return structClass.validate(this, structOffset); - } + }; Validator.prototype.validateUnion = function( offset, unionClass, nullable) { @@ -220,7 +287,7 @@ define("mojo/public/js/validator", [ } return unionClass.validate(this, offset); - } + }; Validator.prototype.validateNestedUnion = function( offset, unionClass, nullable) { @@ -233,7 +300,7 @@ define("mojo/public/js/validator", [ validationError.NONE : validationError.UNEXPECTED_NULL_UNION; return this.validateUnion(unionOffset, unionClass, nullable); - } + }; // This method assumes that the array at arrayPointerOffset has // been validated. @@ -241,7 +308,7 @@ define("mojo/public/js/validator", [ Validator.prototype.arrayLength = function(arrayPointerOffset) { var arrayOffset = this.decodePointer(arrayPointerOffset); return this.message.buffer.getUint32(arrayOffset + 4); - } + }; Validator.prototype.validateMapPointer = function( offset, mapIsNullable, keyClass, valueClass, valueIsNullable) { @@ -256,7 +323,7 @@ define("mojo/public/js/validator", [ validationError.NONE : validationError.UNEXPECTED_NULL_POINTER; var mapEncodedSize = codec.kStructHeaderSize + codec.kMapStructPayloadSize; - var err = this.validateStructHeader(structOffset, mapEncodedSize, 0); + var err = this.validateStructHeader(structOffset, mapEncodedSize); if (err !== validationError.NONE) return err; @@ -289,12 +356,12 @@ define("mojo/public/js/validator", [ return validationError.DIFFERENT_SIZED_ARRAYS_IN_MAP; return validationError.NONE; - } + }; Validator.prototype.validateStringPointer = function(offset, nullable) { return this.validateArrayPointer( offset, codec.Uint8.encodedSize, codec.Uint8, nullable, [0], 0); - } + }; // Similar to Array_Data<T>::Validate() // mojo/public/cpp/bindings/lib/array_internal.h @@ -337,6 +404,9 @@ define("mojo/public/js/validator", [ if (isInterfaceClass(elementType)) return this.validateInterfaceElements( elementsOffset, numElements, nullable); + if (isInterfaceRequestClass(elementType)) + return this.validateInterfaceRequestElements( + elementsOffset, numElements, nullable); if (isStringClass(elementType)) return this.validateArrayElements( elementsOffset, numElements, codec.Uint8, nullable, [0], 0); @@ -347,9 +417,12 @@ define("mojo/public/js/validator", [ return this.validateArrayElements( elementsOffset, numElements, elementType.cls, nullable, expectedDimensionSizes, currentDimension + 1); + if (isEnumClass(elementType)) + return this.validateEnumElements(elementsOffset, numElements, + elementType.cls); return validationError.NONE; - } + }; // Note: the |offset + i * elementSize| computation in the validateFooElements // methods below is "safe" because elementSize <= 8, offset and @@ -365,11 +438,11 @@ define("mojo/public/js/validator", [ return err; } return validationError.NONE; - } + }; Validator.prototype.validateInterfaceElements = function(offset, numElements, nullable) { - var elementSize = codec.Interface.encodedSize; + var elementSize = codec.Interface.prototype.encodedSize; for (var i = 0; i < numElements; i++) { var elementOffset = offset + i * elementSize; var err = this.validateInterface(elementOffset, nullable); @@ -377,7 +450,19 @@ define("mojo/public/js/validator", [ return err; } return validationError.NONE; - } + }; + + Validator.prototype.validateInterfaceRequestElements = + function(offset, numElements, nullable) { + var elementSize = codec.InterfaceRequest.encodedSize; + for (var i = 0; i < numElements; i++) { + var elementOffset = offset + i * elementSize; + var err = this.validateInterfaceRequest(elementOffset, nullable); + if (err != validationError.NONE) + return err; + } + return validationError.NONE; + }; // The elementClass parameter is the element type of the element arrays. Validator.prototype.validateArrayElements = @@ -393,7 +478,7 @@ define("mojo/public/js/validator", [ return err; } return validationError.NONE; - } + }; Validator.prototype.validateStructElements = function(offset, numElements, structClass, nullable) { @@ -406,7 +491,19 @@ define("mojo/public/js/validator", [ return err; } return validationError.NONE; - } + }; + + Validator.prototype.validateEnumElements = + function(offset, numElements, enumClass) { + var elementSize = codec.Enum.prototype.encodedSize; + for (var i = 0; i < numElements; i++) { + var elementOffset = offset + i * elementSize; + var err = this.validateEnum(elementOffset, enumClass); + if (err != validationError.NONE) + return err; + } + return validationError.NONE; + }; var exports = {}; exports.validationError = validationError; |