blob: 0eb51327abd30c53a3638a168c33193069393a4e [file] [log] [blame]
/*
* weinre is available under *either* the terms of the modified BSD license *or* the
* MIT License (2008). See http://opensource.org/licenses/alphabetical for full text.
*
* Copyright (c) 2010, 2011 IBM Corporation
*/
requireClass ./Ex
requireClass ./Weinre
requireClass ./EventListeners
requireClass ./Native
//-----------------------------------------------------------------------------
class WebSocketXhr(url)
this.initialize(url)
//-----------------------------------------------------------------------------
init
var XMLHttpRequest = Native.XMLHttpRequest
WebSocketXhr.CONNECTING = 0
WebSocketXhr.OPEN = 1
WebSocketXhr.CLOSING = 2
WebSocketXhr.CLOSED = 3
//-----------------------------------------------------------------------------
method initialize(url)
this.readyState = WebSocketXhr.CONNECTING
this._url = url
this._urlChannel = null
this._queuedSends = []
this._sendInProgress = true
this._listeners = {
open: new EventListeners(),
message: new EventListeners(),
error: new EventListeners(),
close: new EventListeners()
}
this._getChannel()
//-----------------------------------------------------------------------------
method _getChannel
this._xhr(this._url, "POST", "", this._handleXhrResponseGetChannel)
//-----------------------------------------------------------------------------
method _handleXhrResponseGetChannel(xhr)
if (xhr.status != 200) return this._handleXhrResponseError(xhr)
try {
var object = JSON.parse(xhr.responseText)
}
catch (e) {
this._fireEventListeners("error", {message: "non-JSON response from channel open request"})
this.close()
return
}
if (!object.channel) {
this._fireEventListeners("error", {message: "channel open request did not include a channel"})
this.close()
return
}
Weinre.connectorId = "" + object.channel
this._urlChannel = this._url + "/" + object.channel
this.readyState = WebSocketXhr.OPEN
this._fireEventListeners("open", { message: "open" })
this._sendInProgress = false
this._sendQueued()
this._readLoop()
//-----------------------------------------------------------------------------
method _readLoop
if (this.readyState == WebSocketXhr.CLOSED) return
if (this.readyState == WebSocketXhr.CLOSING) return
this._xhr(this._urlChannel, "GET", "", this._handleXhrResponseGet)
//-----------------------------------------------------------------------------
method _handleXhrResponseGet(xhr)
var self = this
if (xhr.status != 200) return this._handleXhrResponseError(xhr)
try {
var datum = JSON.parse(xhr.responseText)
}
catch (e) {
this.readyState = WebSocketXhr.CLOSED
this._fireEventListeners("error", {
message: "non-JSON response from read request"
})
return
}
Native.setTimeout(function() {self._readLoop()}, 0)
datum.forEach(function(data) {
self._fireEventListeners("message", {data: data})
})
//-----------------------------------------------------------------------------
method send(data)
if (typeof data != "string") throw new Ex(arguments, this.constructor.name + "." + this.caller)
this._queuedSends.push(data)
if (this._sendInProgress) return
this._sendQueued();
//-----------------------------------------------------------------------------
method _sendQueued
if (this._queuedSends.length == 0) return
if (this.readyState == WebSocketXhr.CLOSED) return
if (this.readyState == WebSocketXhr.CLOSING) return
datum = JSON.stringify(this._queuedSends)
this._queuedSends = []
this._sendInProgress = true
this._xhr(this._urlChannel, "POST", datum, this._handleXhrResponseSend)
//-----------------------------------------------------------------------------
method _handleXhrResponseSend(xhr)
var httpSocket = this
if (xhr.status != 200) return this._handleXhrResponseError(xhr)
this._sendInProgress = false
Native.setTimeout(function() {httpSocket._sendQueued()}, 0)
//-----------------------------------------------------------------------------
method close
this._sendInProgress = true
this.readyState = WebSocketXhr.CLOSING
this._fireEventListeners("close", {
message: "closing",
wasClean: true
})
this.readyState = WebSocketXhr.CLOSED
//-----------------------------------------------------------------------------
method addEventListener(type, listener, useCapture)
this._getListeners(type).add(listener, useCapture)
//-----------------------------------------------------------------------------
method removeEventListener(type, listener, useCapture)
this._getListeners(type).remove(listener, useCapture)
//-----------------------------------------------------------------------------
method _fireEventListeners(type, event)
if (this.readyState == WebSocketXhr.CLOSED) return
event.target = this
this._getListeners(type).fire(event)
//-----------------------------------------------------------------------------
method _getListeners(type)
var listeners = this._listeners[type]
if (null == listeners) throw new Ex(arguments, "invalid event listener type: '" + type + "'")
return listeners
//-----------------------------------------------------------------------------
method _handleXhrResponseError(xhr)
if (xhr.status == 404) {
this.close()
return
}
this._fireEventListeners("error", {
target: this,
status: xhr.status,
message: "error from XHR invocation: " + xhr.statusText
})
Weinre.logError("error from XHR invocation: " + xhr.status + ": " + xhr.statusText)
//-----------------------------------------------------------------------------
method _xhr(url, method, data, handler)
if (null == handler) throw new Ex(arguments, "handler must not be null")
var xhr = new XMLHttpRequest()
xhr.httpSocket = this
xhr.httpSocketHandler = handler
xhr.onreadystatechange = _xhrEventHandler
xhr.open(method, url, true)
xhr.setRequestHeader("Content-Type", "text/plain")
xhr.send(data)
//-----------------------------------------------------------------------------
function _xhrEventHandler(event)
var xhr = event.target
if (xhr.readyState != 4) return
xhr.httpSocketHandler.call(xhr.httpSocket, xhr)