text: # MSHTML implementation of pyjd runtime.  Uses ctypes and comtypes to
  # create a Win32 GDI window, embed an IWebBrowser2 OCX and allow access
  # to the DOM functions using Python-DCOM.
  #
  # Copyright (C) 2009, 2010 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
  #
  # get_xml_doc added 2010-09-08,
  # copyright (C) Phil Charlesworth 2010, see Issue 481
  #
  
  #import win32traceutil
  
  import traceback
  import sys
  import os
  import time
  import new
  
  # these three are required pre-imported, for pyjamas to work
  # with the pyjd imputil etc.  awful, i know...
  import threading
  import encodings
  import encodings.cp437
  
  
  from windows import *
  from ctypes import *
  from ctypes.wintypes import *
  
  import comtypes
  from comtypes import IUnknown, GUID, COMMETHOD, CoCreateInstanceEx
  from comtypes.automation import IDispatch, VARIANT
  from comtypes.client import wrap, GetModule
  from comtypes.client.dynamic import Dispatch
  
  import comtypes.gen
  
  if not hasattr(sys, 'frozen'):
      GetModule('shdocvw.dll')
      try:
          GetModule('msxml2.dll')
      except:
          pass
      try:
          GetModule('msxml3.dll')
      except:
          pass
      try:
          GetModule('msxml6.dll')
      except:
          pass
      GetModule('mshtml.tlb')
      #GetModule('progdlg.tlb')
  
  from comtypes.gen import SHDocVw
  from comtypes.gen import MSHTML
  try:
      from comtypes.gen import MSXML2
  except:
      pass
  try:
      from comtypes.gen import MSXML6
  except:
      pass
  try:
      from comtypes.gen import MSXML3
  except:
      pass
  
  atl = windll.atl                  # If this fails, you need atl.dll
  
  # do this after gen stuff, above
  import mshtmlevents
  
  SID_SShellBrowser = GUID("{000214E2-0000-0000-C000-000000000046}")
  
  class IPlayer2(IUnknown):
      _case_insensitive_ = True
      _iid_ = GUID('{6BF52A52-394A-11d3-B153-00C04F79FAA6}')
      _idlflags_ = []
  
      _methods_ = [
          ]
  
  class IOleWindow(IUnknown):
      _case_insensitive_ = True
      u'IOleWindow Interface'
      _iid_ = GUID('{00000114-0000-0000-C000-000000000046}')
      _idlflags_ = []
  
      _methods_ = [
          COMMETHOD([], HRESULT, 'GetWindow',
                    ( ['in'], POINTER(c_void_p), 'pHwnd' ))
          ]
  
  class IOleInPlaceActiveObject(IOleWindow):
      _iid_ = GUID("{00000117-0000-0000-C000-000000000046}")
      _idlflags_ = []
      _methods_ = IOleWindow._methods_ + [
          COMMETHOD([], HRESULT, 'TranslateAccelerator',
                    ( ['in'], POINTER(MSG), 'pMsg' ))
          ]
  
  
  # http://www.mail-archive.com/comtypes-users@lists.sourceforge.net/msg00439.html
  class IServiceProvider(IUnknown):
      _iid_ = GUID('{6D5140C1-7436-11CE-8034-00AA006009FA}')
  
      # Overridden QueryService to make it nicer to use (passing it an
      # interface and it returns a pointer to that interface)
      def QueryService(self, serviceIID, interface):
          p = POINTER(interface)()
          self._QueryService(byref(serviceIID), byref(interface._iid_), byref(p))
          return p
  
      _methods_ = [
          COMMETHOD([], HRESULT, 'QueryService',
                    ( ['in'], POINTER(GUID), 'guidService' ),
                    ( ['in'], POINTER(GUID), 'riid' ),
                    ( ['in'], POINTER(c_void_p), 'ppvObject' ))
          ]
  
  #class IInputObject(IUnknown):
  #    _iid_= GUID("{68284FAA-6A48-11D0-8C78-00C04FD918B4}")
  #
  #    _methods_= IUnknown._methods_ + [
  #        (STDMETHOD (HRESULT, "UIActivateIO", BOOL, POINTER(MSG))),
  #        (STDMETHOD (HRESULT, "HasFocusIO")),
  #        (STDMETHOD (HRESULT, "TranslateAcceleratorIO", POINTER(MSG)))]
  
  class EventSink(object):
      # some DWebBrowserEvents
      def OnVisible(self, this, *args):
          print "OnVisible", args
  
      def BeforeNavigate(self, this, *args):
          print "BeforeNavigate", args
  
      def NavigateComplete(self, this, *args):
          print "NavigateComplete", this, args
          return
  
      # some DWebBrowserEvents2
      def BeforeNavigate2(self, this, *args):
          print "BeforeNavigate2", args
  
      def NavigateComplete2(self, this, *args):
          print "NavigateComplete2", args
  
      def DocumentComplete(self, this, *args):
          print "DocumentComplete", args
          if self.workaround_ignore_first_doc_complete == False:
              # ignore first about:blank.  *sigh*...
              # TODO: work out how to parse *args byref VARIANT
              # in order to get at the URI.
              self.workaround_ignore_first_doc_complete = True
              return
  
          self._loaded()
  
      def NewWindow2(self, this, *args):
          print "NewWindow2", args
          return
          v = cast(args[1]._.c_void_p, POINTER(VARIANT))[0]
          v.value = True
  
      def NewWindow3(self, this, *args):
          print "NewWindow3", args
          return
          v = cast(args[1]._.c_void_p, POINTER(VARIANT))[0]
          v.value = True
  
  fn_txt = """\
  def event_fn(self, *args):
      #print "event %s", self, args
      #print "event callbacks", self._listeners
      callbacks = self._listeners.get('%s', [])
      for fn in callbacks:
          try:
              fn(self._sender, Dispatch(args[0]), True)
          except:
              traceback.print_exc()
              sys.stderr.flush()
  """
  
  class EventCaller:
      def __init__(self, handler, name):
          self.handler = handler
          self.name = name
      def __call__(self, *args):
          callbacks = self.handler._listeners.get(self.name, [])
          #print "event", self.name, callbacks
          for fn in callbacks:
              try:
                  fn(self.handler._sender, Dispatch(args[0]), True)
              except:
                  sys.stderr.write( traceback.print_exc() )
                  sys.stderr.flush()
  
  class EventHandler(object):
      def __init__(self, sender):
          self._sender = sender
          self._listeners = {}
      def __getattr__(self, name):
          if name.startswith('__') and name.endswith('__'):
              raise AttributeError(name)
          #print "EventHandler requested ", name
          if name.startswith('_') or name == 'addEventListener':
              return self.__dict__[name]
          idx = name.find('_on')
          if idx >= 0:
              if idx > 0:
                  name = name[idx+1:]
              #return EventCaller(self, name)
              exec fn_txt % (name[2:], name[2:])
              #exec fn_txt % (name[2:])
              #print event_fn
              return new.instancemethod(event_fn, self)
          raise AttributeError(name)
  
      def addEventListener(self, name, fn):
          if not self._listeners.has_key(name):
              self._listeners[name] = []
          self._listeners[name].append(fn)
  
  class Browser(EventSink):
      def __init__(self, application, appdir):
          EventSink.__init__(self)
          self.platform = 'mshtml'
          self.application = application
          self.appdir = appdir
          self.already_initialised = False
          self.workaround_ignore_first_doc_complete = False
          self.window_handler = None
          self.node_handlers = {}
  
          # Create an instance of IE via AtlAxWin.
          atl.AtlAxWinInit()
          hInstance = GetModuleHandle(None)
  
          self.hwnd = CreateWindowEx(0,
                                "AtlAxWin",
                                "about:blank",
                                WS_OVERLAPPEDWINDOW |
                                WS_VISIBLE |
                                WS_HSCROLL | WS_VSCROLL,
                                CW_USEDEFAULT,
                                CW_USEDEFAULT,
                                CW_USEDEFAULT,
                                CW_USEDEFAULT,
                                NULL,
                                NULL,
                                hInstance,
                                NULL)
  
          # Get the IWebBrowser2 interface for the IE control.
          self.pBrowserUnk = POINTER(IUnknown)()
          atl.AtlAxGetControl(self.hwnd, byref(self.pBrowserUnk))
          # the wrap call querys for the default interface
          self.pBrowser = wrap(self.pBrowserUnk)
          self.pBrowser.RegisterAsBrowser = True
          self.pBrowser.AddRef()
  
          print dir(SHDocVw)
          print SHDocVw.IWebBrowser
          print SHDocVw.IWebBrowser2
  
          print dir(MSHTML)
  
          self.conn = mshtmlevents.GetEvents(self.pBrowser, sink=self,
                          interface=SHDocVw.DWebBrowserEvents2)
  
          #print "browser HWND", SetFocus(self.pBrowser.HWND)
  
      def _alert(self, txt):
          self.getDomWindow().alert(txt)
  
      def load_app(self):
  
          uri = self.application
          if uri.find(":") == -1:
              # assume file
              uri = 'file://'+os.path.abspath(uri)
  
          #print "load_app", uri
  
          self.application = uri
          v = byref(VARIANT())
          self.pBrowser.Navigate(uri, v, v, v, v)
  
          # Show Window
          cw = c_int(self.hwnd)
          ShowWindow(cw, c_int(SW_SHOW))
          UpdateWindow(cw)
  
  
          # http://msdn.microsoft.com/en-us/library/aa752126%28VS.85%29.aspx
          wba = self.pBrowser.QueryInterface(IServiceProvider)
          wn = wrap(wba.QueryService(SID_SShellBrowser, IOleWindow))
  
          hwnd = c_void_p(0)
          pHnd = byref(hwnd)
          wn.GetWindow(pHnd)
  
          #PostMessage(pHnd.value, WM_SETFOCUS,0,0)
          SetFocus(hwnd)
          #print self.hwnd, pHnd.value
  
      def getDomDocument(self):
          return Dispatch(self.pBrowser.Document)
  
      def getDomWindow(self):
          return self.getDomDocument().parentWindow
  
      def _addXMLHttpRequestEventListener(self, node, event_name, event_fn):
  
          #print "_addXMLHttpRequestEventListener", event_name
  
          rcvr = mshtmlevents._DispEventReceiver()
          rcvr.dispmap = {0: event_fn}
  
          #print rcvr
          rcvr.sender = node
          #print rcvr.sender
          ifc = rcvr.QueryInterface(IDispatch)
          #print ifc
          v = VARIANT(ifc)
          #print v
          setattr(node, event_name, v)
          return ifc
  
      def addEventListener(self, node, event_name, event_fn):
  
          rcvr = mshtmlevents._DispEventReceiver()
          rcvr.dispmap = {0: event_fn}
  
          rcvr.sender = node
          ifc = rcvr.QueryInterface(IDispatch)
          v = VARIANT(ifc)
          setattr(node, "on"+event_name, v)
          return ifc
  
          rcvr = mshtmlevents.GetDispEventReceiver(MSHTML.HTMLElementEvents2, event_fn, "on%s" % event_name)
          rcvr.sender = node
          ifc = rcvr.QueryInterface(IDispatch)
          node.attachEvent("on%s" % event_name, ifc)
          return ifc
  
      def mash_attrib(self, attrib_name):
          return attrib_name
  
      def _addWindowEventListener(self, event_name, event_fn, wnd=None):
  
          #print "_addWindowEventListener", event_name, event_fn
          #rcvr = mshtmlevents.GetDispEventReceiver(MSHTML.HTMLWindowEvents,
          #                   event_fn, "on%s" % event_name)
          #print rcvr
          #rcvr.sender = self.getDomWindow()
          #print rcvr.sender
          #ifc = rcvr.QueryInterface(IDispatch)
          #print ifc
          #v = VARIANT(ifc)
          #print v
          #setattr(self.getDomWindow(), "on%s" % event_name, v)
          #return ifc
  
          if wnd is None:
              wnd = self.pBrowser.Document.parentWindow
          if self.window_handler is None:
              self.window_handler = EventHandler(self)
              self.window_conn = mshtmlevents.GetEvents(wnd,
                                          sink=self.window_handler,
                                      interface=MSHTML.HTMLWindowEvents2)
          self.window_handler.addEventListener(event_name, event_fn)
          return event_name # hmmm...
  
      def getXmlHttpRequest(self):
          #print "getXMLHttpRequest"
          o = comtypes.client.CreateObject('MSXML2.XMLHTTP.3.0')
          #print "getXMLHttpRequest", o
          return Dispatch(o)
  
      def getDOMParser(self):
          o = comtypes.client.CreateObject('MSXML.DOMDocument')
          return Dispatch(o)
  
      def getUri(self):
          return self.application
  
      def GetPlayer(self, obj):
          return obj.QueryInterface(IPlayer2)
  
      def CoCreateInstanceEx(self, clsid):
          return CoCreateInstanceEx(GUID(clsid))
  
      def _loaded(self):
  
          #print "loaded"
  
          if self.already_initialised:
              return
          self.already_initialised = True
  
          self._addWindowEventListener("unload", self.on_unload_callback)
  
          from __pyjamas__ import pygwt_processMetas, set_main_frame
          set_main_frame(self)
  
          (pth, app) = os.path.split(self.application)
          if self.appdir:
              pth = os.path.abspath(self.appdir)
          sys.path.append(pth)
  
      def on_unload_callback(self, *args):
          PostQuitMessage(0)
  
  global timer_q
  timer_q = []
  
  WM_USER_TIMER = RegisterWindowMessage("Timer Notify")
  
  global wv
  wv = None
  
  def MainWin(one_event):
  
      # Pump Messages
      msg = MSG()
      pMsg = pointer(msg)
  
      while 1:
          res = GetMessage( pMsg, NULL, 0, 0)
          if res == -1:
              return 0
          if res == 0:
              break
  
          if timer_q:
              fn = timer_q.pop()
              fn()
  
          if msg.message == WM_USER_TIMER:
              continue
  
          app = wv.pBrowser.Application
          ao = app.QueryInterface(IOleInPlaceActiveObject)
          if ao.TranslateAccelerator(pMsg):
          #if not TranslateAccelerator(
          #                wv.hwnd,  #handle to receiving window
          #                NULL,    #handle to active accelerator table
          #                pMsg):     #message data
              TranslateMessage(pMsg)
              DispatchMessage(pMsg)
  
          if one_event:
              break
  
      return msg.wParam
  
  def add_timer_queue(fn):
      timer_q.append(fn)
      PostMessage(c_int(wv.hwnd), UINT(WM_USER_TIMER), WPARAM(0), LPARAM(0xffff))
  
  def is_loaded():
      return wv.already_initialised
  
  def run(one_event=False, block=True):
      try:
          MainWin(one_event) # TODO: ignore block arg for now
      except:
          sys.stderr.write( traceback.print_exc() )
          sys.stderr.flush()
  
  def setup(application, appdir=None, width=800, height=600):
  
      global wv
      wv = Browser(application, appdir)
  
      wv.load_app()
  
      while 1:
          if is_loaded():
              return
          run(one_event=True)
  
  