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 5 (function() { 6 var internal = mojo.internal; 7 8 function Connector(handle) { 9 if (!(handle instanceof MojoHandle)) 10 throw new Error("Connector: not a handle " + handle); 11 this.handle_ = handle; 12 this.dropWrites_ = false; 13 this.error_ = false; 14 this.incomingReceiver_ = null; 15 this.readWatcher_ = null; 16 this.errorHandler_ = null; 17 this.paused_ = false; 18 19 this.waitToReadMore(); 20 } 21 22 Connector.prototype.close = function() { 23 this.cancelWait(); 24 if (this.handle_ != null) { 25 this.handle_.close(); 26 this.handle_ = null; 27 } 28 }; 29 30 Connector.prototype.pauseIncomingMethodCallProcessing = function() { 31 if (this.paused_) { 32 return; 33 } 34 this.paused_= true; 35 this.cancelWait(); 36 }; 37 38 Connector.prototype.resumeIncomingMethodCallProcessing = function() { 39 if (!this.paused_) { 40 return; 41 } 42 this.paused_= false; 43 this.waitToReadMore(); 44 }; 45 46 Connector.prototype.accept = function(message) { 47 if (this.error_) 48 return false; 49 50 if (this.dropWrites_) 51 return true; 52 53 var result = this.handle_.writeMessage( 54 new Uint8Array(message.buffer.arrayBuffer), message.handles); 55 switch (result) { 56 case Mojo.RESULT_OK: 57 // The handles were successfully transferred, so we don't own them 58 // anymore. 59 message.handles = []; 60 break; 61 case Mojo.RESULT_FAILED_PRECONDITION: 62 // There's no point in continuing to write to this pipe since the other 63 // end is gone. Avoid writing any future messages. Hide write failures 64 // from the caller since we'd like them to continue consuming any 65 // backlog of incoming messages before regarding the message pipe as 66 // closed. 67 this.dropWrites_ = true; 68 break; 69 default: 70 // This particular write was rejected, presumably because of bad input. 71 // The pipe is not necessarily in a bad state. 72 return false; 73 } 74 return true; 75 }; 76 77 Connector.prototype.setIncomingReceiver = function(receiver) { 78 this.incomingReceiver_ = receiver; 79 }; 80 81 Connector.prototype.setErrorHandler = function(handler) { 82 this.errorHandler_ = handler; 83 }; 84 85 Connector.prototype.readMore_ = function(result) { 86 for (;;) { 87 if (this.paused_) { 88 return; 89 } 90 91 var read = this.handle_.readMessage(); 92 if (this.handle_ == null) // The connector has been closed. 93 return; 94 if (read.result == Mojo.RESULT_SHOULD_WAIT) 95 return; 96 if (read.result != Mojo.RESULT_OK) { 97 this.handleError(read.result !== Mojo.RESULT_FAILED_PRECONDITION, 98 false); 99 return; 100 } 101 var messageBuffer = new internal.Buffer(read.buffer); 102 var message = new internal.Message(messageBuffer, read.handles); 103 var receiverResult = this.incomingReceiver_ && 104 this.incomingReceiver_.accept(message); 105 106 // Dispatching the message may have closed the connector. 107 if (this.handle_ == null) 108 return; 109 110 // Handle invalid incoming message. 111 if (!internal.isTestingMode() && !receiverResult) { 112 // TODO(yzshen): Consider notifying the embedder. 113 this.handleError(true, false); 114 } 115 } 116 }; 117 118 Connector.prototype.cancelWait = function() { 119 if (this.readWatcher_) { 120 this.readWatcher_.cancel(); 121 this.readWatcher_ = null; 122 } 123 }; 124 125 Connector.prototype.waitToReadMore = function() { 126 if (this.handle_) { 127 this.readWatcher_ = this.handle_.watch({readable: true}, 128 this.readMore_.bind(this)); 129 } 130 }; 131 132 Connector.prototype.handleError = function(forcePipeReset, 133 forceAsyncHandler) { 134 if (this.error_ || this.handle_ === null) { 135 return; 136 } 137 138 if (this.paused_) { 139 // Enforce calling the error handler asynchronously if the user has 140 // paused receiving messages. We need to wait until the user starts 141 // receiving messages again. 142 forceAsyncHandler = true; 143 } 144 145 if (!forcePipeReset && forceAsyncHandler) { 146 forcePipeReset = true; 147 } 148 149 this.cancelWait(); 150 if (forcePipeReset) { 151 this.handle_.close(); 152 var dummyPipe = Mojo.createMessagePipe(); 153 this.handle_ = dummyPipe.handle0; 154 } 155 156 if (forceAsyncHandler) { 157 if (!this.paused_) { 158 this.waitToReadMore(); 159 } 160 } else { 161 this.error_ = true; 162 if (this.errorHandler_) { 163 this.errorHandler_.onError(); 164 } 165 } 166 }; 167 168 internal.Connector = Connector; 169 })(); 170