1 // Copyright (c) 2012 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.
5 // Automation connection handler is responsible for reading requests from the
6 // stream, finding and executing appropriate extension API method.
7 function ConnectionHandler() {
8 // Event listener registration map socket->event->callback
9 this.eventListener_
= {};
12 ConnectionHandler
.prototype = {
13 // Stream delegate callback.
14 onStreamError: function(stream
) {
15 this.unregisterListeners_(stream
);
18 // Stream delegate callback.
19 onStreamTerminated: function(stream
) {
20 this.unregisterListeners_(stream
);
23 // Pairs event |listenerMethod| with a given |stream|.
24 registerListener_: function(stream
, eventName
, eventObject
,
26 if (!this.eventListener_
[stream
.socketId_
])
27 this.eventListener_
[stream
.socketId_
] = {};
29 if (!this.eventListener_
[stream
.socketId_
][eventName
]) {
30 this.eventListener_
[stream
.socketId_
][eventName
] = {
32 'method': listenerMethod
};
36 // Removes event listeners.
37 unregisterListeners_: function(stream
) {
38 if (!this.eventListener_
[stream
.socketId_
])
41 for (var eventName
in this.eventListener_
[stream
.socketId_
]) {
42 var listenerDefinition
= this.eventListener_
[stream
.socketId_
][eventName
];
43 var removeFunction
= listenerDefinition
.event
['removeListener'];
45 removeFunction
.call(listenerDefinition
.event
,
46 listenerDefinition
.method
);
49 delete this.eventListener_
[stream
.socketId_
];
52 // Finds appropriate method/event to invoke/register.
53 findExecutionTarget_: function(functionName
) {
54 var funcSegments
= functionName
.split('.');
55 if (funcSegments
.size
< 2)
58 if (funcSegments
[0] != 'chrome')
62 var prevSegName
= null;
63 var prevSegment
= null;
64 var segmentObject
= null;
66 for (var i
= 0; i
< funcSegments
.length
; i
++) {
71 eventName
+= prevSegName
;
74 segName
= funcSegments
[i
];
75 prevSegName
= segName
;
77 // TODO(zelidrag): Get rid of this eval.
78 segmentObject
= eval(segName
);
82 prevSegment
= segmentObject
;
83 if (segmentObject
[segName
])
84 segmentObject
= segmentObject
[segName
];
88 if (segmentObject
== window
)
91 var isEventMethod
= segName
== 'addListener';
92 return {'method': segmentObject
,
93 'eventName': (isEventMethod
? eventName
: null),
94 'event': (isEventMethod
? prevSegment
: null)};
97 // TODO(zelidrag): Figure out how to automatically detect or generate list of
99 isSyncFunction_: function(funcName
) {
100 if (funcName
== 'chrome.omnibox.setDefaultSuggestion')
106 // Parses |command|, finds appropriate JS method runs it with |argsJson|.
107 // If the method is an event registration, it will register an event listener
108 // method and start sending data from its callback.
109 processCommand_: function(stream
, command
, argsJson
) {
110 var target
= this.findExecutionTarget_(command
);
111 if (!target
|| !target
.method
) {
112 return {'result': false,
113 'objectName': command
};
116 var args
= JSON
.parse(decodeURIComponent(argsJson
));
120 console
.log(command
+ '(' + decodeURIComponent(argsJson
) + ')',
122 // Check if we need to register an event listener.
124 // Register listener method.
125 var listener = function() {
126 stream
.write(JSON
.stringify({ 'type': 'eventCallback',
127 'eventName': target
.eventName
,
128 'arguments' : arguments
}));
130 // Add event handler method to arguments.
132 args
.push(null); // for |filters|.
133 target
.method
.apply(target
.event
, args
);
134 this.registerListener_(stream
, target
.eventName
,
135 target
.event
, listener
);
136 stream
.write(JSON
.stringify({'type': 'eventRegistration',
137 'eventName': command
}));
138 return {'result': true,
142 // Run extension method directly.
143 if (this.isSyncFunction_(command
)) {
145 console
.log(command
+ '(' + unescape(argsJson
) + ')');
146 var result
= target
.method
.apply(undefined, args
);
147 stream
.write(JSON
.stringify({'type': 'methodResult',
148 'methodName': command
,
150 'result' : result
}));
151 } else { // Async method.
152 // Add callback method to arguments.
153 args
.push(function() {
154 stream
.write(JSON
.stringify({'type': 'methodCallback',
155 'methodName': command
,
157 'arguments' : arguments
}));
159 target
.method
.apply(undefined, args
);
161 return {'result': true,
165 arrayBufferToString_: function(buffer
) {
167 var uArrayVal
= new Uint8Array(buffer
);
168 for(var s
= 0; s
< uArrayVal
.length
; s
++) {
169 str
+= String
.fromCharCode(uArrayVal
[s
]);
174 // Callback for stream read requests.
175 onStreamRead_: function(stream
, readInfo
) {
176 console
.log("READ", readInfo
);
177 // Parse the request.
178 var data
= this.arrayBufferToString_(readInfo
.data
);
179 var spacePos
= data
.indexOf(" ");
181 if (spacePos
== -1) {
182 spacePos
= data
.indexOf("\r\n");
184 throw {'code': 400, 'description': 'Bad Request'};
187 var verb
= data
.substring(0, spacePos
);
191 throw {'code': 200, 'description': 'OK'};
199 throw {'code': 400, 'description': 'Bad Request: ' + verb
};
203 var command
= data
.substring(verb
.length
+ 1);
204 var endLine
= command
.indexOf('\r\n');
206 command
= command
.substring(0, endLine
);
208 var objectNames
= command
;
210 var funcNameEnd
= command
.indexOf("?");
211 if (funcNameEnd
>= 0) {
212 objectNames
= command
.substring(0, funcNameEnd
);
213 argsJson
= command
.substring(funcNameEnd
+ 1);
215 var functions
= objectNames
.split(',');
216 for (var i
= 0; i
< functions
.length
; i
++) {
217 var objectName
= functions
[i
];
219 this.processCommand_(stream
, objectName
, argsJson
);
220 if (!commandStatus
.result
) {
222 'description': 'Not Found: ' + commandStatus
.objectName
};
224 // If we have run all requested commands, read the socket again.
225 if (i
== (functions
.length
- 1)) {
226 setTimeout(function() {
227 this.readRequest_(stream
);
232 console
.warn('Error', err
);
233 stream
.writeError(err
.code
, err
.description
);
237 // Reads next request from the |stream|.
238 readRequest_: function(stream
) {
239 console
.log("Reading socket " + stream
.socketId_
);
241 stream
.read(this.onStreamRead_
.bind(this));