Commit d07a6983 authored by Ken Rockot's avatar Ken Rockot Committed by Chromium LUCI CQ

Prepare to support associated interfaces in JS

Lays groundwork for associated interfaces in Mojo JS modules. The
overall theme here is to introduce a new internal Endpoint abstraction
to back each remote and receiver. Each Endpoint is in turn backed by a
new internal Router abstraction which models shared ownership of a
message pipe handle.

Router multiplexes the pipe by routing incoming messages to a specific
Endpoint based on the message header's interface ID, and can also
send messages on each Endpoint's behalf.

JS (lite) bindings are refactored here to be built on these
abstractions, with no interesting behavioral changes intended.

Bug: 914165, 1004256
Change-Id: I3c9aef111ccb5a285124d1f94f3c45f40aa8d7ae
Test: covered by many WebUI browser tests and Blink web tests
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2585549
Commit-Queue: Ken Rockot <rockot@google.com>
Reviewed-by: default avatarOksana Zhuravlova <oksamyt@chromium.org>
Cr-Commit-Position: refs/heads/master@{#840258}
parent bcd65e68
......@@ -239,14 +239,15 @@ mojo.internal.computeTotalArraySize = function(arraySpec, value) {
/** Owns an outgoing message buffer and facilitates serialization. */
mojo.internal.Message = class {
/**
* @param {number} interfaceId
* @param {number} flags
* @param {number} ordinal
* @param {number} requestId
* @param {!mojo.internal.StructSpec} paramStructSpec
* @param {!Object} value
* @private
* @public
*/
constructor(flags, ordinal, requestId, paramStructSpec, value) {
constructor(interfaceId, flags, ordinal, requestId, paramStructSpec, value) {
let headerSize, version;
if ((flags &
(mojo.internal.kMessageFlagExpectsResponse |
......@@ -270,7 +271,7 @@ mojo.internal.Message = class {
const header = new DataView(this.buffer);
header.setUint32(0, headerSize, mojo.internal.kHostLittleEndian);
header.setUint32(4, version, mojo.internal.kHostLittleEndian);
header.setUint32(8, 0); // Interface ID (only for associated interfaces)
header.setUint32(8, interfaceId, mojo.internal.kHostLittleEndian);
header.setUint32(12, ordinal, mojo.internal.kHostLittleEndian);
header.setUint32(16, flags, mojo.internal.kHostLittleEndian);
header.setUint32(20, 0); // Padding
......@@ -814,7 +815,7 @@ mojo.internal.Decoder = class {
return null;
const decoder = new mojo.internal.Decoder(
new DataView(this.data_.buffer, unionOffset), this.handles_);
new DataView(this.data_.buffer, unionOffset), this.handles_);
return decoder.decodeUnion(unionSpec, 0);
}
......@@ -867,7 +868,7 @@ mojo.internal.Decoder = class {
const handle = this.decodeHandle(offset);
if (!handle)
return null;
return new type(handle);
return new type(mojo.internal.interfaceSupport.createEndpoint(handle));
}
};
......@@ -875,30 +876,20 @@ mojo.internal.Decoder = class {
mojo.internal.Decoder.textDecoder = null;
/**
* @param {!MojoHandle} handle
* @param {number} ordinal
* @param {number} requestId
* @param {number} flags
* @param {!mojo.internal.MojomType} paramStruct
* @param {!Object} value
* @typedef {{
* headerSize: number,
* headerVersion: number,
* interfaceId: number,
* ordinal: number,
* flags: number,
* requestId: number,
* }}
*/
mojo.internal.serializeAndSendMessage = function(
handle, ordinal, requestId, flags, paramStruct, value) {
const message = new mojo.internal.Message(
flags, ordinal, requestId,
/** @type {!mojo.internal.StructSpec} */ (paramStruct.$.structSpec),
value);
handle.writeMessage(message.buffer, message.handles);
};
mojo.internal.MessageHeader;
/**
* @param {!DataView} data
* @return {{
* headerSize: number,
* ordinal: number,
* flags: number,
* requestId: number,
* }}
* @return {!mojo.internal.MessageHeader}
*/
mojo.internal.deserializeMessageHeader = function(data) {
const headerSize = data.getUint32(0, mojo.internal.kHostLittleEndian);
......@@ -910,18 +901,16 @@ mojo.internal.deserializeMessageHeader = function(data) {
headerVersion > 2) {
throw new Error('Received invalid message header');
}
if (headerVersion == 2)
throw new Error('v2 messages not yet supported');
const header = {
headerSize: headerSize,
return {
headerSize,
headerVersion,
interfaceId: data.getUint32(8, mojo.internal.kHostLittleEndian),
ordinal: data.getUint32(12, mojo.internal.kHostLittleEndian),
flags: data.getUint32(16, mojo.internal.kHostLittleEndian),
requestId: (headerVersion < 1) ?
0 :
data.getUint32(24, mojo.internal.kHostLittleEndian),
};
if (headerVersion > 0)
header.requestId = data.getUint32(24, mojo.internal.kHostLittleEndian);
else
header.requestId = 0;
return header;
};
/**
......@@ -1368,8 +1357,8 @@ mojo.internal.StructField = function(
* @param {Array<!Array<number>>=} versionData
* @export
*/
mojo.internal.Struct =
function(objectToBlessAsType, name, fields, versionData) {
mojo.internal.Struct = function(
objectToBlessAsType, name, fields, versionData) {
const versions = versionData.map(v => ({version: v[0], packedSize: v[1]}));
const packedSize = versions[versions.length - 1].packedSize;
const structSpec = {name, packedSize, fields, versions};
......@@ -1399,7 +1388,7 @@ mojo.internal.createStructDeserializer = function(structMojomType) {
return function(dataView) {
if (structMojomType.$ == undefined ||
structMojomType.$.structSpec == undefined) {
throw new Error("Invalid struct mojom type!");
throw new Error('Invalid struct mojom type!');
}
const decoder = new mojo.internal.Decoder(dataView, []);
return decoder.decodeStructInline(structMojomType.$.structSpec);
......@@ -1445,12 +1434,13 @@ mojo.internal.InterfaceProxy = function(type) {
encode: function(value, encoder, byteOffset, bitOffset, nullable) {
if (!(value instanceof type))
throw new Error('Invalid proxy type. Expected ' + type.name);
if (!value.proxy.handle)
throw new Error('Unexpected null ' + type.name);
encoder.encodeHandle(byteOffset, value.proxy.handle);
const endpoint = value.proxy.unbind();
console.assert(endpoint, `unexpected null ${type.name}`);
const pipe = endpoint.releasePipe();
encoder.encodeHandle(byteOffset, pipe);
encoder.encodeUint32(byteOffset + 4, 0); // TODO: Support versioning
value.proxy.unbind();
},
encodeNull: function(encoder, byteOffset) {
encoder.encodeUint32(byteOffset, 0xffffffff);
......@@ -1476,7 +1466,8 @@ mojo.internal.InterfaceRequest = function(type) {
throw new Error('Invalid request type. Expected ' + type.name);
if (!value.handle)
throw new Error('Unexpected null ' + type.name);
encoder.encodeHandle(byteOffset, value.handle);
encoder.encodeHandle(byteOffset, value.handle.releasePipe());
},
encodeNull: function(encoder, byteOffset) {
encoder.encodeUint32(byteOffset, 0xffffffff);
......
This diff is collapsed.
......@@ -31,10 +31,20 @@ goog.provide('{{module.namespace}}.{{interface.name}}PendingReceiver');
* @export
*/
{{module.namespace}}.{{interface.name}}PendingReceiver = class {
/** @param {!MojoHandle} handle */
/**
* @param {!MojoHandle|!mojo.internal.interfaceSupport.Endpoint} handle
*/
constructor(handle) {
/** @public {!MojoHandle} */
this.handle = handle;
/** @public {!mojo.internal.interfaceSupport.Endpoint} */
this.handle = mojo.internal.interfaceSupport.getEndpointForReceiver(handle);
}
/** @param {string=} scope */
bindInBrowser(scope = 'context') {
mojo.internal.interfaceSupport.bind(
this.handle,
{{module.namespace}}.{{interface.name}}.$interfaceName,
scope);
}
};
......@@ -57,15 +67,15 @@ goog.provide('{{module.namespace}}.{{interface.name}}PendingReceiver');
* @implements { {{module.namespace}}.{{interface.name}}Interface }
*/
{{module.namespace}}.{{interface.name}}Remote = class {
/** @param {MojoHandle=} opt_handle */
constructor(opt_handle) {
/** @param {MojoHandle|mojo.internal.interfaceSupport.Endpoint=} handle */
constructor(handle = undefined) {
/**
* @private {!mojo.internal.interfaceSupport.InterfaceRemoteBase<!{{module.namespace}}.{{interface.name}}PendingReceiver>}
*/
this.proxy =
new mojo.internal.interfaceSupport.InterfaceRemoteBase(
{{module.namespace}}.{{interface.name}}PendingReceiver,
opt_handle);
handle);
/**
* @public {!mojo.internal.interfaceSupport.InterfaceRemoteBaseWrapper<!{{module.namespace}}.{{interface.name}}PendingReceiver>}
......@@ -166,8 +176,7 @@ goog.provide('{{module.namespace}}.{{interface.name}}PendingReceiver');
*/
static getRemote() {
let remote = new {{module.namespace}}.{{interface.name}}Remote;
Mojo.bindInterface({{module.namespace}}.{{interface.name}}.$interfaceName,
remote.$.bindNewPipeAndPassReceiver().handle);
remote.$.bindNewPipeAndPassReceiver().bindInBrowser();
return remote;
}
};
......
......@@ -21,10 +21,18 @@
* @implements {mojo.internal.interfaceSupport.PendingReceiver}
*/
export const {{interface.name}}PendingReceiver = class {
/** @param {!MojoHandle} handle */
/**
* @param {!MojoHandle|!mojo.internal.interfaceSupport.Endpoint} handle
*/
constructor(handle) {
/** @public {!MojoHandle} */
this.handle = handle;
/** @public {!mojo.internal.interfaceSupport.Endpoint} */
this.handle = mojo.internal.interfaceSupport.getEndpointForReceiver(handle);
}
/** @param {string=} scope */
bindInBrowser(scope = 'context') {
mojo.internal.interfaceSupport.bind(
this.handle, '{{mojom_namespace}}.{{interface.name}}', scope);
}
};
......@@ -44,15 +52,15 @@ export const {{interface.name}}Interface = class {
* @implements { {{interface.name}}Interface }
*/
export const {{interface.name}}Remote = class {
/** @param {MojoHandle=} opt_handle */
constructor(opt_handle) {
/** @param {MojoHandle|mojo.internal.interfaceSupport.Endpoint=} handle */
constructor(handle = undefined) {
/**
* @private {!mojo.internal.interfaceSupport.InterfaceRemoteBase<!{{interface.name}}PendingReceiver>}
*/
this.proxy =
new mojo.internal.interfaceSupport.InterfaceRemoteBase(
{{interface.name}}PendingReceiver,
opt_handle);
handle);
/**
* @public {!mojo.internal.interfaceSupport.InterfaceRemoteBaseWrapper<!{{interface.name}}PendingReceiver>}
......@@ -147,8 +155,7 @@ export const {{interface.name}} = class {
*/
static getRemote() {
let remote = new {{interface.name}}Remote;
Mojo.bindInterface("{{mojom_namespace}}.{{interface.name}}",
remote.$.bindNewPipeAndPassReceiver().handle);
remote.$.bindNewPipeAndPassReceiver().bindInBrowser();
return remote;
}
};
......
......@@ -231,12 +231,8 @@ promise_test(() => {
}, 'InterfaceTarget connection error handler runs when set on an InterfaceCallbackRouter object');
function getMojoEchoRemote() {
// content.mojom.MojoEchoRemote.getRemote() only works for frame interfaces
// and MojoEcho is a process interface.
let remote = new content.mojom.MojoEchoRemote;
Mojo.bindInterface(content.mojom.MojoEcho.$interfaceName,
remote.$.bindNewPipeAndPassReceiver().handle,
'process');
remote.$.bindNewPipeAndPassReceiver().bindInBrowser('process');
return remote;
}
......
......@@ -241,9 +241,7 @@ function getMojoEchoRemote() {
// MojoEchoRemote.getRemote() only works for frame interfaces
// and MojoEcho is a process interface.
let remote = new MojoEchoRemote;
Mojo.bindInterface(MojoEcho.$interfaceName,
remote.$.bindNewPipeAndPassReceiver().handle,
'process');
remote.$.bindNewPipeAndPassReceiver().bindInBrowser('process');
return remote;
}
......
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