blob: ff6d9b505f5ce7cd8abcd91fbf0cbcfa8413a925 [file] [log] [blame]
// Copyright 2016 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.
/**
* @interface
*/
function Service() {
}
Service.prototype = {
/**
* @return {!Promise}
*/
dispose() {},
/**
* @return {function(string)}
*/
setNotify(notify) {}
};
/**
* @unrestricted
*/
ServiceDispatcher = class {
/**
* @param {!ServicePort} port
*/
constructor(port) {
/** @type {!Map<string, !Object>} */
this._objects = new Map();
this._lastObjectId = 1;
this._port = port;
this._port.setHandlers(this._dispatchMessageWrapped.bind(this), this._connectionClosed.bind(this));
}
/**
* @param {string} data
*/
_dispatchMessageWrapped(data) {
let message;
try {
message = JSON.parse(data);
if (!(message instanceof Object)) {
this._sendErrorResponse(message['id'], 'Malformed message');
return;
}
this._dispatchMessage(message);
} catch (e) {
this._sendErrorResponse(message ? message['id'] : '', e.toString() + ' ' + e.stack);
}
}
/**
* @param {!Object} message
*/
_dispatchMessage(message) {
const domainAndMethod = message['method'].split('.');
const serviceName = domainAndMethod[0];
const method = domainAndMethod[1];
if (method === 'create') {
const extensions =
self.runtime.extensions(Service).filter(extension => extension.descriptor()['name'] === serviceName);
if (!extensions.length) {
this._sendErrorResponse(message['id'], 'Could not resolve service \'' + serviceName + '\'');
return;
}
extensions[0].instance().then(object => {
const id = String(this._lastObjectId++);
object.setNotify(this._notify.bind(this, id, serviceName));
this._objects.set(id, object);
this._sendResponse(message['id'], {id: id});
});
} else if (method === 'dispose') {
const object = this._objects.get(message['params']['id']);
if (!object) {
console.error('Could not look up object with id for ' + JSON.stringify(message));
return;
}
this._objects.delete(message['params']['id']);
object.dispose().then(() => this._sendResponse(message['id'], {}));
} else {
if (!message['params']) {
console.error('No params in the message: ' + JSON.stringify(message));
return;
}
const object = this._objects.get(message['params']['id']);
if (!object) {
console.error('Could not look up object with id for ' + JSON.stringify(message));
return;
}
const handler = object[method];
if (!(handler instanceof Function)) {
console.error('Handler for \'' + method + '\' is missing.');
return;
}
object[method](message['params']).then(result => this._sendResponse(message['id'], result));
}
}
_connectionClosed() {
for (const object of this._objects.values()) {
object.dispose();
}
this._objects.clear();
}
/**
* @param {string} objectId
* @param {string} serviceName
* @param {string} method
* @param {!Object} params
*/
_notify(objectId, serviceName, method, params) {
params['id'] = objectId;
const message = {method: serviceName + '.' + method, params: params};
this._port.send(JSON.stringify(message));
}
/**
* @param {string} messageId
* @param {!Object} result
*/
_sendResponse(messageId, result) {
const message = {id: messageId, result: result};
this._port.send(JSON.stringify(message));
}
/**
* @param {string} messageId
* @param {string} error
*/
_sendErrorResponse(messageId, error) {
const message = {id: messageId, error: error};
this._port.send(JSON.stringify(message));
}
};
/**
* @implements {ServicePort}
* @unrestricted
*/
WorkerServicePort = class {
/**
* @param {!Port|!Worker} port
*/
constructor(port) {
this._port = port;
this._port.onmessage = this._onMessage.bind(this);
this._port.onerror = console.error;
}
/**
* @override
* @param {function(string)} messageHandler
* @param {function(string)} closeHandler
*/
setHandlers(messageHandler, closeHandler) {
this._messageHandler = messageHandler;
this._closeHandler = closeHandler;
}
/**
* @override
* @param {string} data
* @return {!Promise}
*/
send(data) {
this._port.postMessage(data);
return Promise.resolve();
}
/**
* @override
* @return {!Promise}
*/
close() {
return Promise.resolve();
}
/**
* @param {!MessageEvent} event
*/
_onMessage(event) {
this._messageHandler(event.data);
}
};
const dispatchers = [];
const worker = /** @type {!Object} */ (self);
const servicePort = new WorkerServicePort(/** @type {!Worker} */ (worker));
dispatchers.push(new ServiceDispatcher(servicePort));