Commit 0c641b54 authored by yzshen's avatar yzshen Committed by Commit bot

Mojo JS bindings: BindingSet support.

It also makes binding to a raw handle easier.

BUG=579646

Review-Url: https://codereview.chromium.org/2578333002
Cr-Commit-Position: refs/heads/master@{#438973}
parent 1bd8046a
...@@ -17,6 +17,7 @@ define([ ...@@ -17,6 +17,7 @@ define([
.then(testReusable) .then(testReusable)
.then(testConnectionError) .then(testConnectionError)
.then(testUnbind) .then(testUnbind)
.then(testBindingSet)
.then(function() { .then(function() {
this.result = "PASS"; this.result = "PASS";
gc.collectGarbage(); // should not crash gc.collectGarbage(); // should not crash
...@@ -118,4 +119,41 @@ define([ ...@@ -118,4 +119,41 @@ define([
return promise; return promise;
} }
function testBindingSet() {
var calc1 = new math.CalculatorPtr();
var calc2 = new math.CalculatorPtr();
var calcImpl = new CalculatorImpl();
var bindingSet = new bindings.BindingSet(math.Calculator);
expect(bindingSet.isEmpty()).toBeTruthy();
bindingSet.addBinding(calcImpl, bindings.makeRequest(calc1));
bindingSet.addBinding(calcImpl, bindings.makeRequest(calc2));
expect(bindingSet.isEmpty()).toBeFalsy();
var promise = calc1.add(3).then(function(response) {
expect(response.value).toBe(3);
return calc2.add(4);
}).then(function(response) {
expect(response.value).toBe(7);
var promiseOfConnectionError = new Promise(function(resolve, reject) {
bindingSet.setConnectionErrorHandler(function() {
resolve();
});
});
calc1.ptr.reset();
return promiseOfConnectionError;
}).then(function() {
return calc2.add(5);
}).then(function(response) {
expect(response.value).toBe(12);
bindingSet.closeAllBindings();
expect(bindingSet.isEmpty()).toBeTruthy();
return Promise.resolve();
});
return promise;
}
}); });
...@@ -5,11 +5,13 @@ ...@@ -5,11 +5,13 @@
define([ define([
"gin/test/expect", "gin/test/expect",
"mojo/public/js/bindings", "mojo/public/js/bindings",
"mojo/public/js/core",
"mojo/public/interfaces/bindings/tests/math_calculator.mojom", "mojo/public/interfaces/bindings/tests/math_calculator.mojom",
"mojo/public/js/threading", "mojo/public/js/threading",
"gc", "gc",
], function(expect, ], function(expect,
bindings, bindings,
core,
math, math,
threading, threading,
gc) { gc) {
...@@ -18,6 +20,7 @@ define([ ...@@ -18,6 +20,7 @@ define([
.then(testReusable) .then(testReusable)
.then(testConnectionError) .then(testConnectionError)
.then(testPassInterface) .then(testPassInterface)
.then(testBindRawHandle)
.then(function() { .then(function() {
this.result = "PASS"; this.result = "PASS";
gc.collectGarbage(); // should not crash gc.collectGarbage(); // should not crash
...@@ -138,4 +141,20 @@ define([ ...@@ -138,4 +141,20 @@ define([
return promise; return promise;
} }
function testBindRawHandle() {
var pipe = core.createMessagePipe();
var calc = new math.CalculatorPtr(pipe.handle0);
var newCalc = null;
var calcBinding = new bindings.Binding(math.Calculator,
new CalculatorImpl(),
pipe.handle1);
var promise = calc.add(2).then(function(response) {
expect(response.value).toBe(2);
return Promise.resolve();
});
return promise;
}
}); });
...@@ -6,10 +6,9 @@ define('mojo/edk/js/tests/js_to_cpp_tests', [ ...@@ -6,10 +6,9 @@ define('mojo/edk/js/tests/js_to_cpp_tests', [
'console', 'console',
'mojo/edk/js/tests/js_to_cpp.mojom', 'mojo/edk/js/tests/js_to_cpp.mojom',
'mojo/public/js/bindings', 'mojo/public/js/bindings',
'mojo/public/js/connection',
'mojo/public/js/connector', 'mojo/public/js/connector',
'mojo/public/js/core', 'mojo/public/js/core',
], function (console, jsToCpp, bindings, connection, connector, core) { ], function (console, jsToCpp, bindings, connector, core) {
var retainedJsSide; var retainedJsSide;
var retainedJsSideStub; var retainedJsSideStub;
var sampleData; var sampleData;
...@@ -22,11 +21,9 @@ define('mojo/edk/js/tests/js_to_cpp_tests', [ ...@@ -22,11 +21,9 @@ define('mojo/edk/js/tests/js_to_cpp_tests', [
}; };
function JsSideConnection() { function JsSideConnection() {
this.binding = new bindings.Binding(jsToCpp.JsSide, this);
} }
JsSideConnection.prototype =
Object.create(jsToCpp.JsSide.stubClass.prototype);
JsSideConnection.prototype.setCppSide = function(cppSide) { JsSideConnection.prototype.setCppSide = function(cppSide) {
this.cppSide_ = cppSide; this.cppSide_ = cppSide;
this.cppSide_.startTest(); this.cppSide_.startTest();
...@@ -220,9 +217,7 @@ define('mojo/edk/js/tests/js_to_cpp_tests', [ ...@@ -220,9 +217,7 @@ define('mojo/edk/js/tests/js_to_cpp_tests', [
for (i = 0; i < sampleMessage.length; ++i) { for (i = 0; i < sampleMessage.length; ++i) {
sampleMessage[i] = 255 - i; sampleMessage[i] = 255 - i;
} }
retainedJsSideStub =
connection.bindHandleToStub(jsSideRequestHandle, jsToCpp.JsSide);
retainedJsSide = new JsSideConnection; retainedJsSide = new JsSideConnection;
bindings.StubBindings(retainedJsSideStub).delegate = retainedJsSide; retainedJsSide.binding.bind(jsSideRequestHandle);
}; };
}); });
...@@ -20,7 +20,8 @@ define("mojo/public/js/bindings", [ ...@@ -20,7 +20,8 @@ define("mojo/public/js/bindings", [
// Operations used to setup/configure an interface pointer. Exposed as the // Operations used to setup/configure an interface pointer. Exposed as the
// |ptr| field of generated interface pointer classes. // |ptr| field of generated interface pointer classes.
function InterfacePtrController(interfaceType) { // |ptrInfoOrHandle| could be omitted and passed into bind() later.
function InterfacePtrController(interfaceType, ptrInfoOrHandle) {
this.version = 0; this.version = 0;
this.interfaceType_ = interfaceType; this.interfaceType_ = interfaceType;
...@@ -28,13 +29,20 @@ define("mojo/public/js/bindings", [ ...@@ -28,13 +29,20 @@ define("mojo/public/js/bindings", [
// |connection_| is lazily initialized. |handle_| is valid between bind() // |connection_| is lazily initialized. |handle_| is valid between bind()
// and the initialization of |connection_|. // and the initialization of |connection_|.
this.handle_ = null; this.handle_ = null;
if (ptrInfoOrHandle)
this.bind(ptrInfoOrHandle);
} }
InterfacePtrController.prototype.bind = function(interfacePtrInfo) { InterfacePtrController.prototype.bind = function(ptrInfoOrHandle) {
this.reset(); this.reset();
this.version = interfacePtrInfo.version; if (ptrInfoOrHandle instanceof types.InterfacePtrInfo) {
this.handle_ = interfacePtrInfo.handle; this.version = ptrInfoOrHandle.version;
this.handle_ = ptrInfoOrHandle.handle;
} else {
this.handle_ = ptrInfoOrHandle;
}
}; };
InterfacePtrController.prototype.isBound = function() { InterfacePtrController.prototype.isBound = function() {
...@@ -101,8 +109,6 @@ define("mojo/public/js/bindings", [ ...@@ -101,8 +109,6 @@ define("mojo/public/js/bindings", [
// --------------------------------------------------------------------------- // ---------------------------------------------------------------------------
// |request| could be omitted and passed into bind() later. // |request| could be omitted and passed into bind() later.
// NOTE: |impl| shouldn't hold a reference to this object, because that
// results in circular references.
// //
// Example: // Example:
// //
...@@ -115,13 +121,13 @@ define("mojo/public/js/bindings", [ ...@@ -115,13 +121,13 @@ define("mojo/public/js/bindings", [
// var request = makeRequest(fooPtr); // var request = makeRequest(fooPtr);
// var binding = new Binding(mojom.Foo, new FooImpl(), request); // var binding = new Binding(mojom.Foo, new FooImpl(), request);
// fooPtr.fooMethod1(); // fooPtr.fooMethod1();
function Binding(interfaceType, impl, request) { function Binding(interfaceType, impl, requestOrHandle) {
this.interfaceType_ = interfaceType; this.interfaceType_ = interfaceType;
this.impl_ = impl; this.impl_ = impl;
this.stub_ = null; this.stub_ = null;
if (request) if (requestOrHandle)
this.bind(request); this.bind(requestOrHandle);
} }
Binding.prototype.isBound = function() { Binding.prototype.isBound = function() {
...@@ -135,11 +141,13 @@ define("mojo/public/js/bindings", [ ...@@ -135,11 +141,13 @@ define("mojo/public/js/bindings", [
return ptr; return ptr;
} }
Binding.prototype.bind = function(request) { Binding.prototype.bind = function(requestOrHandle) {
this.close(); this.close();
if (request.isValid()) {
this.stub_ = connection.bindHandleToStub(request.handle, var handle = requestOrHandle instanceof types.InterfaceRequest ?
this.interfaceType_); requestOrHandle.handle : requestOrHandle;
if (core.isHandle(handle)) {
this.stub_ = connection.bindHandleToStub(handle, this.interfaceType_);
connection.StubBindings(this.stub_).delegate = this.impl_; connection.StubBindings(this.stub_).delegate = this.impl_;
} }
}; };
...@@ -172,12 +180,65 @@ define("mojo/public/js/bindings", [ ...@@ -172,12 +180,65 @@ define("mojo/public/js/bindings", [
return result; return result;
}; };
// ---------------------------------------------------------------------------
function BindingSetEntry(bindingSet, interfaceType, impl, requestOrHandle,
bindingId) {
this.bindingSet_ = bindingSet;
this.bindingId_ = bindingId;
this.binding_ = new Binding(interfaceType, impl, requestOrHandle);
this.binding_.setConnectionErrorHandler(
() => this.bindingSet_.onConnectionError(bindingId));
}
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 = {}; var exports = {};
exports.InterfacePtrInfo = types.InterfacePtrInfo; exports.InterfacePtrInfo = types.InterfacePtrInfo;
exports.InterfaceRequest = types.InterfaceRequest; exports.InterfaceRequest = types.InterfaceRequest;
exports.makeRequest = makeRequest; exports.makeRequest = makeRequest;
exports.InterfacePtrController = InterfacePtrController; exports.InterfacePtrController = InterfacePtrController;
exports.Binding = Binding; exports.Binding = Binding;
exports.BindingSet = BindingSet;
// TODO(yzshen): Remove the following exports. // TODO(yzshen): Remove the following exports.
exports.EmptyProxy = connection.EmptyProxy; exports.EmptyProxy = connection.EmptyProxy;
......
...@@ -8,6 +8,9 @@ define("mojo/public/js/connection", [ ...@@ -8,6 +8,9 @@ define("mojo/public/js/connection", [
"mojo/public/js/router", "mojo/public/js/router",
], function(connector, core, router) { ], function(connector, core, router) {
// TODO(yzshen): This module should only be used by the JS bindings internally
// and it will be removed soon.
var Router = router.Router; var Router = router.Router;
var TestConnector = connector.TestConnector; var TestConnector = connector.TestConnector;
var TestRouter = router.TestRouter; var TestRouter = router.TestRouter;
......
...@@ -2,8 +2,9 @@ ...@@ -2,8 +2,9 @@
var k{{interface.name}}_{{method.name}}_Name = {{method.ordinal}}; var k{{interface.name}}_{{method.name}}_Name = {{method.ordinal}};
{%- endfor %} {%- endfor %}
function {{interface.name}}Ptr() { function {{interface.name}}Ptr(handleOrPtrInfo) {
this.ptr = new bindings.InterfacePtrController({{interface.name}}); this.ptr = new bindings.InterfacePtrController({{interface.name}},
handleOrPtrInfo);
} }
function {{interface.name}}Proxy(receiver) { function {{interface.name}}Proxy(receiver) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment