mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
293 lines
9.8 KiB
Python
293 lines
9.8 KiB
Python
# ***** BEGIN LICENSE BLOCK *****
|
|
# Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
#
|
|
# The contents of this file are subject to the Mozilla Public License Version
|
|
# 1.1 (the "License"); you may not use this file except in compliance with
|
|
# the License. You may obtain a copy of the License at
|
|
# http://www.mozilla.org/MPL/
|
|
#
|
|
# Software distributed under the License is distributed on an "AS IS" basis,
|
|
# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
# for the specific language governing rights and limitations under the
|
|
# License.
|
|
#
|
|
# The Original Code is Mozilla Corporation Code.
|
|
#
|
|
# The Initial Developer of the Original Code is
|
|
# Mikeal Rogers.
|
|
# Portions created by the Initial Developer are Copyright (C) 2008
|
|
# the Initial Developer. All Rights Reserved.
|
|
#
|
|
# Contributor(s):
|
|
# Mikeal Rogers <mikeal.rogers@gmail.com>
|
|
#
|
|
# Alternatively, the contents of this file may be used under the terms of
|
|
# either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
# in which case the provisions of the GPL or the LGPL are applicable instead
|
|
# of those above. If you wish to allow use of your version of this file only
|
|
# under the terms of either the GPL or the LGPL, and not to allow others to
|
|
# use your version of this file under the terms of the MPL, indicate your
|
|
# decision by deleting the provisions above and replace them with the notice
|
|
# and other provisions required by the GPL or the LGPL. If you do not delete
|
|
# the provisions above, a recipient may use your version of this file under
|
|
# the terms of any one of the MPL, the GPL or the LGPL.
|
|
#
|
|
# ***** END LICENSE BLOCK *****
|
|
|
|
import asyncore
|
|
import socket
|
|
import select
|
|
import logging
|
|
import uuid
|
|
from time import sleep
|
|
from threading import Thread
|
|
|
|
try:
|
|
import json as simplejson
|
|
except:
|
|
import simplejson
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
class JavaScriptException(Exception): pass
|
|
|
|
class Telnet(object, asyncore.dispatcher):
|
|
def __init__(self, host, port):
|
|
self.host, self.port = host, port
|
|
asyncore.dispatcher.__init__(self)
|
|
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
|
|
self.connect((host, port))
|
|
self.buffer = ''
|
|
self.logger = logger
|
|
|
|
def __del__(self):
|
|
self.close()
|
|
|
|
def handle_close(self):
|
|
self.close()
|
|
|
|
def handle_expt(self): self.close() # connection failed, shutdown
|
|
|
|
def writable(self):
|
|
return (len(self.buffer) > 0)
|
|
|
|
def handle_write(self):
|
|
sent = self.send(self.buffer)
|
|
self.buffer = self.buffer[sent:]
|
|
|
|
def send(self, b):
|
|
asyncore.dispatcher.send(self, b)
|
|
|
|
def read_all(self):
|
|
import socket
|
|
data = ''
|
|
while 1:
|
|
try:
|
|
data += self.recv(4096)
|
|
except socket.error:
|
|
return data
|
|
|
|
def handle_read(self):
|
|
self.data = self.read_all()
|
|
self.process_read(self.data)
|
|
|
|
read_callback = lambda self, data: None
|
|
|
|
decoder = simplejson.JSONDecoder()
|
|
|
|
try:
|
|
from json.encoder import encode_basestring_ascii, encode_basestring
|
|
except:
|
|
from simplejson.encoder import encode_basestring_ascii, encode_basestring
|
|
|
|
class JSObjectEncoder(simplejson.JSONEncoder):
|
|
"""Encoder that supports jsobject references by name."""
|
|
|
|
def encode(self, o):
|
|
import jsobjects
|
|
if isinstance(o, jsobjects.JSObject):
|
|
return o._name_
|
|
else:
|
|
return simplejson.JSONEncoder.encode(self, o)
|
|
|
|
def _iterencode(self, o, markers=None):
|
|
import jsobjects
|
|
if isinstance(o, jsobjects.JSObject):
|
|
yield o._name_
|
|
elif isinstance(o, basestring):
|
|
if self.ensure_ascii:
|
|
encoder = encode_basestring_ascii
|
|
else:
|
|
encoder = encode_basestring
|
|
_encoding = self.encoding
|
|
if (_encoding is not None and isinstance(o, str)
|
|
and not (_encoding == 'utf-8')):
|
|
o = o.decode(_encoding)
|
|
yield encoder(o)
|
|
elif o is None:
|
|
yield 'null'
|
|
elif o is True:
|
|
yield 'true'
|
|
elif o is False:
|
|
yield 'false'
|
|
elif isinstance(o, (int, long)):
|
|
yield str(o)
|
|
elif isinstance(o, float):
|
|
yield getattr(simplejson.encoder, 'floatstr', simplejson.encoder._floatstr)(o, self.allow_nan)
|
|
elif isinstance(o, (list, tuple)):
|
|
for chunk in self._iterencode_list(o, markers):
|
|
yield chunk
|
|
elif isinstance(o, dict):
|
|
for chunk in self._iterencode_dict(o, markers):
|
|
yield chunk
|
|
else:
|
|
if markers is not None:
|
|
markerid = id(o)
|
|
if markerid in markers:
|
|
raise ValueError("Circular reference detected")
|
|
markers[markerid] = o
|
|
for chunk in self._iterencode_default(o, markers):
|
|
yield chunk
|
|
if markers is not None:
|
|
del markers[markerid]
|
|
|
|
encoder = JSObjectEncoder()
|
|
|
|
class JSBridgeDisconnectError(Exception): pass
|
|
|
|
class Bridge(Telnet):
|
|
|
|
trashes = []
|
|
reading = False
|
|
sbuffer = ''
|
|
events_list = []
|
|
|
|
callbacks = {}
|
|
|
|
bridge_type = "bridge"
|
|
|
|
registered = False
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
Telnet.__init__(self, *args, **kwargs)
|
|
sleep(.1)
|
|
self.connect(args)
|
|
|
|
def handle_connect(self):
|
|
self.register()
|
|
|
|
def run(self, _uuid, exec_string, interval=.2, raise_exeption=True):
|
|
exec_string += '\r\n'
|
|
self.send(exec_string)
|
|
|
|
while _uuid not in self.callbacks.keys():
|
|
sleep(interval)
|
|
try:
|
|
self.send('')
|
|
except socket.error:
|
|
raise JSBridgeDisconnectError("Connected disconnected.")
|
|
|
|
callback = self.callbacks.pop(_uuid)
|
|
if callback['result'] is False and raise_exeption is True:
|
|
raise JavaScriptException(callback['exception'])
|
|
return callback
|
|
|
|
def register(self):
|
|
_uuid = str(uuid.uuid1())
|
|
self.send('bridge.register("'+_uuid+'", "'+self.bridge_type+'")\r\n')
|
|
self.registered = True
|
|
|
|
def execFunction(self, func_name, args, interval=.25):
|
|
_uuid = str(uuid.uuid1())
|
|
exec_args = [encoder.encode(_uuid), func_name, encoder.encode(args)]
|
|
return self.run(_uuid, 'bridge.execFunction('+ ', '.join(exec_args)+')', interval)
|
|
|
|
def setAttribute(self, obj_name, name, value):
|
|
_uuid = str(uuid.uuid1())
|
|
exec_args = [encoder.encode(_uuid), obj_name, encoder.encode(name), encoder.encode(value)]
|
|
return self.run(_uuid, 'bridge.setAttribute('+', '.join(exec_args)+')')
|
|
|
|
def set(self, obj_name):
|
|
_uuid = str(uuid.uuid1())
|
|
return self.run(_uuid, 'bridge.set('+', '.join([encoder.encode(_uuid), obj_name])+')')
|
|
|
|
def describe(self, obj_name):
|
|
_uuid = str(uuid.uuid1())
|
|
return self.run(_uuid, 'bridge.describe('+', '.join([encoder.encode(_uuid), obj_name])+')')
|
|
|
|
def fire_callbacks(self, obj):
|
|
self.callbacks[obj['uuid']] = obj
|
|
|
|
def process_read(self, data):
|
|
"""Parse out json objects and fire callbacks."""
|
|
self.sbuffer += data
|
|
self.reading = True
|
|
self.parsing = True
|
|
while self.parsing:
|
|
# Remove erroneus data in front of callback object
|
|
index = self.sbuffer.find('{')
|
|
if index is not -1 and index is not 0:
|
|
self.sbuffer = self.sbuffer[index:]
|
|
# Try to get a json object from the data stream
|
|
try:
|
|
obj, index = decoder.raw_decode(self.sbuffer)
|
|
except Exception, e:
|
|
self.parsing = False
|
|
# If we got an object fire the callback infra
|
|
if self.parsing:
|
|
self.fire_callbacks(obj)
|
|
self.sbuffer = self.sbuffer[index:]
|
|
|
|
class BackChannel(Bridge):
|
|
|
|
bridge_type = "backchannel"
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super(BackChannel, self).__init__(*args, **kwargs)
|
|
self.uuid_listener_index = {}
|
|
self.event_listener_index = {}
|
|
self.global_listeners = []
|
|
|
|
def fire_callbacks(self, obj):
|
|
"""Handle all callback fireing on json objects pulled from the data stream."""
|
|
self.fire_event(**dict([(str(key), value,) for key, value in obj.items()]))
|
|
|
|
def add_listener(self, callback, uuid=None, eventType=None):
|
|
if uuid is not None:
|
|
self.uuid_listener_index.setdefault(uuid, []).append(callback)
|
|
if eventType is not None:
|
|
self.event_listener_index.setdefault(eventType, []).append(callback)
|
|
|
|
def add_global_listener(self, callback):
|
|
self.global_listeners.append(callback)
|
|
|
|
def fire_event(self, eventType=None, uuid=None, result=None, exception=None):
|
|
event = eventType
|
|
if uuid is not None and self.uuid_listener_index.has_key(uuid):
|
|
for callback in self.uuid_listener_index[uuid]:
|
|
callback(result)
|
|
if event is not None and self.event_listener_index.has_key(event):
|
|
for callback in self.event_listener_index[event]:
|
|
callback(result)
|
|
for listener in self.global_listeners:
|
|
listener(eventType, result)
|
|
|
|
thread = None
|
|
|
|
def create_network(hostname, port):
|
|
back_channel = BackChannel(hostname, port)
|
|
bridge = Bridge(hostname, port)
|
|
global thread
|
|
if not thread or not thread.isAlive():
|
|
def do():
|
|
try: asyncore.loop(use_poll=True)
|
|
except select.error:pass
|
|
|
|
thread = Thread(target=do)
|
|
getattr(thread, 'setDaemon', lambda x : None)(True)
|
|
thread.start()
|
|
|
|
return back_channel, bridge
|