Revert "libmojo: Uprev the library to r456626 from Chromium"
This reverts commit 8ac9103e05b66812c25348943383f9365d1ce3e0.
Reason for revert: Broke the mac_sdk
Exempt-From-Owner-Approval: Fixing mac_sdk
Change-Id: I0b74d1abaa66933a93fd6f82ff018e8948c1204e
diff --git a/mojo/public/js/BUILD.gn b/mojo/public/js/BUILD.gn
index 0fae4b4..eda7e04 100644
--- a/mojo/public/js/BUILD.gn
+++ b/mojo/public/js/BUILD.gn
@@ -2,8 +2,6 @@
# 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",
@@ -13,43 +11,34 @@
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 f3e40d2..2fdcae3 100644
--- a/mojo/public/js/bindings.js
+++ b/mojo/public/js/bindings.js
@@ -3,283 +3,115 @@
// found in the LICENSE file.
define("mojo/public/js/bindings", [
- "mojo/public/js/core",
- "mojo/public/js/lib/control_message_proxy",
- "mojo/public/js/interface_types",
"mojo/public/js/router",
-], function(core, controlMessageProxy, types, router) {
+ "mojo/public/js/core",
+], function(router, core) {
- // ---------------------------------------------------------------------------
+ var Router = router.Router;
- function makeRequest(interfacePtr) {
- var pipe = core.createMessagePipe();
- interfacePtr.ptr.bind(new types.InterfacePtrInfo(pipe.handle0, 0));
- return new types.InterfaceRequest(pipe.handle1);
+ 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;
}
- // ---------------------------------------------------------------------------
-
- // 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;
-
- this.interfaceType_ = interfaceType;
- this.router_ = null;
- this.proxy_ = null;
-
- // |router_| is lazily initialized. |handle_| is valid between bind() and
- // the initialization of |router_|.
- this.handle_ = null;
- this.controlMessageProxy_ = null;
-
- if (ptrInfoOrHandle)
- this.bind(ptrInfoOrHandle);
+ // TODO(hansmuller): remove then after 'Client=' has been removed from Mojom.
+ ProxyProperties.prototype.getLocalDelegate = function() {
+ return this.local && StubBindings(this.local).delegate;
}
- InterfacePtrController.prototype.bind = function(ptrInfoOrHandle) {
- this.reset();
+ // 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");
+ }
- if (ptrInfoOrHandle instanceof types.InterfacePtrInfo) {
- this.version = ptrInfoOrHandle.version;
- this.handle_ = ptrInfoOrHandle.handle;
- } else {
- this.handle_ = ptrInfoOrHandle;
- }
- };
+ ProxyProperties.prototype.close = function() {
+ this.connection.close();
+ }
- InterfacePtrController.prototype.isBound = function() {
- return this.router_ !== null || this.handle_ !== null;
- };
+ // Public stub class properties that are managed at runtime by the JS
+ // bindings. See StubBindings below.
+ function StubProperties(delegate) {
+ this.delegate = delegate;
+ }
- // 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;
+ StubProperties.prototype.close = function() {
+ this.connection.close();
+ }
- this.proxy_ = null;
- }
- if (this.handle_) {
- core.close(this.handle_);
- this.handle_ = null;
- }
- };
+ // The base class for generated proxy classes.
+ function ProxyBase(receiver) {
+ this[kProxyProperties] = new ProxyProperties(receiver);
- InterfacePtrController.prototype.setConnectionErrorHandler
- = function(callback) {
- if (!this.isBound())
- throw new Error("Cannot set connection error handler if not bound.");
+ // TODO(hansmuller): Temporary, for Chrome backwards compatibility.
+ if (receiver instanceof Router)
+ this.receiver_ = receiver;
+ }
- this.configureProxyIfNecessary_();
- this.router_.setErrorHandler(callback);
- };
+ // The base class for generated stub classes.
+ function StubBase(delegate) {
+ this[kStubProperties] = new StubProperties(delegate);
+ }
- 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;
- }
+ // TODO(hansmuller): remove everything except the connection property doc
+ // after 'Client=' has been removed from Mojom.
- 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.
+ // 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.
//
- // Example:
+ // 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.
//
- // // FooImpl implements mojom.Foo.
- // function FooImpl() { ... }
- // FooImpl.prototype.fooMethod1 = function() { ... }
- // FooImpl.prototype.fooMethod2 = function() { ... }
+ // ProxyBindings(proxy).local - The "local" stub object whose delegate
+ // implements the proxy's Mojo client interface.
//
- // 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;
+ // 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|.
+ //
+ // ProxyBindings(proxy).getLocalDelegate() - Returns the implementation
+ // delegate of the proxy's client stub object. This is just shorthand
+ // for |StubBindings(ProxyBindings(proxy).local).delegate|.
- if (requestOrHandle)
- this.bind(requestOrHandle);
+ function ProxyBindings(proxy) {
+ return (proxy instanceof ProxyBase) ? proxy[kProxyProperties] : proxy;
}
- Binding.prototype.isBound = function() {
- return this.router_ !== null;
- };
+ // TODO(hansmuller): remove the remote doc after 'Client=' has been
+ // removed from Mojom.
- 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;
+ // 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.
+
+ function StubBindings(stub) {
+ return stub instanceof StubBase ? stub[kStubProperties] : stub;
}
- 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 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.InterfacePtrInfo = types.InterfacePtrInfo;
- exports.InterfaceRequest = types.InterfaceRequest;
- exports.makeRequest = makeRequest;
- exports.InterfacePtrController = InterfacePtrController;
- exports.Binding = Binding;
- exports.BindingSet = BindingSet;
-
+ exports.EmptyProxy = ProxyBase;
+ exports.EmptyStub = StubBase;
+ exports.ProxyBase = ProxyBase;
+ exports.ProxyBindings = ProxyBindings;
+ exports.StubBase = StubBase;
+ exports.StubBindings = StubBindings;
return exports;
});
diff --git a/mojo/public/js/codec.js b/mojo/public/js/codec.js
index ff5d31a..4003b51 100644
--- a/mojo/public/js/codec.js
+++ b/mojo/public/js/codec.js
@@ -3,10 +3,9 @@
// found in the LICENSE file.
define("mojo/public/js/codec", [
- "mojo/public/js/buffer",
- "mojo/public/js/interface_types",
"mojo/public/js/unicode",
-], function(buffer, types, unicode) {
+ "mojo/public/js/buffer",
+], function(unicode, buffer) {
var kErrorUnsigned = "Passing negative value to unsigned";
var kErrorArray = "Passing non Array for array type";
@@ -304,12 +303,8 @@
};
Encoder.prototype.encodeHandle = function(handle) {
- if (handle) {
- this.handles.push(handle);
- this.writeUint32(this.handles.length - 1);
- } else {
- this.writeUint32(kEncodedInvalidHandleValue);
- }
+ this.handles.push(handle);
+ this.writeUint32(this.handles.length - 1);
};
Encoder.prototype.encodeString = function(val) {
@@ -716,20 +711,6 @@
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;
}
@@ -807,54 +788,33 @@
NullableHandle.encode = Handle.encode;
- function Interface(cls) {
- this.cls = cls;
+ function Interface() {
}
- Interface.prototype.encodedSize = 8;
+ Interface.encodedSize = 8;
- 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;
+ Interface.decode = function(decoder) {
+ var handle = decoder.decodeHandle();
+ // Ignore the version field for now.
+ decoder.readUint32();
+
+ 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);
+ function NullableInterface() {
}
- NullableInterface.prototype = Object.create(Interface.prototype);
+ NullableInterface.encodedSize = Interface.encodedSize;
- function InterfaceRequest() {
- }
+ NullableInterface.decode = Interface.decode;
- 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 NullableInterfaceRequest() {
- }
-
- NullableInterfaceRequest.encodedSize = InterfaceRequest.encodedSize;
-
- NullableInterfaceRequest.decode = InterfaceRequest.decode;
-
- NullableInterfaceRequest.encode = InterfaceRequest.encode;
+ NullableInterface.encode = Interface.encode;
function MapOf(keyClass, valueClass) {
this.keyClass = keyClass;
@@ -903,7 +863,6 @@
exports.Float = Float;
exports.Double = Double;
exports.String = String;
- exports.Enum = Enum;
exports.NullableString = NullableString;
exports.PointerTo = PointerTo;
exports.NullablePointerTo = NullablePointerTo;
@@ -914,8 +873,6 @@
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
new file mode 100644
index 0000000..b610d9a
--- /dev/null
+++ b/mojo/public/js/codec_unittests.js
@@ -0,0 +1,296 @@
+// 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
new file mode 100644
index 0000000..3f7e839
--- /dev/null
+++ b/mojo/public/js/connection.js
@@ -0,0 +1,176 @@
+// 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 ee16be8..674f36b 100644
--- a/mojo/public/js/connector.js
+++ b/mojo/public/js/connector.js
@@ -82,12 +82,6 @@
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_,
@@ -104,12 +98,29 @@
}
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 58cf274..d29f5cb 100644
--- a/mojo/public/js/constants.cc
+++ b/mojo/public/js/constants.cc
@@ -9,15 +9,10 @@
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 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 kRouterModuleName[] = "mojo/public/js/router";
const char kValidatorModuleName[] = "mojo/public/js/validator";
+
} // namespace mojo
diff --git a/mojo/public/js/constants.h b/mojo/public/js/constants.h
index 9d32d20..de75a90 100644
--- a/mojo/public/js/constants.h
+++ b/mojo/public/js/constants.h
@@ -11,13 +11,10 @@
extern const char kBindingsModuleName[];
extern const char kBufferModuleName[];
extern const char kCodecModuleName[];
+extern const char kConnectionModuleName[];
extern const char kConnectorModuleName[];
-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 kRouterModuleName[];
extern const char kValidatorModuleName[];
} // namespace mojo
diff --git a/mojo/public/js/core.js b/mojo/public/js/core.js
index ef480ee..b89a956 100644
--- a/mojo/public/js/core.js
+++ b/mojo/public/js/core.js
@@ -114,27 +114,6 @@
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.
@@ -257,57 +236,3 @@
* @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
new file mode 100644
index 0000000..12364dc
--- /dev/null
+++ b/mojo/public/js/core_unittests.js
@@ -0,0 +1,198 @@
+// 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 e94c5eb..e3db0a6 100644
--- a/mojo/public/js/router.js
+++ b/mojo/public/js/router.js
@@ -3,20 +3,17 @@
// 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(console, codec, core, connector, controlMessageHandler, validator) {
+], function(codec, core, connector, validator) {
var Connector = connector.Connector;
var MessageReader = codec.MessageReader;
var Validator = validator.Validator;
- var ControlMessageHandler = controlMessageHandler.ControlMessageHandler;
- function Router(handle, interface_version, connectorFactory) {
+ function Router(handle, connectorFactory) {
if (!core.isHandle(handle))
throw new Error("Router constructor: Not a handle");
if (connectorFactory === undefined)
@@ -27,12 +24,6 @@
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),
@@ -45,7 +36,6 @@
Router.prototype.close = function() {
this.completers_.clear(); // Drop any responders.
this.connector_.close();
- this.testingController_ = null;
};
Router.prototype.accept = function(message) {
@@ -91,11 +81,6 @@
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);
@@ -110,17 +95,8 @@
};
Router.prototype.handleValidIncomingMessage_ = function(message) {
- if (this.testingController_)
- return;
-
if (message.expectsResponse()) {
- if (controlMessageHandler.isControlMessage(message)) {
- if (this.controlMessageHandler_) {
- this.controlMessageHandler_.acceptWithResponder(message, this);
- } else {
- this.close();
- }
- } else if (this.incomingReceiver_) {
+ if (this.incomingReceiver_) {
this.incomingReceiver_.acceptWithResponder(message, this);
} else {
// If we receive a request expecting a response when the client is not
@@ -131,39 +107,17 @@
var reader = new MessageReader(message);
var requestID = reader.requestID;
var completer = this.completers_.get(requestID);
- if (completer) {
- this.completers_.delete(requestID);
- completer.resolve(message);
- } else {
- console.log("Unexpected response with request ID: " + requestID);
- }
+ this.completers_.delete(requestID);
+ completer.resolve(message);
} else {
- if (controlMessageHandler.isControlMessage(message)) {
- if (this.controlMessageHandler_) {
- var ok = this.controlMessageHandler_.accept(message);
- if (ok) return;
- }
- this.close();
- } else if (this.incomingReceiver_) {
+ if (this.incomingReceiver_)
this.incomingReceiver_.accept(message);
- }
}
- };
+ }
Router.prototype.handleInvalidIncomingMessage_ = function(message, error) {
- 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);
- };
+ this.close();
+ }
Router.prototype.handleConnectionError_ = function(result) {
this.completers_.forEach(function(value) {
@@ -174,30 +128,25 @@
this.close();
};
- // The RouterTestingController is used in unit tests. It defeats valid message
- // handling and delgates invalid message handling.
+ // The TestRouter subclass is only intended to be used in unit tests.
+ // It defeats valid message handling and delgates invalid message handling.
- function RouterTestingController(connector) {
- this.connector_ = connector;
- this.invalidMessageHandler_ = null;
+ function TestRouter(handle, connectorFactory) {
+ Router.call(this, handle, connectorFactory);
}
- RouterTestingController.prototype.waitForNextMessage = function() {
- this.connector_.waitForNextMessageForTesting();
+ TestRouter.prototype = Object.create(Router.prototype);
+
+ TestRouter.prototype.handleValidIncomingMessage_ = function() {
};
- RouterTestingController.prototype.setInvalidIncomingMessageHandler =
- function(callback) {
- this.invalidMessageHandler_ = callback;
- };
-
- RouterTestingController.prototype.onInvalidIncomingMessage =
- function(error) {
- if (this.invalidMessageHandler_)
- this.invalidMessageHandler_(error);
- };
+ TestRouter.prototype.handleInvalidIncomingMessage_ =
+ function(message, error) {
+ this.validationErrorHandler(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
new file mode 100644
index 0000000..691d51b
--- /dev/null
+++ b/mojo/public/js/struct_unittests.js
@@ -0,0 +1,279 @@
+// 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
new file mode 100644
index 0000000..f5a57f9
--- /dev/null
+++ b/mojo/public/js/test/validation_test_input_parser.js
@@ -0,0 +1,299 @@
+// 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 49ab5c9..cfe5037 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 Service implemented in JS
+// This module provides a way for a Mojo application 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
new file mode 100644
index 0000000..5dcda7d
--- /dev/null
+++ b/mojo/public/js/union_unittests.js
@@ -0,0 +1,184 @@
+// 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
new file mode 100644
index 0000000..817d42c
--- /dev/null
+++ b/mojo/public/js/validation_unittests.js
@@ -0,0 +1,349 @@
+// 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 fee742d..cbf7521 100644
--- a/mojo/public/js/validator.js
+++ b/mojo/public/js/validator.js
@@ -24,15 +24,10 @@
'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;
}
@@ -42,18 +37,12 @@
}
function isInterfaceClass(cls) {
- return cls instanceof codec.Interface;
- }
-
- function isInterfaceRequestClass(cls) {
- return cls === codec.InterfaceRequest ||
- cls === codec.NullableInterfaceRequest;
+ return cls === codec.Interface || cls === codec.NullableInterface;
}
function isNullable(type) {
return type === codec.NullableString || type === codec.NullableHandle ||
type === codec.NullableInterface ||
- type === codec.NullableInterfaceRequest ||
type instanceof codec.NullableArrayOf ||
type instanceof codec.NullablePointerTo;
}
@@ -87,7 +76,7 @@
return false;
return true;
- };
+ }
Validator.prototype.claimRange = function(start, numBytes) {
if (this.isValidRange(start, numBytes)) {
@@ -95,7 +84,7 @@
return true;
}
return false;
- };
+ }
Validator.prototype.claimHandle = function(index) {
if (index === codec.kEncodedInvalidHandleValue)
@@ -107,13 +96,6 @@
// 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) {
@@ -125,19 +107,15 @@
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) {
+ Validator.prototype.validateStructHeader =
+ function(offset, minNumBytes, minVersion) {
if (!codec.isAligned(offset))
return validationError.MISALIGNED_OBJECT;
@@ -145,44 +123,20 @@
return validationError.ILLEGAL_MEMORY_RANGE;
var numBytes = this.message.buffer.getUint32(offset);
+ var version = this.message.buffer.getUint32(offset + 4);
- if (numBytes < minNumBytes)
+ // Backward compatibility is not yet supported.
+ if (numBytes < minNumBytes || version < minVersion)
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);
+ var err = this.validateStructHeader(0, codec.kMessageHeaderSize, 0);
if (err != validationError.NONE)
return err;
@@ -208,28 +162,7 @@
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
@@ -240,7 +173,7 @@
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);
@@ -263,7 +196,7 @@
return this.validateArray(arrayOffset, elementSize, elementType,
expectedDimensionSizes, currentDimension);
- };
+ }
Validator.prototype.validateStructPointer = function(
offset, structClass, nullable) {
@@ -276,7 +209,7 @@
validationError.NONE : validationError.UNEXPECTED_NULL_POINTER;
return structClass.validate(this, structOffset);
- };
+ }
Validator.prototype.validateUnion = function(
offset, unionClass, nullable) {
@@ -287,7 +220,7 @@
}
return unionClass.validate(this, offset);
- };
+ }
Validator.prototype.validateNestedUnion = function(
offset, unionClass, nullable) {
@@ -300,7 +233,7 @@
validationError.NONE : validationError.UNEXPECTED_NULL_UNION;
return this.validateUnion(unionOffset, unionClass, nullable);
- };
+ }
// This method assumes that the array at arrayPointerOffset has
// been validated.
@@ -308,7 +241,7 @@
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) {
@@ -323,7 +256,7 @@
validationError.NONE : validationError.UNEXPECTED_NULL_POINTER;
var mapEncodedSize = codec.kStructHeaderSize + codec.kMapStructPayloadSize;
- var err = this.validateStructHeader(structOffset, mapEncodedSize);
+ var err = this.validateStructHeader(structOffset, mapEncodedSize, 0);
if (err !== validationError.NONE)
return err;
@@ -356,12 +289,12 @@
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
@@ -404,9 +337,6 @@
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);
@@ -417,12 +347,9 @@
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
@@ -438,11 +365,11 @@
return err;
}
return validationError.NONE;
- };
+ }
Validator.prototype.validateInterfaceElements =
function(offset, numElements, nullable) {
- var elementSize = codec.Interface.prototype.encodedSize;
+ var elementSize = codec.Interface.encodedSize;
for (var i = 0; i < numElements; i++) {
var elementOffset = offset + i * elementSize;
var err = this.validateInterface(elementOffset, nullable);
@@ -450,19 +377,7 @@
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 =
@@ -478,7 +393,7 @@
return err;
}
return validationError.NONE;
- };
+ }
Validator.prototype.validateStructElements =
function(offset, numElements, structClass, nullable) {
@@ -491,19 +406,7 @@
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;