From d3d01eebe4aa13a42e3a3a14f08cbc219bbef945 Mon Sep 17 00:00:00 2001 From: sammc Date: Mon, 9 Feb 2015 23:45:53 -0800 Subject: [PATCH] Use the JS stash client from the JS serial service. This allows persistent serial connections to be supported on the mojo-service-backed serial implementation. BUG=389016 Review URL: https://codereview.chromium.org/908153002 Cr-Commit-Position: refs/heads/master@{#315514} --- device/serial/data_stream_serialization.mojom | 2 +- .../renderer/api/serial/serial_api_unittest.cc | 52 +++++++++++++++++++-- .../renderer/resources/serial_custom_bindings.js | 31 +++++++++---- extensions/renderer/resources/serial_service.js | 38 ++++++++++++++-- extensions/test/data/serial_unittest.js | 53 +++++++++++++++++++++- 5 files changed, 155 insertions(+), 21 deletions(-) diff --git a/device/serial/data_stream_serialization.mojom b/device/serial/data_stream_serialization.mojom index 3112dd5b227b..cf2cc6b3df6d 100644 --- a/device/serial/data_stream_serialization.mojom +++ b/device/serial/data_stream_serialization.mojom @@ -30,7 +30,7 @@ struct SerializedDataReceiver { // data. DataSource source; // DataSourceClient& - handle source_client; + handle client; // The error to report for a receive in progress when a fatal error occurs. int32 fatal_error_value; diff --git a/extensions/renderer/api/serial/serial_api_unittest.cc b/extensions/renderer/api/serial/serial_api_unittest.cc index 81c5c352f5e7..0d247b94aed6 100644 --- a/extensions/renderer/api/serial/serial_api_unittest.cc +++ b/extensions/renderer/api/serial/serial_api_unittest.cc @@ -5,6 +5,7 @@ #include "device/serial/serial_device_enumerator.h" #include "device/serial/serial_service_impl.h" #include "device/serial/test_serial_io_handler.h" +#include "extensions/browser/mojo/stash_backend.h" #include "extensions/common/mojo/keep_alive.mojom.h" #include "extensions/renderer/api_test_base.h" #include "grit/extensions_renderer_resources.h" @@ -406,14 +407,25 @@ class SerialApiTest : public ApiTestBase { void SetUp() override { ApiTestBase::SetUp(); - env()->RegisterModule("serial", IDR_SERIAL_CUSTOM_BINDINGS_JS); - service_provider()->AddService(base::Bind( - &SerialApiTest::CreateSerialService, base::Unretained(this))); - service_provider()->IgnoreServiceRequests(); + stash_backend_.reset(new StashBackend(base::Closure())); + PrepareEnvironment(api_test_env(), stash_backend_.get()); + } + + void PrepareEnvironment(ApiTestEnvironment* environment, + StashBackend* stash_backend) { + environment->env()->RegisterModule("serial", IDR_SERIAL_CUSTOM_BINDINGS_JS); + environment->service_provider()->AddService( + base::Bind(&SerialApiTest::CreateSerialService, + base::Unretained(this))); + environment->service_provider()->AddService(base::Bind( + &StashBackend::BindToRequest, base::Unretained(stash_backend))); + environment->service_provider()->IgnoreServiceRequests(); } scoped_refptr io_handler_; + scoped_ptr stash_backend_; + private: scoped_refptr GetIoHandler() { if (!io_handler_.get()) @@ -640,4 +652,36 @@ TEST_F(SerialApiTest, SendUnknownConnectionId) { RunTest("serial_unittest.js", "testSendUnknownConnectionId"); } +TEST_F(SerialApiTest, StashAndRestoreDuringEcho) { + ASSERT_NO_FATAL_FAILURE(RunTest("serial_unittest.js", "testSendAndStash")); + env()->context()->DispatchOnUnloadEvent(); + scoped_ptr new_env(CreateEnvironment()); + ApiTestEnvironment new_api_test_env(new_env.get()); + PrepareEnvironment(&new_api_test_env, stash_backend_.get()); + new_api_test_env.RunTest("serial_unittest.js", "testRestoreAndReceive"); +} + +TEST_F(SerialApiTest, StashAndRestoreDuringEchoError) { + io_handler_ = + new ReceiveErrorTestIoHandler(device::serial::RECEIVE_ERROR_DEVICE_LOST); + ASSERT_NO_FATAL_FAILURE( + RunTest("serial_unittest.js", "testRestoreAndReceiveErrorSetUp")); + env()->context()->DispatchOnUnloadEvent(); + scoped_ptr new_env(CreateEnvironment()); + ApiTestEnvironment new_api_test_env(new_env.get()); + PrepareEnvironment(&new_api_test_env, stash_backend_.get()); + new_api_test_env.RunTest("serial_unittest.js", "testRestoreAndReceiveError"); +} + +TEST_F(SerialApiTest, StashAndRestoreNoConnections) { + ASSERT_NO_FATAL_FAILURE( + RunTest("serial_unittest.js", "testStashNoConnections")); + env()->context()->DispatchOnUnloadEvent(); + io_handler_ = nullptr; + scoped_ptr new_env(CreateEnvironment()); + ApiTestEnvironment new_api_test_env(new_env.get()); + PrepareEnvironment(&new_api_test_env, stash_backend_.get()); + new_api_test_env.RunTest("serial_unittest.js", "testRestoreNoConnections"); +} + } // namespace extensions diff --git a/extensions/renderer/resources/serial_custom_bindings.js b/extensions/renderer/resources/serial_custom_bindings.js index 8c943ed0162d..20d669a3b7b4 100644 --- a/extensions/renderer/resources/serial_custom_bindings.js +++ b/extensions/renderer/resources/serial_custom_bindings.js @@ -44,6 +44,27 @@ function forwardToConnection(methodName) { }; } +function addEventListeners(connection, id) { + connection.onData = function(data) { + eventBindings.dispatchEvent( + 'serial.onReceive', [{connectionId: id, data: data}]); + }; + connection.onError = function(error) { + eventBindings.dispatchEvent( + 'serial.onReceiveError', [{connectionId: id, error: error}]); + }; +} + +serialServicePromise.then(function(serialService) { + return serialService.getConnections().then(function(connections) { + for (var entry of connections) { + var connection = entry[1]; + addEventListeners(connection, entry[0]); + connection.resumeReceives(); + }; + }); +}); + binding.registerCustomHook(function(bindingsAPI) { var apiFunctions = bindingsAPI.apiFunctions; apiFunctions.setHandleRequestWithPromise('getDevices', function() { @@ -56,15 +77,7 @@ binding.registerCustomHook(function(bindingsAPI) { return serialServicePromise.then(function(serialService) { return serialService.createConnection(path, options); }).then(function(result) { - var id = result.info.connectionId; - result.connection.onData = function(data) { - eventBindings.dispatchEvent( - 'serial.onReceive', [{connectionId: id, data: data}]); - }; - result.connection.onError = function(error) { - eventBindings.dispatchEvent( - 'serial.onReceiveError', [{connectionId: id, error: error}]); - }; + addEventListeners(result.connection, result.info.connectionId); return result.info; }).catch (function(e) { throw new Error('Failed to connect to the port.'); diff --git a/extensions/renderer/resources/serial_service.js b/extensions/renderer/resources/serial_service.js index 21efb7b9e846..e5d909d175a8 100644 --- a/extensions/renderer/resources/serial_service.js +++ b/extensions/renderer/resources/serial_service.js @@ -10,13 +10,15 @@ define('serial_service', [ 'device/serial/serial_serialization.mojom', 'mojo/public/js/core', 'mojo/public/js/router', + 'stash_client', ], function(serviceProvider, dataReceiver, dataSender, serialMojom, serialization, core, - routerModule) { + routerModule, + stashClient) { /** * A Javascript client for the serial service and connection Mojo services. * @@ -132,6 +134,8 @@ define('serial_service', [ clientOptions.sendTimeout = options.sendTimeout; if ('bufferSize' in options) clientOptions.bufferSize = options.bufferSize; + if ('persistent' in options) + clientOptions.persistent = options.persistent; }; function Connection(connection, router, receivePipe, receiveClientPipe, @@ -446,7 +450,7 @@ define('serial_service', [ Connection.deserialize = function(serialized) { var serialConnection = $Object.create(Connection.prototype); var router = new routerModule.Router(serialized.connection); - var connection = new serialMojom.ConnectionProxy(router); + var connection = new serialMojom.Connection.proxyClass(router); var receiver = dataReceiver.DataReceiver.deserialize(serialized.receiver); var sender = dataSender.DataSender.deserialize(serialized.sender); @@ -480,9 +484,12 @@ define('serial_service', [ // All accesses to connections_ and nextConnectionId_ other than those // involved in deserialization should ensure that // connectionDeserializationComplete_ has resolved first. - // Note: this will not immediately resolve once serial connection stashing and - // restoring is implemented. - var connectionDeserializationComplete_ = Promise.resolve(); + var connectionDeserializationComplete_ = stashClient.retrieve( + 'serial', serialization.SerializedConnection).then(function(decoded) { + if (!decoded) + return; + return Promise.all($Array.map(decoded, Connection.deserialize)); + }); // The map of connection ID to connection object. var connections_ = new Map(); @@ -510,6 +517,27 @@ define('serial_service', [ }); } + stashClient.registerClient( + 'serial', serialization.SerializedConnection, function() { + return connectionDeserializationComplete_.then(function() { + var clientPromises = []; + for (var connection of connections_.values()) { + if (connection.state_.persistent) + clientPromises.push(connection.serialize()); + else + connection.close(); + } + return Promise.all($Array.map(clientPromises, function(promise) { + return promise.then(function(serialization) { + return { + serialization: serialization, + monitorHandles: !serialization.paused, + }; + }); + })); + }); + }); + return { getDevices: getDevices, createConnection: Connection.create, diff --git a/extensions/test/data/serial_unittest.js b/extensions/test/data/serial_unittest.js index 56ebee3466a8..33197cf3bd62 100644 --- a/extensions/test/data/serial_unittest.js +++ b/extensions/test/data/serial_unittest.js @@ -63,7 +63,7 @@ function serializeRoundTrip() { return requireAsync('serial_service').then(function(serialService) { function serializeConnections(connections) { var serializedConnections = []; - for (var connection in connections.values()) { + for (var connection of connections.values()) { serializedConnections.push(serializeConnection(connection)); } return Promise.all(serializedConnections); @@ -212,7 +212,7 @@ function disconnect() { } function checkClientConnectionInfo(connectionInfo) { - test.assertFalse(connectionInfo.persistent); + test.assertTrue(connectionInfo.persistent); test.assertEq('test connection', connectionInfo.name); test.assertEq(12345, connectionInfo.receiveTimeout); test.assertEq(6789, connectionInfo.sendTimeout); @@ -836,4 +836,53 @@ unittestBindings.exportTests([ var buffer = new ArrayBuffer(1); serial.send(-1, buffer, test.callbackFail('Serial connection not found.')); }, + + function testSendAndStash() { + connect() + .then(setPaused(true)) + .then(sendData) + .then(expectSendResult(4)) + .then(test.succeed, test.fail); + }, + + function testRestoreAndReceive() { + connectionId = 0; + Promise.all([ + utils.promise(serial.setPaused, connectionId, false), + listenOnce(serial.onReceive).then(checkReceivedData), + ]) + .then(disconnect) + .then(test.succeed, test.fail); + }, + + function testRestoreAndReceiveErrorSetUp() { + connect().then(test.succeed, test.fail); + }, + + function testRestoreAndReceiveError() { + connectionId = 0; + Promise.all([ + utils.promise(serial.setPaused, connectionId, false), + listenOnce(serial.onReceiveError) + .then(checkReceiveError('device_lost')), + ]) + .then(disconnect) + .then(test.succeed, test.fail); + }, + + function testStashNoConnections() { + connect({persistent: false}).then(test.succeed, test.fail); + }, + + function testRestoreNoConnections() { + connect() + .then(function(connectionInfo) { + test.assertEq(0, connectionInfo.connectionId); + return connectionInfo; + }) + .then(checkConnectionInfo) + .then(disconnect) + .then(test.succeed, test.fail); + }, + ], test.runTests, exports); -- 2.11.4.GIT