1// Copyright 2014 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5define("mojo/public/js/connector", [ 6 "mojo/public/js/buffer", 7 "mojo/public/js/codec", 8 "mojo/public/js/core", 9 "mojo/public/js/support", 10], function(buffer, codec, core, support) { 11 12 function Connector(handle) { 13 if (!core.isHandle(handle)) 14 throw new Error("Connector: not a handle " + handle); 15 this.handle_ = handle; 16 this.dropWrites_ = false; 17 this.error_ = false; 18 this.incomingReceiver_ = null; 19 this.readWatcher_ = null; 20 this.errorHandler_ = null; 21 22 if (handle) { 23 this.readWatcher_ = support.watch(handle, 24 core.HANDLE_SIGNAL_READABLE, 25 this.readMore_.bind(this)); 26 } 27 } 28 29 Connector.prototype.close = function() { 30 if (this.readWatcher_) { 31 support.cancelWatch(this.readWatcher_); 32 this.readWatcher_ = null; 33 } 34 if (this.handle_ != null) { 35 core.close(this.handle_); 36 this.handle_ = null; 37 } 38 }; 39 40 Connector.prototype.accept = function(message) { 41 if (this.error_) 42 return false; 43 44 if (this.dropWrites_) 45 return true; 46 47 var result = core.writeMessage(this.handle_, 48 new Uint8Array(message.buffer.arrayBuffer), 49 message.handles, 50 core.WRITE_MESSAGE_FLAG_NONE); 51 switch (result) { 52 case core.RESULT_OK: 53 // The handles were successfully transferred, so we don't own them 54 // anymore. 55 message.handles = []; 56 break; 57 case core.RESULT_FAILED_PRECONDITION: 58 // There's no point in continuing to write to this pipe since the other 59 // end is gone. Avoid writing any future messages. Hide write failures 60 // from the caller since we'd like them to continue consuming any 61 // backlog of incoming messages before regarding the message pipe as 62 // closed. 63 this.dropWrites_ = true; 64 break; 65 default: 66 // This particular write was rejected, presumably because of bad input. 67 // The pipe is not necessarily in a bad state. 68 return false; 69 } 70 return true; 71 }; 72 73 Connector.prototype.setIncomingReceiver = function(receiver) { 74 this.incomingReceiver_ = receiver; 75 }; 76 77 Connector.prototype.setErrorHandler = function(handler) { 78 this.errorHandler_ = handler; 79 }; 80 81 Connector.prototype.encounteredError = function() { 82 return this.error_; 83 }; 84 85 Connector.prototype.readMore_ = function(result) { 86 for (;;) { 87 var read = core.readMessage(this.handle_, 88 core.READ_MESSAGE_FLAG_NONE); 89 if (this.handle_ == null) // The connector has been closed. 90 return; 91 if (read.result == core.RESULT_SHOULD_WAIT) 92 return; 93 if (read.result != core.RESULT_OK) { 94 this.error_ = true; 95 if (this.errorHandler_) 96 this.errorHandler_.onError(read.result); 97 return; 98 } 99 var messageBuffer = new buffer.Buffer(read.buffer); 100 var message = new codec.Message(messageBuffer, read.handles); 101 if (this.incomingReceiver_) { 102 this.incomingReceiver_.accept(message); 103 } 104 } 105 }; 106 107 // The TestConnector subclass is only intended to be used in unit tests. It 108 // doesn't automatically listen for input messages. Instead, you need to 109 // call waitForNextMessage to block and wait for the next incoming message. 110 function TestConnector(handle) { 111 Connector.call(this, handle); 112 } 113 114 TestConnector.prototype = Object.create(Connector.prototype); 115 116 TestConnector.prototype.waitForNextMessage = function() { 117 var wait = core.wait(this.handle_, core.HANDLE_SIGNAL_READABLE, 118 core.DEADLINE_INDEFINITE); 119 this.readMore_(wait.result); 120 } 121 122 var exports = {}; 123 exports.Connector = Connector; 124 exports.TestConnector = TestConnector; 125 return exports; 126}); 127