mirror of
https://github.com/novnc/noVNC.git
synced 2026-06-06 20:39:39 +00:00
Import web-socket-js: a0fb3933ce5c824bcb882f5a1cf87e46de773ea8
web-socket-js is a flash based WebSockets emulator. From: http://github.com/gimite/web-socket-js
This commit is contained in:
254
include/web-socket-js/flash-src/WebSocket.as
Normal file
254
include/web-socket-js/flash-src/WebSocket.as
Normal file
@@ -0,0 +1,254 @@
|
||||
// Copyright: Hiroshi Ichikawa <http://gimite.net/en/>
|
||||
// Lincense: New BSD Lincense
|
||||
// Reference: http://dev.w3.org/html5/websockets/
|
||||
// Reference: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-31
|
||||
|
||||
package {
|
||||
|
||||
import flash.display.*;
|
||||
import flash.events.*;
|
||||
import flash.external.*;
|
||||
import flash.net.*;
|
||||
import flash.system.*;
|
||||
import flash.utils.*;
|
||||
import mx.core.*;
|
||||
import mx.controls.*;
|
||||
import mx.events.*;
|
||||
import mx.utils.*;
|
||||
import com.adobe.net.proxies.RFC2817Socket;
|
||||
|
||||
[Event(name="message", type="WebSocketMessageEvent")]
|
||||
[Event(name="open", type="flash.events.Event")]
|
||||
[Event(name="close", type="flash.events.Event")]
|
||||
[Event(name="stateChange", type="WebSocketStateEvent")]
|
||||
public class WebSocket extends EventDispatcher {
|
||||
|
||||
private static var CONNECTING:int = 0;
|
||||
private static var OPEN:int = 1;
|
||||
private static var CLOSED:int = 2;
|
||||
|
||||
private var socket:RFC2817Socket;
|
||||
private var main:WebSocketMain;
|
||||
private var scheme:String;
|
||||
private var host:String;
|
||||
private var port:uint;
|
||||
private var path:String;
|
||||
private var origin:String;
|
||||
private var protocol:String;
|
||||
private var buffer:ByteArray = new ByteArray();
|
||||
private var headerState:int = 0;
|
||||
private var readyState:int = CONNECTING;
|
||||
private var bufferedAmount:int = 0;
|
||||
private var headers:String;
|
||||
|
||||
public function WebSocket(
|
||||
main:WebSocketMain, url:String, protocol:String,
|
||||
proxyHost:String = null, proxyPort:int = 0,
|
||||
headers:String = null) {
|
||||
this.main = main;
|
||||
var m:Array = url.match(/^(\w+):\/\/([^\/:]+)(:(\d+))?(\/.*)?$/);
|
||||
if (!m) main.fatal("invalid url: " + url);
|
||||
this.scheme = m[1];
|
||||
this.host = m[2];
|
||||
this.port = parseInt(m[4] || "80");
|
||||
this.path = m[5] || "/";
|
||||
this.origin = main.getOrigin();
|
||||
this.protocol = protocol;
|
||||
// if present and not the empty string, headers MUST end with \r\n
|
||||
// headers should be zero or more complete lines, for example
|
||||
// "Header1: xxx\r\nHeader2: yyyy\r\n"
|
||||
this.headers = headers;
|
||||
|
||||
socket = new RFC2817Socket();
|
||||
|
||||
// if no proxy information is supplied, it acts like a normal Socket
|
||||
// @see RFC2817Socket::connect
|
||||
if (proxyHost != null && proxyPort != 0){
|
||||
socket.setProxyInfo(proxyHost, proxyPort);
|
||||
}
|
||||
|
||||
socket.addEventListener(Event.CLOSE, onSocketClose);
|
||||
socket.addEventListener(Event.CONNECT, onSocketConnect);
|
||||
socket.addEventListener(IOErrorEvent.IO_ERROR, onSocketIoError);
|
||||
socket.addEventListener(SecurityErrorEvent.SECURITY_ERROR, onSocketSecurityError);
|
||||
socket.addEventListener(ProgressEvent.SOCKET_DATA, onSocketData);
|
||||
socket.connect(host, port);
|
||||
}
|
||||
|
||||
public function send(data:String):int {
|
||||
if (readyState == OPEN) {
|
||||
socket.writeByte(0x00);
|
||||
socket.writeUTFBytes(data);
|
||||
socket.writeByte(0xff);
|
||||
socket.flush();
|
||||
main.log("sent: " + data);
|
||||
return -1;
|
||||
} else if (readyState == CLOSED) {
|
||||
var bytes:ByteArray = new ByteArray();
|
||||
bytes.writeUTFBytes(data);
|
||||
bufferedAmount += bytes.length; // not sure whether it should include \x00 and \xff
|
||||
// We use return value to let caller know bufferedAmount because we cannot fire
|
||||
// stateChange event here which causes weird error:
|
||||
// > You are trying to call recursively into the Flash Player which is not allowed.
|
||||
return bufferedAmount;
|
||||
} else {
|
||||
main.fatal("invalid state");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public function close():void {
|
||||
main.log("close");
|
||||
try {
|
||||
socket.close();
|
||||
} catch (ex:Error) { }
|
||||
readyState = CLOSED;
|
||||
// We don't fire any events here because it causes weird error:
|
||||
// > You are trying to call recursively into the Flash Player which is not allowed.
|
||||
// We do something equivalent in JavaScript WebSocket#close instead.
|
||||
}
|
||||
|
||||
public function getReadyState():int {
|
||||
return readyState;
|
||||
}
|
||||
|
||||
public function getBufferedAmount():int {
|
||||
return bufferedAmount;
|
||||
}
|
||||
|
||||
private function onSocketConnect(event:Event):void {
|
||||
main.log("connected");
|
||||
var hostValue:String = host + (port == 80 ? "" : ":" + port);
|
||||
var cookie:String = "";
|
||||
if (main.getCallerHost() == host) {
|
||||
cookie = ExternalInterface.call("function(){return document.cookie}");
|
||||
}
|
||||
var opt:String = "";
|
||||
if (protocol) opt += "WebSocket-Protocol: " + protocol + "\r\n";
|
||||
// if caller passes additional headers they must end with "\r\n"
|
||||
if (headers) opt += headers;
|
||||
|
||||
var req:String = StringUtil.substitute(
|
||||
"GET {0} HTTP/1.1\r\n" +
|
||||
"Upgrade: WebSocket\r\n" +
|
||||
"Connection: Upgrade\r\n" +
|
||||
"Host: {1}\r\n" +
|
||||
"Origin: {2}\r\n" +
|
||||
"Cookie: {4}\r\n" +
|
||||
"{3}" +
|
||||
"\r\n",
|
||||
path, hostValue, origin, opt, cookie);
|
||||
main.log("request header:\n" + req);
|
||||
socket.writeUTFBytes(req);
|
||||
socket.flush();
|
||||
}
|
||||
|
||||
private function onSocketClose(event:Event):void {
|
||||
main.log("closed");
|
||||
readyState = CLOSED;
|
||||
notifyStateChange();
|
||||
dispatchEvent(new Event("close"));
|
||||
}
|
||||
|
||||
private function onSocketIoError(event:IOErrorEvent):void {
|
||||
close();
|
||||
main.fatal("failed to connect Web Socket server (IoError)");
|
||||
}
|
||||
|
||||
private function onSocketSecurityError(event:SecurityErrorEvent):void {
|
||||
close();
|
||||
main.fatal(
|
||||
"failed to connect Web Socket server (SecurityError)\n" +
|
||||
"make sure the server is running and Flash socket policy file is correctly placed");
|
||||
}
|
||||
|
||||
private function onSocketData(event:ProgressEvent):void {
|
||||
var pos:int = buffer.length;
|
||||
socket.readBytes(buffer, pos);
|
||||
for (; pos < buffer.length; ++pos) {
|
||||
if (headerState != 4) {
|
||||
// try to find "\r\n\r\n"
|
||||
if ((headerState == 0 || headerState == 2) && buffer[pos] == 0x0d) {
|
||||
++headerState;
|
||||
} else if ((headerState == 1 || headerState == 3) && buffer[pos] == 0x0a) {
|
||||
++headerState;
|
||||
} else {
|
||||
headerState = 0;
|
||||
}
|
||||
if (headerState == 4) {
|
||||
var headerStr:String = buffer.readUTFBytes(pos + 1);
|
||||
main.log("response header:\n" + headerStr);
|
||||
validateHeader(headerStr);
|
||||
makeBufferCompact();
|
||||
pos = -1;
|
||||
readyState = OPEN;
|
||||
notifyStateChange();
|
||||
dispatchEvent(new Event("open"));
|
||||
}
|
||||
} else {
|
||||
if (buffer[pos] == 0xff) {
|
||||
if (buffer.readByte() != 0x00) {
|
||||
close();
|
||||
main.fatal("data must start with \\x00");
|
||||
}
|
||||
var data:String = buffer.readUTFBytes(pos - 1);
|
||||
main.log("received: " + data);
|
||||
dispatchEvent(new WebSocketMessageEvent("message", encodeURIComponent(data)));
|
||||
buffer.readByte();
|
||||
makeBufferCompact();
|
||||
pos = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function validateHeader(headerStr:String):void {
|
||||
var lines:Array = headerStr.split(/\r\n/);
|
||||
if (!lines[0].match(/^HTTP\/1.1 101 /)) {
|
||||
close();
|
||||
main.fatal("bad response: " + lines[0]);
|
||||
}
|
||||
var header:Object = {};
|
||||
for (var i:int = 1; i < lines.length; ++i) {
|
||||
if (lines[i].length == 0) continue;
|
||||
var m:Array = lines[i].match(/^(\S+): (.*)$/);
|
||||
if (!m) {
|
||||
close();
|
||||
main.fatal("failed to parse response header line: " + lines[i]);
|
||||
}
|
||||
header[m[1]] = m[2];
|
||||
}
|
||||
if (header["Upgrade"] != "WebSocket") {
|
||||
close();
|
||||
main.fatal("invalid Upgrade: " + header["Upgrade"]);
|
||||
}
|
||||
if (header["Connection"] != "Upgrade") {
|
||||
close();
|
||||
main.fatal("invalid Connection: " + header["Connection"]);
|
||||
}
|
||||
var resOrigin:String = header["WebSocket-Origin"].toLowerCase();
|
||||
if (resOrigin != origin) {
|
||||
close();
|
||||
main.fatal("origin doesn't match: '" + resOrigin + "' != '" + origin + "'");
|
||||
}
|
||||
if (protocol && header["WebSocket-Protocol"] != protocol) {
|
||||
close();
|
||||
main.fatal("protocol doesn't match: '" +
|
||||
header["WebSocket-Protocol"] + "' != '" + protocol + "'");
|
||||
}
|
||||
}
|
||||
|
||||
private function makeBufferCompact():void {
|
||||
if (buffer.position == 0) return;
|
||||
var nextBuffer:ByteArray = new ByteArray();
|
||||
buffer.readBytes(nextBuffer);
|
||||
buffer = nextBuffer;
|
||||
}
|
||||
|
||||
private function notifyStateChange():void {
|
||||
dispatchEvent(new WebSocketStateEvent("stateChange", readyState, bufferedAmount));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
83
include/web-socket-js/flash-src/WebSocketMain.as
Normal file
83
include/web-socket-js/flash-src/WebSocketMain.as
Normal file
@@ -0,0 +1,83 @@
|
||||
// Copyright: Hiroshi Ichikawa <http://gimite.net/en/>
|
||||
// Lincense: New BSD Lincense
|
||||
// Reference: http://dev.w3.org/html5/websockets/
|
||||
// Reference: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-31
|
||||
|
||||
package {
|
||||
|
||||
import flash.display.*;
|
||||
import flash.events.*;
|
||||
import flash.external.*;
|
||||
import flash.net.*;
|
||||
import flash.system.*;
|
||||
import flash.utils.*;
|
||||
import mx.core.*;
|
||||
import mx.controls.*;
|
||||
import mx.events.*;
|
||||
import mx.utils.*;
|
||||
import bridge.FABridge;
|
||||
|
||||
public class WebSocketMain extends Sprite {
|
||||
|
||||
private var policyLoaded:Boolean = false;
|
||||
private var callerUrl:String;
|
||||
|
||||
public function WebSocketMain() {
|
||||
|
||||
// This is to avoid "You are trying to call recursively into the Flash Player ..."
|
||||
// error which (I heard) happens when you pass bunch of messages.
|
||||
// This workaround was written here:
|
||||
// http://www.themorphicgroup.com/blog/2009/02/14/fabridge-error-you-are-trying-to-call-recursively-into-the-flash-player-which-is-not-allowed/
|
||||
FABridge.EventsToCallLater["flash.events::Event"] = "true";
|
||||
FABridge.EventsToCallLater["WebSocketMessageEvent"] = "true";
|
||||
FABridge.EventsToCallLater["WebSocketStateEvent"] = "true";
|
||||
|
||||
var fab:FABridge = new FABridge();
|
||||
fab.rootObject = this;
|
||||
//log("Flash initialized");
|
||||
|
||||
}
|
||||
|
||||
public function setCallerUrl(url:String):void {
|
||||
callerUrl = url;
|
||||
}
|
||||
|
||||
public function create(
|
||||
url:String, protocol:String,
|
||||
proxyHost:String = null, proxyPort:int = 0,
|
||||
headers:String = null):WebSocket {
|
||||
loadPolicyFile(null);
|
||||
return new WebSocket(this, url, protocol, proxyHost, proxyPort, headers);
|
||||
}
|
||||
|
||||
public function getOrigin():String {
|
||||
return (URLUtil.getProtocol(this.callerUrl) + "://" +
|
||||
URLUtil.getServerNameWithPort(this.callerUrl)).toLowerCase();
|
||||
}
|
||||
|
||||
public function getCallerHost():String {
|
||||
return URLUtil.getServerName(this.callerUrl);
|
||||
}
|
||||
|
||||
public function loadPolicyFile(url:String):void {
|
||||
if (policyLoaded && !url) return;
|
||||
if (!url) {
|
||||
url = "xmlsocket://" + URLUtil.getServerName(this.callerUrl) + ":843";
|
||||
}
|
||||
log("policy file: " + url);
|
||||
Security.loadPolicyFile(url);
|
||||
policyLoaded = true;
|
||||
}
|
||||
|
||||
public function log(message:String):void {
|
||||
ExternalInterface.call("webSocketLog", encodeURIComponent("[WebSocket] " + message));
|
||||
}
|
||||
|
||||
public function fatal(message:String):void {
|
||||
ExternalInterface.call("webSocketError", encodeURIComponent("[WebSocket] " + message));
|
||||
throw message;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
30
include/web-socket-js/flash-src/WebSocketMessageEvent.as
Normal file
30
include/web-socket-js/flash-src/WebSocketMessageEvent.as
Normal file
@@ -0,0 +1,30 @@
|
||||
// Copyright: Hiroshi Ichikawa <http://gimite.net/en/>
|
||||
// Lincense: New BSD Lincense
|
||||
// Reference: http://dev.w3.org/html5/websockets/
|
||||
// Reference: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-31
|
||||
|
||||
package {
|
||||
|
||||
import flash.display.*;
|
||||
import flash.events.*;
|
||||
import flash.external.*;
|
||||
import flash.net.*;
|
||||
import flash.system.*;
|
||||
import flash.utils.*;
|
||||
import mx.core.*;
|
||||
import mx.controls.*;
|
||||
import mx.events.*;
|
||||
import mx.utils.*;
|
||||
|
||||
public class WebSocketMessageEvent extends Event {
|
||||
|
||||
public var data:String;
|
||||
|
||||
public function WebSocketMessageEvent(type:String, data:String) {
|
||||
super(type);
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
32
include/web-socket-js/flash-src/WebSocketStateEvent.as
Normal file
32
include/web-socket-js/flash-src/WebSocketStateEvent.as
Normal file
@@ -0,0 +1,32 @@
|
||||
// Copyright: Hiroshi Ichikawa <http://gimite.net/en/>
|
||||
// Lincense: New BSD Lincense
|
||||
// Reference: http://dev.w3.org/html5/websockets/
|
||||
// Reference: http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-31
|
||||
|
||||
package {
|
||||
|
||||
import flash.display.*;
|
||||
import flash.events.*;
|
||||
import flash.external.*;
|
||||
import flash.net.*;
|
||||
import flash.system.*;
|
||||
import flash.utils.*;
|
||||
import mx.core.*;
|
||||
import mx.controls.*;
|
||||
import mx.events.*;
|
||||
import mx.utils.*;
|
||||
|
||||
public class WebSocketStateEvent extends Event {
|
||||
|
||||
public var readyState:int;
|
||||
public var bufferedAmount:int;
|
||||
|
||||
public function WebSocketStateEvent(type:String, readyState:int, bufferedAmount:int) {
|
||||
super(type);
|
||||
this.readyState = readyState;
|
||||
this.bufferedAmount = bufferedAmount;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
943
include/web-socket-js/flash-src/bridge/FABridge.as
Normal file
943
include/web-socket-js/flash-src/bridge/FABridge.as
Normal file
@@ -0,0 +1,943 @@
|
||||
/*
|
||||
Copyright <20> 2006 Adobe Systems Incorporated
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
|
||||
OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* The Bridge class, responsible for navigating JS instances
|
||||
*/
|
||||
package bridge
|
||||
{
|
||||
|
||||
/*
|
||||
* imports
|
||||
*/
|
||||
import flash.external.ExternalInterface;
|
||||
import flash.utils.Timer;
|
||||
import flash.events.*;
|
||||
import flash.display.DisplayObject;
|
||||
import flash.system.ApplicationDomain;
|
||||
import flash.utils.Dictionary;
|
||||
import flash.utils.setTimeout;
|
||||
|
||||
import mx.collections.errors.ItemPendingError;
|
||||
import mx.core.IMXMLObject;
|
||||
|
||||
import flash.utils.getQualifiedClassName;
|
||||
import flash.utils.describeType;
|
||||
import flash.events.TimerEvent;
|
||||
|
||||
/**
|
||||
* The FABridge class, responsible for proxying AS objects into javascript
|
||||
*/
|
||||
public class FABridge extends EventDispatcher implements IMXMLObject
|
||||
{
|
||||
|
||||
//holds a list of stuff to call later, to break the recurrence of the js <> as calls
|
||||
//you must use the full class name, as returned by the getQualifiedClassName() function
|
||||
public static const MethodsToCallLater:Object = new Object();
|
||||
MethodsToCallLater["mx.collections::ArrayCollection"]="refresh,removeItemAt";
|
||||
|
||||
public static const EventsToCallLater:Object = new Object();
|
||||
EventsToCallLater["mx.data.events::UnresolvedConflictsEvent"]="true";
|
||||
EventsToCallLater["mx.events::PropertyChangeEvent"]="true";
|
||||
|
||||
public static const INITIALIZED:String = "bridgeInitialized";
|
||||
|
||||
// constructor
|
||||
public function FABridge()
|
||||
{
|
||||
super();
|
||||
initializeCallbacks();
|
||||
}
|
||||
|
||||
// private vars
|
||||
|
||||
/**
|
||||
* stores a cache of descriptions of AS types suitable for sending to JS
|
||||
*/
|
||||
private var localTypeMap:Dictionary = new Dictionary();
|
||||
|
||||
/**
|
||||
* stores an id-referenced dictionary of objects exported to JS
|
||||
*/
|
||||
private var localInstanceMap:Dictionary = new Dictionary();
|
||||
|
||||
/**
|
||||
* stores an id-referenced dictionary of functions exported to JS
|
||||
*/
|
||||
private var localFunctionMap:Dictionary = new Dictionary();
|
||||
|
||||
/**
|
||||
* stores an id-referenced dictionary of proxy functions imported from JS
|
||||
*/
|
||||
private var remoteFunctionCache:Dictionary = new Dictionary();
|
||||
|
||||
/**
|
||||
* stores a list of custom serialization functions
|
||||
*/
|
||||
private var customSerializersMap:Dictionary = new Dictionary();
|
||||
|
||||
/**
|
||||
* stores a map of object ID's and their reference count
|
||||
*/
|
||||
private var refMap:Dictionary = new Dictionary();
|
||||
/**
|
||||
* a local counter for generating unique IDs
|
||||
*/
|
||||
private var nextID:Number = 0;
|
||||
|
||||
private var lastRef:int;
|
||||
|
||||
/* values that can't be serialized natively across the bridge are packed and identified by type.
|
||||
These constants represent different serialization types */
|
||||
public static const TYPE_ASINSTANCE:uint = 1;
|
||||
public static const TYPE_ASFUNCTION:uint = 2;
|
||||
public static const TYPE_JSFUNCTION:uint = 3;
|
||||
public static const TYPE_ANONYMOUS:uint = 4;
|
||||
|
||||
private var _initChecked:Boolean = false;
|
||||
|
||||
// properties
|
||||
|
||||
//getters and setters for the main component in the swf - the root
|
||||
public function get rootObject():DisplayObject {return _rootObject;}
|
||||
public function set rootObject(value:DisplayObject):void
|
||||
{
|
||||
_rootObject = value;
|
||||
checkInitialized();
|
||||
}
|
||||
|
||||
/**
|
||||
* the bridge name
|
||||
*/
|
||||
public var bridgeName:String;
|
||||
private var _registerComplete:Boolean = false;
|
||||
|
||||
/**
|
||||
* increment the reference count for an object being passed over the bridge
|
||||
*/
|
||||
public function incRef(objId:int):void
|
||||
{
|
||||
if(refMap[objId] == null) {
|
||||
//the object is being created; we now add it to the map and set its refCount = 1
|
||||
refMap[objId] = 1;
|
||||
} else {
|
||||
refMap[objId] = refMap[objId] +1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* when an object has been completely passed to JS its reference count is decreased with 1
|
||||
*/
|
||||
public function releaseRef(objId:int):void
|
||||
{
|
||||
if(refMap[objId] != null)
|
||||
{
|
||||
var newRefVal:int = refMap[objId] - 1;
|
||||
// if the object exists in the referenceMap and its count equals or has dropped under 0 we clean it up
|
||||
if(refMap[objId] != null && newRefVal <= 0)
|
||||
{
|
||||
delete refMap[objId];
|
||||
delete localInstanceMap[objId];
|
||||
}
|
||||
else
|
||||
{
|
||||
refMap[objId] = newRefVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* attaches the callbacks to external interface
|
||||
*/
|
||||
public function initializeCallbacks():void
|
||||
{
|
||||
if (ExternalInterface.available == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ExternalInterface.addCallback("getRoot", js_getRoot);
|
||||
ExternalInterface.addCallback("getPropFromAS", js_getPropFromAS);
|
||||
ExternalInterface.addCallback("setPropInAS", js_setPropertyInAS);
|
||||
ExternalInterface.addCallback("invokeASMethod", js_invokeMethod);
|
||||
ExternalInterface.addCallback("invokeASFunction", js_invokeFunction);
|
||||
ExternalInterface.addCallback("releaseASObjects", js_releaseASObjects);
|
||||
ExternalInterface.addCallback("create", js_create);
|
||||
ExternalInterface.addCallback("releaseNamedASObject",js_releaseNamedASObject);
|
||||
ExternalInterface.addCallback("incRef", incRef);
|
||||
ExternalInterface.addCallback("releaseRef", releaseRef);
|
||||
}
|
||||
|
||||
private var _rootObject:DisplayObject;
|
||||
|
||||
private var _document:DisplayObject;
|
||||
|
||||
/**
|
||||
* called to check whether the bridge has been initialized for the specified document/id pairs
|
||||
*/
|
||||
public function initialized(document:Object, id:String):void
|
||||
{
|
||||
_document = (document as DisplayObject);
|
||||
|
||||
if (_document != null)
|
||||
{
|
||||
checkInitialized();
|
||||
}
|
||||
}
|
||||
|
||||
private function get baseObject():DisplayObject
|
||||
{
|
||||
return (rootObject == null)? _document:rootObject;
|
||||
}
|
||||
|
||||
|
||||
private function checkInitialized():void
|
||||
{
|
||||
if(_initChecked== true)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_initChecked = true;
|
||||
|
||||
// oops! timing error. Player team is working on it.
|
||||
var t:Timer = new Timer(200,1);
|
||||
t.addEventListener(TimerEvent.TIMER,auxCheckInitialized);
|
||||
t.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* auxiliary initialization check that is called after the timing has occurred
|
||||
*/
|
||||
private function auxCheckInitialized(e:Event):void
|
||||
{
|
||||
|
||||
var bCanGetParams:Boolean = true;
|
||||
|
||||
try
|
||||
{
|
||||
var params:Object = baseObject.root.loaderInfo.parameters;
|
||||
}
|
||||
catch (e:Error)
|
||||
{
|
||||
bCanGetParams = false;
|
||||
}
|
||||
|
||||
if (bCanGetParams == false)
|
||||
{
|
||||
var t:Timer = new Timer(100);
|
||||
var timerFunc:Function = function(e:TimerEvent):void
|
||||
{
|
||||
if(baseObject.root != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
bCanGetParams = true;
|
||||
var params:Object = baseObject.root.loaderInfo.parameters;
|
||||
}
|
||||
catch (err:Error)
|
||||
{
|
||||
bCanGetParams = false;
|
||||
}
|
||||
if (bCanGetParams)
|
||||
{
|
||||
t.removeEventListener(TimerEvent.TIMER, timerFunc);
|
||||
t.stop();
|
||||
dispatchInit();
|
||||
}
|
||||
}
|
||||
}
|
||||
t.addEventListener(TimerEvent.TIMER, timerFunc);
|
||||
t.start();
|
||||
}
|
||||
else
|
||||
{
|
||||
dispatchInit();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* call into JS to annunce that the bridge is ready to be used
|
||||
*/
|
||||
private function dispatchInit(e:Event = null):void
|
||||
{
|
||||
if(_registerComplete == true)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (ExternalInterface.available == false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (bridgeName == null)
|
||||
{
|
||||
bridgeName = baseObject.root.loaderInfo.parameters["bridgeName"];
|
||||
|
||||
if(bridgeName == null)
|
||||
{
|
||||
bridgeName = "flash";
|
||||
}
|
||||
}
|
||||
|
||||
_registerComplete = ExternalInterface.call("FABridge__bridgeInitialized", [bridgeName]);
|
||||
dispatchEvent(new Event(FABridge.INITIALIZED));
|
||||
}
|
||||
|
||||
// serialization/deserialization
|
||||
|
||||
/** serializes a value for transfer across the bridge. primitive types are left as is. Arrays are left as arrays, but individual
|
||||
* values in the array are serialized according to their type. Functions and class instances are inserted into a hash table and sent
|
||||
* across as keys into the table.
|
||||
*
|
||||
* For class instances, if the instance has been sent before, only its id is passed. If This is the first time the instance has been sent,
|
||||
* a ref descriptor is sent associating the id with a type string. If this is the first time any instance of that type has been sent
|
||||
* across, a descriptor indicating methods, properties, and variables of the type is also sent across
|
||||
*/
|
||||
public function serialize(value:*, keep_refs:Boolean=false):*
|
||||
{
|
||||
var result:* = {};
|
||||
result.newTypes = [];
|
||||
result.newRefs = {};
|
||||
|
||||
if (value is Number || value is Boolean || value is String || value == null || value == undefined || value is int || value is uint)
|
||||
{
|
||||
result = value;
|
||||
}
|
||||
else if (value is Array)
|
||||
{
|
||||
result = [];
|
||||
for(var i:int = 0; i < value.length; i++)
|
||||
{
|
||||
result[i] = serialize(value[i], keep_refs);
|
||||
}
|
||||
}
|
||||
else if (value is Function)
|
||||
{
|
||||
// serialize a class
|
||||
result.type = TYPE_ASFUNCTION;
|
||||
result.value = getFunctionID(value, true);
|
||||
}
|
||||
else if (getQualifiedClassName(value) == "Object")
|
||||
{
|
||||
result.type = TYPE_ANONYMOUS;
|
||||
result.value = value;
|
||||
}
|
||||
else
|
||||
{
|
||||
// serialize a class
|
||||
result.type = TYPE_ASINSTANCE;
|
||||
// make sure the type info is available
|
||||
var className:String = getQualifiedClassName(value);
|
||||
|
||||
var serializer:Function = customSerializersMap[className];
|
||||
|
||||
// try looking up the serializer under an alternate name
|
||||
if (serializer == null)
|
||||
{
|
||||
if (className.indexOf('$') > 0)
|
||||
{
|
||||
var split:int = className.lastIndexOf(':');
|
||||
if (split > 0)
|
||||
{
|
||||
var alternate:String = className.substring(split+1);
|
||||
serializer = customSerializersMap[alternate];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (serializer != null)
|
||||
{
|
||||
return serializer.apply(null, [value, keep_refs]);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (retrieveCachedTypeDescription(className, false) == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
result.newTypes.push(retrieveCachedTypeDescription(className, true));
|
||||
}
|
||||
catch(err:Error)
|
||||
{
|
||||
var interfaceInfo:XMLList = describeType(value).implementsInterface;
|
||||
for each (var interf:XML in interfaceInfo)
|
||||
{
|
||||
className = interf.@type.toString();
|
||||
if (retrieveCachedTypeDescription(className, false) == null){
|
||||
result.newTypes.push(retrieveCachedTypeDescription(className, true));
|
||||
} //end if push new data type
|
||||
|
||||
} //end for going through interfaces
|
||||
var baseClass:String = describeType(value).@base.toString();
|
||||
if (retrieveCachedTypeDescription(baseClass, false) == null){
|
||||
result.newTypes.push(retrieveCachedTypeDescription(baseClass, true));
|
||||
} //end if push new data type
|
||||
}
|
||||
}
|
||||
|
||||
// make sure the reference is known
|
||||
var objRef:Number = getRef(value, false);
|
||||
var should_keep_ref:Boolean = false;
|
||||
if (isNaN(objRef))
|
||||
{
|
||||
//create the reference if necessary
|
||||
objRef = getRef(value, true);
|
||||
should_keep_ref = true;
|
||||
}
|
||||
|
||||
result.newRefs[objRef] = className;
|
||||
//trace("serializing new reference: " + className + " with value" + value);
|
||||
|
||||
//the result is a getProperty / invokeMethod call. How can we know how much you will need the object ?
|
||||
if (keep_refs && should_keep_ref) {
|
||||
incRef(objRef);
|
||||
}
|
||||
result.value = objRef;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* deserializes a value passed in from javascript. See serialize for details on how values are packed and
|
||||
* unpacked for transfer across the bridge.
|
||||
*/
|
||||
public function deserialize(valuePackage:*):*
|
||||
{
|
||||
var result:*;
|
||||
if (valuePackage is Number || valuePackage is Boolean || valuePackage is String || valuePackage === null || valuePackage === undefined || valuePackage is int || valuePackage is uint)
|
||||
{
|
||||
result = valuePackage;
|
||||
}
|
||||
else if(valuePackage is Array)
|
||||
{
|
||||
result = [];
|
||||
for (var i:int = 0; i < valuePackage.length; i++)
|
||||
{
|
||||
result[i] = deserialize(valuePackage[i]);
|
||||
}
|
||||
}
|
||||
else if (valuePackage.type == FABridge.TYPE_JSFUNCTION)
|
||||
{
|
||||
result = getRemoteFunctionProxy(valuePackage.value, true);
|
||||
}
|
||||
else if (valuePackage.type == FABridge.TYPE_ASFUNCTION)
|
||||
{
|
||||
throw new Error("as functions can't be passed back to as yet");
|
||||
}
|
||||
else if (valuePackage.type == FABridge.TYPE_ASINSTANCE)
|
||||
{
|
||||
result = resolveRef(valuePackage.value);
|
||||
}
|
||||
else if (valuePackage.type == FABridge.TYPE_ANONYMOUS)
|
||||
{
|
||||
result = valuePackage.value;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public function addCustomSerialization(className:String, serializationFunction:Function):void
|
||||
{
|
||||
customSerializersMap[className] = serializationFunction;
|
||||
}
|
||||
|
||||
|
||||
// type management
|
||||
|
||||
/**
|
||||
* retrieves a type description for the type indicated by className, building one and caching it if necessary
|
||||
*/
|
||||
public function retrieveCachedTypeDescription(className:String, createifNecessary:Boolean):Object
|
||||
{
|
||||
if(localTypeMap[className] == null && createifNecessary == true)
|
||||
{
|
||||
localTypeMap[className] = buildTypeDescription(className);
|
||||
}
|
||||
return localTypeMap[className];
|
||||
}
|
||||
|
||||
public function addCachedTypeDescription(className:String, desc:Object):Object
|
||||
{
|
||||
if (localTypeMap[className] == null)
|
||||
{
|
||||
localTypeMap[className] = desc;
|
||||
}
|
||||
return localTypeMap[className];
|
||||
}
|
||||
|
||||
/**
|
||||
* builds a type description for the type indiciated by className
|
||||
*/
|
||||
public function buildTypeDescription(className:String):Object
|
||||
{
|
||||
var desc:Object = {};
|
||||
|
||||
className = className.replace(/::/,".");
|
||||
|
||||
var objClass:Class = Class(ApplicationDomain.currentDomain.getDefinition(className));
|
||||
|
||||
var xData:XML = describeType(objClass);
|
||||
|
||||
desc.name = xData.@name.toString();
|
||||
|
||||
var methods:Array = [];
|
||||
var xMethods:XMLList = xData.factory.method;
|
||||
for (var i:int = 0; i < xMethods.length(); i++)
|
||||
{
|
||||
methods.push(xMethods[i].@name.toString());
|
||||
}
|
||||
desc.methods = methods;
|
||||
|
||||
var accessors:Array = [];
|
||||
var xAcc:XMLList = xData.factory.accessor;
|
||||
for (i = 0; i < xAcc.length(); i++)
|
||||
{
|
||||
accessors.push(xAcc[i].@name.toString());
|
||||
}
|
||||
xAcc = xData.factory.variable;
|
||||
for (i = 0; i < xAcc.length(); i++)
|
||||
{
|
||||
accessors.push(xAcc[i].@name.toString());
|
||||
}
|
||||
desc.accessors = accessors;
|
||||
|
||||
return desc;
|
||||
}
|
||||
|
||||
// instance mgmt
|
||||
|
||||
/**
|
||||
* resolves an instance id passed from JS to an instance previously cached for representing in JS
|
||||
*/
|
||||
private function resolveRef(objRef:Number):Object
|
||||
{
|
||||
try
|
||||
{
|
||||
return (objRef == -1)? baseObject : localInstanceMap[objRef];
|
||||
}
|
||||
catch(e:Error)
|
||||
{
|
||||
return serialize("__FLASHERROR__"+"||"+e.message);
|
||||
}
|
||||
|
||||
return (objRef == -1)? baseObject : localInstanceMap[objRef];
|
||||
}
|
||||
|
||||
/**
|
||||
* returns an id associated with the object provided for passing across the bridge to JS
|
||||
*/
|
||||
public function getRef(obj:Object, createIfNecessary:Boolean):Number
|
||||
{
|
||||
try
|
||||
{
|
||||
var ref:Number;
|
||||
|
||||
if (createIfNecessary)
|
||||
{
|
||||
var newRef:Number = nextID++;
|
||||
localInstanceMap[newRef] = obj;
|
||||
ref = newRef;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var key:* in localInstanceMap)
|
||||
{
|
||||
if (localInstanceMap[key] === obj)
|
||||
{
|
||||
ref = key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(e:Error)
|
||||
{
|
||||
return serialize("__FLASHERROR__"+"||"+e.message)
|
||||
}
|
||||
|
||||
return ref;
|
||||
}
|
||||
|
||||
|
||||
// function management
|
||||
|
||||
/**
|
||||
* resolves a function ID passed from JS to a local function previously cached for representation in JS
|
||||
*/
|
||||
private function resolveFunctionID(funcID:Number):Function
|
||||
{
|
||||
return localFunctionMap[funcID];
|
||||
}
|
||||
|
||||
/**
|
||||
* associates a unique ID with a local function suitable for passing across the bridge to proxy in Javascript
|
||||
*/
|
||||
public function getFunctionID(f:Function, createIfNecessary:Boolean):Number
|
||||
{
|
||||
var ref:Number;
|
||||
|
||||
if (createIfNecessary)
|
||||
{
|
||||
var newID:Number = nextID++;
|
||||
localFunctionMap[newID] = f;
|
||||
ref = newID;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var key:* in localFunctionMap)
|
||||
{
|
||||
if (localFunctionMap[key] === f) {
|
||||
ref = key;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ref;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns a proxy function that represents a function defined in javascript. This function can be called syncrhonously, and will
|
||||
* return any values returned by the JS function
|
||||
*/
|
||||
public function getRemoteFunctionProxy(functionID:Number, createIfNecessary:Boolean):Function
|
||||
{
|
||||
try
|
||||
{
|
||||
if (remoteFunctionCache[functionID] == null)
|
||||
{
|
||||
remoteFunctionCache[functionID] = function(...args):*
|
||||
{
|
||||
var externalArgs:Array = args.concat();
|
||||
externalArgs.unshift(functionID);
|
||||
var serializedArgs:* = serialize(externalArgs, true);
|
||||
|
||||
if(checkToThrowLater(serializedArgs[1]))
|
||||
{
|
||||
setTimeout(function a():* {
|
||||
try {
|
||||
var retVal:* = ExternalInterface.call("FABridge__invokeJSFunction", serializedArgs);
|
||||
for(var i:int = 0; i<serializedArgs.length; i++)
|
||||
{
|
||||
if(typeof(serializedArgs[i]) == "object" && serializedArgs[i]!=null)
|
||||
{
|
||||
releaseRef(serializedArgs[i].value);
|
||||
}
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
catch(e:Error)
|
||||
{
|
||||
return serialize("__FLASHERROR__"+"||"+e.message);
|
||||
}
|
||||
},1);
|
||||
}
|
||||
else
|
||||
{
|
||||
var retVal:* = ExternalInterface.call("FABridge__invokeJSFunction", serializedArgs);
|
||||
for(var i:int = 0; i<serializedArgs.length; i++)
|
||||
{
|
||||
if(typeof(serializedArgs[i]) == "object" && serializedArgs[i]!=null)
|
||||
{
|
||||
releaseRef(serializedArgs[i].value);
|
||||
}
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch(e:Error)
|
||||
{
|
||||
return serialize("__FLASHERROR__"+"||"+e.message);
|
||||
}
|
||||
|
||||
return remoteFunctionCache[functionID];
|
||||
}
|
||||
|
||||
/**
|
||||
* function that checks if the object on which we are working demands that it should be called at a later time, breaking the call chain
|
||||
* we check the actual object, as well as the bsae class and interfaces
|
||||
*/
|
||||
private function checkToThrowLater(obj:Object):Boolean
|
||||
{
|
||||
obj = resolveRef(obj.value);
|
||||
var className:String = getQualifiedClassName(obj);
|
||||
var classInfo:XML = describeType(obj);
|
||||
|
||||
if (FABridge.EventsToCallLater[className] != null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//check if this class doesn't inherit from one of the entries in the table
|
||||
var inheritanceInfo:XMLList = describeType(obj).extendsClass;
|
||||
for each (var inherit:XML in inheritanceInfo)
|
||||
{
|
||||
className = inherit.@type.toString();
|
||||
if (FABridge.EventsToCallLater[className] != null) {
|
||||
return true;
|
||||
}
|
||||
} //end for going through inheritance tree
|
||||
|
||||
//if we're still here, check the interfaces as well
|
||||
|
||||
var interfaceInfo:XMLList = describeType(obj).implementsInterface;
|
||||
for each (var interf:XML in interfaceInfo)
|
||||
{
|
||||
className = interf.@type.toString();
|
||||
if (FABridge.EventsToCallLater[className] != null) {
|
||||
return true;
|
||||
}
|
||||
} //end for going through inheritance tree
|
||||
|
||||
//if nothing was found, return false, so the function gets executed
|
||||
return false;
|
||||
}
|
||||
|
||||
// callbacks exposed to JS
|
||||
|
||||
/**
|
||||
* called to fetch a named property off the instanced associated with objID
|
||||
*/
|
||||
public function js_getPropFromAS(objID:Number, propName:String):*
|
||||
{
|
||||
incRef(objID);
|
||||
try
|
||||
{
|
||||
var obj:Object = resolveRef(objID);
|
||||
var ret:* = serialize(obj[propName], true);
|
||||
releaseRef(objID);
|
||||
return ret;
|
||||
}
|
||||
catch (e:ItemPendingError)
|
||||
{
|
||||
releaseRef(objID);
|
||||
//ItemPendingError
|
||||
//return serialize("an error occcured with" + obj[propName]);
|
||||
}
|
||||
catch(e:Error)
|
||||
{
|
||||
releaseRef(objID);
|
||||
return serialize("__FLASHERROR__" + "||" + e.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* called to set a named property on the instance associated with objID
|
||||
*/
|
||||
private function js_setPropertyInAS(objID:Number, propRef:String, value:*):*
|
||||
{
|
||||
incRef(objID);
|
||||
try {
|
||||
var obj:Object = resolveRef(objID);
|
||||
obj[propRef] = deserialize(value);
|
||||
releaseRef(objID);
|
||||
}
|
||||
catch(e:Error)
|
||||
{
|
||||
releaseRef(objID);
|
||||
return serialize("__FLASHERROR__" + "||" + e.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* accessor for retrieveing a proxy to the root object from JS
|
||||
*/
|
||||
private function js_getRoot():*
|
||||
{
|
||||
try
|
||||
{
|
||||
//always get the root; this is the same as the get property, only it is the root object
|
||||
var objRef:Number = getRef(baseObject, false);
|
||||
if (isNaN(objRef))
|
||||
{
|
||||
//create the reference if necessary
|
||||
objRef = getRef(baseObject, true);
|
||||
incRef(objRef);
|
||||
}
|
||||
return serialize(baseObject);
|
||||
}
|
||||
catch(e:Error)
|
||||
{
|
||||
return serialize("__FLASHERROR__"+"||"+e.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* called to invoke a function or closure associated with funcID
|
||||
*/
|
||||
private function js_invokeFunction(funcID:Number, args:Object):*
|
||||
{
|
||||
var result:*;
|
||||
try
|
||||
{
|
||||
var func:Function = resolveFunctionID(funcID);
|
||||
if(func != null)
|
||||
result = func.apply(null, deserialize(args));
|
||||
|
||||
return serialize(result, true);
|
||||
}
|
||||
catch(e:Error)
|
||||
{
|
||||
return serialize("__FLASHERROR__"+"||"+e.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* called to invoke a named method on the object associated with objID
|
||||
*/
|
||||
private function js_invokeMethod(objID:Number, methodName:String, args:Object):*
|
||||
{
|
||||
incRef(objID);
|
||||
try
|
||||
{
|
||||
var obj:Object = resolveRef(objID);
|
||||
var result:*;
|
||||
|
||||
//check if the method is callable right now, or later
|
||||
var callLater:Boolean = checkToExecuteLater(obj, methodName);
|
||||
|
||||
if (callLater) {
|
||||
var t:Timer = new Timer(200, 1);
|
||||
t.addEventListener(TimerEvent.TIMER, function():void {
|
||||
var ret_inner:* = serialize(obj[methodName].apply(null, deserialize(args)), true);
|
||||
releaseRef(objID);
|
||||
});
|
||||
t.start();
|
||||
} else {
|
||||
var ret:* = serialize(obj[methodName].apply(null, deserialize(args)), true);
|
||||
releaseRef(objID);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
catch (e:ItemPendingError)
|
||||
{
|
||||
releaseRef(objID);
|
||||
// ignore ItemPendingError
|
||||
}
|
||||
catch(e:Error)
|
||||
{
|
||||
releaseRef(objID);
|
||||
return serialize("__FLASHERROR__" + "||" + e.message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* method that performs a check on the specified object and method to see if their execution should be delayed or not
|
||||
* it checks the object, its base class and implemented interfaces
|
||||
*/
|
||||
private function checkToExecuteLater(obj:Object, methodName:String):Boolean
|
||||
{
|
||||
var methods:String;
|
||||
var className:String = getQualifiedClassName(obj);
|
||||
var classInfo:XML = describeType(obj);
|
||||
|
||||
if (FABridge.MethodsToCallLater[className] != null) {
|
||||
methods = FABridge.MethodsToCallLater[className];
|
||||
//must call later
|
||||
if(methods.match(methodName))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
//check if this class doesn't inherit from one of the entries in the table
|
||||
var inheritanceInfo:XMLList = describeType(obj).extendsClass;
|
||||
for each (var inherit:XML in inheritanceInfo)
|
||||
{
|
||||
className = inherit.@type.toString();
|
||||
if (FABridge.MethodsToCallLater[className] != null) {
|
||||
methods = FABridge.MethodsToCallLater[className];
|
||||
//must call later
|
||||
if(methods.match(methodName))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} //end for going through inheritance tree
|
||||
|
||||
//if we're still here, check the interfaces as well
|
||||
|
||||
var interfaceInfo:XMLList = describeType(obj).implementsInterface;
|
||||
for each (var interf:XML in interfaceInfo)
|
||||
{
|
||||
className = interf.@type.toString();
|
||||
if (FABridge.MethodsToCallLater[className] != null) {
|
||||
methods = FABridge.MethodsToCallLater[className];
|
||||
//must call later
|
||||
if(methods.match(methodName))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} //end for going through inheritance tree
|
||||
|
||||
//if nothing was found, return false, so the function gets executed
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* callback from JS to release all AS Objects from the local cache maps
|
||||
*/
|
||||
private function js_releaseASObjects():void
|
||||
{
|
||||
localTypeMap = new Dictionary();
|
||||
localInstanceMap = new Dictionary();
|
||||
localFunctionMap = new Dictionary();
|
||||
}
|
||||
|
||||
/**
|
||||
* callback from JS to release a specific object, identified by its ID
|
||||
*/
|
||||
private function js_releaseNamedASObject(objId:int):Boolean
|
||||
{
|
||||
var retVal:Boolean = false;
|
||||
if (localInstanceMap[objId] != null)
|
||||
{
|
||||
delete refMap[objId];
|
||||
delete localInstanceMap[objId];
|
||||
retVal = true;
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* callback for js to create a new class instance.
|
||||
*/
|
||||
|
||||
private function js_create(className:String):*
|
||||
{
|
||||
try
|
||||
{
|
||||
var c:Class = Class(ApplicationDomain.currentDomain.getDefinition(className));
|
||||
var instance:Object = new c();
|
||||
}
|
||||
catch(e:Error)
|
||||
{
|
||||
return serialize("__FLASHERROR__" + "||" + e.message);
|
||||
}
|
||||
|
||||
// make sure the reference is known
|
||||
var objRef:Number = getRef(instance, true);
|
||||
incRef(objRef);
|
||||
return serialize(instance);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
Adobe Systems Incorporated(r) Source Code License Agreement
|
||||
Copyright(c) 2005 Adobe Systems Incorporated. All rights reserved.
|
||||
|
||||
Please read this Source Code License Agreement carefully before using
|
||||
the source code.
|
||||
|
||||
Adobe Systems Incorporated grants to you a perpetual, worldwide, non-exclusive,
|
||||
no-charge, royalty-free, irrevocable copyright license, to reproduce,
|
||||
prepare derivative works of, publicly display, publicly perform, and
|
||||
distribute this source code and such derivative works in source or
|
||||
object code form without any attribution requirements.
|
||||
|
||||
The name "Adobe Systems Incorporated" must not be used to endorse or promote products
|
||||
derived from the source code without prior written permission.
|
||||
|
||||
You agree to indemnify, hold harmless and defend Adobe Systems Incorporated from and
|
||||
against any loss, damage, claims or lawsuits, including attorney's
|
||||
fees that arise or result from your use or distribution of the source
|
||||
code.
|
||||
|
||||
THIS SOURCE CODE IS PROVIDED "AS IS" AND "WITH ALL FAULTS", WITHOUT
|
||||
ANY TECHNICAL SUPPORT OR ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
|
||||
BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
FOR A PARTICULAR PURPOSE ARE DISCLAIMED. ALSO, THERE IS NO WARRANTY OF
|
||||
NON-INFRINGEMENT, TITLE OR QUIET ENJOYMENT. IN NO EVENT SHALL MACROMEDIA
|
||||
OR ITS SUPPLIERS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOURCE CODE, EVEN IF
|
||||
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
package com.adobe.net.proxies
|
||||
{
|
||||
import flash.events.Event;
|
||||
import flash.events.IOErrorEvent;
|
||||
import flash.events.ProgressEvent;
|
||||
import flash.net.Socket;
|
||||
|
||||
/**
|
||||
* This class allows TCP socket connections through HTTP proxies in accordance with
|
||||
* RFC 2817:
|
||||
*
|
||||
* ftp://ftp.rfc-editor.org/in-notes/rfc2817.txt
|
||||
*
|
||||
* It can also be used to make direct connections to a destination, as well. If you
|
||||
* pass the host and port into the constructor, no proxy will be used. You can also
|
||||
* call connect, passing in the host and the port, and if you didn't set the proxy
|
||||
* info, a direct connection will be made. A proxy is only used after you have called
|
||||
* the setProxyInfo function.
|
||||
*
|
||||
* The connection to and negotiation with the proxy is completely hidden. All the
|
||||
* same events are thrown whether you are using a proxy or not, and the data you
|
||||
* receive from the target server will look exact as it would if you were connected
|
||||
* to it directly rather than through a proxy.
|
||||
*
|
||||
* @author Christian Cantrell
|
||||
*
|
||||
**/
|
||||
public class RFC2817Socket
|
||||
extends Socket
|
||||
{
|
||||
private var proxyHost:String = null;
|
||||
private var host:String = null;
|
||||
private var proxyPort:int = 0;
|
||||
private var port:int = 0;
|
||||
private var deferredEventHandlers:Object = new Object();
|
||||
private var buffer:String = new String();
|
||||
|
||||
/**
|
||||
* Construct a new RFC2817Socket object. If you pass in the host and the port,
|
||||
* no proxy will be used. If you want to use a proxy, instantiate with no
|
||||
* arguments, call setProxyInfo, then call connect.
|
||||
**/
|
||||
public function RFC2817Socket(host:String = null, port:int = 0)
|
||||
{
|
||||
if (host != null && port != 0)
|
||||
{
|
||||
super(host, port);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the proxy host and port number. Your connection will only proxied if
|
||||
* this function has been called.
|
||||
**/
|
||||
public function setProxyInfo(host:String, port:int):void
|
||||
{
|
||||
this.proxyHost = host;
|
||||
this.proxyPort = port;
|
||||
|
||||
var deferredSocketDataHandler:Object = this.deferredEventHandlers[ProgressEvent.SOCKET_DATA];
|
||||
var deferredConnectHandler:Object = this.deferredEventHandlers[Event.CONNECT];
|
||||
|
||||
if (deferredSocketDataHandler != null)
|
||||
{
|
||||
super.removeEventListener(ProgressEvent.SOCKET_DATA, deferredSocketDataHandler.listener, deferredSocketDataHandler.useCapture);
|
||||
}
|
||||
|
||||
if (deferredConnectHandler != null)
|
||||
{
|
||||
super.removeEventListener(Event.CONNECT, deferredConnectHandler.listener, deferredConnectHandler.useCapture);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Connect to the specified host over the specified port. If you want your
|
||||
* connection proxied, call the setProxyInfo function first.
|
||||
**/
|
||||
public override function connect(host:String, port:int):void
|
||||
{
|
||||
if (this.proxyHost == null)
|
||||
{
|
||||
this.redirectConnectEvent();
|
||||
this.redirectSocketDataEvent();
|
||||
super.connect(host, port);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
super.addEventListener(Event.CONNECT, this.onConnect);
|
||||
super.addEventListener(ProgressEvent.SOCKET_DATA, this.onSocketData);
|
||||
super.connect(this.proxyHost, this.proxyPort);
|
||||
}
|
||||
}
|
||||
|
||||
private function onConnect(event:Event):void
|
||||
{
|
||||
this.writeUTFBytes("CONNECT "+this.host+":"+this.port+" HTTP/1.1\n\n");
|
||||
this.flush();
|
||||
this.redirectConnectEvent();
|
||||
}
|
||||
|
||||
private function onSocketData(event:ProgressEvent):void
|
||||
{
|
||||
while (this.bytesAvailable != 0)
|
||||
{
|
||||
this.buffer += this.readUTFBytes(1);
|
||||
if (this.buffer.search(/\r?\n\r?\n$/) != -1)
|
||||
{
|
||||
this.checkResponse(event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function checkResponse(event:ProgressEvent):void
|
||||
{
|
||||
var responseCode:String = this.buffer.substr(this.buffer.indexOf(" ")+1, 3);
|
||||
|
||||
if (responseCode.search(/^2/) == -1)
|
||||
{
|
||||
var ioError:IOErrorEvent = new IOErrorEvent(IOErrorEvent.IO_ERROR);
|
||||
ioError.text = "Error connecting to the proxy ["+this.proxyHost+"] on port ["+this.proxyPort+"]: " + this.buffer;
|
||||
this.dispatchEvent(ioError);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.redirectSocketDataEvent();
|
||||
this.dispatchEvent(new Event(Event.CONNECT));
|
||||
if (this.bytesAvailable > 0)
|
||||
{
|
||||
this.dispatchEvent(event);
|
||||
}
|
||||
}
|
||||
this.buffer = null;
|
||||
}
|
||||
|
||||
private function redirectConnectEvent():void
|
||||
{
|
||||
super.removeEventListener(Event.CONNECT, onConnect);
|
||||
var deferredEventHandler:Object = this.deferredEventHandlers[Event.CONNECT];
|
||||
if (deferredEventHandler != null)
|
||||
{
|
||||
super.addEventListener(Event.CONNECT, deferredEventHandler.listener, deferredEventHandler.useCapture, deferredEventHandler.priority, deferredEventHandler.useWeakReference);
|
||||
}
|
||||
}
|
||||
|
||||
private function redirectSocketDataEvent():void
|
||||
{
|
||||
super.removeEventListener(ProgressEvent.SOCKET_DATA, onSocketData);
|
||||
var deferredEventHandler:Object = this.deferredEventHandlers[ProgressEvent.SOCKET_DATA];
|
||||
if (deferredEventHandler != null)
|
||||
{
|
||||
super.addEventListener(ProgressEvent.SOCKET_DATA, deferredEventHandler.listener, deferredEventHandler.useCapture, deferredEventHandler.priority, deferredEventHandler.useWeakReference);
|
||||
}
|
||||
}
|
||||
|
||||
public override function addEventListener(type:String, listener:Function, useCapture:Boolean = false, priority:int=0.0, useWeakReference:Boolean=false):void
|
||||
{
|
||||
if (type == Event.CONNECT || type == ProgressEvent.SOCKET_DATA)
|
||||
{
|
||||
this.deferredEventHandlers[type] = {listener:listener,useCapture:useCapture, priority:priority, useWeakReference:useWeakReference};
|
||||
}
|
||||
else
|
||||
{
|
||||
super.addEventListener(type, listener, useCapture, priority, useWeakReference);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user